How to use CC_MD5 method in swift language - md5

in Objective-C, we can hash a string like this:
const char *cStr = [someString UTF8String];
unsigned char result[16];
CC_MD5( cStr, strlen(cStr), result );
md5String = [NSString stringWithFormat:
#"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
result[0], result[1], result[2], result[3],
result[4], result[5], result[6], result[7],
result[8], result[9], result[10], result[11],
result[12], result[13], result[14], result[15]
];
But CC_MD5 doesn't work in Swift. How do we deal with this?

This is what I came up with. It's an extension to String.
Don't forget to add #import <CommonCrypto/CommonCrypto.h> to the ObjC-Swift bridging header that Xcode creates.
extension String {
var md5: String! {
let str = self.cStringUsingEncoding(NSUTF8StringEncoding)
let strLen = CC_LONG(self.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))
let digestLen = Int(CC_MD5_DIGEST_LENGTH)
let result = UnsafeMutablePointer<CUnsignedChar>.alloc(digestLen)
CC_MD5(str!, strLen, result)
let hash = NSMutableString()
for i in 0..<digestLen {
hash.appendFormat("%02x", result[i])
}
result.dealloc(digestLen)
return String(format: hash as String)
}
}

Here's my version in Swift 3.0, I believe it to be safer and faster than the other answers here.
A bridging header with #import <CommonCrypto/CommonCrypto.h> is required.
func MD5(_ string: String) -> String? {
let length = Int(CC_MD5_DIGEST_LENGTH)
var digest = [UInt8](repeating: 0, count: length)
if let d = string.data(using: String.Encoding.utf8) {
_ = d.withUnsafeBytes { (body: UnsafePointer<UInt8>) in
CC_MD5(body, CC_LONG(d.count), &digest)
}
}
return (0..<length).reduce("") {
$0 + String(format: "%02x", digest[$1])
}
}

I did pure Swift implementation of MD5 as part of CryptoSwift project.
I could copy code here but it uses extensions that are part of this project so it may be useless for copy&paste usage. However you can take a look there and use it.

So here is the Solution and I know It will save your time 100%
BridgingHeader
- > Used to Expose Objective-c code to a Swift Project
CommonCrypto
- > is the file needed to use md5 hash
Since Common Crypto is a Objective-c file, you need to use BridgingHeader to use method needed for hashing
(e.g)
extension String {
func md5() -> String! {
let str = self.cStringUsingEncoding(NSUTF8StringEncoding)
let strLen = CUnsignedInt(self.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))
let digestLen = Int(CC_MD5_DIGEST_LENGTH)
let result = UnsafeMutablePointer<CUnsignedChar>.alloc(digestLen)
CC_MD5(str!, strLen, result)
var hash = NSMutableString()
for i in 0..<digestLen {
hash.appendFormat("%02x", result[i])
}
result.destroy()
return String(format: hash as String)
}
}
How to add Common Crypto into a Swift Project??
This link will teach you how (STEP by STEP).
I recommend using Bridging Header
*************Updated Swift 3****************
extension String {
func toMD5() -> String {
if let messageData = self.data(using:String.Encoding.utf8) {
var digestData = Data(count: Int(CC_MD5_DIGEST_LENGTH))
_ = digestData.withUnsafeMutableBytes {digestBytes in
messageData.withUnsafeBytes {messageBytes in
CC_MD5(messageBytes, CC_LONG((messageData.count)), digestBytes)
}
}
return digestData.hexString()
}
return self
}
}
extension Data {
func hexString() -> String {
let string = self.map{ String($0, radix:16) }.joined()
return string
}
}
How to use?
let stringConvertedToMD5 = "foo".toMD5()

Xcode 6 beta 5 now uses an UnsafeMutablePointer in place of an UnsafePointer. String conversion also requires the format: argument label.
extension String {
func md5() -> String! {
let str = self.cStringUsingEncoding(NSUTF8StringEncoding)
let strLen = CUnsignedInt(self.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))
let digestLen = Int(CC_MD5_DIGEST_LENGTH)
let result = UnsafeMutablePointer<CUnsignedChar>.alloc(digestLen)
CC_MD5(str!, strLen, result)
var hash = NSMutableString()
for i in 0..<digestLen {
hash.appendFormat("%02x", result[i])
}
result.destroy()
return String(format: hash)
}
}

evntually if you want calculate MD5 out of NSData, take a look at this:
func md5() -> NSData {
var ctx = UnsafePointer<CC_MD5_CTX>.alloc(sizeof(CC_MD5_CTX))
CC_MD5_Init(ctx);
CC_MD5_Update(ctx, self.bytes, UInt32(self.length));
let length = Int(CC_MD5_DIGEST_LENGTH) * sizeof(Byte)
var output = UnsafePointer<Byte>.alloc(length)
CC_MD5_Final(output, ctx);
let outData = NSData(bytes: output, length: Int(CC_MD5_DIGEST_LENGTH))
output.destroy()
ctx.destroy()
//withUnsafePointer
return outData;
}
to get idea.

Need import #import <CommonCrypto/CommonCrypto.h> into Bridging Header
I am calculating MD5 hash, but using only the first 16 byte I am using
class func hash(data: NSData) -> String {
let data2 = NSMutableData(length: Int(CC_MD5_DIGEST_LENGTH))!
CC_MD5(data.bytes, CC_LONG(data.length), UnsafeMutablePointer<UInt8>(data2.mutableBytes))
let data3 = UnsafePointer<CUnsignedChar>(data2.bytes)
var hash = ""
for (var i = 0; i < 16; ++i) {
hash += String(format: "%02X", data3[i])
}
return hash
}

For cases where a bridging header isn't an option (eg, in a shell script), you can use the command line tool /sbin/md5 via NSTask:
import Foundation
func md5hash(string: String) -> String
{
let t = NSTask()
t.launchPath = "/sbin/md5"
t.arguments = ["-q", "-s", string]
t.standardOutput = NSPipe()
t.launch()
let outData = t.standardOutput.fileHandleForReading.readDataToEndOfFile()
var outBytes = [UInt8](count:outData.length, repeatedValue:0)
outData.getBytes(&outBytes, length: outData.length)
var outString = String(bytes: outBytes, encoding: NSASCIIStringEncoding)
assert(outString != nil, "failed to md5 input string")
return outString!.stringByTrimmingCharactersInSet(NSCharacterSet.newlineCharacterSet())
}
Usage:
let md5 = md5hash("hello world")
// 5eb63bbbe01eeed093cb22bb8f5acdc3

Here are some changes I had to make to this code to get it working in Swift 5:
func md5(inString: String) -> String! {
let str = inString.cString(using: String.Encoding.utf8)
let strLen = CUnsignedInt(inString.lengthOfBytes(using: String.Encoding.utf8))
let digestLen = Int(CC_MD5_DIGEST_LENGTH)
let result = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: digestLen)
CC_MD5(str!, strLen, result)
var hash = NSMutableString()
for i in 0..<digestLen {
hash.appendFormat("%02x", result[i])
}
result.deallocate()
return String(format: hash as String)
}

Updated for contemporary Swift syntax (Swift 5.6 at the time of writing):
StringProtocol extension (so can be used on strings or substrings).
Allow caller to specify encoding, but default to UTF8.
Move actual MD5/SHA256 calculation into Data extension.
Use UnsafeRawBufferPointer rendition (rather than deprecated UnsafePointer rendition) of withUnsafeBytes and withMutableUnsafeBytes.
Add #availability warning for md5 that Apple has added to CC_MD5, to make sure that developers understand that this is not cryptographically sound.
Thus:
extension StringProtocol {
#available(iOS, deprecated: 13.0, message: "This function is cryptographically broken and should not be used in security contexts. Clients should migrate to SHA256 (or stronger).")
func md5String(encoding: String.Encoding = .utf8) -> String? {
data(using: encoding)?.md5.hexadecimal
}
#available(iOS, deprecated: 13.0, message: "This function is cryptographically broken and should not be used in security contexts. Clients should migrate to SHA256 (or stronger).")
func md5(encoding: String.Encoding = .utf8) -> Data? {
data(using: encoding)?.md5
}
func sha256String(encoding: String.Encoding = .utf8) -> String? {
data(using: encoding)?.sha256.hexadecimal
}
func sha256(encoding: String.Encoding = .utf8) -> Data? {
data(using: encoding)?.sha256
}
}
extension Data {
var hexadecimal: String {
map { String(format: "%02x", $0) }
.joined()
}
#available(iOS, deprecated: 13.0, message: "This function is cryptographically broken and should not be used in security contexts. Clients should migrate to SHA256 (or stronger).")
var md5: Data {
withUnsafeBytes { dataBuffer in
var digest = Data(count: Int(CC_MD5_DIGEST_LENGTH))
digest.withUnsafeMutableBytes { digestBuffer in
_ = CC_MD5(dataBuffer.baseAddress, CC_LONG(dataBuffer.count), digestBuffer.baseAddress)
}
return digest
}
}
var sha256: Data {
withUnsafeBytes { dataBuffer in
var digest = Data(count: Int(CC_SHA256_DIGEST_LENGTH))
digest.withUnsafeMutableBytes { digestBuffer in
_ = CC_SHA256(dataBuffer.baseAddress, CC_LONG(dataBuffer.count), digestBuffer.baseAddress)
}
return digest
}
}
}
Used as follows:
let string = "Hello, World"
guard let result1 = string.md5String() else { return }
// 82bb413746aee42f89dea2b59614f9ef
guard let result2 = string.sha256String() else { return }
// 03675ac53ff9cd1535ccc7dfcdfa2c458c5218371f418dc136f2d19ac1fbe8a5

Related

convert array of string into Double in swift

I'm trying to convert a string into a double in swift. I managed to extract the string from a website (www.x-rates.com) into an array but I cannot convert it after in a double in order to make some work around this number. Can anyone tell me what I'm supposed to do or what I did wrong? I know that my label don't update now but I will do it later, the first thing that I'm trying to do is the conversion.
thx a lot!
Here is the code:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var resultLabel: UILabel!
#IBOutlet weak var moneyTextField: UITextField!
#IBAction func convert(_ sender: Any) {
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let url = URL(string: "https://www.x-rates.com/calculator/?from=EUR&to=USD&amount=1")!
let request = NSMutableURLRequest(url : url)
let task = URLSession.shared.dataTask(with: request as URLRequest) {
data, response, error in
var message = ""
if let error = error {
print(error)
} else {
if let unwrappedData = data {
let dataString = NSString(data: unwrappedData, encoding: String.Encoding.utf8.rawValue)
var stringSeperator = "<span class=\"ccOutputRslt\">"
if let contentArray = dataString?.components(separatedBy: stringSeperator){
if contentArray.count > 0 {
stringSeperator = "<span"
let newContentArray = contentArray[1].components(separatedBy: stringSeperator)
if newContentArray.count > 0 {
message = newContentArray[0]
var message = Float(newContentArray[0])! + 10
}
}
}
}
}
DispatchQueue.main.sync(execute: {
self.resultLabel.text = "the value of the dollar is " + message
}
)}
task.resume()
func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I will talk about convert an Array of String to Array of Double.
In swift Array has a method called map, this is responsable to map the value from array, example, in map function you will receive an object referent to your array, this will convert this object to your new array ex.
let arrOfStrings = ["0.3", "0.4", "0.6"];
let arrOfDoubles = arrOfStrings.map { (value) -> Double in
return Double(value)!
}
The result will be
UPDATE:
#LeoDabus comments an important tip, this example is considering an perfect datasource, but if you have a dynamic source you can put ? on return and it will work, but this will return an array with nil
like that
let arrOfStrings = ["0.3", "0.4", "0.6", "a"];
let arrOfDoubles = arrOfStrings.map { (value) -> Double? in
return Double(value)
}
Look this, the return array has a nil element
If you use the tips from #LeoDabus you will protect this case, but you need understand what do you need in your problem to choose the better option between map or compactMap
example with compactMap
let arrOfStrings = ["0.3", "0.4", "0.6", "a"];
let arrOfDoubles = arrOfStrings.compactMap { (value) -> Double? in
return Double(value)
}
look the result
UPDATE:
After talk with the author (#davidandersson) of issue, this solution with map ou contactMap isn't his problem, I did a modification in his code and work nice.
first I replaced var message = "" per var rateValue:Double = 0.0 and replacedFloattoDouble`
look the final code
let url = URL(string: "https://www.x-rates.com/calculator/?from=EUR&to=USD&amount=1")!
let request = NSMutableURLRequest(url : url)
let task = URLSession.shared.dataTask(with: request as URLRequest) {
data, response, error in
var rateValue:Double = 0.0;
if let error = error {
print(error)
} else {
if let unwrappedData = data {
let dataString = NSString(data: unwrappedData, encoding: String.Encoding.utf8.rawValue)
var stringSeperator = "<span class=\"ccOutputRslt\">"
if let contentArray = dataString?.components(separatedBy: stringSeperator){
if contentArray.count > 0 {
stringSeperator = "<span"
let newContentArray = contentArray[1].components(separatedBy: stringSeperator)
if newContentArray.count > 0 {
rateValue = Double(newContentArray[0])! + 10
}
}
}
}
}
//
print("Rate is \(rateValue)"); //Rate is 11.167
}
task.resume()
Hope to help you
The reason your code doesn’t work in my opinion is that you have two variables with the same name that are defined in different scopes and you use the wrong one at the end.
At the beginning you define
var message = ""
And then when converting to a number further down
var message = Float(newContentArray[0])! + 10
So change the last line to something like
var number = Float(newContentArray[0])! + 10
And use number in your calculations. Although I think
var number = Double(message)
should work equally fine since you have assigned newContentArray[0] to message already and Double is more commonly used than Float (I don’t understand + 10)

Regular Expression Check: return boolean and answers

(Make it short to help others)
check if a string matches to a regex:
text.range(of: regex, options: .regularExpression) == nil
check string selected part matches:
let nsString = self as NSString
let regex = try NSRegularExpression(pattern: pattern)
let allMatches = regex
.matches(in: self, options: [], range: NSRange(location: 0, length: nsString.length))
return allMatches.map { match in
return (1 ..< match.numberOfRanges)
.map { matchIndex in
return nsString.substring(with: match.range(at: matchIndex))
}
}
The first one is easy and we don't actually need NSRegularExpression for that:
extension String {
func hasMatches(_ pattern: String) -> Bool {
return self.range(of: pattern, options: .regularExpression) != nil
}
}
let regex = "(.*) [:] (.*)"
let string = "Tom : how are you?"
print(string.hasMatches(regex))
I would say that we don't even need a utility function for that.
The second is harder to understand, mostly because NSRegularExpression API is not really converted to Swift and it even uses old NSString:
extension String {
func getMatches(_ pattern: String) throws -> [[String]] {
let nsString = self as NSString
let expression = try NSRegularExpression(pattern: pattern)
let matches = expression
.matches(in: self, options: [], range: NSRange(location: 0, length: nsString.length))
return matches.map { match in
let numGroups = match.numberOfRanges
// we are skipping group 0 which contains the pattern itself
return (1 ..< numGroups)
.map { groupIndex in
return nsString.substring(with: match.range(at: groupIndex))
}
}
}
}
print(try! string.getMatches(regex)) // [["Tom", "how are you?"]]
Note that I am returning an array of arrays because the expression can match multiple times.
For example:
let regex = "(\\d+):(\\d+)"
let string = "01:23, 02:34"
print(try! string.getMatches(regex)) // [["01", "23"], ["02", "34"]]

How to encode an array of CGPoints with NSCoder in Swift?

I am trying to save a copy of my custom class to a file, my class has 2 arrays of CGPoints which I append to every so often, they look like this:
class BlockAttributes: NSObject {
var positions:[CGPoint] = []
var spawns:[CGPoint] = []
}
Everything is working great as far as just as using and accessing the class goes, but archiving it does not work. I can archive arrays of Strings, Bools, and Ints just fine in my other classes but my game fails every time I try to use NSCoder to encode my arrays of CGPoints. Here is my code for archiving:
func encodeWithCoder(coder: NSCoder!) {
coder.encodeObject(positions, forKey: "positions")
coder.encodeObject(spawns, forKey: "spawns")
}
....
class ArchiveData: NSObject {
var documentDirectories:NSArray = []
var documentDirectory:String = ""
var path:String = ""
func saveData(data: BlockAttributes) {
documentDirectories = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
documentDirectory = documentDirectories.objectAtIndex(0) as! String
path = documentDirectory.stringByAppendingPathComponent("data.archive")
if NSKeyedArchiver.archiveRootObject(data, toFile: path) {
print("Success writing to file!")
} else {
print("Unable to write to file!")
}
}
func retrieveData() -> NSObject {
var dataToRetrieve = BlockAttributes()
documentDirectories = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
documentDirectory = documentDirectories.objectAtIndex(0) as! String
path = documentDirectory.stringByAppendingPathComponent("data.archive")
if let dataToRetrieve2 = NSKeyedUnarchiver.unarchiveObjectWithFile(path) as? BlockAttributes {
dataToRetrieve = dataToRetrieve2 as BlockAttributes
}
return(dataToRetrieve)
}
}
....
And to save:
let archiveData = ArchiveData()
archiveData.saveData(myBlockActionsObject)
I even tried creating my own custom class to save the CGPoints to, which I call MyCGPoint (I read somewhere on SO that creating custom classes for some data types resolves some NSCoder issues):
class MyCGPoint: NSObject {
var x: CGFloat = 0.0
var y: CGFloat = 0.0
init(X: CGFloat, Y: CGFloat) {
x = X
y = Y
}
override init() {
}
}
....
class BlockAttributes: NSObject {
var positions:[MyCGPoint] = []
var spawns:[MyCGPoint] = []
}
But alas, I am still getting this error:
[Game.MyCGPoint encodeWithCoder:]:
unrecognized selector sent to instance 0x137f1d1a0 Game[20953:5814436]
*** Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: '-[Game.MyCGPoint encodeWithCoder:]:
unrecognized selector sent to instance 0x137f1d1a0'
Any idea how I can use encodeObject to encode my array of CGPoints/MyCGPoints?
You can convert them to and from strings:
//To string
let point = CGPointMake(0, 0)
let string = NSStringFromCGPoint(point)
//Or if you want String instead of NSString
let string = String(point)
//From string
let point2 = CGPointFromString(string)
CGPoint (and its Cocoa's twin NSPoint) are structs, i.e. value type, so you can't encode them directly. Wrap them in NSValue:
let positionValues = positions.map { NSValue(point:$0) }
let spawnValues = spawns.map { NSValue(point:$0) }
coder.encodeObject(positionValues, forKey: "positions")
coder.encodeObject(spawnValues, forKey: "spawns")
// Decode:
positons = (coder.decodeObjectForKey("positions") as! [NSValue]).map { $0.pointValue }
spawns = (coder.decodeObjectForKey("spawns") as! [NSValue]).map { $0.pointValue }
When you write your custom wrapper class, you have to make it compliant with NSCoding too, which NSValeu had already done for you, for free.

Convert array items to string on condition - Swift

I am trying to create a file name from the URL. I have to forcefully remove the first item in the array every time. Like the C# implementation where they check for empty string.
Is it possible to remove the "/" in the array? Is there a better way to implement this?
let url = "https://stackoverflow.com/questions/ask"
var filePathComponents:[String] = []
filePathComponents = assetURL.pathComponents as! [String]
filePathComponents.removeAtIndex(0)
let fileName = "-".join(filePathComponents)
I would recommend
filePathComponents.filter { return $0 != "/" }
You don't have to use mutability if you use range indexing (with ..<).
And to make it convenient to use, let's put it in an extension as a computed property.
Example for String:
extension String {
var pathNameWithoutPrefix: String {
get {
return "-".join(self.pathComponents[2 ..< self.pathComponents.count])
}
}
}
let filePath = "http://stackoverflow.com/questions/ask".pathNameWithoutPrefix
println(filePath) // "questions-ask"
Example for NSURL:
extension NSURL {
var pathURLWithoutPrefix: NSURL? {
get {
if let filePathComponents = self.pathComponents as? [String] {
return NSURL(string: "-".join(filePathComponents[1 ..< filePathComponents.count]))
}
return nil
}
}
}
if let url = NSURL(string: "http://stackoverflow.com/questions/ask"),
let fileURL = url.pathURLWithoutPrefix {
println(fileURL) // "questions-ask"
}
Here's the same extensions for Swift 2:
extension String {
var pathNameWithoutPrefix: String {
get {
let str = self as NSString
return "-".join(str.pathComponents[2 ..< str.pathComponents.count])
}
}
}
extension NSURL {
var pathURLWithoutPrefix: NSURL? {
get {
if let filePathComponents = self.pathComponents {
return NSURL(string: "-".join(filePathComponents[1 ..< filePathComponents.count]))
}
return nil
}
}
}
let linkURL = NSURL(string: "http://stackoverflow.com/questions/ask")!
if let comps = linkURL.pathComponents as? [String] {
let fileName = "-".join(dropFirst(comps)) // "questions-ask"
}
Swift 2.0
let linkURL = NSURL(string: "http://stackoverflow.com/questions/ask")!
if let comps = linkURL.pathComponents?.dropFirst() {
let fileName = "-".join(comps) // "questions-ask"
}
Different approach using the host and path properties of NSURL
if let assetURL = NSURL(string: "http://stackoverflow.com/questions/ask") {
let fileNameWithHost = assetURL.host!.stringByAppendingPathComponent(assetURL.path!) // "stackoverflow.com/questions/ask"
let fileNameWithoutHost = assetURL.path!.substringFromIndex(assetURL.path!.startIndex.successor()) // "questions/ask"
}

Iterate over two arrays simultaneously

I am new to Swift. I have been doing Java programming. I have a scenario to code for in Swift.
The following code is in Java. I need to code in Swift for the following scenario
// With String array - strArr1
String strArr1[] = {"Some1","Some2"}
String strArr2[] = {"Somethingelse1","Somethingelse2"}
for( int i=0;i< strArr1.length;i++){
System.out.println(strArr1[i] + " - "+ strArr2[i]);
}
I have a couple of arrays in swift
var strArr1: [String] = ["Some1","Some2"]
var strArr2: [String] = ["Somethingelse1","Somethingelse2"]
for data in strArr1{
println(data)
}
for data in strArr2{
println(data)
}
// I need to loop over in single for loop based on index.
Could you please provide your help on the syntaxes for looping over based on index
You can use zip(), which creates
a sequence of pairs from the two given sequences:
let strArr1 = ["Some1", "Some2"]
let strArr2 = ["Somethingelse1", "Somethingelse2"]
for (e1, e2) in zip(strArr1, strArr2) {
print("\(e1) - \(e2)")
}
The sequence enumerates only the "common elements" of the given sequences/arrays. If they have different length then the additional
elements of the longer array/sequence are simply ignored.
With Swift 5, you can use one of the 4 following Playground codes in order to solve your problem.
#1. Using zip(_:_:) function
In the simplest case, you can use zip(_:_:) to create a new sequence of pairs (tuple) of the elements of your initial arrays.
let strArr1 = ["Some1", "Some2", "Some3"]
let strArr2 = ["Somethingelse1", "Somethingelse2"]
let sequence = zip(strArr1, strArr2)
for (el1, el2) in sequence {
print("\(el1) - \(el2)")
}
/*
prints:
Some1 - Somethingelse1
Some2 - Somethingelse2
*/
#2. Using Array's makeIterator() method and a while loop
It is also easy to loop over two arrays simultaneously with a simple while loop and iterators:
let strArr1 = ["Some1", "Some2", "Some3"]
let strArr2 = ["Somethingelse1", "Somethingelse2"]
var iter1 = strArr1.makeIterator()
var iter2 = strArr2.makeIterator()
while let el1 = iter1.next(), let el2 = iter2.next() {
print("\(el1) - \(el2)")
}
/*
prints:
Some1 - Somethingelse1
Some2 - Somethingelse2
*/
#3. Using a custom type that conforms to IteratorProtocol
In some circumstances, you may want to create you own type that pairs the elements of your initials arrays. This is possible by making your type conform to IteratorProtocol. Note that by making your type also conform to Sequence protocol, you can use instances of it directly in a for loop:
struct TupleIterator: Sequence, IteratorProtocol {
private var firstIterator: IndexingIterator<[String]>
private var secondIterator: IndexingIterator<[String]>
init(firstArray: [String], secondArray: [String]) {
self.firstIterator = firstArray.makeIterator()
self.secondIterator = secondArray.makeIterator()
}
mutating func next() -> (String, String)? {
guard let el1 = firstIterator.next(), let el2 = secondIterator.next() else { return nil }
return (el1, el2)
}
}
let strArr1 = ["Some1", "Some2", "Some3"]
let strArr2 = ["Somethingelse1", "Somethingelse2"]
let tupleSequence = TupleIterator(firstArray: strArr1, secondArray: strArr2)
for (el1, el2) in tupleSequence {
print("\(el1) - \(el2)")
}
/*
prints:
Some1 - Somethingelse1
Some2 - Somethingelse2
*/
#4. Using AnyIterator
As an alternative to the previous example, you can use AnyIterator. The following code shows a possible implementation of it inside an Array extension method:
extension Array {
func pairWithElements(of array: Array) -> AnyIterator<(Element, Element)> {
var iter1 = self.makeIterator()
var iter2 = array.makeIterator()
return AnyIterator({
guard let el1 = iter1.next(), let el2 = iter2.next() else { return nil }
return (el1, el2)
})
}
}
let strArr1 = ["Some1", "Some2", "Some3"]
let strArr2 = ["Somethingelse1", "Somethingelse2"]
let iterator = strArr1.pairWithElements(of: strArr2)
for (el1, el2) in iterator {
print("\(el1) - \(el2)")
}
/*
prints:
Some1 - Somethingelse1
Some2 - Somethingelse2
*/
Try This:
zip([0,2,4,6], [1,3,5,7]).forEach {
print($0,$1)
}
zip([0,2,4,6], [1,3,5,7]).forEach {
print($0.0,$0.1)
}
You could also enumerate over one array and used the index to look inside the second array:
Swift 1.2:
for (index, element) in enumerate(strArr1) {
println(element)
println(strArr2[index])
}
Swift 2:
for (index, element) in strArr1.enumerate() {
print(element)
print(strArr2[index])
}
Swift 3:
for (index, element) in strArr1.enumerated() {
print(element)
print(strArr2[index])
}
You could use Range if you still want to use for in.
var strArr1: [String] = ["Some1","Some2"]
var strArr2: [String] = ["Somethingelse1","Somethingelse2"]
for i in Range(start: 0, end: strArr1.count) {
println(strArr1[i] + " - " + strArr2[i])
}
for(var i = 0; i < strArr1.count ; i++)
{
println(strArr1[i] + strArr2[i])
}
That should do it. Never used swift before so make sure to test.
Updated to recent Swift syntax
for i in 0..< strArr1.count {
print(strArr1[i] + strArr2[i])
}
> Incase of unequal count
let array1 = ["some1","some2"]
let array2 = ["some1","some2","some3"]
var iterated = array1.makeIterator()
let finalArray = array2.map({
let itemValue = iterated.next()
return "\($0)\(itemValue != nil ? "-"+itemValue! : EmptyString)" })
// result : ["some1-some1","some2-some2","some3"]

Resources