CoreDataServices 4.0.0

CoreDataServices 4.0.0

TestsTested
LangLanguage SwiftSwift
License MIT
ReleasedLast Release Dec 2017
SwiftSwift Version 4.0
SPMSupports SPM

Maintained by William Boles.



  • By
  • William Boles

CoreDataServices is a suite of helper classes and categories to help to remove some of the boilerplate that surrounds using Core Data.

Usage

CoreDataServices is mainly composed of a suite of categories/extensions that extend NSManagedObjectContext.

Init

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        ServiceManager.sharedInstance.setupModel(name: "Model")
        return true
}

Retrieving

lazy var users: [User] = {
    let ageSort = NSSortDescriptor(key: "age", ascending: true)

    let users = ServiceManager.sharedInstance.mainManagedObjectContext.retrieveEntries(entityClass: User.self, sortDescriptors: [ageSort])

    return users
}

Counting

func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    let totalUsers = ServiceManager.sharedInstance.mainManagedObjectContext.retrieveEntriesCount(entityClass: User.self)

    return "Total Users: \(totalUsers)"
}

Deleting

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    let user = users[indexPath.row]

    let predicate = NSPredicate(format: "userID==%@", user.userID!)
    ServiceManager.sharedInstance.mainManagedObjectContext.deleteEntries(entityClass: User.self, predicate: predicate)
    ServiceManager.sharedInstance.saveMainManagedObjectContext()
        
    clearAndReloadUsers()
}

Saving

    ServiceManager.sharedInstance.mainManagedObjectContext.saveAndForcePushChangesIfNeeded()

What is interesting to note is when calling saveAndForcePushChangesIfNeeded on a background/private context the changes will be propagated through parent contexts until save is called on the main context. This introduces a small performance overhead but ensures that saved changes are not lost if the app crashes.

Below are two convenience methods to make saving easier.

    //Main thread's context
    ServiceManager.sharedInstance.saveMainManagedObjectContext()
    
    //Background thread's context
    ServiceManager.sharedInstance.saveBackgroundManagedObjectContext()

Using BackgroundManagedObjectContext

func addUserOnBackgroundContext() {
    DispatchQueue.global(qos: .background).async { [weak self] in
        ServiceManager.sharedInstance.backgroundManagedObjectContext.performAndWait({
            let user = NSEntityDescription.insertNewObject(entityClass: User.self, managedObjectContext: ServiceManager.sharedInstance.backgroundManagedObjectContext)
            
            user.userID = UUID().uuidString
            user.name = "Anna BackgroundContext"
            user.age = Int16(arc4random_uniform(102))
            
            ServiceManager.sharedInstance.saveBackgroundManagedObjectContext()
            
            DispatchQueue.main.async(execute: {
                self?.clearAndReloadUsers()
            })
        })
    }
}

Using in multi-threaded project

CoreDataServices has the following implementation of Core Data stack:

  • One NSManagedObjectContext using the .mainQueueConcurrencyType concurrency type that is attached directly to the PersistentStoreCoordinator - the intention is for this context to only be used on the main-thread.
  • One NSManagedObjectContext using the .privateQueueConcurrencyType concurrency type that has the .mainQueueConcurrencyType context as it’s parent - the intention is for this context to only be used on background-threads.

CoreDataServices uses the newer main/private concurrency solution rather than confinement concurrency as it offers conceptually the easiest solution. However in order for this to behave as expected when on a background-thread you will need to ensure that you use either perform or performAndWait to access the background-thread context. to ensure that the context is being used on the correct thread.

An interesting article about different configurations to the Core Data stack can be found here.

CoreDataServices comes with an example project to provide more details than listed above.

CoreDataServices uses modules for importing/using frameworks - you will need to enable this in your project.

Found an issue?

Please open a new Issue here if you run into a problem specific to CoreDataServices, have a feature request, or want to share a comment. Note that general Core Data questions should be asked on Stack Overflow.

Pull requests are encouraged and greatly appreciated! Please try to maintain consistency with the existing code style. If you’re considering taking on significant changes or additions to the project, please communicate in advance by opening a new Issue. This allows everyone to get onboard with upcoming changes, ensures that changes align with the project’s design philosophy, and avoids duplicated work.