-
What's new in CloudKit
CloudKit provides a secure, convenient, and reliable cloud database for your apps — and it's only getting better. Discover how you can unravel your threads with support for async/await and convenience API additions. We'll also show you how to encourage collaboration between people using your app through sharing entire record zones of data, and explore how to adopt CloudKit features like encrypted values and help protect sensitive data within your app.
To get the most out of this session, we recommend being familiar with CloudKit and its operations on containers, as well as a basic understanding of record and data types.Recursos
Vídeos relacionados
WWDC23
WWDC22
WWDC21
- Build apps that share data through CloudKit and Core Data
- Meet async/await in Swift
- Meet CloudKit Console
Tech Talks
-
Buscar neste vídeo...
-
-
3:34 - CloudKit: Existing convenience API
// Sample code using existing Convenience API /// Delete the last person record. /// - Parameter completionHandler: An optional handler to process completion `success` or `failure`. func deleteLastPerson(completionHandler: ((Result<Void, Error>) -> Void)? = nil) { database.delete(withRecordID: lastPersonRecordId) { recordId, error in if let recordId = recordId { os_log("Record with ID \(recordId.recordName) was deleted.") } if let error = error { self.reportError(error) // If there is a completion handler, pass along the error here. completionHandler?(.failure(error)) } else { // If there is a completion handler, like during tests, call it back now. completionHandler?(.success(())) } } } -
4:04 - CloudKit: Async convenience API
// Sample code updated to CloudKit Async API /// Delete the last person record. func deleteLastPerson() async throws { do { let recordId = try await database.deleteRecord(with: lastPersonRecordId) os_log("Record with ID \(recordId.recordName) was deleted.") } catch { self.reportError(error) throw error } } -
5:39 - CloudKit: Existing completion blocks
// Error reporting in CKFetchRecordsOperation extension CKFetchRecordsOperation { var perRecordCompletionBlock: ((CKRecord?, CKRecord.ID?, Error?) -> Void)? var fetchRecordsCompletionBlock: (([CKRecord.ID : CKRecord]?, Error?) -> Void)? } fetchRecordsOp.perRecordCompletionBlock = { record, recordID, error in // error is CKError.unknownItem. } fetchRecordsOp.fetchRecordsCompletionBlock = { recordsByRecordID, operationError in // operationError is CKError.partialFailure. // operationError.partialErrorsByItemID[missingRecordID] is CKError.unknownItem. } -
6:35 - CloudKit: Result type completion blocks
// Error reporting in CKFetchRecordsOperation extension CKFetchRecordsOperation { var perRecordResultBlock: ((CKRecord.ID, Result<CKRecord, Error>) -> Void)? var fetchRecordsResultBlock: ((Result<Void, Error>) -> Void)? } fetchRecordsOp.perRecordResultBlock = { recordID, result in // result is .failure(CKError.unknownItem) or .success(record). } fetchRecordsOp.fetchRecordsResultBlock = { result in // result is .success. } -
9:14 - CloudKit: Delete single item
// Single item delete func deleteLastPerson() async throws { do { let recordId = try await database.deleteRecord(with: lastPersonRecordId) os_log("Record with ID \(recordId.recordName) was deleted.") } catch { self.reportError(error) throw error } } -
9:37 - CloudKit: Delete batch
// Batched modifications func deleteLastPeople() async throws { do { let recordIds = [lastPersonRecordId, penultimatePersonRecordId] let (_, deleteResults) = try await database.modifyRecords(deleting: recordIds) for (recordId, deleteResult) in deleteResults { switch deleteResult { case .failure(let error): self.reportError(error, itemId: recordId) case .success: os_log("Record with ID \(recordId.recordName) was deleted.") } } } catch let operationError { self.reportError(operationError) throw operationError } } -
13:43 - CloudKit: Encrypted values
extension CKRecord { @NSCopying open var encryptedValues: CKRecordKeyValueSetting { get } } -
14:29 - CloudKit: Using encrypted values
// Device 1: Encrypt data before calling CKModifyRecordsOperation. myRecord.encryptedValues["encryptedStringField"] = "Sensitive value" // Device 2: Decrypt data after calling CKFetchRecordsOperation. let decryptedString = myRecord.encryptedValues["encryptedStringField"] as? String -
16:35 - CloudKit: Account status
open func accountStatus(completionHandler: @escaping (CKAccountStatus, Error?) -> Void) -
16:46 - CloudKit: CKAccountStatus
public enum CKAccountStatus : Int { case couldNotDetermine case available case restricted case noAccount case temporarilyUnavailable } -
21:10 - CloudKit: Setup a record hierarchy
// Share a record hierarchy let zone = CKRecordZone(zoneName: "MyZone") // Save zone... let fileRecordA = CKRecord(recordType: "File", recordID: CKRecord.ID(zoneID: zone.zoneID)) let fileRecordB = CKRecord(recordType: "File", recordID: CKRecord.ID(zoneID: zone.zoneID)) let folderRecord = CKRecord(recordType: "Folder", recordID: CKRecord.ID(zoneID: zone.zoneID)) fileRecordA.setParent(folderRecord) fileRecordB.setParent(folderRecord) // Save records... -
21:41 - CloudKit: Record Hierarchy, Share
// Share a record hierarchy let share = CKShare(rootRecord: folderRecord) do { let (saveResults, _) = try await database.modifyRecords(saving: [folderRecord, share]) for (recordID, saveResult) in saveResults { // Handle per-record result. } } catch let operationError { // Handle operation error. } -
22:51 - CloudKit: Share a Record Zone
// Share a record zone let zone = CKRecordZone(zoneName: "MyZone") // Save zone... let share = CKShare(recordZoneID: zone.zoneID) do { let (saveResults, _) = try await database.modifyRecords(saving: [share]) for (recordID, saveResult) in saveResults { // Handle per-record result. } } catch let operationError { // Handle operation error. }
-