Lightweight Active-record-ish pattern.
☁️ Works great with, or without, CloudKit.
✅ Manage your NSManagedObjectContext(s) however you want.
👨💻 Works with automatic or manual NSManagedObject code generation.
import CoreDataPlus
// An existing NSManagedObject
public class Drawing: NSManagedObject { }
extension Drawing: ManagedObjectPlus {
}You can also import just the features you want to use by replacing ManagedObjectPlus with one or more of the available ManagedObject* protocols:
extension Drawing: ManagedObjectDeletable,
ManagedObjectCountable,
ManagedObjectSearchable,
ManagedObjectFindOrCreateBy {
}Open Example App/Example App.xcodeproj.
Adds a new, more concise, initializer for NSPredicate. Aliased as Predicate.
You can type
Predicate("color = %@ and city = %@", "Blue", city)Instead of
NSPredicate(format:"color = %@ and city = %@", argumentArray:["Blue", city])Returns a new, non-nil, NSPredicate object that will match every single row in the database.
This is especially helpful with:
- The @FetchRequest property wrapper. Using a nil predicate can cause unexpected behavior if you dynamically assign or edit the predicate at runtime.
- Other Core Data quirks around nil predicates and updating when you do fancy stuff.
Before:
@FetchRequest(sortDescriptors: [], predicate: nil)After:@FetchRequest(sortDescriptors: [], predicate: Predicate.empty())
Finds an instance of the NSManagedObject that has a column (property) matching the passed value. If it doesn't exist in the database, creates one, and returns it.
let d = Drawing.findOrCreate(column: "my-column", value: "123456789", context: viewContext)
d.timestamp = Date()The idea is that each object has a unique, deterministic, human-readable, identifier. So if you have a User with an id of 123, you can do let user = User.findOrCreate(id: "123") whenever and wherever you need access to the user object.
If your xcdatamodel has a String property called id, you can use this shortcut:
let d = Drawing.findOrCreate(id: "123", context: viewContext)Tip: If you're using SwiftUI, adding a String column id plays nicely with Identifiable
Same as findOrCreate, but returns nil if there is no object in the database.
if let d = Drawing.findButDoNotCreate(id: "10001", context: viewContext) {
d.timestamp = Date()
d.title = "My title"
}Gets a count of objects matching the passed Predicate
let count = Drawing.countFor(Predicate("someField = %@", true), context: viewContext)Gets all objects matching the passed Predicate
let results = Drawing.searchFor(Predicate("foo = %@", "bar"), context: viewContext)
print("\(results.count) objects found")
for drawing in results {
dump(drawing)
}Removes all objects from Core Data. Deletes them from the context.
Drawing.destroyAll(context: viewContext)You can also specify a predicate to only delete some items:
let withSpanishLanguage = Predicate("languages contains %@", es)
Country.destroyAll(matching: withSpanishLanguage)Remember to save the context after deleting objects.
By default, log messages do not appear. To take action when there's a problem, use CoreDataPlus.Logger.configure(...)
CoreDataPlus.Logger.configure(logHandler: { message in
print("A log message from the data layer: \(message)")
})Recommended: set the logHandler in your AppDelegate. Example below:
// Your app's main .swift file:
import SwiftUI
@main
struct MyFancyApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
// A new file called AppDelegate.swift
import Foundation
import UIKit
import CoreDataPlus
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
CoreDataPlus.Logger.configure(logHandler: { message in
print("A log message from the data layer: \(message)")
})
return true
}
}Optional. If you specify a view context and a background context using CoreDataPlus.setup(...), you can simplify passing of NSManagedObjectContext objects.
For example, Book.findOrCreate(id:"123", context: foo) becomes Book.findOrCreate(id:"123") and Author.findOrCreate(id:"123", context: bar) becomes Author.findOrCreate(id:"123", using: .background). See the included example app for more examples.
You can pass the viewContext variable as the context for all this library's methods.
Most SwiftUI quickstart guides use .environment(\.managedObjectContext, foo) on the view, along with
Main app file:
import SwiftUI
@main
struct MyFancyApp: App {
let persistenceController = PersistenceController.shared // from the default Xcode new project setup
var body: some Scene {
WindowGroup {
ContentView()
.environment(\.managedObjectContext, persistenceController.container.viewContext)
}
}
}ContentView file:
import SwiftUI
import CoreData
import CoreDataPlus
struct ContentView: View {
@Environment(\.managedObjectContext) private var viewContext
var body: some View {
VStack {
Button("Hello", action: {
Drawing.destroyAll(context: self.viewContext)
})
}
}
}- Unit tests
- Option to disable automatic saving when using
destroyAll - Add docs on
NSPersistentContaineragnosticity - Have an idea? Open an issue!
