Recently I’ve been working with CloudKit in Swift. Many CloudKit methods take in a completion handler. Take a look at the following methods for CKDataBase object:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func saveRecord(_ record: CKRecord,
completionHandler completionHandler: (CKRecord?,
NSError?) -> Void)
func performQuery(_ query: CKQuery,
inZoneWithID zoneID: CKRecordZoneID?,
completionHandler completionHandler: ([CKRecord]?,
NSError?) -> Void)
func fetchRecordWithID(_ recordID: CKRecordID,
completionHandler completionHandler: (CKRecord?,
NSError?) -> Void)
func deleteRecordWithID(_ recordID: CKRecordID,
completionHandler completionHandler: (CKRecordID?,
NSError?) -> Void)

All of the completion handlers they take have two optional parameters, and the latter one is NSError?. I want similar logic in all of these completion handlers:

1
2
3
4
5
6
7
8
{(anotherPara, error) in
if error != nil {
print (error!)
}
if anotherPara != nil {
//Do Some Other Stuff
}
}

It causes a lot of code duplication if I rewrite the above logic over and over again in every single CloudKit method. So what I did is create a method takes a closure to handle the Do Some Other Stuff and output a completion handler with the above logic.

1
2
3
4
5
6
7
8
9
10
11
12
private func CKHandler<A>(recordOrIDHandler: ((recordOrID : A) -> Void)?) -> (A?, NSError?) -> Void {
return {(result, error) in
if error != nil {
self.CKprint(error!)
}
if result != nil {
if let handler = recordOrIDHandler {
handler(recordOrID: result!)
}
}
}
}

And, with this helper function, if I want to save a record to CloudKit and print the record ID when success, instead of writing:

1
2
3
4
5
6
7
8
CKContainer.defaultContainer().privateCloudDatabase.saveRecord(record, completionHandler: {(record, error) in
if error != nil {
print (error!)
}
if record != nil {
print (record!.recordID)
}
})

I can simply write

1
CKContainer.defaultContainer().privateCloudDatabase.saveRecord(record, completionHandler: self.CKHandler({(record) in print (record)}))

Another example:

1
2
3
4
5
CKContainer.defaultContainer().privateCloudDatabase.performQuery(query, inZoneWithID: nil, completionHandler: self.CKHandler({(records) in
let ids : [String] = records.map({$0.valueForKey("postID")}).filter({$0 != nil}).map({($0! as! String)})
let distinctIDs : [String] = Array(Set(ids))
print ("Found \(distinctIDs.count) results")
}))

The above code perform a query on the database, get the distinct values of the attribute postID and print out the number of entities having distinct postID. The performQuery function has [CKRecord]? in its completion handler, and with the power of Swift generics type, our CKHandler function can still handle this situation.