In this lecture that I presented in the TDC, I will explain a brief experience using the framework CloudKit.
The problems found and the solutions to solve them.
28. Database
CKDatabase
Public PrivatePrivate
Public Private
let publicDatabase = CKContainer.defaultContainer().publicCloudDatabase
let privateDatabase = CKContainer.defaultContainer().privateCloudDatabase
• Every app access to at least one container.
• Every container has two databases
29. Database
Public Private
Data Type Shared Data User’s Data
Account Required for writing Required
Quota Developer User
Default Permissions World Redable User readable
Editing Permissions Dashboard Rules N/A
31. CKRecord class
Record
Structured data
Wraps key/values pairs
Metadata
Record Values
• String
• NSNumber
• NSDate
• NSData
• CLLocation
• CKAsset
• CKReference
Array of these types
32. let session = CKRecord(recordType: "Session")
session["name"] = "Endentendo a Cloudkit da Apple"
session["author"] = "Rodrigo Leite"
session["start_date"] = NSDate()
Record
33. Record
class session: CKRecord{
func name() -> String?{
return self["name"] as? String
}
func author() -> String?{
return self["author"] as? String
}
....
}
• Subclassing
Apple Docs
34. Record
class Session: NSObject{
let record: CKRecord?
var name: String?{
didSet{
record!["name"] = name
}
}
var author: String?{
didSet{
record!["author"] = name
}
}
init(record: CKRecord){
self.record = record
self.name = self.record!["name"] as? String
self.author = self.record!["author"] as? String
}
}
Apple Docs
36. Record Identifier
public class CKRecord : NSObject, NSSecureCoding, NSCopying {
/* These create the record in the default zone. */
public init(recordType: String)
public init(recordType: String, recordID: CKRecordID)
public init(recordType: String, zoneID: CKRecordZoneID)
public var recordType: String { get }
@NSCopying public var recordID: CKRecordID { get }
/* Change tags are updated by the server to a unique value every time a record is modified.
A different change tag necessarily means that the contents of the record are different. */
public var recordChangeTag: String? { get }
/* This is a User Record recordID, identifying the user that created this record. */
@NSCopying public var creatorUserRecordID: CKRecordID? { get }
@NSCopying public var creationDate: NSDate? { get }
/* This is a User Record recordID, identifying the user that last modified this record. */
@NSCopying public var lastModifiedUserRecordID: CKRecordID? { get }
@NSCopying public var modificationDate: NSDate? { get }
..
}
• The identifier of the record in the cloud kit
41. Assets
• CKAsset class
• Large, unstructured data
•Files on disk
•Owned by records
•Removed when owner is deleted
let attendee = CKRecord(recordType: "Attendee")
let image = UIImage(named: "")!
let documentDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory,
.UserDomainMask,
true).first! as String
let localPath = documentDirectory + "/profilePicture"
let data = UIImageJPEGRepresentation(image, 0.85)
data!.writeToFile(localPath, atomically: true)
let photoURL = NSURL(fileURLWithPath: localPath)
let asset = CKAsset(fileURL: photoURL)
attendee["profileImage"] = asset
45. extension CKDatabase {
/* Convenience APIs
These calls operate on a single item in the default zone and allow for simple operations.
If you'd like to batch your requests, add dependencies between requests, set priorities,
or schedule operations on your own queue, take a look at the corresponding CKOperation.
This work is treated as having NSQualityOfServiceUserInitiated quality of service.
*/
/* CKFetchRecordsOperation and CKModifyRecordsOperation are the more configurable,
CKOperation-based alternatives to these methods */
public func fetchRecordWithID(recordID: CKRecordID, completionHandler: (CKRecord?, NSError?) -> Void)
public func saveRecord(record: CKRecord, completionHandler: (CKRecord?, NSError?) -> Void)
public func deleteRecordWithID(recordID: CKRecordID, completionHandler: (CKRecordID?, NSError?) -> Void)
/* CKQueryOperation is the more configurable, CKOperation-based
alternative to this method */
public func performQuery(query: CKQuery, inZoneWithID zoneID: CKRecordZoneID?, completionHandler: ([CKRecord]?,
NSError?) -> Void)
/* CKFetchRecordZonesOperation and CKModifyRecordZonesOperation are the more configurable,
CKOperation-based alternatives to these methods */
public func fetchAllRecordZonesWithCompletionHandler(completionHandler: ([CKRecordZone]?, NSError?) -> Void)
public func fetchRecordZoneWithID(zoneID: CKRecordZoneID, completionHandler: (CKRecordZone?, NSError?) -> Void)
public func saveRecordZone(zone: CKRecordZone, completionHandler: (CKRecordZone?, NSError?) -> Void)
public func deleteRecordZoneWithID(zoneID: CKRecordZoneID, completionHandler: (CKRecordZoneID?, NSError?) -> Void)
/* CKFetchSubscriptionsOperation and CKModifySubscriptionsOperation are the more configurable,
CKOperation-based alternative to these methods */
public func fetchSubscriptionWithID(subscriptionID: String, completionHandler: (CKSubscription?, NSError?) -> Void)
public func fetchAllSubscriptionsWithCompletionHandler(completionHandler: ([CKSubscription]?, NSError?) -> Void)
public func saveSubscription(subscription: CKSubscription, completionHandler: (CKSubscription?, NSError?) -> Void)
public func deleteSubscriptionWithID(subscriptionID: String, completionHandler: (String?, NSError?) -> Void)
}
Convenience API
46. let session = CKRecord(recordType: "Session")
session["name"] = "Endentendo a Cloudkit da Apple"
session["author"] = "Rodrigo Leite"
session["start_date"] = NSDate()
let publicDatabase = CKContainer.defaultContainer().publicCloudDatabase
publicDatabase.saveRecord(session) { (record: CKRecord?, error: NSError?) in
if (error != nil){
/* Error handling */
}else{
}
}
Save Object
Convenience API
47. Fetch Object
Convenience API
let database = CKContainer.defaultContainer().publicCloudDatabase
database.fetchRecordWithID(recordID) { (record, error) in
if error != nil {
/* *** ERROR HANDLING **** */
}else{
}
}
49. Basic NSOperation
public class NSOperation : NSObject {
/* **** LIFE CYCLE **** */
public var completionBlock: (() -> Void)?
public func cancel()
/* ***** STATE ***** */
public var executing: Bool { get }
public var finished: Bool { get }
/* ***** DEPENDENCIES **** */
public func addDependency(op: NSOperation)
public func removeDependency(op: NSOperation)
}
50. Basic NSOperationQueue
public class NSOperationQueue : NSObject {
/* ******* START OPERATION **** */
public func addOperations(ops: [NSOperation], waitUntilFinished wait: Bool)
public var operations: [NSOperation] { get }
/* ****** CANCEL OPERATION **** */
public var suspended: Bool
public func cancelAllOperations()
}
51. CKOperation
public class CKFetchRecordsOperation : CKDatabaseOperation {
public init()
public convenience init(recordIDs: [CKRecordID])
public class func fetchCurrentUserRecordOperation() -> Self
public var recordIDs: [CKRecordID]?
/* Declares which user-defined keys should be fetched and added to the resulting CKRecords. If nil,
declares the entire record should be downloaded. If set to an empty array, declares that no user fields should
be downloaded. Defaults to nil. */
public var desiredKeys: [String]?
/* Called repeatedly during transfer. */
public var perRecordProgressBlock: ((CKRecordID, Double) -> Void)?
/* Called on success or failure for each record. */
public var perRecordCompletionBlock: ((CKRecord?, CKRecordID?, NSError?) -> Void)?
/* This block is called when the operation completes.
The [NSOperation completionBlock] will also be called if both are set.
If the error is CKErrorPartialFailure, the error's userInfo dictionary contains
a dictionary of recordIDs to errors keyed off of CKPartialErrorsByItemIDKey.
*/
public var fetchRecordsCompletionBlock: (([CKRecordID : CKRecord]?, NSError?) -> Void)?
}
52. let database = CKContainer.defaultContainer().publicCloudDatabase
database.fetchRecordWithID(recordID) { (record, error) in
...
database.fetchRecordWithID(otherRecordID, completionHandler: { (record, error) in
...
database.saveRecord(otherRecord, completionHandler: { (record, error) in
....
})
})
}
Convenience API
53. var session : CKRecord?
var attendees = [CKRecord]()
let firstQuery = CKQuery(recordType: "Session", predicate: NSPredicate(format: "name == %@", "Cloudkit"))
let firstFetch = CKQueryOperation(query: firstQuery)
firstFetch.recordFetchedBlock = { record in
session = record
}
let reference = CKReference(record: session!, action: .None )
let secondQuery = CKQuery(recordType: "Atendee", predicate: NSPredicate(format: "reference == %@",
reference))
let secondFetch = CKQueryOperation(query: secondQuery)
secondFetch.recordFetchedBlock = { record in
attendees.append(record)
}
secondFetch.addDependency(firstFetch)
let queue = NSOperationQueue()
queue.addOperations([firstFetch, secondFetch], waitUntilFinished: false)
Dependent Task
• Fetch all attendee in a session
55. NSPredicate
CKQuery
CKQuery only accepts a subset of predicate behaviors
Rules
• Predicates are based on a format string
• Predicates operate only on fields containing primitive data forms