We finish the CloudKitNotesManager by providing a generic save and delete methods that we can use for any CKRecordWrapper type. We also implement a custom notification when a note is saved so that we can update an interested view controllers to update their UI.
new folder New Folder Function func newFolder(name: String) -> Folder { return CloudKitFolder(name: name) } Saving Records Generically fun save<R: CKRecordWrapper>(record: R, conversion: @escaping (T) -> R, completion: @escaping OperationCompletionBlock<T>) { let modifyOp = CKModifyRecordsOperation(recordsToSave: [record.record], recordIDsToDelete: nil) modifyOp.modifyRecordsCompletionBlock = { savedRecords, deletedRecords, error in if let e = error { print("Error saving folder: \(e)") completion(.error(e)) } if let savedRecord = savedRecords?.first { let result = R(record: savedRecord) completion(.success(conversion(result))) } } database.add(modifyOp) } We can use this to save a note like this: func save(note: Note, completion: @escaping (Result<Note>) -> Void) { guard let note = note as? CloudKitNote else { fatalError("must pass in a CloudKitNote") } save(record: note, conversion: { $0 }) { result in switch result { case .success(let savedNote): NotificationCenter.default.post(name: .NoteWasUpdated, object: savedNote) completion(.success(savedNote)) case .error(let e): completion(.error(e)) } } } Here we post a notification when a note is saved so any view controllers that are displaying the note can update their cnotents with the new note that is returned by the server. Deleting Records Generically private func delete<R:CKRecordWrapper>(record: R, completion: @escaping OperationCompletionBlock<Bool>) { let modifyOp = CKModifyRecordsOperation(recordsToSave: nil, recordIDsToDelete: [record.record.recordID]) modifyOp.modifyRecordsCompletionBlock = { saved, deletedIds, error in if let e = error { print("Error deleting record: \(e)") completion(.error(e)) } else { let deletedCount = deletedIds?.count ?? 0 completion(.success(deletedCount > 0)) } } database.add(modifyOp) } We can the change our delete(folder:) method to use this: func delete(folder: Folder, completion: @escaping (Result<Bool>) -> Void) { guard let folder = folder as? CloudKitFolder else { fatalError("must pass in a CloudKitFolder") } delete(record: folder, completion: completion) } Fetching Notes in a Folder func fetchNotes(for folder: Folder, completion: @escaping (Result<[Note]>) -> Void) { guard let folder = folder as? CloudKitFolder else { fatalError("must pass in a CloudKitFolder") } let inFolderPredicate = NSPredicate(format: "folder == %@", CKReference(recordID: folder.record.recordID, action: .deleteSelf)) let sortDescriptors = [NSSortDescriptor(key: "modificationDate", ascending: false)] query(predicate: inFolderPredicate, sortDescriptors: sortDescriptors, conversion: { (note: CloudKitNote) -> Note in note }, completion: completion) }