Skip to content
This repository was archived by the owner on Dec 17, 2018. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions Source/Classes/Fetch/FetchAndSaveOperation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,14 @@ public class FetchAndSaveOperation: Operation {

let recordZoneChangesOperation = FetchRecordZoneChangesOperation(from: database, recordZoneIDs: recordZoneIDs, tokens: tokens)

var objectsWithMissingReferences = [MissingReferences]()
recordZoneChangesOperation.recordChangedBlock = {
// Convert and write CKRecord To NSManagedObject Operation
let convertOperation = RecordToCoreDataOperation(parentContext: context, record: $0)
convertOperation.errorBlock = { self.errorBlock?($0) }
convertOperation.completionBlock = {
objectsWithMissingReferences.append(convertOperation.missingObjectsPerEntities)
}
self.queue.addOperation(convertOperation)
}

Expand All @@ -90,6 +94,44 @@ public class FetchAndSaveOperation: Operation {
recordZoneChangesOperation.errorBlock = { zoneID, error in
self.handle(recordZoneChangesError: error, in: zoneID, database: database, context: context)
}

recordZoneChangesOperation.completionBlock = {
// iterate over all missing references and fix them, now are all NSManagedObjects created
for missingReferences in objectsWithMissingReferences {
for (object, references) in missingReferences {
guard let serviceAttributes = object.entity.serviceAttributeNames else { continue }

for (attributeName, recordIDs) in references {
for recordId in recordIDs {
guard let relationship = object.entity.relationshipsByName[attributeName], let targetEntityName = relationship.destinationEntity?.name else { continue }

let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: targetEntityName)
fetchRequest.predicate = NSPredicate(format: serviceAttributes.recordID + " == %@" , recordId)
fetchRequest.fetchLimit = 1
fetchRequest.includesPropertyValues = false

do {
let foundObject = try context.fetch(fetchRequest).first as? NSManagedObject

if let foundObject = foundObject {
if relationship.isToMany {
let set = object.value(forKey: attributeName) as? NSMutableSet ?? NSMutableSet()
set.add(foundObject)
object.setValue(set, forKey: attributeName)
} else {
object.setValue(foundObject, forKey: attributeName)
}
} else {
print("warning: object not found " + recordId)
}
} catch {
self.errorBlock?(error)
}
}
}
}
}
}

queue.addOperation(recordZoneChangesOperation)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,16 @@
import CoreData
import CloudKit

typealias AttributeName = String
typealias RecordID = String
typealias MissingReferences = [NSManagedObject: [AttributeName: [RecordID]]]

/// Convert CKRecord to NSManagedObject and save it to parent context, thread-safe
class RecordToCoreDataOperation: AsynchronousOperation {
let parentContext: NSManagedObjectContext
let record: CKRecord
var errorBlock: ErrorBlock?
var missingObjectsPerEntities = MissingReferences()

/// - Parameters:
/// - parentContext: operation will be safely performed in that context, **operation doesn't save that context** you need to do it manually
Expand Down Expand Up @@ -78,7 +83,9 @@ class RecordToCoreDataOperation: AsynchronousOperation {

let attribute = CloudKitAttribute(value: recordValue, fieldName: key, entityName: entityName, serviceAttributes: serviceAttributeNames, context: context)
let coreDataValue = try attribute.makeCoreDataValue()
object.setValue(coreDataValue, forKey: key)
object.setValue(coreDataValue, forKey: key)

missingObjectsPerEntities[object] = attribute.notFoundRecordIDsForAttribute
}

// Set system headers
Expand Down
9 changes: 8 additions & 1 deletion Source/Model/CloudKitAttribute.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class CloudKitAttribute {
let entityName: String
let serviceAttributes: ServiceAttributeNames
let context: NSManagedObjectContext
var notFoundRecordIDsForAttribute = [AttributeName: [RecordID]]()

init(value: Any?, fieldName: String, entityName: String, serviceAttributes: ServiceAttributeNames, context: NSManagedObjectContext) {
self.value = value
Expand Down Expand Up @@ -56,7 +57,13 @@ class CloudKitAttribute {
fetchRequest.includesPropertyValues = false

let foundObject = try context.fetch(fetchRequest).first as? NSManagedObject


if foundObject == nil {
var values = notFoundRecordIDsForAttribute[fieldName] ?? []
values.append(recordID.encodedString)
notFoundRecordIDsForAttribute[fieldName] = values
}

return foundObject
}

Expand Down