Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions Project.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -502,10 +502,6 @@
14F6277A1E7AE6E1001C2EA0 /* DataStack.swift in Headers */ = {isa = PBXBuildFile; fileRef = 1467388B1E7ADBA700913C8E /* DataStack.swift */; settings = {ATTRIBUTES = (Public, ); }; };
1AFF18CB1FE1F08900C4BC3D /* 457-products.json in Resources */ = {isa = PBXBuildFile; fileRef = 1AFF18C91FE1F08800C4BC3D /* 457-products.json */; };
1AFF18CC1FE1F08900C4BC3D /* 457-subcategories.json in Resources */ = {isa = PBXBuildFile; fileRef = 1AFF18CA1FE1F08800C4BC3D /* 457-subcategories.json */; };
25A89F821F00ED7E008ADD4B /* Sync+ObjC.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47FF6201F0002A3004A923A /* Sync+ObjC.swift */; };
25A89F831F00ED7E008ADD4B /* Sync+ObjC.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47FF6201F0002A3004A923A /* Sync+ObjC.swift */; };
25A89F841F00ED7F008ADD4B /* Sync+ObjC.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47FF6201F0002A3004A923A /* Sync+ObjC.swift */; };
25A89F851F00ED7F008ADD4B /* Sync+ObjC.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47FF6201F0002A3004A923A /* Sync+ObjC.swift */; };
445851BB1E87BAF50025434E /* DictionaryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4458519C1E87BAF50025434E /* DictionaryTests.swift */; };
445851BC1E87BAF50025434E /* DictionaryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4458519C1E87BAF50025434E /* DictionaryTests.swift */; };
445851BD1E87BAF50025434E /* DictionaryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4458519C1E87BAF50025434E /* DictionaryTests.swift */; };
Expand Down Expand Up @@ -1092,7 +1088,6 @@
CC9494171F379BE1002167B4 /* 373.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = 373.json; sourceTree = "<group>"; };
CCF94956203AE72700C64B80 /* 480.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = 480.xcdatamodel; sourceTree = "<group>"; };
CCF9495A203AF4AF00C64B80 /* 480.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = 480.json; sourceTree = "<group>"; };
E47FF6201F0002A3004A923A /* Sync+ObjC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Sync+ObjC.swift"; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -1585,7 +1580,6 @@
14549A0A1E7C2E1000A77F2E /* Sync+Helpers.swift */,
14D5255E1E7C1EC30063909F /* Sync+NSManagedObjectContext.swift */,
142CD2AC1DEF3A01002FDABE /* Sync+NSPersistentContainer.swift */,
E47FF6201F0002A3004A923A /* Sync+ObjC.swift */,
);
path = Sync;
sourceTree = "<group>";
Expand Down Expand Up @@ -2321,7 +2315,6 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
25A89F821F00ED7E008ADD4B /* Sync+ObjC.swift in Sources */,
14241EA01DBC3A6F0042ED81 /* NSArray+Sync.swift in Sources */,
44B212F81E87BD5D00C81949 /* NSPropertyDescription+Sync.m in Sources */,
14241EA11DBC3A6F0042ED81 /* NSEntityDescription+Sync.swift in Sources */,
Expand Down Expand Up @@ -2349,7 +2342,6 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
25A89F851F00ED7F008ADD4B /* Sync+ObjC.swift in Sources */,
14241EA81DBC3A770042ED81 /* NSArray+Sync.swift in Sources */,
44B212FB1E87BD5D00C81949 /* NSPropertyDescription+Sync.m in Sources */,
14241EA91DBC3A770042ED81 /* NSEntityDescription+Sync.swift in Sources */,
Expand Down Expand Up @@ -2377,7 +2369,6 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
25A89F831F00ED7E008ADD4B /* Sync+ObjC.swift in Sources */,
14241EB01DBC3A7F0042ED81 /* NSArray+Sync.swift in Sources */,
44B212F91E87BD5D00C81949 /* NSPropertyDescription+Sync.m in Sources */,
14241EB11DBC3A7F0042ED81 /* NSEntityDescription+Sync.swift in Sources */,
Expand Down Expand Up @@ -2405,7 +2396,6 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
25A89F841F00ED7F008ADD4B /* Sync+ObjC.swift in Sources */,
14241EB81DBC3A880042ED81 /* NSArray+Sync.swift in Sources */,
44B212FA1E87BD5D00C81949 /* NSPropertyDescription+Sync.m in Sources */,
14241EB91DBC3A880042ED81 /* NSEntityDescription+Sync.swift in Sources */,
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ Syncing JSON to Core Data is a repetitive tasks that often demands adding a lot
DataStack is a wrapper on top of the Core Data boilerplate, it encapsulates dealing with NSPersistentStoreCoordinator and NSManageObjectContexts.

```swift
self.dataStack = DataStack(modelName: "DataModel")
self.dataStack = try! DataStack(modelName: "DataModel")
```

[You can find here more ways of initializing your DataStack](https://github.com/3lvis/Sync/blob/6723c1f9a07014024e0f8f2923d1930789cabb72/Source/DataStack/DataStack.swift#L77-L196).
Expand Down Expand Up @@ -134,7 +134,7 @@ https://github.com/3lvis/StoryboardDemo
Replace your Core Data stack with an instance of [DataStack](https://github.com/3lvis/Sync/blob/master/docs/DataStack.md).

```swift
self.dataStack = DataStack(modelName: "Demo")
self.dataStack = try! DataStack(modelName: "Demo")
```

### Primary key
Expand Down
112 changes: 65 additions & 47 deletions Source/DataStack/DataStack.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,15 @@ import CoreData
}
}

@objc public class DataStack: NSObject {
public enum DataStackError: Error {
case persistentStoreCoordinatorCreationErrorForInMemoryStore(_ error: NSError)
case couldNotCopyPreloadedData(_ error: NSError)
case persistentStoreCoordinatorCreationErrorForSQLite(_ error: NSError)
case persistentStoreCoordinatorRemovalError(_ error: NSError)
case excludingSQLiteFromBackgroundError(_ error: NSError)
}

public class DataStack {
private var storeType = DataStackStoreType.sqLite

private var storeName: String?
Expand All @@ -35,17 +43,7 @@ import CoreData
The context for the main queue. Please do not use this to mutate data, use `performInNewBackgroundContext`
instead.
*/
@objc public lazy var mainContext: NSManagedObjectContext = {
let context = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
context.undoManager = nil
context.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy
context.persistentStoreCoordinator = self.persistentStoreCoordinator

NotificationCenter.default.addObserver(self, selector: #selector(DataStack.mainContextDidSave(_:)), name: .NSManagedObjectContextDidSave, object: context)

return context
}()

@objc public var mainContext: NSManagedObjectContext!
/**
The context for the main queue. Please do not use this to mutate data, use `performBackgroundTask`
instead.
Expand All @@ -63,12 +61,7 @@ import CoreData
return context
}()

@objc public private(set) lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
let persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: self.model)
try! persistentStoreCoordinator.addPersistentStore(storeType: self.storeType, bundle: self.modelBundle, modelName: self.modelName, storeName: self.storeName, containerURL: self.containerURL)

return persistentStoreCoordinator
}()
@objc public private(set) var persistentStoreCoordinator: NSPersistentStoreCoordinator!

private lazy var disposablePersistentStoreCoordinator: NSPersistentStoreCoordinator = {
let model = NSManagedObjectModel(bundle: self.modelBundle, name: self.modelName)
Expand All @@ -82,39 +75,62 @@ import CoreData
Initializes a DataStack using the bundle name as the model name, so if your target is called ModernApp,
it will look for a ModernApp.xcdatamodeld.
*/
@objc public override init() {
public init() throws {
let bundle = Bundle.main
if let bundleName = bundle.infoDictionary?["CFBundleName"] as? String {
self.modelName = bundleName
}
self.model = NSManagedObjectModel(bundle: self.modelBundle, name: self.modelName)

super.init()
// MainContext
let context = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
context.undoManager = nil
context.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy
context.persistentStoreCoordinator = self.persistentStoreCoordinator

self.mainContext = context
NotificationCenter.default.addObserver(self, selector: #selector(DataStack.mainContextDidSave(_:)), name: .NSManagedObjectContextDidSave, object: context)
}

/**
Initializes a DataStack using the provided model name.
- parameter modelName: The name of your Core Data model (xcdatamodeld).
*/
@objc public init(modelName: String) {
public init(modelName: String) throws {
self.modelName = modelName
self.model = NSManagedObjectModel(bundle: self.modelBundle, name: self.modelName)

super.init()
try setup()
}

func setup() throws {
// MainContext
let context = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
context.undoManager = nil
context.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy

let persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: self.model)
try persistentStoreCoordinator.addPersistentStore(storeType: self.storeType, bundle: self.modelBundle, modelName: self.modelName, storeName: self.storeName, containerURL: self.containerURL)
self.persistentStoreCoordinator = persistentStoreCoordinator

context.persistentStoreCoordinator = self.persistentStoreCoordinator

self.mainContext = context
NotificationCenter.default.addObserver(self, selector: #selector(DataStack.mainContextDidSave(_:)), name: .NSManagedObjectContextDidSave, object: context)
}

/**
Initializes a DataStack using the provided model name, bundle and storeType.
- parameter modelName: The name of your Core Data model (xcdatamodeld).
- parameter storeType: The store type to be used, you have .InMemory and .SQLite, the first one is memory
based and doesn't save to disk, while the second one creates a .sqlite file and stores things there.
*/
@objc public init(modelName: String, storeType: DataStackStoreType) {
public init(modelName: String, storeType: DataStackStoreType) throws {
self.modelName = modelName
self.storeType = storeType
self.model = NSManagedObjectModel(bundle: self.modelBundle, name: self.modelName)

super.init()
try setup()
}

/**
Expand All @@ -126,13 +142,13 @@ import CoreData
- parameter storeType: The store type to be used, you have .InMemory and .SQLite, the first one is memory
based and doesn't save to disk, while the second one creates a .sqlite file and stores things there.
*/
@objc public init(modelName: String, bundle: Bundle, storeType: DataStackStoreType) {
public init(modelName: String, bundle: Bundle, storeType: DataStackStoreType) throws {
self.modelName = modelName
self.modelBundle = bundle
self.storeType = storeType
self.model = NSManagedObjectModel(bundle: self.modelBundle, name: self.modelName)

super.init()
try setup()
}

/**
Expand All @@ -147,14 +163,14 @@ import CoreData
name is AwesomeApp then the .sqlite file will be named AwesomeApp.sqlite, this attribute allows your to
change that.
*/
@objc public init(modelName: String, bundle: Bundle, storeType: DataStackStoreType, storeName: String) {
public init(modelName: String, bundle: Bundle, storeType: DataStackStoreType, storeName: String) throws {
self.modelName = modelName
self.modelBundle = bundle
self.storeType = storeType
self.storeName = storeName
self.model = NSManagedObjectModel(bundle: self.modelBundle, name: self.modelName)

super.init()
try setup()
}

/**
Expand All @@ -170,15 +186,15 @@ import CoreData
change that.
- parameter containerURL: The container URL for the sqlite file when a store type of SQLite is used.
*/
@objc public init(modelName: String, bundle: Bundle, storeType: DataStackStoreType, storeName: String, containerURL: URL) {
public init(modelName: String, bundle: Bundle, storeType: DataStackStoreType, storeName: String, containerURL: URL) throws {
self.modelName = modelName
self.modelBundle = bundle
self.storeType = storeType
self.storeName = storeName
self.containerURL = containerURL
self.model = NSManagedObjectModel(bundle: self.modelBundle, name: self.modelName)

super.init()
try setup()
}

/**
Expand All @@ -187,16 +203,16 @@ import CoreData
- parameter storeType: The store type to be used, you have .InMemory and .SQLite, the first one is memory
based and doesn't save to disk, while the second one creates a .sqlite file and stores things there.
*/
@objc public init(model: NSManagedObjectModel, storeType: DataStackStoreType) {
public init(model: NSManagedObjectModel, storeType: DataStackStoreType) throws {
self.model = model
self.storeType = storeType

let bundle = Bundle.main
if let bundleName = bundle.infoDictionary?["CFBundleName"] as? String {
self.storeName = bundleName
}

super.init()
try setup()
}

deinit {
Expand Down Expand Up @@ -235,10 +251,12 @@ import CoreData
/**
Returns a background context perfect for data mutability operations. Make sure to never use it on the main thread. Use `performBlock` or `performBlockAndWait` to use it.
*/
@objc public func newBackgroundContext() -> NSManagedObjectContext {
@objc public func newBackgroundContext() throws -> NSManagedObjectContext {
let context = NSManagedObjectContext(concurrencyType: DataStack.backgroundConcurrencyType())
context.name = backgroundContextName

context.persistentStoreCoordinator = self.persistentStoreCoordinator

context.undoManager = nil
context.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy

Expand All @@ -251,8 +269,8 @@ import CoreData
Returns a background context perfect for data mutability operations.
- parameter operation: The block that contains the created background context.
*/
@objc public func performInNewBackgroundContext(_ operation: @escaping (_ backgroundContext: NSManagedObjectContext) -> Void) {
let context = self.newBackgroundContext()
public func performInNewBackgroundContext(_ operation: @escaping (_ backgroundContext: NSManagedObjectContext) -> Void) throws {
let context = try self.newBackgroundContext()
let contextBlock: @convention(block) () -> Void = {
operation(context)
}
Expand All @@ -264,8 +282,8 @@ import CoreData
Returns a background context perfect for data mutability operations.
- parameter operation: The block that contains the created background context.
*/
@objc public func performBackgroundTask(operation: @escaping (_ backgroundContext: NSManagedObjectContext) -> Void) {
self.performInNewBackgroundContext(operation)
@objc public func performBackgroundTask(operation: @escaping (_ backgroundContext: NSManagedObjectContext) -> Void) throws {
try self.performInNewBackgroundContext(operation)
}

func saveMainThread(completion: ((_ error: NSError?) -> Void)?) {
Expand Down Expand Up @@ -415,7 +433,7 @@ extension NSPersistentStoreCoordinator {
do {
try self.addPersistentStore(ofType: NSInMemoryStoreType, configurationName: nil, at: nil, options: nil)
} catch let error as NSError {
throw NSError(info: "There was an error creating the persistentStoreCoordinator for in memory store", previousError: error)
throw DataStackError.persistentStoreCoordinatorCreationErrorForInMemoryStore(error)
}

break
Expand All @@ -431,7 +449,7 @@ extension NSPersistentStoreCoordinator {
do {
try FileManager.default.copyItem(at: preloadURL, to: storeURL)
} catch let error as NSError {
throw NSError(info: "Oops, could not copy preloaded data", previousError: error)
throw DataStackError.couldNotCopyPreloadedData(error)
}
}
}
Expand All @@ -445,10 +463,10 @@ extension NSPersistentStoreCoordinator {
do {
try self.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storeURL, options: options)
} catch let addPersistentError as NSError {
throw NSError(info: "There was an error creating the persistentStoreCoordinator", previousError: addPersistentError)
throw DataStackError.persistentStoreCoordinatorCreationErrorForSQLite(addPersistentError)
}
} catch let removingError as NSError {
throw NSError(info: "There was an error removing the persistentStoreCoordinator", previousError: removingError)
throw DataStackError.persistentStoreCoordinatorRemovalError(removingError)
}
}

Expand All @@ -457,7 +475,7 @@ extension NSPersistentStoreCoordinator {
do {
try (storeURL as NSURL).setResourceValue(true, forKey: .isExcludedFromBackupKey)
} catch let excludingError as NSError {
throw NSError(info: "Excluding SQLite file from backup caused an error", previousError: excludingError)
throw DataStackError.excludingSQLiteFromBackgroundError(excludingError)
}
}

Expand Down
Loading