CoreDataServices is a suite of helper classes and categories to help to remove some of the boilerplate that surrounds using Core Data.
Installation via CocoaPods
To integrate CoreDataServices into your Xcode project using CocoaPods, specify it in your Podfile
:
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '9.0'
pod 'CoreDataServices'
Then, run the following command:
$ pod install
CocoaPods 1.0.1+ is required to build CoreDataServices.
CoreDataServices is mainly composed of a suite of categories/extensions that extend NSManagedObjectContext
.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
ServiceManager.shared.setupModel(name: "Model")
return true
}
var _users: [User]?
var users: [User] {
if(_users == nil) {
let sortDescriptor = NSSortDescriptor(key: "dateAdded", ascending: false)
_users = ServiceManager.shared.mainManagedObjectContext.retrieveEntries(entityClass: User.self, sortDescriptors: [sortDescriptor])
}
return _users!
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
let totalUsers = ServiceManager.shared.mainManagedObjectContext.retrieveEntriesCount(entityClass: User.self)
return "Total Users: \(totalUsers)"
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let user = users[indexPath.row]
let predicate = NSPredicate(format: "userID==%@", user.userID!)
ServiceManager.shared.mainManagedObjectContext.deleteEntries(entityClass: User.self, predicate: predicate)
ServiceManager.shared.saveMainManagedObjectContext()
clearAndReloadUsers()
}
ServiceManager.shared.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.shared.saveMainManagedObjectContext()
//Background thread's context
ServiceManager.shared.saveBackgroundManagedObjectContext()
func addUserOnBackgroundContext() {
DispatchQueue.global(qos: .background).async { [weak self] in
ServiceManager.shared.backgroundManagedObjectContext.performAndWait({
let user = NSEntityDescription.insertNewObject(entityClass: User.self, managedObjectContext: ServiceManager.shared.backgroundManagedObjectContext)
user.userID = UUID().uuidString
user.name = "Anna BackgroundContext"
user.dateOfBirth = Date.randomDate(daysBack: 30000)
ServiceManager.shared.saveBackgroundManagedObjectContext()
DispatchQueue.main.async(execute: {
self?.clearAndReloadUsers()
})
})
}
}
CoreDataServices has the following implementation of Core Data stack:
- One
NSManagedObjectContext
using the.mainQueueConcurrencyType
concurrency type that is attached directly to thePersistentStoreCoordinator
- 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.
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.