SlideShare a Scribd company logo
Core Data with Swift 3
Capital One Engineering Tech Talks
Korhan Bircan
Senior Engineering Manager, Capital One
Roadmap
• Persistent Store Types
• What is Core Data?
• Demos
• App Architecture
• Core Data setup and common operations
• Concurrency
• Migration
• Faults and Failures
• Core Data vs Realm
Persistent Store Types
Store type Speed Object graph
in memory
Other
XML Slow Whole Externally
parsableBinary Fast Whole N/A
SQLite Fast Partial N/A
In-memory Fast Whole No storage required
What Is Core Data?
• Framework that helps manage the object life
cycle and object graph management, including
persistence
• Grouping, filtering, and organization of data
• Change tracking
• Lazy loading of objects
• User interface synchronization
Demo - No Persistence
Demo - Core Data
Demo - Realm
LoginViewController
TimelineDataSourceSession TimelineViewController
Twitter
TimelineResponseMapper
App Architecture
LoginViewController
TimelineDataSourceSession TimelineViewController
CoreDataStack
Twitter
TimelineResponseMapper
App Architecture
Core Data Stack
Diagram courtesy of Apple
• Entity, attribute
• Managed Object, Managed Object Context
• Managed Object Model
• Persistent Store: NSQLiteStoreType (non-atomic), NSXMLStoreType (macOS,
atomic), NSBinaryStoreType (atomic), NSInMemoryStoreType (atomic),
NSIncrementalStore (JSON, CSV?)
• Persistent Store Coordinator
• Relationships
• Fetch Request, Predicate, Sort Descriptor
• Fetched Results Controller
Core Data Stack
• Persistent Store Container
Setup (Swift 2.3)
lazy var applicationDocumentsDirectory: NSURL = {
// The directory the application uses to store the Core Data store file. This code uses a directory named "CapitalOne.Delete" in the application's documents Application Support directory.
let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
return urls[urls.count-1]
}()
lazy var managedObjectModel: NSManagedObjectModel = {
// The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model.
let modelURL = NSBundle.mainBundle().URLForResource("Delete", withExtension: "momd")!
return NSManagedObjectModel(contentsOfURL: modelURL)!
}()
lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
// The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it. This property is optional
since there are legitimate error conditions that could cause the creation of the store to fail.
// Create the coordinator and store
let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite")
var failureReason = "There was an error creating or loading the application's saved data."
do {
try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: nil)
} catch {
// Report any error we got.
var dict = [String: AnyObject]()
dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data"
dict[NSLocalizedFailureReasonErrorKey] = failureReason
dict[NSUnderlyingErrorKey] = error as NSError
let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
// Replace this with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog("Unresolved error (wrappedError), (wrappedError.userInfo)")
abort()
}
return coordinator
}()
lazy var managedObjectContext: NSManagedObjectContext = {
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are
legitimate error conditions that could cause the creation of the context to fail.
let coordinator = self.persistentStoreCoordinator
var managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = coordinator
return managedObjectContext
}()
// MARK: - Core Data Saving support
func saveContext () {
if managedObjectContext.hasChanges {
do {
try managedObjectContext.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nserror = error as NSError
NSLog("Unresolved error (nserror), (nserror.userInfo)")
abort()
}
}
}
Setup (Swift 3)
lazy var managedContext: NSManagedObjectContext = {
let context = self.storeContainer.viewContext
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
return context
}()
lazy var storeContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: self.modelName)
container.loadPersistentStores { (storeDescription, error) in
if let error = error as NSError? {
print("Unresolved error (error), (error.userInfo)")
} else {
print("Persistent store location: (storeDescription.url?.absoluteString)")
}
}
return container
}()
func saveContext () {
guard managedContext.hasChanges else {
return
}
do {
try self.managedContext.save()
} catch let error as NSError {
print("Unresolved error (error), (error.userInfo)")
}
}
Modeling Data
Saving and Retrieving Objects
Save
let tweet = Tweet(context: context)
try! managedObjectContext.save()
Retrieve
let request: NSFetchRequest<Tweet> = Tweet.fetchRequest()
let allTweets = try! context.fetch(request)
Delete
try! managedObjectContext.delete(tweet)
try! managedObjectContext.save()
Filter
let fetchRequest = Tweet.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "text contains[c]
%@", keyword)
fetchRequest.sortDescriptors = [NSSortDescriptor(key:
#keyPath(Tweet.createdAt), ascending: false)]
try! fetchedResultsController?.performFetch()
Fetch Predicates
class Person: NSObject {
let firstName: String
let lastName: String
let age: Int
}
let bobPredicate = NSPredicate(format: "firstName = 'Bob'")
// let smithPredicate = NSPredicate(format: "lastName = %@", "Smith")
// let thirtiesPredicate = NSPredicate(format: "age >= 30")
fetchRequest.predicate = bobPredicate
try! fetchedResultsController?.performFetch()
• Basic Comparisons
=, ==; >=, =>; <=, =<; >; <; !=
• Compound Predicates
AND, &&; OR, ||; NOT, !
• String Comparisons
BEGINSWITH; CONTAINS; ENDSWITH; LIKE; MATCHES
Fetched Results Controller
let fetchRequest.predicate = keyword.isEmpty ? nil : NSPredicate(format: "text contains[c] %@",
keyword)
fetchRequest.sortDescriptors = [NSSortDescriptor(key: #keyPath(Tweet.createdAt), ascending: false)]
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest,
managedObjectContext: CoreDataStack.sharedInstance.managedContext,
sectionNameKeyPath: nil,
cacheName: nil)
do {
try fetchedResultsController?.performFetch()
} catch let error as NSError {
print("Fetching error: (error), (error.userInfo)")
}
Wiring Model to UI
override func numberOfSections(in tableView: UITableView) -> Int {
return fetchedResultsController.sections
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return fetchedResultsController.sections?[section].numberOfObjects
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) ->
UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: TweetCell.identifier, for: indexPath)
if let tweet = fetchedResultsController.object(at: indexPath) {
TimelineViewCellAdapter.configure(cell as? TweetCell, tweet: tweet)
}
return cell
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
let sectionInfo = fetchedResultsController.sections[section]
return sectionInfo?.name
}
Communicating Data Changes
func controllerWillChangeContent(controller: NSFetchedResultsController) {
tableView.beginUpdates()
}
func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo:
NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {
switch type {
case .Insert:
tableView.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade)
case .Delete:
tableView.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade)
case .Move:
break
case .Update:
break
}
}
func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath
indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
switch type {
case .Insert:
tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade)
case .Delete:
tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
case .Update:
configureCell(tableView.cellForRowAtIndexPath(indexPath!)!, indexPath: indexPath!)
case .Move:
tableView.moveRowAtIndexPath(indexPath!, toIndexPath: newIndexPath!)
}
}
func controllerDidChangeContent(controller: NSFetchedResultsController) {
tableView.endUpdates()
}
Concurrency
let jsonArray = … //JSON data to be imported into Core Data
let moc = … //Our primary context on the main queue
let privateMOC =
NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
privateMOC.parentContext = moc
privateMOC.performBlock {
for jsonObject in jsonArray {
let mo = … //Managed object that matches the incoming JSON structure
//update managed object with data from the dictionary
}
do {
try privateMOC.save()
moc.performBlockAndWait {
do {
try moc.save()
} catch {
fatalError("Failure to save context: (error)")
}
}
} catch {
fatalError("Failure to save context: (error)")
}
}
Xcode 8 madness
Swift 2.3 vs Swift 3
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: [])
TimelineResponseMapper.parseResponse(json)
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
} catch let jsonError as NSError {
print("json error: (jsonError.localizedDescription)")
}
})
// vs
DispatchQueue.global(qos: DispatchQoS.QoSClass.default).async {
do {
let json = try JSONSerialization.jsonObject(with: data!, options: [])
TimelineResponseMapper.parseResponse(json)
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch let jsonError as NSError {
print("json error: (jsonError.localizedDescription)")
}
}
Tidbits
• Multiple managed object contexts
• Migration
• Faulting and uniquing
• Concurrency failures
Migration
lazy var storeContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: self.modelName)
container.loadPersistentStores { (storeDescription, error) in
storeDescription.shouldMigrateStoreAutomatically = true
storeDescription.shouldInferMappingModelAutomatically = true
Why faults?
• Dictionary definition:
1. (noun) an unattractive or unsatisfactory feature, esp. in a piece of
work or in a person’s character.
2. (noun) responsibility for an accident or misfortune.
3. (noun) an extended break in a body of rock, marked by the
relative displacement and discontinuity of strata on either side of a
particular surface.
• Apple’s definition:
• A fault is a placeholder object that represents a managed object that
has not yet been fully realized, or a collection object that represents
a relationship.
Why faults?
Diagram courtesy of Apple
Why Faults May Fail
2016-10-11 22:20:24.530 CoreDataTest[24222:60b] * Assertion failure in
-[AppDelegate save], .../AppDelegate.swift:28 2016-10-11 22:20:24.530
CoreDataTest[24222:60b] * Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason: 'Save failed {type =
immutable dict, count = 2, entries => 1 : {contents =
"NSAffectedObjectsErrorKey"} = ( " (entity: Tweet; id: 0x6b8bf10 ;
data: )" ) 2 : {contents = "NSUnderlyingException"} = CoreData could
not fulfill a fault for '0x6b8bf10 ' }
Handling Fault Failures
• managedObjectContext.shouldDeleteInaccessibleFaults
• Prefetch everything
• Write lots of code (existingobjectwithId() to
make sure the object is in the database before
referencing it)
• Use query generators
Concurrency Failures
let firstTweet = Tweet(context: managedContext)
// Synchronously performs the block on the context's queue.
//May safely be called reentrantly.
managedContext.performAndWait {
do {
firstTweet.text = "Something interesting happened today..."
try self.managedContext.save()
} catch let error as NSError {
print("Unresolved error (error), (error.userInfo)")
}
}
managedContext2.performAndWait {
let text = firstTweet.text
print(text)
}
2016-10-11 22:20:24.530 CoreDataTest[24222:60b]
CoreData concurrency failure
Realm
CoreData Realm
Save
let tweet = Tweet(context: context)
try! managedObjectContext.save()
let realm = try! Realm()
let tweet = Tweet()
try! realm.write {
realm.add(tweet)
}
Retrieve
let request: NSFetchRequest<Tweet> =
Tweet.fetchRequest()
let allTweets = try! context.fetch(request)
let tweets = realm.objects(Tweet.self)
Delete
try! managedObjectContext.delete(tweet)
try! managedObjectContext.save()
try! realm.write {
realm.delete(tweet)
}
Filter
let fetchRequest = Tweet.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "text contains[c] %@",
keyword)
fetchRequest.sortDescriptors = [NSSortDescriptor(key:
#keyPath(Tweet.createdAt), ascending: false)]
try! fetchedResultsController?.performFetch()
let predicate = NSPredicate(format: "text contains[c] %@", keyword)
let tweets = realm.objects(Tweet.self).filter(predicate)
let sortedTweets = tweets.sorted("createdAt", ascending: ascending)
Core Data vs Realm
Core Data Realm
Latest Swift compatibility
Future proof
Ease of use
Multithreading
Speed
Encryption
Support
Real life testing
Next Time
• Persisting Data in the Cloud

More Related Content

KEY
Core Data
KEY
Data perisistence in iOS
PDF
Advanced Core Data
PPT
Hibernate jj
PPTX
Hibernate Basic Concepts - Presentation
PPTX
ASP.NET Lecture 4
PDF
Data Binding
PDF
Data Binding in Silverlight
Core Data
Data perisistence in iOS
Advanced Core Data
Hibernate jj
Hibernate Basic Concepts - Presentation
ASP.NET Lecture 4
Data Binding
Data Binding in Silverlight

What's hot (19)

PPT
Hibernate Tutorial
PPTX
Ch 7 data binding
PPTX
Entity Framework Database and Code First
PPTX
Ch06 ado.net fundamentals
PPT
For Beginers - ADO.Net
PDF
Introduction To Hibernate
PPT
ASP.NET Session 11 12
PPT
ADO.NET
PDF
Data Binding and Data Grid View Classes
PPTX
Ado .net
PPSX
ADO.NET
PPTX
For Beginners - Ado.net
PPTX
Chapter 3: ado.net
PPTX
Entity Framework: Code First and Magic Unicorns
PPTX
Ado.Net Tutorial
PPT
ADO .Net
PPT
ASP.NET 08 - Data Binding And Representation
PPTX
PDF
[2015/2016] Local data storage for web-based mobile apps
Hibernate Tutorial
Ch 7 data binding
Entity Framework Database and Code First
Ch06 ado.net fundamentals
For Beginers - ADO.Net
Introduction To Hibernate
ASP.NET Session 11 12
ADO.NET
Data Binding and Data Grid View Classes
Ado .net
ADO.NET
For Beginners - Ado.net
Chapter 3: ado.net
Entity Framework: Code First and Magic Unicorns
Ado.Net Tutorial
ADO .Net
ASP.NET 08 - Data Binding And Representation
[2015/2016] Local data storage for web-based mobile apps
Ad

Viewers also liked (20)

PPTX
ios_summit_2016_korhan
PDF
Korhan bircan
PDF
Useful Tools for Making Video Games - XNA (2008)
PDF
Встреча №8. NSIncrementalStore, или как заставить Core Data варить ваш собств...
PDF
Background Audio Playback
PDF
Useful Tools for Making Video Games - fmod (2008)
PDF
Next-Gen shaders (2008)
PDF
Core Data presentation
PDF
Advanced Imaging on iOS
PDF
Password Cracking with Rainbow Tables
PPTX
Swift Bengaluru Meetup slides
PPTX
SWIFT 3
PDF
Intro to Core Data
PDF
Client Server Security with Flask and iOS
PDF
Localization and Accessibility on iOS
PDF
Error Handling in Swift
PDF
Layout with Stack View, Table View, and Collection View
PDF
What's new in Swift 3
PDF
Improving apps with iOS 10 notifications (do iOS 2016)
PPTX
WWDC 2016
ios_summit_2016_korhan
Korhan bircan
Useful Tools for Making Video Games - XNA (2008)
Встреча №8. NSIncrementalStore, или как заставить Core Data варить ваш собств...
Background Audio Playback
Useful Tools for Making Video Games - fmod (2008)
Next-Gen shaders (2008)
Core Data presentation
Advanced Imaging on iOS
Password Cracking with Rainbow Tables
Swift Bengaluru Meetup slides
SWIFT 3
Intro to Core Data
Client Server Security with Flask and iOS
Localization and Accessibility on iOS
Error Handling in Swift
Layout with Stack View, Table View, and Collection View
What's new in Swift 3
Improving apps with iOS 10 notifications (do iOS 2016)
WWDC 2016
Ad

Similar to Core Data with Swift 3.0 (20)

KEY
iOSDevCamp 2011 Core Data
PDF
Taming Core Data by Arek Holko, Macoscope
PDF
Core data WIPJam workshop @ MWC'14
PPT
Core data orlando i os dev group
PDF
Core data intermediate Workshop at NSSpain 2013
PDF
Core data in Swfit
PDF
Infinum iOS Talks S01E02 - Things every iOS developer should know about Core ...
PDF
Core Data with multiple managed object contexts
PDF
CoreData
PDF
CoreData Best Practices (2021)
PDF
CoreData - there is an ORM you can like!
PDF
Adventures in Multithreaded Core Data
PPT
Connecting to a REST API in iOS
PDF
Iphone programming: Core Data Tutorial for iOS
PDF
Core data basic Workshop slides NSSpain 2013
PDF
CocoaHeads PDX 2014 01 23 : CoreData and iCloud Improvements iOS7 / OSX Maver...
PPTX
Core Data Performance Guide Line
PPT
Core data optimization
KEY
Taking a Test Drive
PDF
High Performance Core Data
iOSDevCamp 2011 Core Data
Taming Core Data by Arek Holko, Macoscope
Core data WIPJam workshop @ MWC'14
Core data orlando i os dev group
Core data intermediate Workshop at NSSpain 2013
Core data in Swfit
Infinum iOS Talks S01E02 - Things every iOS developer should know about Core ...
Core Data with multiple managed object contexts
CoreData
CoreData Best Practices (2021)
CoreData - there is an ORM you can like!
Adventures in Multithreaded Core Data
Connecting to a REST API in iOS
Iphone programming: Core Data Tutorial for iOS
Core data basic Workshop slides NSSpain 2013
CocoaHeads PDX 2014 01 23 : CoreData and iCloud Improvements iOS7 / OSX Maver...
Core Data Performance Guide Line
Core data optimization
Taking a Test Drive
High Performance Core Data

Recently uploaded (20)

PPT
Project quality management in manufacturing
PPTX
Internet of Things (IOT) - A guide to understanding
PPTX
Welding lecture in detail for understanding
PDF
The CXO Playbook 2025 – Future-Ready Strategies for C-Suite Leaders Cerebrai...
PPTX
Lesson 3_Tessellation.pptx finite Mathematics
PPTX
M Tech Sem 1 Civil Engineering Environmental Sciences.pptx
PPTX
UNIT-1 - COAL BASED THERMAL POWER PLANTS
PPTX
bas. eng. economics group 4 presentation 1.pptx
PDF
Operating System & Kernel Study Guide-1 - converted.pdf
PPTX
Lecture Notes Electrical Wiring System Components
PPTX
Engineering Ethics, Safety and Environment [Autosaved] (1).pptx
PPTX
web development for engineering and engineering
PDF
BMEC211 - INTRODUCTION TO MECHATRONICS-1.pdf
PPTX
MET 305 2019 SCHEME MODULE 2 COMPLETE.pptx
PPTX
Strings in CPP - Strings in C++ are sequences of characters used to store and...
PPTX
Recipes for Real Time Voice AI WebRTC, SLMs and Open Source Software.pptx
PDF
composite construction of structures.pdf
PPTX
Foundation to blockchain - A guide to Blockchain Tech
DOCX
ASol_English-Language-Literature-Set-1-27-02-2023-converted.docx
PDF
July 2025 - Top 10 Read Articles in International Journal of Software Enginee...
Project quality management in manufacturing
Internet of Things (IOT) - A guide to understanding
Welding lecture in detail for understanding
The CXO Playbook 2025 – Future-Ready Strategies for C-Suite Leaders Cerebrai...
Lesson 3_Tessellation.pptx finite Mathematics
M Tech Sem 1 Civil Engineering Environmental Sciences.pptx
UNIT-1 - COAL BASED THERMAL POWER PLANTS
bas. eng. economics group 4 presentation 1.pptx
Operating System & Kernel Study Guide-1 - converted.pdf
Lecture Notes Electrical Wiring System Components
Engineering Ethics, Safety and Environment [Autosaved] (1).pptx
web development for engineering and engineering
BMEC211 - INTRODUCTION TO MECHATRONICS-1.pdf
MET 305 2019 SCHEME MODULE 2 COMPLETE.pptx
Strings in CPP - Strings in C++ are sequences of characters used to store and...
Recipes for Real Time Voice AI WebRTC, SLMs and Open Source Software.pptx
composite construction of structures.pdf
Foundation to blockchain - A guide to Blockchain Tech
ASol_English-Language-Literature-Set-1-27-02-2023-converted.docx
July 2025 - Top 10 Read Articles in International Journal of Software Enginee...

Core Data with Swift 3.0

  • 1. Core Data with Swift 3 Capital One Engineering Tech Talks Korhan Bircan Senior Engineering Manager, Capital One
  • 2. Roadmap • Persistent Store Types • What is Core Data? • Demos • App Architecture • Core Data setup and common operations • Concurrency • Migration • Faults and Failures • Core Data vs Realm
  • 3. Persistent Store Types Store type Speed Object graph in memory Other XML Slow Whole Externally parsableBinary Fast Whole N/A SQLite Fast Partial N/A In-memory Fast Whole No storage required
  • 4. What Is Core Data? • Framework that helps manage the object life cycle and object graph management, including persistence • Grouping, filtering, and organization of data • Change tracking • Lazy loading of objects • User interface synchronization
  • 5. Demo - No Persistence
  • 6. Demo - Core Data
  • 10. Core Data Stack Diagram courtesy of Apple
  • 11. • Entity, attribute • Managed Object, Managed Object Context • Managed Object Model • Persistent Store: NSQLiteStoreType (non-atomic), NSXMLStoreType (macOS, atomic), NSBinaryStoreType (atomic), NSInMemoryStoreType (atomic), NSIncrementalStore (JSON, CSV?) • Persistent Store Coordinator • Relationships • Fetch Request, Predicate, Sort Descriptor • Fetched Results Controller Core Data Stack • Persistent Store Container
  • 12. Setup (Swift 2.3) lazy var applicationDocumentsDirectory: NSURL = { // The directory the application uses to store the Core Data store file. This code uses a directory named "CapitalOne.Delete" in the application's documents Application Support directory. let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask) return urls[urls.count-1] }() lazy var managedObjectModel: NSManagedObjectModel = { // The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model. let modelURL = NSBundle.mainBundle().URLForResource("Delete", withExtension: "momd")! return NSManagedObjectModel(contentsOfURL: modelURL)! }() lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = { // The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail. // Create the coordinator and store let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel) let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite") var failureReason = "There was an error creating or loading the application's saved data." do { try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: nil) } catch { // Report any error we got. var dict = [String: AnyObject]() dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data" dict[NSLocalizedFailureReasonErrorKey] = failureReason dict[NSUnderlyingErrorKey] = error as NSError let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict) // Replace this with code to handle the error appropriately. // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. NSLog("Unresolved error (wrappedError), (wrappedError.userInfo)") abort() } return coordinator }() lazy var managedObjectContext: NSManagedObjectContext = { // Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail. let coordinator = self.persistentStoreCoordinator var managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType) managedObjectContext.persistentStoreCoordinator = coordinator return managedObjectContext }() // MARK: - Core Data Saving support func saveContext () { if managedObjectContext.hasChanges { do { try managedObjectContext.save() } catch { // Replace this implementation with code to handle the error appropriately. // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. let nserror = error as NSError NSLog("Unresolved error (nserror), (nserror.userInfo)") abort() } } }
  • 13. Setup (Swift 3) lazy var managedContext: NSManagedObjectContext = { let context = self.storeContainer.viewContext context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy return context }() lazy var storeContainer: NSPersistentContainer = { let container = NSPersistentContainer(name: self.modelName) container.loadPersistentStores { (storeDescription, error) in if let error = error as NSError? { print("Unresolved error (error), (error.userInfo)") } else { print("Persistent store location: (storeDescription.url?.absoluteString)") } } return container }() func saveContext () { guard managedContext.hasChanges else { return } do { try self.managedContext.save() } catch let error as NSError { print("Unresolved error (error), (error.userInfo)") } }
  • 15. Saving and Retrieving Objects Save let tweet = Tweet(context: context) try! managedObjectContext.save() Retrieve let request: NSFetchRequest<Tweet> = Tweet.fetchRequest() let allTweets = try! context.fetch(request) Delete try! managedObjectContext.delete(tweet) try! managedObjectContext.save() Filter let fetchRequest = Tweet.fetchRequest() fetchRequest.predicate = NSPredicate(format: "text contains[c] %@", keyword) fetchRequest.sortDescriptors = [NSSortDescriptor(key: #keyPath(Tweet.createdAt), ascending: false)] try! fetchedResultsController?.performFetch()
  • 16. Fetch Predicates class Person: NSObject { let firstName: String let lastName: String let age: Int } let bobPredicate = NSPredicate(format: "firstName = 'Bob'") // let smithPredicate = NSPredicate(format: "lastName = %@", "Smith") // let thirtiesPredicate = NSPredicate(format: "age >= 30") fetchRequest.predicate = bobPredicate try! fetchedResultsController?.performFetch() • Basic Comparisons =, ==; >=, =>; <=, =<; >; <; != • Compound Predicates AND, &&; OR, ||; NOT, ! • String Comparisons BEGINSWITH; CONTAINS; ENDSWITH; LIKE; MATCHES
  • 17. Fetched Results Controller let fetchRequest.predicate = keyword.isEmpty ? nil : NSPredicate(format: "text contains[c] %@", keyword) fetchRequest.sortDescriptors = [NSSortDescriptor(key: #keyPath(Tweet.createdAt), ascending: false)] let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: CoreDataStack.sharedInstance.managedContext, sectionNameKeyPath: nil, cacheName: nil) do { try fetchedResultsController?.performFetch() } catch let error as NSError { print("Fetching error: (error), (error.userInfo)") }
  • 18. Wiring Model to UI override func numberOfSections(in tableView: UITableView) -> Int { return fetchedResultsController.sections } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return fetchedResultsController.sections?[section].numberOfObjects } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: TweetCell.identifier, for: indexPath) if let tweet = fetchedResultsController.object(at: indexPath) { TimelineViewCellAdapter.configure(cell as? TweetCell, tweet: tweet) } return cell } override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { let sectionInfo = fetchedResultsController.sections[section] return sectionInfo?.name }
  • 19. Communicating Data Changes func controllerWillChangeContent(controller: NSFetchedResultsController) { tableView.beginUpdates() } func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) { switch type { case .Insert: tableView.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade) case .Delete: tableView.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade) case .Move: break case .Update: break } } func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) { switch type { case .Insert: tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade) case .Delete: tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade) case .Update: configureCell(tableView.cellForRowAtIndexPath(indexPath!)!, indexPath: indexPath!) case .Move: tableView.moveRowAtIndexPath(indexPath!, toIndexPath: newIndexPath!) } } func controllerDidChangeContent(controller: NSFetchedResultsController) { tableView.endUpdates() }
  • 20. Concurrency let jsonArray = … //JSON data to be imported into Core Data let moc = … //Our primary context on the main queue let privateMOC = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType) privateMOC.parentContext = moc privateMOC.performBlock { for jsonObject in jsonArray { let mo = … //Managed object that matches the incoming JSON structure //update managed object with data from the dictionary } do { try privateMOC.save() moc.performBlockAndWait { do { try moc.save() } catch { fatalError("Failure to save context: (error)") } } } catch { fatalError("Failure to save context: (error)") } }
  • 22. Swift 2.3 vs Swift 3 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { do { let json = try NSJSONSerialization.JSONObjectWithData(data!, options: []) TimelineResponseMapper.parseResponse(json) dispatch_async(dispatch_get_main_queue(), { self.tableView.reloadData() }) } catch let jsonError as NSError { print("json error: (jsonError.localizedDescription)") } }) // vs DispatchQueue.global(qos: DispatchQoS.QoSClass.default).async { do { let json = try JSONSerialization.jsonObject(with: data!, options: []) TimelineResponseMapper.parseResponse(json) DispatchQueue.main.async { self.tableView.reloadData() } } catch let jsonError as NSError { print("json error: (jsonError.localizedDescription)") } }
  • 23. Tidbits • Multiple managed object contexts • Migration • Faulting and uniquing • Concurrency failures
  • 24. Migration lazy var storeContainer: NSPersistentContainer = { let container = NSPersistentContainer(name: self.modelName) container.loadPersistentStores { (storeDescription, error) in storeDescription.shouldMigrateStoreAutomatically = true storeDescription.shouldInferMappingModelAutomatically = true
  • 25. Why faults? • Dictionary definition: 1. (noun) an unattractive or unsatisfactory feature, esp. in a piece of work or in a person’s character. 2. (noun) responsibility for an accident or misfortune. 3. (noun) an extended break in a body of rock, marked by the relative displacement and discontinuity of strata on either side of a particular surface. • Apple’s definition: • A fault is a placeholder object that represents a managed object that has not yet been fully realized, or a collection object that represents a relationship.
  • 27. Why Faults May Fail 2016-10-11 22:20:24.530 CoreDataTest[24222:60b] * Assertion failure in -[AppDelegate save], .../AppDelegate.swift:28 2016-10-11 22:20:24.530 CoreDataTest[24222:60b] * Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Save failed {type = immutable dict, count = 2, entries => 1 : {contents = "NSAffectedObjectsErrorKey"} = ( " (entity: Tweet; id: 0x6b8bf10 ; data: )" ) 2 : {contents = "NSUnderlyingException"} = CoreData could not fulfill a fault for '0x6b8bf10 ' }
  • 28. Handling Fault Failures • managedObjectContext.shouldDeleteInaccessibleFaults • Prefetch everything • Write lots of code (existingobjectwithId() to make sure the object is in the database before referencing it) • Use query generators
  • 29. Concurrency Failures let firstTweet = Tweet(context: managedContext) // Synchronously performs the block on the context's queue. //May safely be called reentrantly. managedContext.performAndWait { do { firstTweet.text = "Something interesting happened today..." try self.managedContext.save() } catch let error as NSError { print("Unresolved error (error), (error.userInfo)") } } managedContext2.performAndWait { let text = firstTweet.text print(text) } 2016-10-11 22:20:24.530 CoreDataTest[24222:60b] CoreData concurrency failure
  • 30. Realm CoreData Realm Save let tweet = Tweet(context: context) try! managedObjectContext.save() let realm = try! Realm() let tweet = Tweet() try! realm.write { realm.add(tweet) } Retrieve let request: NSFetchRequest<Tweet> = Tweet.fetchRequest() let allTweets = try! context.fetch(request) let tweets = realm.objects(Tweet.self) Delete try! managedObjectContext.delete(tweet) try! managedObjectContext.save() try! realm.write { realm.delete(tweet) } Filter let fetchRequest = Tweet.fetchRequest() fetchRequest.predicate = NSPredicate(format: "text contains[c] %@", keyword) fetchRequest.sortDescriptors = [NSSortDescriptor(key: #keyPath(Tweet.createdAt), ascending: false)] try! fetchedResultsController?.performFetch() let predicate = NSPredicate(format: "text contains[c] %@", keyword) let tweets = realm.objects(Tweet.self).filter(predicate) let sortedTweets = tweets.sorted("createdAt", ascending: ascending)
  • 31. Core Data vs Realm Core Data Realm Latest Swift compatibility Future proof Ease of use Multithreading Speed Encryption Support Real life testing
  • 32. Next Time • Persisting Data in the Cloud