WIP: StorageInterface protocol

This commit is contained in:
John Rommel Estropia
2016-03-03 07:50:43 +09:00
parent 99189d160f
commit ad1ebb3501
17 changed files with 214 additions and 261 deletions

View File

@@ -263,10 +263,10 @@
B5FAD6A91B50A4B400714891 /* NSProgress+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FAD6A81B50A4B300714891 /* NSProgress+Convenience.swift */; };
B5FAD6AC1B51285300714891 /* MigrationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FAD6AB1B51285300714891 /* MigrationManager.swift */; };
B5FAD6AE1B518DCB00714891 /* CoreStore+Migration.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FAD6AD1B518DCB00714891 /* CoreStore+Migration.swift */; };
B5FE4DA21C8481E100FA6A91 /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FE4DA11C8481E100FA6A91 /* Storage.swift */; };
B5FE4DA31C8481E100FA6A91 /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FE4DA11C8481E100FA6A91 /* Storage.swift */; };
B5FE4DA41C8481E100FA6A91 /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FE4DA11C8481E100FA6A91 /* Storage.swift */; };
B5FE4DA51C8481E100FA6A91 /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FE4DA11C8481E100FA6A91 /* Storage.swift */; };
B5FE4DA21C8481E100FA6A91 /* StorageInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FE4DA11C8481E100FA6A91 /* StorageInterface.swift */; };
B5FE4DA31C8481E100FA6A91 /* StorageInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FE4DA11C8481E100FA6A91 /* StorageInterface.swift */; };
B5FE4DA41C8481E100FA6A91 /* StorageInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FE4DA11C8481E100FA6A91 /* StorageInterface.swift */; };
B5FE4DA51C8481E100FA6A91 /* StorageInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FE4DA11C8481E100FA6A91 /* StorageInterface.swift */; };
B5FE4DA71C84FB4400FA6A91 /* InMemoryStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FE4DA61C84FB4400FA6A91 /* InMemoryStore.swift */; };
B5FE4DA81C84FB4400FA6A91 /* InMemoryStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FE4DA61C84FB4400FA6A91 /* InMemoryStore.swift */; };
B5FE4DA91C84FB4400FA6A91 /* InMemoryStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FE4DA61C84FB4400FA6A91 /* InMemoryStore.swift */; };
@@ -391,7 +391,7 @@
B5FAD6A81B50A4B300714891 /* NSProgress+Convenience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSProgress+Convenience.swift"; sourceTree = "<group>"; };
B5FAD6AB1B51285300714891 /* MigrationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MigrationManager.swift; sourceTree = "<group>"; };
B5FAD6AD1B518DCB00714891 /* CoreStore+Migration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CoreStore+Migration.swift"; sourceTree = "<group>"; };
B5FE4DA11C8481E100FA6A91 /* Storage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Storage.swift; sourceTree = "<group>"; };
B5FE4DA11C8481E100FA6A91 /* StorageInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StorageInterface.swift; sourceTree = "<group>"; };
B5FE4DA61C84FB4400FA6A91 /* InMemoryStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InMemoryStore.swift; sourceTree = "<group>"; };
B5FE4DAB1C85D44E00FA6A91 /* SQLiteStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteStore.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
@@ -706,7 +706,7 @@
B5FE4DA01C84818B00FA6A91 /* PersistentStores */ = {
isa = PBXGroup;
children = (
B5FE4DA11C8481E100FA6A91 /* Storage.swift */,
B5FE4DA11C8481E100FA6A91 /* StorageInterface.swift */,
B5FE4DA61C84FB4400FA6A91 /* InMemoryStore.swift */,
B5FE4DAB1C85D44E00FA6A91 /* SQLiteStore.swift */,
);
@@ -1011,7 +1011,7 @@
B5E84EE71AFF84610064E85B /* CoreStore+Logging.swift in Sources */,
B56007111B3F6BD500A9A8F9 /* Into.swift in Sources */,
B5E84F111AFF847B0064E85B /* Select.swift in Sources */,
B5FE4DA21C8481E100FA6A91 /* Storage.swift in Sources */,
B5FE4DA21C8481E100FA6A91 /* StorageInterface.swift in Sources */,
B50392F91C478FF3009900CA /* NSManagedObject+Transaction.swift in Sources */,
B5202CFA1C04688100DED140 /* NSFetchedResultsController+Convenience.swift in Sources */,
B5E84EE11AFF84500064E85B /* PersistentStoreResult.swift in Sources */,
@@ -1100,7 +1100,7 @@
82BA18B01C4BBD3100A0916E /* NSManagedObject+Transaction.swift in Sources */,
82BA18D41C4BBD7100A0916E /* NSManagedObjectContext+Querying.swift in Sources */,
82BA18D51C4BBD7100A0916E /* NSManagedObjectContext+Setup.swift in Sources */,
B5FE4DA31C8481E100FA6A91 /* Storage.swift in Sources */,
B5FE4DA31C8481E100FA6A91 /* StorageInterface.swift in Sources */,
82BA18C91C4BBD5900A0916E /* MigrationType.swift in Sources */,
82BA18D01C4BBD7100A0916E /* MigrationManager.swift in Sources */,
82BA18C61C4BBD5900A0916E /* DataStack+Migration.swift in Sources */,
@@ -1179,7 +1179,7 @@
B52DD1B81BE1F94000949AFE /* DataStack+Migration.swift in Sources */,
B52DD1A51BE1F92F00949AFE /* ImportableUniqueObject.swift in Sources */,
B52DD19C1BE1F92C00949AFE /* Into.swift in Sources */,
B5FE4DA51C8481E100FA6A91 /* Storage.swift in Sources */,
B5FE4DA51C8481E100FA6A91 /* StorageInterface.swift in Sources */,
B5FE4DAA1C84FB4400FA6A91 /* InMemoryStore.swift in Sources */,
B52DD1AF1BE1F93900949AFE /* GroupBy.swift in Sources */,
B52DD1B01BE1F93900949AFE /* Tweak.swift in Sources */,
@@ -1249,7 +1249,7 @@
B56321821BD65216006C9394 /* PersistentStoreResult.swift in Sources */,
B563219C1BD65216006C9394 /* SectionBy.swift in Sources */,
B56321B21BD6521C006C9394 /* NSManagedObjectContext+Querying.swift in Sources */,
B5FE4DA41C8481E100FA6A91 /* Storage.swift in Sources */,
B5FE4DA41C8481E100FA6A91 /* StorageInterface.swift in Sources */,
B56321B31BD6521C006C9394 /* NSManagedObjectContext+Setup.swift in Sources */,
B56321AE1BD6521C006C9394 /* NotificationObserver.swift in Sources */,
B56321931BD65216006C9394 /* DataStack+Querying.swift in Sources */,

View File

@@ -11,7 +11,8 @@
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
buildForAnalyzing = "YES"
hideIssues = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2F03A52F19C5C6DA005002A5"
@@ -25,7 +26,8 @@
buildForRunning = "NO"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "NO">
buildForAnalyzing = "NO"
hideIssues = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2F03A53A19C5C6DA005002A5"

View File

@@ -32,7 +32,6 @@ import CoreData
@available(OSX, unavailable)
internal final class CoreStoreFetchedResultsController<T: NSManagedObject>: NSFetchedResultsController {
// MARK: Internal
internal convenience init<T>(dataStack: DataStack, fetchRequest: NSFetchRequest, from: From<T>? = nil, sectionBy: SectionBy? = nil, fetchClauses: [FetchClause]) {

View File

@@ -65,7 +65,7 @@ public extension CoreStore {
- parameter completion: the closure to be executed on the main queue when the process completes, either due to success or failure. The closure's `PersistentStoreResult` argument indicates the result. This closure is NOT executed if an error is thrown, but will be executed with a `.Failure` result if an error occurs asynchronously.
- returns: an `NSProgress` instance if a migration has started, or `nil` is no migrations are required
*/
public static func addSQLiteStore(fileURL fileURL: NSURL = defaultSQLiteStoreFileURL, configuration: String? = nil, mappingModelBundles: [NSBundle]? = NSBundle.allBundles(), resetStoreOnModelMismatch: Bool = false, completion: (PersistentStoreResult) -> Void) throws -> NSProgress? {
public static func addSQLiteStore(fileURL fileURL: NSURL = DataStack.DeprecatedDefaults.defaultSQLiteStoreURL, configuration: String? = nil, mappingModelBundles: [NSBundle]? = NSBundle.allBundles(), resetStoreOnModelMismatch: Bool = false, completion: (PersistentStoreResult) -> Void) throws -> NSProgress? {
return try self.defaultStack.addSQLiteStore(
fileURL: fileURL,
@@ -104,7 +104,7 @@ public extension CoreStore {
- parameter sourceBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`.
- returns: an `NSProgress` instance if a migration has started, or `nil` is no migrations are required
*/
public static func upgradeSQLiteStoreIfNeeded(fileURL fileURL: NSURL = defaultSQLiteStoreFileURL, configuration: String? = nil, mappingModelBundles: [NSBundle]? = nil, completion: (MigrationResult) -> Void) throws -> NSProgress? {
public static func upgradeSQLiteStoreIfNeeded(fileURL fileURL: NSURL = DataStack.DeprecatedDefaults.defaultSQLiteStoreURL, configuration: String? = nil, mappingModelBundles: [NSBundle]? = nil, completion: (MigrationResult) -> Void) throws -> NSProgress? {
return try self.defaultStack.upgradeSQLiteStoreIfNeeded(
fileURL: fileURL,
@@ -141,7 +141,7 @@ public extension CoreStore {
:return: a `MigrationType` indicating the type of migration required for the store; or `nil` if either inspection of the store failed, or no mapping model was found/inferred. `MigrationType` acts as a `Bool` and evaluates to `false` if no migration is required, and `true` if either a lightweight or custom migration is needed.
*/
@warn_unused_result
public static func requiredMigrationsForSQLiteStore(fileURL fileURL: NSURL = defaultSQLiteStoreFileURL, configuration: String? = nil, mappingModelBundles: [NSBundle] = NSBundle.allBundles() as [NSBundle]) throws -> [MigrationType] {
public static func requiredMigrationsForSQLiteStore(fileURL fileURL: NSURL = DataStack.DeprecatedDefaults.defaultSQLiteStoreURL, configuration: String? = nil, mappingModelBundles: [NSBundle] = NSBundle.allBundles() as [NSBundle]) throws -> [MigrationType] {
return try self.defaultStack.requiredMigrationsForSQLiteStore(
fileURL: fileURL,

View File

@@ -88,7 +88,7 @@ public extension DataStack {
public func addSQLiteStore(fileName fileName: String, configuration: String? = nil, mappingModelBundles: [NSBundle]? = nil, resetStoreOnModelMismatch: Bool = false, completion: (PersistentStoreResult) -> Void) throws -> NSProgress? {
return try self.addSQLiteStore(
fileURL: defaultRootDirectory.URLByAppendingPathComponent(
fileURL: DataStack.DeprecatedDefaults.defaultDirectory.URLByAppendingPathComponent(
fileName,
isDirectory: false
),
@@ -109,7 +109,7 @@ public extension DataStack {
- parameter completion: the closure to be executed on the main queue when the process completes, either due to success or failure. The closure's `PersistentStoreResult` argument indicates the result. This closure is NOT executed if an error is thrown, but will be executed with a `.Failure` result if an error occurs asynchronously.
- returns: an `NSProgress` instance if a migration has started, or `nil` is no migrations are required
*/
public func addSQLiteStore(fileURL fileURL: NSURL = defaultSQLiteStoreFileURL, configuration: String? = nil, mappingModelBundles: [NSBundle]? = NSBundle.allBundles(), resetStoreOnModelMismatch: Bool = false, completion: (PersistentStoreResult) -> Void) throws -> NSProgress? {
public func addSQLiteStore(fileURL fileURL: NSURL = DataStack.DeprecatedDefaults.defaultSQLiteStoreURL, configuration: String? = nil, mappingModelBundles: [NSBundle]? = NSBundle.allBundles(), resetStoreOnModelMismatch: Bool = false, completion: (PersistentStoreResult) -> Void) throws -> NSProgress? {
CoreStore.assert(
fileURL.fileURL,
@@ -242,7 +242,7 @@ public extension DataStack {
public func upgradeSQLiteStoreIfNeeded(fileName fileName: String, configuration: String? = nil, mappingModelBundles: [NSBundle]? = nil, completion: (MigrationResult) -> Void) throws -> NSProgress? {
return try self.upgradeSQLiteStoreIfNeeded(
fileURL: defaultRootDirectory.URLByAppendingPathComponent(
fileURL: DataStack.DeprecatedDefaults.defaultDirectory.URLByAppendingPathComponent(
fileName,
isDirectory: false
),
@@ -261,7 +261,7 @@ public extension DataStack {
- parameter sourceBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`.
- returns: an `NSProgress` instance if a migration has started, or `nil` is no migrations are required
*/
public func upgradeSQLiteStoreIfNeeded(fileURL fileURL: NSURL = defaultSQLiteStoreFileURL, configuration: String? = nil, mappingModelBundles: [NSBundle]? = nil, completion: (MigrationResult) -> Void) throws -> NSProgress? {
public func upgradeSQLiteStoreIfNeeded(fileURL fileURL: NSURL = DataStack.DeprecatedDefaults.defaultSQLiteStoreURL, configuration: String? = nil, mappingModelBundles: [NSBundle]? = nil, completion: (MigrationResult) -> Void) throws -> NSProgress? {
let metadata: [String: AnyObject]
do {
@@ -302,7 +302,7 @@ public extension DataStack {
public func requiredMigrationsForSQLiteStore(fileName fileName: String, configuration: String? = nil, mappingModelBundles: [NSBundle] = NSBundle.allBundles() as [NSBundle]) throws -> [MigrationType] {
return try requiredMigrationsForSQLiteStore(
fileURL: defaultRootDirectory.URLByAppendingPathComponent(
fileURL: DataStack.DeprecatedDefaults.defaultDirectory.URLByAppendingPathComponent(
fileName,
isDirectory: false
),
@@ -320,7 +320,7 @@ public extension DataStack {
:return: a `MigrationType` indicating the type of migration required for the store; or `nil` if either inspection of the store failed, or no mapping model was found/inferred. `MigrationType` acts as a `Bool` and evaluates to `false` if no migration is required, and `true` if either a lightweight or custom migration is needed.
*/
@warn_unused_result
public func requiredMigrationsForSQLiteStore(fileURL fileURL: NSURL = defaultSQLiteStoreFileURL, configuration: String? = nil, mappingModelBundles: [NSBundle] = NSBundle.allBundles() as [NSBundle]) throws -> [MigrationType] {
public func requiredMigrationsForSQLiteStore(fileURL fileURL: NSURL = DataStack.DeprecatedDefaults.defaultSQLiteStoreURL, configuration: String? = nil, mappingModelBundles: [NSBundle] = NSBundle.allBundles() as [NSBundle]) throws -> [MigrationType] {
let metadata: [String : AnyObject]
do {

View File

@@ -1033,19 +1033,19 @@ public final class ListMonitor<T: NSManagedObject> {
object: coordinator,
closure: { [weak self] (note) -> Void in
guard let `self` = self else {
guard let strongSelf = self else {
return
}
self.isPersistentStoreChanging = true
strongSelf.isPersistentStoreChanging = true
guard let removedStores = (note.userInfo?[NSRemovedPersistentStoresKey] as? [NSPersistentStore]).flatMap(Set.init)
where !Set(self.fetchedResultsController.fetchRequest.affectedStores ?? []).intersect(removedStores).isEmpty else {
where !Set(strongSelf.fetchedResultsController.fetchRequest.affectedStores ?? []).intersect(removedStores).isEmpty else {
return
}
self.refetch(fetchClauses)
strongSelf.refetch(fetchClauses)
}
)
@@ -1054,25 +1054,25 @@ public final class ListMonitor<T: NSManagedObject> {
object: coordinator,
closure: { [weak self] (note) -> Void in
guard let `self` = self else {
guard let strongSelf = self else {
return
}
if !self.isPendingRefetch {
if !strongSelf.isPendingRefetch {
let previousStores = Set(self.fetchedResultsController.fetchRequest.affectedStores ?? [])
let previousStores = Set(strongSelf.fetchedResultsController.fetchRequest.affectedStores ?? [])
let currentStores = previousStores
.subtract(note.userInfo?[NSRemovedPersistentStoresKey] as? [NSPersistentStore] ?? [])
.union(note.userInfo?[NSAddedPersistentStoresKey] as? [NSPersistentStore] ?? [])
if previousStores != currentStores {
self.refetch(fetchClauses)
strongSelf.refetch(fetchClauses)
}
}
self.isPersistentStoreChanging = false
strongSelf.isPersistentStoreChanging = false
}
)

View File

@@ -59,35 +59,37 @@ public extension CoreStore {
}
/**
Creates a `Storage` of the specified store type with default values and adds it to the `defaultStack`. This method blocks until completion.
Creates a `StorageInterface` of the specified store type with default values and adds it to the `defaultStack`. This method blocks until completion.
- parameter storeType: the `Storage` type
- returns: the `Storage` added to the `defaultStack`
- parameter storeType: the `StorageInterface` type
- returns: the `StorageInterface` added to the `defaultStack`
*/
public static func addStoreAndWait<T: Storage where T: DefaultInitializableStore>(storeType: T.Type) throws -> T {
public static func addStorageAndWait<T: StorageInterface where T: DefaultInitializableStore>(storeType: T.Type) throws -> T {
return try self.defaultStack.addStoreAndWait(storeType.init())
return try self.defaultStack.addStorageAndWait(storeType.init())
}
/**
Adds a `Storage` to the `defaultStack` and blocks until completion.
Adds a `StorageInterface` to the `defaultStack` and blocks until completion.
- parameter store: the `Storage`
- returns: the `Storage` added to the `defaultStack`
- parameter store: the `StorageInterface`
- returns: the `StorageInterface` added to the `defaultStack`
*/
public static func addStoreAndWait<T: Storage>(store: T) throws -> T {
public static func addStorageAndWait<T: StorageInterface>(store: T) throws -> T {
return try self.defaultStack.addStoreAndWait(store)
return try self.defaultStack.addStorageAndWait(store)
}
/**
Adds to the `defaultStack` an SQLite store from the given SQLite file name.
- parameter fileName: the local filename for the SQLite persistent store in the "Application Support/<bundle id>" directory (or the "Caches/<bundle id>" directory on tvOS). A new SQLite file will be created if it does not exist.
- parameter configuration: an optional configuration name from the model file. If not specified, defaults to nil.
- parameter resetStoreOnModelMismatch: Set to true to delete the store on model mismatch; or set to false to throw exceptions on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false
- returns: the `NSPersistentStore` added to the stack.
*/
// MARK: Deprecated
@available(*, deprecated=2.0.0, obsoleted=2.0.0, message="Use addStorageAndWait(_:) by passing an InMemoryStore instance.")
public static func addInMemoryStoreAndWait(configuration configuration: String? = nil) throws -> NSPersistentStore {
return try self.defaultStack.addInMemoryStoreAndWait(configuration: configuration)
}
@available(*, deprecated=2.0.0, message="Use addStorageAndWait(_:) by passing an SQLiteStore instance. Note that the previous default directory for the SQLite file was in the \"Application Support\" directory (or the \"Caches\" directory on tvOS), but the new addStorageAndWait(_:configuration:) method's default directory is now in the \"Application Support/<bundle id>\" directory (or the \"Caches/<bundle id>\" directory on tvOS)")
public static func addSQLiteStoreAndWait(fileName fileName: String, configuration: String? = nil, resetStoreOnModelMismatch: Bool = false) throws -> NSPersistentStore {
return try self.defaultStack.addSQLiteStoreAndWait(
@@ -97,29 +99,13 @@ public extension CoreStore {
)
}
/**
Adds to the `defaultStack` an SQLite store from the given SQLite file URL.
- parameter fileURL: the local file URL for the SQLite persistent store. A new SQLite file will be created if it does not exist. If not specified, defaults to a file URL pointing to a "<Application name>.sqlite" file in the "Application Support/<bundle id>" directory (or the "Caches/<bundle id>" directory on tvOS).
- parameter configuration: an optional configuration name from the model file. If not specified, defaults to nil.
- parameter resetStoreOnModelMismatch: Set to true to delete the store on model mismatch; or set to false to throw exceptions on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false.
- returns: the `NSPersistentStore` added to the stack.
*/
public static func addSQLiteStoreAndWait(fileURL: NSURL = defaultSQLiteStoreFileURL, configuration: String? = nil, resetStoreOnModelMismatch: Bool = false) throws -> NSPersistentStore {
@available(*, deprecated=2.0.0, message="Use addStorageAndWait(_:) by passing an SQLiteStore instance. Note that the previous default URL for the SQLite file was in the \"Application Support/<bundle name>.sqlite\" directory (or the \"Caches/<bundle name>.sqlite\" directory on tvOS), but the new addStorageAndWait(_:configuration:) method's default directory is now in the \"Application Support/<bundle id>/<bundle name>.sqlite\" directory (or the \"Caches/<bundle id>/<bundle name>.sqlite\" directory on tvOS)")
public static func addSQLiteStoreAndWait(fileURL fileURL: NSURL = DataStack.DeprecatedDefaults.defaultSQLiteStoreURL, configuration: String? = nil, resetStoreOnModelMismatch: Bool = false) throws -> NSPersistentStore {
return try self.defaultStack.addSQLiteStoreAndWait(
return try self.addSQLiteStoreAndWait(
fileURL: fileURL,
configuration: configuration,
resetStoreOnModelMismatch: resetStoreOnModelMismatch
)
}
// MARK: Deprecated
@available(*, deprecated=2.0.0, message="Use addStoreAndWait(_:configuration:) by passing an InMemoryStore instance")
public static func addInMemoryStoreAndWait(configuration configuration: String? = nil) throws -> NSPersistentStore {
return try self.defaultStack.addInMemoryStoreAndWait(configuration: configuration)
}
}

View File

@@ -30,28 +30,6 @@ import CoreData
#endif
// TODO: move these to PersistentStore wrapper
#if os(tvOS)
internal let systemDirectorySearchPath = NSSearchPathDirectory.CachesDirectory
#else
internal let systemDirectorySearchPath = NSSearchPathDirectory.ApplicationSupportDirectory
#endif
internal let defaultSystemDirectory = NSFileManager.defaultManager().URLsForDirectory(
systemDirectorySearchPath,
inDomains: .UserDomainMask
).first!
internal let defaultRootDirectory = defaultSystemDirectory.URLByAppendingPathComponent(
NSBundle.mainBundle().bundleIdentifier ?? "com.CoreStore.DataStack",
isDirectory: true
)
internal let applicationName = (NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleName") as? String) ?? "CoreData"
internal let defaultSQLiteStoreFileURL = defaultRootDirectory
.URLByAppendingPathComponent(applicationName, isDirectory: false)
.URLByAppendingPathExtension("sqlite")
// MARK: - DataStack
/**
@@ -62,11 +40,11 @@ public final class DataStack {
/**
Initializes a `DataStack` from an `NSManagedObjectModel`.
- parameter modelName: the name of the (.xcdatamodeld) model file. If not specified, the application name will be used.
- parameter modelName: the name of the (.xcdatamodeld) model file. If not specified, the application name (CFBundleName) will be used if it exists, or "CoreData" if it the bundle name was not set.
- parameter bundle: an optional bundle to load models from. If not specified, the main bundle will be used.
- parameter migrationChain: the `MigrationChain` that indicates the sequence of model versions to be used as the order for progressive migrations. If not specified, will default to a non-migrating data stack.
*/
public required init(modelName: String = applicationName, bundle: NSBundle = NSBundle.mainBundle(), migrationChain: MigrationChain = nil) {
public required init(modelName: String = DataStack.applicationName, bundle: NSBundle = NSBundle.mainBundle(), migrationChain: MigrationChain = nil) {
CoreStore.assert(
migrationChain.valid,
@@ -124,23 +102,23 @@ public final class DataStack {
}
/**
Creates a `Storage` of the specified store type with default values and adds it to the stack. This method blocks until completion.
Creates a `StorageInterface` of the specified store type with default values and adds it to the stack. This method blocks until completion.
- parameter storeType: the `Storage` type
- returns: the `Storage` added to the stack
- parameter storeType: the `StorageInterface` type
- returns: the `StorageInterface` added to the stack
*/
public func addStoreAndWait<T: Storage where T: DefaultInitializableStore>(storeType: T.Type) throws -> T {
public func addStorageAndWait<T: StorageInterface where T: DefaultInitializableStore>(storeType: T.Type) throws -> T {
return try self.addStoreAndWait(storeType.init())
return try self.addStorageAndWait(storeType.init())
}
/**
Adds a `Storage` to the stack and blocks until completion.
Adds a `StorageInterface` to the stack and blocks until completion.
- parameter store: the `Storage`
- returns: the `Storage` added to the stack
- parameter store: the `StorageInterface`
- returns: the `StorageInterface` added to the stack
*/
public func addStoreAndWait<T: Storage>(store: T) throws -> T {
public func addStorageAndWait<T: StorageInterface>(store: T) throws -> T {
CoreStore.assert(
store.internalStore == nil,
@@ -164,113 +142,6 @@ public final class DataStack {
}
}
/**
Adds to the stack an SQLite store from the given SQLite file name.
- parameter fileName: the local filename for the SQLite persistent store in the "Application Support/<bundle id>" directory (or the "Caches/<bundle id>" directory on tvOS). A new SQLite file will be created if it does not exist. Note that if you have multiple configurations, you will need to specify a different `fileName` explicitly for each of them.
- parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil`, the "Default" configuration. Note that if you have multiple configurations, you will need to specify a different `fileName` explicitly for each of them.
- parameter resetStoreOnModelMismatch: Set to true to delete the store on model mismatch; or set to false to throw exceptions on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false
- returns: the `NSPersistentStore` added to the stack.
*/
public func addSQLiteStoreAndWait(fileName fileName: String, configuration: String? = nil, resetStoreOnModelMismatch: Bool = false) throws -> NSPersistentStore {
return try self.addSQLiteStoreAndWait(
fileURL: defaultRootDirectory
.URLByAppendingPathComponent(
fileName,
isDirectory: false
),
configuration: configuration,
resetStoreOnModelMismatch: resetStoreOnModelMismatch
)
}
/**
Adds to the stack an SQLite store from the given SQLite file URL.
- parameter fileURL: the local file URL for the SQLite persistent store. A new SQLite file will be created if it does not exist. If not specified, defaults to a file URL pointing to a "<Application name>.sqlite" file in the "Application Support/<bundle id>" directory (or the "Caches/<bundle id>" directory on tvOS). Note that if you have multiple configurations, you will need to specify a different `fileURL` explicitly for each of them.
- parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil`, the "Default" configuration. Note that if you have multiple configurations, you will need to specify a different `fileURL` explicitly for each of them.
- parameter resetStoreOnModelMismatch: Set to true to delete the store on model mismatch; or set to false to throw exceptions on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false.
- returns: the `NSPersistentStore` added to the stack.
*/
public func addSQLiteStoreAndWait(fileURL fileURL: NSURL = defaultSQLiteStoreFileURL, configuration: String? = nil, resetStoreOnModelMismatch: Bool = false) throws -> NSPersistentStore {
CoreStore.assert(
fileURL.fileURL,
"The specified file URL for the SQLite store is invalid: \"\(fileURL)\""
)
let coordinator = self.coordinator;
if let store = coordinator.persistentStoreForURL(fileURL) {
guard store.type == NSSQLiteStoreType
&& store.configurationName == (configuration ?? Into.defaultConfigurationName) else {
let error = NSError(coreStoreErrorCode: .DifferentPersistentStoreExistsAtURL)
CoreStore.handleError(
error,
"Failed to add SQLite \(typeName(NSPersistentStore)) at \"\(fileURL)\" because a different \(typeName(NSPersistentStore)) at that URL already exists."
)
throw error
}
return store
}
let fileManager = NSFileManager.defaultManager()
_ = try? fileManager.createDirectoryAtURL(
fileURL.URLByDeletingLastPathComponent!,
withIntermediateDirectories: true,
attributes: nil
)
let options = self.optionsForSQLiteStore()
do {
let store = try coordinator.addPersistentStoreSynchronously(
NSSQLiteStoreType,
configuration: configuration,
URL: fileURL,
options: options
)
self.updateMetadataForPersistentStore(store)
return store
}
catch let error as NSError where resetStoreOnModelMismatch && error.isCoreDataMigrationError {
fileManager.removeSQLiteStoreAtURL(fileURL)
do {
let store = try coordinator.addPersistentStoreSynchronously(
NSSQLiteStoreType,
configuration: configuration,
URL: fileURL,
options: options
)
self.updateMetadataForPersistentStore(store)
return store
}
catch {
CoreStore.handleError(
error as NSError,
"Failed to add SQLite \(typeName(NSPersistentStore)) at \"\(fileURL)\"."
)
throw error
}
}
catch {
CoreStore.handleError(
error as NSError,
"Failed to add SQLite \(typeName(NSPersistentStore)) at \"\(fileURL)\"."
)
throw error
}
}
// MARK: Internal
@@ -280,6 +151,7 @@ public final class DataStack {
internal let model: NSManagedObjectModel
internal let migrationChain: MigrationChain
internal let childTransactionQueue: GCDQueue = .createSerial("com.coreStore.dataStack.childTransactionQueue")
internal let storeMetadataUpdateQueue = GCDQueue.createConcurrent("com.coreStore.persistentStoreBarrierQueue")
internal let migrationQueue: NSOperationQueue = {
let migrationQueue = NSOperationQueue()
@@ -371,9 +243,10 @@ public final class DataStack {
}
// MARK: Private
// MARK: Private]
private static let applicationName = (NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleName") as? String) ?? "CoreData"
private let storeMetadataUpdateQueue = GCDQueue.createConcurrent("com.coreStore.persistentStoreBarrierQueue")
private var configurationStoreMapping = [String: NSPersistentStore]()
private var entityConfigurationsMapping = [String: Set<String>]()
@@ -386,9 +259,57 @@ public final class DataStack {
// MARK: Deprecated
@available(*, deprecated=2.0.0, message="Use addStoreAndWait(_:configuration:) by passing an InMemoryStore instance")
internal enum DeprecatedDefaults {
#if os(tvOS)
internal static let systemDirectorySearchPath = NSSearchPathDirectory.CachesDirectory
#else
internal static let systemDirectorySearchPath = NSSearchPathDirectory.ApplicationSupportDirectory
#endif
internal static let defaultDirectory = NSFileManager.defaultManager().URLsForDirectory(
DeprecatedDefaults.systemDirectorySearchPath,
inDomains: .UserDomainMask
).first!
internal static let defaultSQLiteStoreURL = DeprecatedDefaults.defaultDirectory
.URLByAppendingPathComponent(DataStack.applicationName, isDirectory: false)
.URLByAppendingPathExtension("sqlite")
}
@available(*, deprecated=2.0.0, message="Use addStorageAndWait(_:) by passing an InMemoryStore instance.")
public func addInMemoryStoreAndWait(configuration configuration: String? = nil) throws -> NSPersistentStore {
return try self.addStoreAndWait(InMemoryStore).internalStore!
let storage = try self.addStorageAndWait(InMemoryStore(configuration: configuration))
return storage.internalStore!
}
@available(*, deprecated=2.0.0, message="Use addStorageAndWait(_:) by passing an SQLiteStore instance. Note that the previous default directory for the SQLite file was in the \"Application Support\" directory (or the \"Caches\" directory on tvOS), but the new addStorageAndWait(_:configuration:) method's default directory is now in the \"Application Support/<bundle id>\" directory (or the \"Caches/<bundle id>\" directory on tvOS)")
public func addSQLiteStoreAndWait(fileName fileName: String, configuration: String? = nil, resetStoreOnModelMismatch: Bool = false) throws -> NSPersistentStore {
let storage = try self.addStorageAndWait(
SQLiteStore(
fileURL: DeprecatedDefaults.defaultDirectory.URLByAppendingPathComponent(
fileName,
isDirectory: false
),
configuration: configuration,
resetStoreOnModelMismatch: resetStoreOnModelMismatch
)
)
return storage.internalStore!
}
@available(*, deprecated=2.0.0, message="Use addStorageAndWait(_:) by passing an SQLiteStore instance. Note that the previous default URL for the SQLite file was in the \"Application Support/<bundle name>.sqlite\" directory (or the \"Caches/<bundle name>.sqlite\" directory on tvOS), but the new addStorageAndWait(_:configuration:) method's default directory is now in the \"Application Support/<bundle id>/<bundle name>.sqlite\" directory (or the \"Caches/<bundle id>/<bundle name>.sqlite\" directory on tvOS)")
public func addSQLiteStoreAndWait(fileURL fileURL: NSURL = DeprecatedDefaults.defaultSQLiteStoreURL, configuration: String? = nil, resetStoreOnModelMismatch: Bool = false) throws -> NSPersistentStore {
let storage = try self.addStorageAndWait(
SQLiteStore(
fileURL: DeprecatedDefaults.defaultSQLiteStoreURL,
configuration: configuration,
resetStoreOnModelMismatch: resetStoreOnModelMismatch
)
)
return storage.internalStore!
}
}

View File

@@ -28,7 +28,7 @@ import CoreData
// MARK: - InMemoryStore
public class InMemoryStore: Storage, DefaultInitializableStore {
public class InMemoryStore: StorageInterface, DefaultInitializableStore {
public required init(configuration: String?) {
@@ -44,7 +44,7 @@ public class InMemoryStore: Storage, DefaultInitializableStore {
}
// MARK: Storage
// MARK: StorageInterface
public static let storeType = NSInMemoryStoreType

View File

@@ -28,7 +28,7 @@ import CoreData
// MARK: - SQLiteStore
public class SQLiteStore: Storage, DefaultInitializableStore {
public class SQLiteStore: StorageInterface, DefaultInitializableStore {
public static let defaultRootDirectory: NSURL = {
@@ -57,6 +57,13 @@ public class SQLiteStore: Storage, DefaultInitializableStore {
.URLByAppendingPathExtension("sqlite")
}()
/**
Initializes an SQLite store interface from the given SQLite file URL. When this instance is passed to the `DataStack`'s `addStorage()` methods, a new SQLite file will be created if it does not exist.
- parameter fileURL: the local file URL for the target SQLite persistent store. If not specified, defaults to a file URL pointing to a "<Application name>.sqlite" file in the "Application Support/<bundle id>" directory (or the "Caches/<bundle id>" directory on tvOS). Note that if you have multiple configurations, you will need to specify a different `fileURL` explicitly for each of them.
- parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil`, the "Default" configuration. Note that if you have multiple configurations, you will need to specify a different `fileURL` explicitly for each of them.
- parameter resetStoreOnModelMismatch: When the `SQLiteStore` is passed to the `DataStack`'s `addStorage()` methods, a true value tells the `DataStack` to delete the store on model mismatch; a false value lets exceptions be thrown on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false.
*/
public required init(fileURL: NSURL, configuration: String? = nil, resetStoreOnModelMismatch: Bool = false) {
self.fileURL = fileURL
@@ -64,6 +71,13 @@ public class SQLiteStore: Storage, DefaultInitializableStore {
self.resetStoreOnModelMismatch = resetStoreOnModelMismatch
}
/**
Initializes an SQLite store interface from the given SQLite file name. When this instance is passed to the `DataStack`'s `addStorage()` methods, a new SQLite file will be created if it does not exist.
- parameter fileName: the local filename for the SQLite persistent store in the "Application Support/<bundle id>" directory (or the "Caches/<bundle id>" directory on tvOS). Note that if you have multiple configurations, you will need to specify a different `fileName` explicitly for each of them.
- parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil`, the "Default" configuration. Note that if you have multiple configurations, you will need to specify a different `fileName` explicitly for each of them.
- parameter resetStoreOnModelMismatch: When the `SQLiteStore` is passed to the `DataStack`'s `addStorage()` methods, a true value tells the `DataStack` to delete the store on model mismatch; a false value lets exceptions be thrown on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false.
*/
public required init(fileName: String, configuration: String? = nil, resetStoreOnModelMismatch: Bool = false) {
self.fileURL = SQLiteStore.defaultRootDirectory
@@ -83,7 +97,7 @@ public class SQLiteStore: Storage, DefaultInitializableStore {
}
// MARK: Storage
// MARK: StorageInterface
public static let storeType = NSSQLiteStoreType

View File

@@ -1,5 +1,5 @@
//
// Storage.swift
// StorageInterface.swift
// CoreStore
//
// Copyright © 2016 John Rommel Estropia
@@ -26,9 +26,9 @@
import CoreData
// MARK: - Storage
// MARK: - StorageInterface
public protocol Storage: class {
public protocol StorageInterface: class {
static var storeType: String { get }
@@ -45,7 +45,7 @@ public protocol Storage: class {
func addToPersistentStoreCoordinatorAsynchronously(coordinator: NSPersistentStoreCoordinator, completion: (NSPersistentStore) -> Void, failure: (NSError) -> Void) throws
}
public extension Storage {
public extension StorageInterface {
public func addToPersistentStoreCoordinatorSynchronously(coordinator: NSPersistentStoreCoordinator) throws -> NSPersistentStore {
@@ -82,7 +82,7 @@ public extension Storage {
// MARK: - DefaultInitializableStore
public protocol DefaultInitializableStore: Storage {
public protocol DefaultInitializableStore: StorageInterface {
init()
}

View File

@@ -15,10 +15,12 @@ private struct Static {
static let timeZonesStack: DataStack = {
let dataStack = DataStack()
try! dataStack.addSQLiteStoreAndWait(
fileName: "TimeZoneDemo.sqlite",
configuration: "FetchingAndQueryingDemo",
resetStoreOnModelMismatch: true
try! dataStack.addStorageAndWait(
SQLiteStore(
fileName: "TimeZoneDemo.sqlite",
configuration: "FetchingAndQueryingDemo",
resetStoreOnModelMismatch: true
)
)
dataStack.beginSynchronous { (transaction) -> Void in

View File

@@ -49,10 +49,12 @@ private struct Static {
static let palettes: ListMonitor<Palette> = {
try! CoreStore.addSQLiteStoreAndWait(
fileName: "ColorsDemo.sqlite",
configuration: "ObservingDemo",
resetStoreOnModelMismatch: true
try! CoreStore.addStorageAndWait(
SQLiteStore(
fileName: "ColorsDemo.sqlite",
configuration: "ObservingDemo",
resetStoreOnModelMismatch: true
)
)
return CoreStore.monitorSectionedList(

View File

@@ -30,7 +30,7 @@ class CustomLoggerViewController: UIViewController, CoreStoreLogger {
super.viewDidLoad()
try! self.dataStack.addSQLiteStoreAndWait(fileName: "emptyStore.sqlite")
try! self.dataStack.addStorageAndWait(SQLiteStore(fileName: "emptyStore.sqlite"))
CoreStore.logger = self
}
@@ -109,11 +109,12 @@ class CustomLoggerViewController: UIViewController, CoreStoreLogger {
}
case .Some(1):
do {
try self.dataStack.addSQLiteStoreAndWait(fileName: "emptyStore.sqlite", configuration: "invalidStore")
}
catch _ { }
_ = try? dataStack.addStorageAndWait(
SQLiteStore(
fileName: "emptyStore.sqlite",
configuration: "invalidStore"
)
)
case .Some(2):
self.dataStack.beginAsynchronous { (transaction) -> Void in

View File

@@ -18,15 +18,19 @@ private struct Static {
static let facebookStack: DataStack = {
let dataStack = DataStack(modelName: "StackSetupDemo")
try! dataStack.addSQLiteStoreAndWait(
fileName: "AccountsDemo_FB_Male.sqlite",
configuration: maleConfiguration,
resetStoreOnModelMismatch: true
try! dataStack.addStorageAndWait(
SQLiteStore(
fileName: "AccountsDemo_FB_Male.sqlite",
configuration: maleConfiguration,
resetStoreOnModelMismatch: true
)
)
try! dataStack.addSQLiteStoreAndWait(
fileName: "AccountsDemo_FB_Female.sqlite",
configuration: femaleConfiguration,
resetStoreOnModelMismatch: true
try! dataStack.addStorageAndWait(
SQLiteStore(
fileName: "AccountsDemo_FB_Female.sqlite",
configuration: femaleConfiguration,
resetStoreOnModelMismatch: true
)
)
dataStack.beginSynchronous { (transaction) -> Void in
@@ -52,15 +56,19 @@ private struct Static {
static let twitterStack: DataStack = {
let dataStack = DataStack(modelName: "StackSetupDemo")
try! dataStack.addSQLiteStoreAndWait(
fileName: "AccountsDemo_TW_Male.sqlite",
configuration: maleConfiguration,
resetStoreOnModelMismatch: true
try! dataStack.addStorageAndWait(
SQLiteStore(
fileName: "AccountsDemo_TW_Male.sqlite",
configuration: maleConfiguration,
resetStoreOnModelMismatch: true
)
)
try! dataStack.addSQLiteStoreAndWait(
fileName: "AccountsDemo_TW_Female.sqlite",
configuration: femaleConfiguration,
resetStoreOnModelMismatch: true
try! dataStack.addStorageAndWait(
SQLiteStore(
fileName: "AccountsDemo_TW_Female.sqlite",
configuration: femaleConfiguration,
resetStoreOnModelMismatch: true
)
)
dataStack.beginSynchronous { (transaction) -> Void in

View File

@@ -18,10 +18,12 @@ private struct Static {
static let placeController: ObjectMonitor<Place> = {
try! CoreStore.addSQLiteStoreAndWait(
fileName: "PlaceDemo.sqlite",
configuration: "TransactionsDemo",
resetStoreOnModelMismatch: true
try! CoreStore.addStorageAndWait(
SQLiteStore(
fileName: "PlaceDemo.sqlite",
configuration: "TransactionsDemo",
resetStoreOnModelMismatch: true
)
)
var place = CoreStore.fetchOne(From(Place))

View File

@@ -86,7 +86,13 @@ class CoreStoreTests: XCTestCase {
do {
try stack.addSQLiteStoreAndWait(fileName: "ConfigStore1.sqlite", configuration: "Config1", resetStoreOnModelMismatch: true)
try stack.addStorageAndWait(
SQLiteStore(
fileName: "ConfigStore1.sqlite",
configuration: "Config1",
resetStoreOnModelMismatch: true
)
)
}
catch let error as NSError {
@@ -95,7 +101,13 @@ class CoreStoreTests: XCTestCase {
do {
try stack.addSQLiteStoreAndWait(fileName: "ConfigStore2.sqlite", configuration: "Config2", resetStoreOnModelMismatch: true)
try stack.addStorageAndWait(
SQLiteStore(
fileName: "ConfigStore2.sqlite",
configuration: "Config2",
resetStoreOnModelMismatch: true
)
)
}
catch let error as NSError {
@@ -265,7 +277,7 @@ class CoreStoreTests: XCTestCase {
)
XCTAssertTrue(numberOfDeletedObjects2 == 2, "numberOfDeletedObjects2 == 2 (actual: \(numberOfDeletedObjects2))")
transaction.commit()
transaction.commitAndWait()
}
CoreStore.beginSynchronous({ (transaction) -> Void in
@@ -277,7 +289,7 @@ class CoreStoreTests: XCTestCase {
obj.testEntityID = oldID
}
transaction.commit()
transaction.commitAndWait()
})
let objs1 = CoreStore.fetchAll(From(TestEntity1))
@@ -351,7 +363,7 @@ class CoreStoreTests: XCTestCase {
let obj5 = transaction.edit(obj5)
transaction.delete(obj5, obj6)
transaction.commit()
transaction.commitAndWait()
}
let count2 = CoreStore.queryValue(
@@ -379,8 +391,12 @@ class CoreStoreTests: XCTestCase {
do {
let defaultDirectory = NSFileManager.defaultManager().URLsForDirectory(
.ApplicationSupportDirectory,
inDomains: .UserDomainMask
).first!
let fileManager = NSFileManager.defaultManager()
try fileManager.removeItemAtURL(defaultRootDirectory)
try fileManager.removeItemAtURL(defaultDirectory)
}
catch _ { }
}