Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

2020 Developer ToolsSwiftUI & UI FrameworksSystem Services

WWDC20 · 17 min · Developer Tools / SwiftUI & UI Frameworks / System Services

Core Data: Sundries and maxims

Core Data is the central way to durably and persistently store information from your app — and we’re going to show you how to refine that implementation for even faster data ingest and fetching. Discover how you can improve data capture with batch insert, tailor fetch requests to your data needs, and react to notifications about changes in the persistent store. To get the most out of this session, you should know and have interacted with Core Data in the past. For more information on the framework, watch “Making Apps with Core Data.”

Watch at developer.apple.com ↗

Transcript all transcripts

Code shown on screen · 13 snippets

Batch Operations - Enable Persistent History swift · at 1:48 ↗
storeDesc.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
NSBatchInsertRequest.h swift · at 2:32 ↗
//NSBatchInsertRequest.h

@available(iOS 13.0, *)
open class NSBatchInsertRequest : NSPersistentStoreRequest {
    open var resultType: NSBatchInsertRequestResultType

    public convenience init(entityName: String, objects dictionaries: [[String : Any]])
    public convenience init(entity: NSEntityDescription, objects dictionaries: [[String : Any]])

    @available(iOS 14.0, *)
    open var dictionaryHandler: ((inout Dictionary<String, Any>) -> Void)?
    open var managedObjectHandler: ((inout NSManagedObject) -> Void)?

    public convenience init(entity: NSEntityDescription, dictionaryHandler handler: @escaping (inout Dictionary<String, Any>) -> Void)
    public convenience init(entity: NSEntityDescription, managedObjectHandler handler: @escaping (inout NSManagedObject) -> Void)
}
Earthquakes Sample - Regular Save swift · at 3:01 ↗
//Earthquakes Sample - Regular Save

   for quakeData in quakesBatch {
        guard let quake = NSEntityDescription.insertNewObject(forEntityName: "Quake", into: taskContext) as? Quake else { ... }
        do {
            try quake.update(with: quakeData)
        } catch QuakeError.missingData {
            ...
            taskContext.delete(quake)
        }
        ...
    }
    do {
        try taskContext.save()
    } catch { ... }
Earthquakes Sample - Batch Insert with Array of Dictionaries swift · at 3:16 ↗
//Earthquakes Sample - Batch Insert

var quakePropertiesArray = [[String:Any]]()
for quake in quakesBatch {
    quakePropertiesArray.append(quake.dictionary)
}

let batchInsert = NSBatchInsertRequest(entityName: "Quake", objects: quakePropertiesArray)

var insertResult : NSBatchInsertResult
do {
    insertResult = try taskContext.execute(batchInsert) as! NSBatchInsertResult
    ... 
}
Earthquakes Sample - Batch Insert with a block swift · at 3:28 ↗
//Earthquakes Sample - Batch Insert with a block

var batchInsert = NSBatchInsertRequest(entityName: "Quake", dictionaryHandler: { 
    (dictionary) in
        if (blockCount == batchSize) {
            return true
        } else {
            dictionary = quakesBatch[blockCount]
            blockCount += 1
        }
    })
    var insertResult : NSBatchInsertResult
    do {
        insertResult = try taskContext.execute(batchInsert) as! NSBatchInsertResult
        ...
    }
NSBatchInsertRequest - UPSERT swift · at 5:42 ↗
let moc = NSManagedObjectContext(concurrencyType:
                           NSManagedObjectContextConcurrencyType.privateQueueConcurrencyType)
    
moc.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy

insertResult = try moc.execute(insertRequest)
Batch Update Example swift · at 6:30 ↗
//Earthquakes Sample - Batch Update

let updateRequest = NSBatchUpdateRequest(entityName: "Quake")
updateRequest.propertiesToUpdate = ["validated" : true]
updateRequest.predicate = NSPredicate("%K > 2.5", "magnitude")

var updateResult : NSBatchUpdateResult
do {
    updateResult = try taskContext.execute(updateRequest) as! NSBatchUpdateResult
    ... 
}
Batch Delete without and with a Fetch Limit swift · at 7:33 ↗
// Batch Delete without and with a Fetch Limit

   DispatchQueue.global(qos: .background).async {
       moc.performAndWait { () -> Void in
          do {
              let expirationDate = Date.init().addingTimeInterval(-30*24*3600)

              let request = NSFetchRequest<Quake>(entityName: "Quake")
              request.predicate = NSPredicate(format:"creationDate < %@", expirationDate)

              let batchDelete = NSBatchDeleteRequest(fetchRequest: request)
              batchDelete.fetchLimit = 1000
              moc.execute(batchDelete)
           }
       }
   }
Fetch average magnitude of each place swift · at 12:18 ↗
//Fetch average magnitude of each place

let magnitudeExp = NSExpression(forKeyPath: "magnitude")
let avgExp = NSExpression(forFunction: "avg:", arguments: [magnitudeExp])

let avgDesc = NSExpressionDescription()
avgDesc.expression = avgExp
avgDesc.name = "average magnitude"
avgDesc.expressionResultType = .floatAttributeType

let fetch = NSFetchRequest<NSFetchRequestResult>(entityName: "Quake")
fetch.propertiesToFetch = [avgDesc, "place"]
fetch.propertiesToGroupBy = ["place"]
fetch.resultType = .dictionaryResultType
NSManagedObjectContext.h - Modernized Notifications swift · at 13:36 ↗
//NSManagedObjectContext.h

@available(iOS 14.0, *)
extension NSManagedObjectContext {
    public static let willSaveObjectsNotification: Notification.Name
    public static let didSaveObjectsNotification: Notification.Name
    public static let didChangeObjectsNotification: Notification.Name
         
    public static let didSaveObjectIDsNotification: Notification.Name
    public static let didMergeChangesObjectIDsNotification: Notification.Name
}
NSManagedObjectContext.h - Modernized Keys swift · at 13:54 ↗
//NSManagedObjectContext.h

@available(iOS 14.0, *)
extension NSManagedObjectContext {
    public enum NotificationKey : String {  
        case sourceContext
        case queryGeneration
        case invalidatedAllObjects
        case insertedObjects
        case updatedObjects
        case deletedObjects
        case refreshedObjects
        case invalidatedObjects
        case insertedObjectIDs
        case updatedObjectIDs
        case deletedObjectIDs
        case refreshedObjectIDs
        case invalidatedObjectIDs
    }
}
Enable Remote Change Notifications with Persistent History swift · at 14:08 ↗
storeDesc.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
storeDesc.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
History Pointers swift · at 16:19 ↗
let changeDesc = NSPersistentHistoryChange.entityDescription(with: moc)
let request = NSFetchRequest<NSFetchRequestResult>()

//Set fetch request entity and predicate
request.entity = changeDesc
request.predicate = 
    NSPredicate(format: "%K = %@",changeDesc?.attributesByName["changedObjectID"], objectID)
   
//Set up history request with distantPast and set fetch request              
let historyReq = NSPersistentHistoryChangeRequest.fetchHistory(after: Date.distantPast)
historyReq.fetchRequest = request
                    
let results = try moc.execute(historyReq)

Resources