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 ↗Code shown on screen · 13 snippets
Batch Operations - Enable Persistent History
storeDesc.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey) NSBatchInsertRequest.h
//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
//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
//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
//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
let moc = NSManagedObjectContext(concurrencyType:
NSManagedObjectContextConcurrencyType.privateQueueConcurrencyType)
moc.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
insertResult = try moc.execute(insertRequest) Batch Update Example
//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
// 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
//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
//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
//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
storeDesc.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
storeDesc.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey) History Pointers
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
Related sessions
-
33 min