mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-03-20 16:43:55 +01:00
Swift Package Manager support
This commit is contained in:
248
Sources/Importing Data/BaseDataTransaction+Importing.swift
Normal file
248
Sources/Importing Data/BaseDataTransaction+Importing.swift
Normal file
@@ -0,0 +1,248 @@
|
||||
//
|
||||
// BaseDataTransaction+Importing.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2015 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
|
||||
|
||||
// MARK: - BaseDataTransaction
|
||||
|
||||
public extension BaseDataTransaction {
|
||||
|
||||
/**
|
||||
Creates an `ImportableObject` by importing from the specified import source.
|
||||
|
||||
- parameter into: an `Into` clause specifying the entity type
|
||||
- parameter source: the object to import values from
|
||||
- returns: the created `ImportableObject` instance, or `nil` if the import was ignored
|
||||
*/
|
||||
public func importObject<T where T: NSManagedObject, T: ImportableObject>(
|
||||
into: Into<T>,
|
||||
source: T.ImportSource) throws -> T? {
|
||||
|
||||
CoreStore.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
"Attempted to import an object of type \(typeName(into.entityClass)) outside the transaction's designated queue."
|
||||
)
|
||||
|
||||
return try autoreleasepool {
|
||||
|
||||
guard T.shouldInsertFromImportSource(source, inTransaction: self) else {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
let object = self.create(into)
|
||||
try object.didInsertFromImportSource(source, inTransaction: self)
|
||||
return object
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Updates an existing `ImportableObject` by importing values from the specified import source.
|
||||
|
||||
- parameter object: the `NSManagedObject` to update
|
||||
- parameter source: the object to import values from
|
||||
*/
|
||||
public func importObject<T where T: NSManagedObject, T: ImportableObject>(
|
||||
object: T,
|
||||
source: T.ImportSource) throws {
|
||||
|
||||
CoreStore.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
"Attempted to import an object of type \(typeName(object)) outside the transaction's designated queue."
|
||||
)
|
||||
|
||||
try autoreleasepool {
|
||||
|
||||
guard T.shouldInsertFromImportSource(source, inTransaction: self) else {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
try object.didInsertFromImportSource(source, inTransaction: self)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Creates multiple `ImportableObject`s by importing from the specified array of import sources.
|
||||
|
||||
- parameter into: an `Into` clause specifying the entity type
|
||||
- parameter sourceArray: the array of objects to import values from
|
||||
- returns: the array of created `ImportableObject` instances
|
||||
*/
|
||||
public func importObjects<T, S: SequenceType where T: NSManagedObject, T: ImportableObject, S.Generator.Element == T.ImportSource>(
|
||||
into: Into<T>,
|
||||
sourceArray: S) throws -> [T] {
|
||||
|
||||
CoreStore.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
"Attempted to import an object of type \(typeName(into.entityClass)) outside the transaction's designated queue."
|
||||
)
|
||||
|
||||
return try autoreleasepool {
|
||||
|
||||
return try sourceArray.flatMap { (source) -> T? in
|
||||
|
||||
guard T.shouldInsertFromImportSource(source, inTransaction: self) else {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return try autoreleasepool {
|
||||
|
||||
let object = self.create(into)
|
||||
try object.didInsertFromImportSource(source, inTransaction: self)
|
||||
return object
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Updates an existing `ImportableUniqueObject` or creates a new instance by importing from the specified import source.
|
||||
|
||||
- parameter into: an `Into` clause specifying the entity type
|
||||
- parameter source: the object to import values from
|
||||
- returns: the created/updated `ImportableUniqueObject` instance, or `nil` if the import was ignored
|
||||
*/
|
||||
public func importUniqueObject<T where T: NSManagedObject, T: ImportableUniqueObject>(
|
||||
into: Into<T>,
|
||||
source: T.ImportSource) throws -> T? {
|
||||
|
||||
CoreStore.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
"Attempted to import an object of type \(typeName(into.entityClass)) outside the transaction's designated queue."
|
||||
)
|
||||
|
||||
return try autoreleasepool {
|
||||
|
||||
let uniqueIDKeyPath = T.uniqueIDKeyPath
|
||||
guard let uniqueIDValue = try T.uniqueIDFromImportSource(source, inTransaction: self) else {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if let object = self.fetchOne(From(T), Where(uniqueIDKeyPath, isEqualTo: uniqueIDValue)) {
|
||||
|
||||
guard T.shouldUpdateFromImportSource(source, inTransaction: self) else {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
try object.updateFromImportSource(source, inTransaction: self)
|
||||
return object
|
||||
}
|
||||
else {
|
||||
|
||||
guard T.shouldInsertFromImportSource(source, inTransaction: self) else {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
let object = self.create(into)
|
||||
object.uniqueIDValue = uniqueIDValue
|
||||
try object.didInsertFromImportSource(source, inTransaction: self)
|
||||
return object
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Updates existing `ImportableUniqueObject`s or creates them by importing from the specified array of import sources.
|
||||
|
||||
- parameter into: an `Into` clause specifying the entity type
|
||||
- parameter sourceArray: the array of objects to import values from
|
||||
- parameter preProcess: a closure that lets the caller tweak the internal `UniqueIDType`-to-`ImportSource` mapping to be used for importing. Callers can remove from/add to/update `mapping` and return the updated array from the closure.
|
||||
- returns: the array of created/updated `ImportableUniqueObject` instances
|
||||
*/
|
||||
public func importUniqueObjects<T, S: SequenceType where T: NSManagedObject, T: ImportableUniqueObject, S.Generator.Element == T.ImportSource>(
|
||||
into: Into<T>,
|
||||
sourceArray: S,
|
||||
@noescape preProcess: (mapping: [T.UniqueIDType: T.ImportSource]) throws -> [T.UniqueIDType: T.ImportSource] = { $0 }) throws -> [T] {
|
||||
|
||||
CoreStore.assert(
|
||||
self.isRunningInAllowedQueue(),
|
||||
"Attempted to import an object of type \(typeName(into.entityClass)) outside the transaction's designated queue."
|
||||
)
|
||||
|
||||
return try autoreleasepool {
|
||||
|
||||
var mapping = Dictionary<T.UniqueIDType, T.ImportSource>()
|
||||
let sortedIDs = try autoreleasepool {
|
||||
|
||||
return try sourceArray.flatMap { (source) -> T.UniqueIDType? in
|
||||
|
||||
guard let uniqueIDValue = try T.uniqueIDFromImportSource(source, inTransaction: self) else {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
mapping[uniqueIDValue] = source
|
||||
return uniqueIDValue
|
||||
}
|
||||
}
|
||||
|
||||
mapping = try autoreleasepool { try preProcess(mapping: mapping) }
|
||||
|
||||
var objects = Dictionary<T.UniqueIDType, T>()
|
||||
for object in self.fetchAll(From(T), Where(T.uniqueIDKeyPath, isMemberOf: mapping.keys)) ?? [] {
|
||||
|
||||
try autoreleasepool {
|
||||
|
||||
let uniqueIDValue = object.uniqueIDValue
|
||||
|
||||
guard let source = mapping.removeValueForKey(uniqueIDValue)
|
||||
where T.shouldUpdateFromImportSource(source, inTransaction: self) else {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
try object.updateFromImportSource(source, inTransaction: self)
|
||||
objects[uniqueIDValue] = object
|
||||
}
|
||||
}
|
||||
|
||||
for (uniqueIDValue, source) in mapping {
|
||||
|
||||
try autoreleasepool {
|
||||
|
||||
guard T.shouldInsertFromImportSource(source, inTransaction: self) else {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
let object = self.create(into)
|
||||
object.uniqueIDValue = uniqueIDValue
|
||||
try object.didInsertFromImportSource(source, inTransaction: self)
|
||||
|
||||
objects[uniqueIDValue] = object
|
||||
}
|
||||
}
|
||||
|
||||
return sortedIDs.flatMap { objects[$0] }
|
||||
}
|
||||
}
|
||||
}
|
||||
85
Sources/Importing Data/ImportableObject.swift
Normal file
85
Sources/Importing Data/ImportableObject.swift
Normal file
@@ -0,0 +1,85 @@
|
||||
//
|
||||
// ImportableObject.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2015 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
|
||||
|
||||
// MARK: - ImportableObject
|
||||
|
||||
/**
|
||||
`NSManagedObject` subclasses that conform to the `ImportableObject` protocol can be imported from a specified `ImportSource`. This allows transactions to create and insert instances this way:
|
||||
```
|
||||
class MyPersonEntity: NSManagedObject, ImportableObject {
|
||||
typealias ImportSource = NSDictionary
|
||||
// ...
|
||||
}
|
||||
|
||||
CoreStore.beginAsynchronous { (transaction) -> Void in
|
||||
let json: NSDictionary = // ...
|
||||
let person = try! transaction.importObject(
|
||||
Into(MyPersonEntity),
|
||||
source: json
|
||||
)
|
||||
// ...
|
||||
transaction.commit()
|
||||
}
|
||||
```
|
||||
*/
|
||||
public protocol ImportableObject: class {
|
||||
|
||||
/**
|
||||
The data type for the import source. This is most commonly an `NSDictionary` or another external source such as an `NSUserDefaults`.
|
||||
*/
|
||||
associatedtype ImportSource
|
||||
|
||||
/**
|
||||
Return `true` if an object should be created from `source`. Return `false` to ignore and skip `source`. The default implementation returns `true`.
|
||||
|
||||
- parameter source: the object to import from
|
||||
- parameter transaction: the transaction that invoked the import. Use the transaction to fetch or create related objects if needed.
|
||||
- returns: `true` if an object should be created from `source`. Return `false` to ignore.
|
||||
*/
|
||||
static func shouldInsertFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) -> Bool
|
||||
|
||||
/**
|
||||
Implements the actual importing of data from `source`. Implementers should pull values from `source` and assign them to the receiver's attributes. Note that throwing from this method will cause subsequent imports that are part of the same `importObjects(:sourceArray:)` call to be cancelled.
|
||||
|
||||
- parameter source: the object to import from
|
||||
- parameter transaction: the transaction that invoked the import. Use the transaction to fetch or create related objects if needed.
|
||||
*/
|
||||
func didInsertFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) throws
|
||||
}
|
||||
|
||||
|
||||
// MARK: - ImportableObject (Default Implementations)
|
||||
|
||||
public extension ImportableObject {
|
||||
|
||||
static func shouldInsertFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) -> Bool {
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
137
Sources/Importing Data/ImportableUniqueObject.swift
Normal file
137
Sources/Importing Data/ImportableUniqueObject.swift
Normal file
@@ -0,0 +1,137 @@
|
||||
//
|
||||
// ImportableUniqueObject.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2015 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
|
||||
|
||||
// MARK: - ImportableUniqueObject
|
||||
|
||||
/**
|
||||
`NSManagedObject` subclasses that conform to the `ImportableUniqueObject` protocol can be imported from a specified `ImportSource`. This allows transactions to either update existing objects or create new instances this way:
|
||||
```
|
||||
class MyPersonEntity: NSManagedObject, ImportableUniqueObject {
|
||||
typealias ImportSource = NSDictionary
|
||||
typealias UniqueIDType = NSString
|
||||
// ...
|
||||
}
|
||||
|
||||
CoreStore.beginAsynchronous { (transaction) -> Void in
|
||||
let json: NSDictionary = // ...
|
||||
let person = try! transaction.importUniqueObject(
|
||||
Into(MyPersonEntity),
|
||||
source: json
|
||||
)
|
||||
// ...
|
||||
transaction.commit()
|
||||
}
|
||||
```
|
||||
*/
|
||||
public protocol ImportableUniqueObject: ImportableObject {
|
||||
|
||||
/**
|
||||
The data type for the import source. This is most commonly an `NSDictionary` or another external source such as an `NSUserDefaults`.
|
||||
*/
|
||||
associatedtype ImportSource
|
||||
|
||||
/**
|
||||
The data type for the entity's unique ID attribute
|
||||
*/
|
||||
associatedtype UniqueIDType: NSObject
|
||||
|
||||
/**
|
||||
The keyPath to the entity's unique ID attribute
|
||||
*/
|
||||
static var uniqueIDKeyPath: String { get }
|
||||
|
||||
/**
|
||||
The object's unique ID value
|
||||
*/
|
||||
var uniqueIDValue: UniqueIDType { get set }
|
||||
|
||||
/**
|
||||
Return `true` if an object should be created from `source`. Return `false` to ignore and skip `source`. The default implementation returns the value returned by the `shouldUpdateFromImportSource(:inTransaction:)` implementation.
|
||||
|
||||
- parameter source: the object to import from
|
||||
- parameter transaction: the transaction that invoked the import. Use the transaction to fetch or create related objects if needed.
|
||||
- returns: `true` if an object should be created from `source`. Return `false` to ignore.
|
||||
*/
|
||||
static func shouldInsertFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) -> Bool
|
||||
|
||||
/**
|
||||
Return `true` if an object should be updated from `source`. Return `false` to ignore and skip `source`. The default implementation returns `true`.
|
||||
|
||||
- parameter source: the object to import from
|
||||
- parameter transaction: the transaction that invoked the import. Use the transaction to fetch or create related objects if needed.
|
||||
- returns: `true` if an object should be updated from `source`. Return `false` to ignore.
|
||||
*/
|
||||
static func shouldUpdateFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) -> Bool
|
||||
|
||||
/**
|
||||
Return the unique ID as extracted from `source`. This method is called before `shouldInsertFromImportSource(...)` or `shouldUpdateFromImportSource(...)`. Return `nil` to skip importing from `source`. Note that throwing from this method will cause subsequent imports that are part of the same `importUniqueObjects(:sourceArray:)` call to be cancelled.
|
||||
|
||||
- parameter source: the object to import from
|
||||
- parameter transaction: the transaction that invoked the import. Use the transaction to fetch or create related objects if needed.
|
||||
- returns: the unique ID as extracted from `source`, or `nil` to skip importing from `source`.
|
||||
*/
|
||||
static func uniqueIDFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) throws -> UniqueIDType?
|
||||
|
||||
/**
|
||||
Implements the actual importing of data from `source`. This method is called just after the object is created and assigned its unique ID as returned from `uniqueIDFromImportSource(...)`. Implementers should pull values from `source` and assign them to the receiver's attributes. Note that throwing from this method will cause subsequent imports that are part of the same `importUniqueObjects(:sourceArray:)` call to be cancelled. The default implementation simply calls `updateFromImportSource(...)`.
|
||||
|
||||
- parameter source: the object to import from
|
||||
- parameter transaction: the transaction that invoked the import. Use the transaction to fetch or create related objects if needed.
|
||||
*/
|
||||
func didInsertFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) throws
|
||||
|
||||
/**
|
||||
Implements the actual importing of data from `source`. This method is called just after the existing object is fetched using its unique ID. Implementers should pull values from `source` and assign them to the receiver's attributes. Note that throwing from this method will cause subsequent imports that are part of the same `importUniqueObjects(:sourceArray:)` call to be cancelled.
|
||||
|
||||
- parameter source: the object to import from
|
||||
- parameter transaction: the transaction that invoked the import. Use the transaction to fetch or create related objects if needed.
|
||||
*/
|
||||
func updateFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) throws
|
||||
}
|
||||
|
||||
|
||||
// MARK: - ImportableUniqueObject (Default Implementations)
|
||||
|
||||
public extension ImportableUniqueObject {
|
||||
|
||||
static func shouldInsertFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) -> Bool {
|
||||
|
||||
return self.shouldUpdateFromImportSource(source, inTransaction: transaction)
|
||||
}
|
||||
|
||||
static func shouldUpdateFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) -> Bool {
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func didInsertFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) throws {
|
||||
|
||||
try self.updateFromImportSource(source, inTransaction: transaction)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user