Big update. Migration tools in progress

This commit is contained in:
John Rommel Estropia
2015-07-01 08:03:09 +09:00
parent 71c80c3497
commit 40f22761bf
53 changed files with 2177 additions and 970 deletions

View File

@@ -90,11 +90,11 @@ public extension BaseDataTransaction {
}
/**
Fetches the number of `NSManagedObject`'s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
Fetches the number of `NSManagedObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:param: from a `From` clause indicating the entity type
:param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: the number `NSManagedObject`'s that satisfy the specified `FetchClause`s
:returns: the number `NSManagedObject`s that satisfy the specified `FetchClause`s
*/
public func fetchCount<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> Int? {
@@ -104,11 +104,11 @@ public extension BaseDataTransaction {
}
/**
Fetches the number of `NSManagedObject`'s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
Fetches the number of `NSManagedObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:param: from a `From` clause indicating the entity type
:param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: the number `NSManagedObject`'s that satisfy the specified `FetchClause`s
:returns: the number `NSManagedObject`s that satisfy the specified `FetchClause`s
*/
public func fetchCount<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> Int? {
@@ -146,11 +146,11 @@ public extension BaseDataTransaction {
}
/**
Fetches the `NSManagedObjectID` for all `NSManagedObject`'s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
Fetches the `NSManagedObjectID` for all `NSManagedObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:param: from a `From` clause indicating the entity type
:param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: the `NSManagedObjectID` for all `NSManagedObject`'s that satisfy the specified `FetchClause`s
:returns: the `NSManagedObjectID` for all `NSManagedObject`s that satisfy the specified `FetchClause`s
*/
public func fetchObjectIDs<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> [NSManagedObjectID]? {
@@ -160,11 +160,11 @@ public extension BaseDataTransaction {
}
/**
Fetches the `NSManagedObjectID` for all `NSManagedObject`'s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
Fetches the `NSManagedObjectID` for all `NSManagedObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:param: from a `From` clause indicating the entity type
:param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: the `NSManagedObjectID` for all `NSManagedObject`'s that satisfy the specified `FetchClause`s
:returns: the `NSManagedObjectID` for all `NSManagedObject`s that satisfy the specified `FetchClause`s
*/
public func fetchObjectIDs<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> [NSManagedObjectID]? {
@@ -174,11 +174,11 @@ public extension BaseDataTransaction {
}
/**
Deletes all `NSManagedObject`'s that satisfy the specified `DeleteClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
Deletes all `NSManagedObject`s that satisfy the specified `DeleteClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:param: from a `From` clause indicating the entity type
:param: deleteClauses a series of `DeleteClause` instances for the delete request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: the number of `NSManagedObject`'s deleted
:returns: the number of `NSManagedObject`s deleted
*/
public func deleteAll<T: NSManagedObject>(from: From<T>, _ deleteClauses: DeleteClause...) -> Int? {
@@ -188,11 +188,11 @@ public extension BaseDataTransaction {
}
/**
Deletes all `NSManagedObject`'s that satisfy the specified `DeleteClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
Deletes all `NSManagedObject`s that satisfy the specified `DeleteClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:param: from a `From` clause indicating the entity type
:param: deleteClauses a series of `DeleteClause` instances for the delete request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: the number of `NSManagedObject`'s deleted
:returns: the number of `NSManagedObject`s deleted
*/
public func deleteAll<T: NSManagedObject>(from: From<T>, _ deleteClauses: [DeleteClause]) -> Int? {
@@ -202,7 +202,7 @@ public extension BaseDataTransaction {
}
/**
Queries aggregate values as specified by the `QueryClause`'s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
Queries aggregate values as specified by the `QueryClause`s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result.
@@ -219,7 +219,7 @@ public extension BaseDataTransaction {
}
/**
Queries aggregate values or aggregates as specified by the `QueryClause`'s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
Queries aggregate values or aggregates as specified by the `QueryClause`s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result.
@@ -236,7 +236,7 @@ public extension BaseDataTransaction {
}
/**
Queries a dictionary of attribute values as specified by the `QueryClause`'s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
Queries a dictionary of attribute values as specified by the `QueryClause`s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result.
@@ -253,7 +253,7 @@ public extension BaseDataTransaction {
}
/**
Queries a dictionary of attribute values as specified by the `QueryClause`'s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
Queries a dictionary of attribute values as specified by the `QueryClause`s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result.

View File

@@ -38,72 +38,110 @@ public struct From<T: NSManagedObject> {
public init(){
self.entityClass = T.self
self.findPersistentStores = { _ in nil }
}
public init(_ entity: T.Type) {
self.entityClass = entity
self.findPersistentStores = { _ in nil }
}
public init(_ entityClass: AnyClass) {
self.entityClass = entityClass
self.findPersistentStores = { _ in nil }
}
public init(_ configurations: String?...) {
self.init(configurations: configurations)
self.init(entityClass: T.self, configurations: configurations)
}
public init(_ configurations: [String?]) {
self.init(configurations: configurations)
self.init(entityClass: T.self, configurations: configurations)
}
public init(_ entity: T.Type, _ configurations: String?...) {
self.init(configurations: configurations)
self.init(entityClass: entity, configurations: configurations)
}
public init(_ entity: T.Type, _ configurations: [String?]) {
self.init(configurations: configurations)
self.init(entityClass: entity, configurations: configurations)
}
public init(_ entityClass: AnyClass, _ configurations: String?...) {
self.init(entityClass: entityClass, configurations: configurations)
}
public init(_ entityClass: AnyClass, _ configurations: [String?]) {
self.init(entityClass: entityClass, configurations: configurations)
}
public init(_ storeURLs: NSURL...) {
self.init(storeURLs: storeURLs)
self.init(entityClass: T.self, storeURLs: storeURLs)
}
public init(_ storeURLs: [NSURL]) {
self.init(storeURLs: storeURLs)
self.init(entityClass: T.self, storeURLs: storeURLs)
}
public init(_ entity: T.Type, _ storeURLs: NSURL...) {
self.init(storeURLs: storeURLs)
self.init(entityClass: entity, storeURLs: storeURLs)
}
public init(_ entity: T.Type, _ storeURLs: [NSURL]) {
self.init(storeURLs: storeURLs)
self.init(entityClass: entity, storeURLs: storeURLs)
}
public init(_ entityClass: AnyClass, _ storeURLs: NSURL...) {
self.init(entityClass: entityClass, storeURLs: storeURLs)
}
public init(_ entityClass: AnyClass, _ storeURLs: [NSURL]) {
self.init(entityClass: entityClass, storeURLs: storeURLs)
}
public init(_ persistentStores: NSPersistentStore...) {
self.init(persistentStores: persistentStores)
self.init(entityClass: T.self, persistentStores: persistentStores)
}
public init(_ persistentStores: [NSPersistentStore]) {
self.init(persistentStores: persistentStores)
self.init(entityClass: T.self, persistentStores: persistentStores)
}
public init(_ entity: T.Type, _ persistentStores: NSPersistentStore...) {
self.init(persistentStores: persistentStores)
self.init(entityClass: entity, persistentStores: persistentStores)
}
public init(_ entity: T.Type, _ persistentStores: [NSPersistentStore]) {
self.init(persistentStores: persistentStores)
self.init(entityClass: entity, persistentStores: persistentStores)
}
public init(_ entityClass: AnyClass, _ persistentStores: NSPersistentStore...) {
self.init(entityClass: entityClass, persistentStores: persistentStores)
}
public init(_ entityClass: AnyClass, _ persistentStores: [NSPersistentStore]) {
self.init(entityClass: entityClass, persistentStores: persistentStores)
}
@@ -111,45 +149,50 @@ public struct From<T: NSManagedObject> {
internal func applyToFetchRequest(fetchRequest: NSFetchRequest, context: NSManagedObjectContext) {
fetchRequest.entity = context.entityDescriptionForEntityClass(T.self)
fetchRequest.entity = context.entityDescriptionForEntityClass(self.entityClass)
fetchRequest.affectedStores = self.findPersistentStores(context: context)
}
// MARK: Private
private let entityClass: AnyClass
private let findPersistentStores: (context: NSManagedObjectContext) -> [NSPersistentStore]?
private init(configurations: [String?]) {
private init(entityClass: AnyClass, configurations: [String?]) {
let configurationsSet = Set(configurations.map { $0 ?? Into.defaultConfigurationName })
self.entityClass = entityClass
self.findPersistentStores = { (context: NSManagedObjectContext) -> [NSPersistentStore]? in
return context.parentStack?.persistentStoresForEntityClass(T.self)?.filter {
return context.parentStack?.persistentStoresForEntityClass(entityClass)?.filter {
return configurationsSet.contains($0.configurationName)
}
}
}
private init(storeURLs: [NSURL]) {
private init(entityClass: AnyClass, storeURLs: [NSURL]) {
let storeURLsSet = Set(storeURLs)
self.entityClass = entityClass
self.findPersistentStores = { (context: NSManagedObjectContext) -> [NSPersistentStore]? in
return context.parentStack?.persistentStoresForEntityClass(T.self)?.filter {
return context.parentStack?.persistentStoresForEntityClass(entityClass)?.filter {
return $0.URL != nil && storeURLsSet.contains($0.URL!)
}
}
}
private init(persistentStores: [NSPersistentStore]) {
private init(entityClass: AnyClass, persistentStores: [NSPersistentStore]) {
let persistentStores = Set(persistentStores)
self.entityClass = entityClass
self.findPersistentStores = { (context: NSManagedObjectContext) -> [NSPersistentStore]? in
return context.parentStack?.persistentStoresForEntityClass(T.self)?.filter {
return context.parentStack?.persistentStoresForEntityClass(entityClass)?.filter {
return persistentStores.contains($0)
}

View File

@@ -73,7 +73,7 @@ public struct OrderBy: FetchClause, QueryClause, DeleteClause {
/**
Initializes a `OrderBy` clause with a list of sort descriptors
:param: sortDescriptors a series of `NSSortDescriptor`'s
:param: sortDescriptors a series of `NSSortDescriptor`s
*/
public init(_ sortDescriptors: [NSSortDescriptor]) {
@@ -99,9 +99,9 @@ public struct OrderBy: FetchClause, QueryClause, DeleteClause {
}
/**
Initializes a `OrderBy` clause with a series of `SortKey`'s
Initializes a `OrderBy` clause with a series of `SortKey`s
:param: sortKey a series of `SortKey`'s
:param: sortKey a series of `SortKey`s
*/
public init(_ sortKey: [SortKey]) {
@@ -121,10 +121,10 @@ public struct OrderBy: FetchClause, QueryClause, DeleteClause {
}
/**
Initializes a `OrderBy` clause with a series of `SortKey`'s
Initializes a `OrderBy` clause with a series of `SortKey`s
:param: sortKey a single `SortKey`
:param: sortKeys a series of `SortKey`'s
:param: sortKeys a series of `SortKey`s
*/
public init(_ sortKey: SortKey, _ sortKeys: SortKey...) {

View File

@@ -267,7 +267,7 @@ Valid return types depend on the query:
- for `queryAttributes(...)` methods:
- `NSDictionary`
:param: sortDescriptors a series of `NSSortDescriptor`'s
:param: sortDescriptors a series of `NSSortDescriptor`s
*/
public struct Select<T: SelectResultType> {
@@ -279,10 +279,10 @@ public struct Select<T: SelectResultType> {
public typealias ReturnType = T
/**
Initializes a `Select` clause with a list of `SelectTerm`'s
Initializes a `Select` clause with a list of `SelectTerm`s
:param: selectTerm a `SelectTerm`
:param: selectTerms a series of `SelectTerm`'s
:param: selectTerms a series of `SelectTerm`s
*/
public init(_ selectTerm: SelectTerm, _ selectTerms: SelectTerm...) {

View File

@@ -80,11 +80,11 @@ public extension CoreStore {
}
/**
Using the `defaultStack`, fetches the number of `NSManagedObject`'s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
Using the `defaultStack`, fetches the number of `NSManagedObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:param: from a `From` clause indicating the entity type
:param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: the number `NSManagedObject`'s that satisfy the specified `FetchClause`s
:returns: the number `NSManagedObject`s that satisfy the specified `FetchClause`s
*/
public static func fetchCount<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> Int? {
@@ -92,11 +92,11 @@ public extension CoreStore {
}
/**
Using the `defaultStack`, fetches the number of `NSManagedObject`'s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
Using the `defaultStack`, fetches the number of `NSManagedObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:param: from a `From` clause indicating the entity type
:param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: the number `NSManagedObject`'s that satisfy the specified `FetchClause`s
:returns: the number `NSManagedObject`s that satisfy the specified `FetchClause`s
*/
public static func fetchCount<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> Int? {
@@ -128,11 +128,11 @@ public extension CoreStore {
}
/**
Using the `defaultStack`, fetches the `NSManagedObjectID` for all `NSManagedObject`'s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
Using the `defaultStack`, fetches the `NSManagedObjectID` for all `NSManagedObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:param: from a `From` clause indicating the entity type
:param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: the `NSManagedObjectID` for all `NSManagedObject`'s that satisfy the specified `FetchClause`s
:returns: the `NSManagedObjectID` for all `NSManagedObject`s that satisfy the specified `FetchClause`s
*/
public static func fetchObjectIDs<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> [NSManagedObjectID]? {
@@ -140,11 +140,11 @@ public extension CoreStore {
}
/**
Using the `defaultStack`, fetches the `NSManagedObjectID` for all `NSManagedObject`'s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
Using the `defaultStack`, fetches the `NSManagedObjectID` for all `NSManagedObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:param: from a `From` clause indicating the entity type
:param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: the `NSManagedObjectID` for all `NSManagedObject`'s that satisfy the specified `FetchClause`s
:returns: the `NSManagedObjectID` for all `NSManagedObject`s that satisfy the specified `FetchClause`s
*/
public static func fetchObjectIDs<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> [NSManagedObjectID]? {
@@ -152,7 +152,7 @@ public extension CoreStore {
}
/**
Using the `defaultStack`, queries aggregate values as specified by the `QueryClause`'s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
Using the `defaultStack`, queries aggregate values as specified by the `QueryClause`s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result.
@@ -167,7 +167,7 @@ public extension CoreStore {
}
/**
Using the `defaultStack`, queries aggregate values as specified by the `QueryClause`'s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
Using the `defaultStack`, queries aggregate values as specified by the `QueryClause`s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result.
@@ -182,7 +182,7 @@ public extension CoreStore {
}
/**
Using the `defaultStack`, queries a dictionary of attribtue values as specified by the `QueryClause`'s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
Using the `defaultStack`, queries a dictionary of attribtue values as specified by the `QueryClause`s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result.
@@ -197,7 +197,7 @@ public extension CoreStore {
}
/**
Using the `defaultStack`, queries a dictionary of attribute values as specified by the `QueryClause`'s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
Using the `defaultStack`, queries a dictionary of attribute values as specified by the `QueryClause`s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result.

View File

@@ -91,11 +91,11 @@ public extension DataStack {
}
/**
Fetches the number of `NSManagedObject`'s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
Fetches the number of `NSManagedObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:param: from a `From` clause indicating the entity type
:param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: the number `NSManagedObject`'s that satisfy the specified `FetchClause`s
:returns: the number `NSManagedObject`s that satisfy the specified `FetchClause`s
*/
public func fetchCount<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> Int? {
@@ -105,11 +105,11 @@ public extension DataStack {
}
/**
Fetches the number of `NSManagedObject`'s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
Fetches the number of `NSManagedObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:param: from a `From` clause indicating the entity type
:param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: the number `NSManagedObject`'s that satisfy the specified `FetchClause`s
:returns: the number `NSManagedObject`s that satisfy the specified `FetchClause`s
*/
public func fetchCount<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> Int? {
@@ -147,11 +147,11 @@ public extension DataStack {
}
/**
Fetches the `NSManagedObjectID` for all `NSManagedObject`'s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
Fetches the `NSManagedObjectID` for all `NSManagedObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:param: from a `From` clause indicating the entity type
:param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: the `NSManagedObjectID` for all `NSManagedObject`'s that satisfy the specified `FetchClause`s
:returns: the `NSManagedObjectID` for all `NSManagedObject`s that satisfy the specified `FetchClause`s
*/
public func fetchObjectIDs<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> [NSManagedObjectID]? {
@@ -161,11 +161,11 @@ public extension DataStack {
}
/**
Fetches the `NSManagedObjectID` for all `NSManagedObject`'s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
Fetches the `NSManagedObjectID` for all `NSManagedObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:param: from a `From` clause indicating the entity type
:param: fetchClauses a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: the `NSManagedObjectID` for all `NSManagedObject`'s that satisfy the specified `FetchClause`s
:returns: the `NSManagedObjectID` for all `NSManagedObject`s that satisfy the specified `FetchClause`s
*/
public func fetchObjectIDs<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> [NSManagedObjectID]? {
@@ -175,7 +175,7 @@ public extension DataStack {
}
/**
Queries aggregate values as specified by the `QueryClause`'s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
Queries aggregate values as specified by the `QueryClause`s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result.
@@ -192,7 +192,7 @@ public extension DataStack {
}
/**
Queries aggregate values as specified by the `QueryClause`'s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
Queries aggregate values as specified by the `QueryClause`s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result.
@@ -209,7 +209,7 @@ public extension DataStack {
}
/**
Queries a dictionary of attribute values as specified by the `QueryClause`'s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
Queries a dictionary of attribute values as specified by the `QueryClause`s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result.
@@ -226,7 +226,7 @@ public extension DataStack {
}
/**
Queries a dictionary of attribute values as specified by the `QueryClause`'s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
Queries a dictionary of attribute values as specified by the `QueryClause`s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result.

View File

@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.2.0</string>
<string>1.0.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>

View File

@@ -33,15 +33,15 @@ internal extension NSManagedObject {
// MARK: Internal
internal class func createInContext(context: NSManagedObjectContext) -> Self {
internal dynamic class func createInContext(context: NSManagedObjectContext) -> Self {
return self(
entity: context.entityDescriptionForEntityClass(self)!,
entity: context.entityDescriptionForEntityType(self)!,
insertIntoManagedObjectContext: context
)
}
internal class func inContext(context: NSManagedObjectContext, withObjectID objectID: NSManagedObjectID) -> Self? {
internal dynamic class func inContext(context: NSManagedObjectContext, withObjectID objectID: NSManagedObjectID) -> Self? {
return self.typedObjectInContext(context, objectID: objectID)
}

View File

@@ -54,7 +54,12 @@ internal extension NSManagedObjectContext {
}
}
internal func entityDescriptionForEntityClass(entity: NSManagedObject.Type) -> NSEntityDescription? {
internal func entityDescriptionForEntityType(entity: NSManagedObject.Type) -> NSEntityDescription? {
return self.entityDescriptionForEntityClass(entity)
}
internal func entityDescriptionForEntityClass(entity: AnyClass) -> NSEntityDescription? {
if let entityName = self.parentStack?.entityNameForEntityClass(entity) {

View File

@@ -88,3 +88,13 @@ internal func typeName<T>(value: T) -> String {
return "<\(_stdlib_getDemangledTypeName(value))>"
}
internal func typeName<T>(value: T.Type) -> String {
return "<\(value)>"
}
internal func typeName(value: AnyClass) -> String {
return "<\(value)>"
}

View File

@@ -32,55 +32,38 @@ import GCDKit
public extension DataStack {
// MARK: Public
/**
Initializes a `DataStack` from the specified model name and a version-specific model name.
:param: rootModelName the name of the (.xcdatamodeld) model file
:param: versionModelName the name of the version-specific (.xcdatamodeld) model file
*/
public convenience init(rootModelName: String, versionModelName: String) {
let modelVersionURL: NSURL! = NSBundle.mainBundle().URLForResource(
rootModelName.stringByAppendingPathExtension("momd")!.stringByAppendingPathComponent(versionModelName),
withExtension: "mom"
)
CoreStore.assert(modelVersionURL != nil, "Could not find a \"mom\" resource from the main bundle.")
let managedObjectModel: NSManagedObjectModel! = NSManagedObjectModel(contentsOfURL: modelVersionURL)
CoreStore.assert(managedObjectModel != nil, "Could not create an <\(NSManagedObjectModel.self)> from the resource at URL \"\(modelVersionURL)\".")
self.init(managedObjectModel: managedObjectModel)
}
/**
Checks if the store at the specified filename and configuration needs to be migrated to the `DataStack`'s managed object model version.
Checks if the store with the specified filename and configuration needs to be migrated to the `DataStack`'s managed object model version.
:param: fileName the local filename for the SQLite persistent store in the "Application Support" directory.
:param: configuration an optional configuration name from the model file. If not specified, defaults to `nil`, the "Default" configuration.
:param: configuration an optional configuration name from the model file. If not specified, defaults to `nil` which indicates the "Default" configuration.
:param: mappingModelBundles an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.allBundles()`.
: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.
*/
public func needsMigrationForSQLiteStore(fileName: String, configuration: String? = nil) -> Bool? {
public func needsMigrationForSQLiteStore(fileName: String, configuration: String? = nil, mappingModelBundles: [NSBundle] = NSBundle.allBundles() as! [NSBundle]) -> MigrationType? {
return needsMigrationForSQLiteStore(
fileURL: applicationSupportDirectory.URLByAppendingPathComponent(
fileName,
isDirectory: false
),
configuration: configuration
configuration: configuration,
sourceBundles: sourceBundles
)
}
/**
Checks if the store at the specified file URL and configuration needs to be migrated to the `DataStack`'s managed object model version.
:param: fileName the local filename for the SQLite persistent store in the "Application Support" directory.
:param: configuration an optional configuration name from the model file. If not specified, defaults to `nil`, the "Default" configuration.
:param: fileURL the local file URL for the SQLite persistent store.
:param: configuration an optional configuration name from the model file. If not specified, defaults to `nil` which indicates the "Default" configuration.
:param: sourceBundles an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.allBundles()`.
: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.
*/
public func needsMigrationForSQLiteStore(fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil) -> Bool? {
public func needsMigrationForSQLiteStore(fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, mappingModelBundles: [NSBundle] = NSBundle.allBundles() as! [NSBundle]) -> MigrationType? {
var error: NSError?
let metadata = NSPersistentStoreCoordinator.metadataForPersistentStoreOfType(
let metadata: [NSObject : AnyObject]! = NSPersistentStoreCoordinator.metadataForPersistentStoreOfType(
NSSQLiteStoreType,
URL: fileURL,
error: &error
@@ -89,35 +72,74 @@ public extension DataStack {
CoreStore.handleError(
error ?? NSError(coreStoreErrorCode: .UnknownError),
"Failed to add SQLite <\(NSPersistentStore.self)> at \"\(fileURL)\".")
"Failed to add SQLite <\(NSPersistentStore.self)> at \"\(fileURL)\"."
)
return nil
}
return !self.coordinator.managedObjectModel.isConfiguration(
let coordinator = self.coordinator;
let destinationModel = coordinator.managedObjectModel
if destinationModel.isConfiguration(
configuration,
compatibleWithStoreMetadata: metadata
)
compatibleWithStoreMetadata: metadata) {
return .None
}
let sourceModel = NSManagedObjectModel(
byMergingModels: [destinationModel],
forStoreMetadata: metadata
)!
if NSMappingModel(
fromBundles: mappingModelBundles,
forSourceModel: sourceModel,
destinationModel: destinationModel) != nil {
return .Heavyweight
}
if NSMappingModel.inferredMappingModelForSourceModel(
sourceModel,
destinationModel: destinationModel,
error: nil) != nil {
return .Lightweight
}
return nil
}
/**
EXPERIMENTAL
Migrates an SQLite store with the specified filename to the `DataStack`'s managed object model version. This method does NOT add the migrated store to the data stack.
:param: fileName the local filename for the SQLite persistent store in the "Application Support" directory.
:param: configuration an optional configuration name from the model file. If not specified, defaults to `nil` which indicates the "Default" configuration.
:param: sourceBundles an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`.
:param: sourceBundles an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`.
*/
private func upgradeSQLiteStoreIfNeeded(fileName: String, configuration: String? = nil, completion: (PersistentStoreResult) -> Void) {
public func upgradeSQLiteStoreIfNeeded(fileName: String, configuration: String? = nil, sourceBundles: [NSBundle]? = nil, completion: (MigrationResult) -> Void) -> MigrationType? {
self.upgradeSQLiteStoreIfNeeded(
return self.upgradeSQLiteStoreIfNeeded(
fileURL: applicationSupportDirectory.URLByAppendingPathComponent(
fileName,
isDirectory: false
),
configuration: configuration,
sourceBundles: sourceBundles,
completion: completion
)
}
/**
EXPERIMENTAL
Migrates an SQLite store at the specified file URL and configuration name to the `DataStack`'s managed object model version. This method does NOT add the migrated store to the data stack.
:param: fileName the local filename for the SQLite persistent store in the "Application Support" directory.
:param: configuration an optional configuration name from the model file. If not specified, defaults to `nil` which indicates the "Default" configuration.
:param: sourceBundles an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`.
:param: sourceBundles an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`.
*/
private func upgradeSQLiteStoreIfNeeded(fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, completion: (PersistentStoreResult) -> Void) {
public func upgradeSQLiteStoreIfNeeded(fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, sourceBundles: [NSBundle]? = nil, completion: (MigrationResult) -> Void) -> MigrationType? {
var metadataError: NSError?
let metadata: [NSObject: AnyObject]! = NSPersistentStoreCoordinator.metadataForPersistentStoreOfType(
@@ -127,106 +149,79 @@ public extension DataStack {
)
if metadata == nil {
let error = metadataError ?? NSError(coreStoreErrorCode: .UnknownError)
CoreStore.handleError(
metadataError ?? NSError(coreStoreErrorCode: .UnknownError),
"Failed to load SQLite <\(NSPersistentStore.self)> metadata at \"\(fileURL)\".")
error,
"Failed to load SQLite <\(NSPersistentStore.self)> metadata at \"\(fileURL)\"."
)
GCDQueue.Main.async {
// TODO: inspect valid errors for metadataForPersistentStoreOfType()
completion(PersistentStoreResult(.PersistentStoreNotFound))
completion(MigrationResult(error))
}
return
return nil
}
let coordinator = self.coordinator;
if let store = coordinator.persistentStoreForURL(fileURL) {
let isExistingStoreAutomigrating = ((store.options?[NSMigratePersistentStoresAutomaticallyOption] as? Bool) ?? false)
if store.type == NSSQLiteStoreType
&& isExistingStoreAutomigrating
&& store.configurationName == (configuration ?? Into.defaultConfigurationName) {
GCDQueue.Main.async {
completion(PersistentStoreResult(store))
}
return
}
CoreStore.handleError(
NSError(coreStoreErrorCode: .DifferentPersistentStoreExistsAtURL),
"Failed to add SQLite <\(NSPersistentStore.self)> at \"\(fileURL)\" because a different <\(NSPersistentStore.self)> at that URL already exists.")
GCDQueue.Main.async {
completion(PersistentStoreResult(.DifferentPersistentStoreExistsAtURL))
}
return
}
let managedObjectModel = self.coordinator.managedObjectModel
let migrationManager = NSMigrationManager(
sourceModel: NSManagedObjectModel(
byMergingModels: [managedObjectModel],
forStoreMetadata: metadata!
)!,
destinationModel: managedObjectModel
)
var mappingModel: NSMappingModel! = NSMappingModel(
fromBundles: nil, // TODO: parametize
forSourceModel: migrationManager.sourceModel,
destinationModel: migrationManager.destinationModel
)
var modelError: NSError?
if mappingModel == nil {
mappingModel = NSMappingModel.inferredMappingModelForSourceModel(
migrationManager.sourceModel,
destinationModel: migrationManager.destinationModel,
error: &modelError
)
}
if mappingModel == nil {
CoreStore.handleError(
NSError(coreStoreErrorCode: .UnknownError),
"Failed to load an <\(NSMappingModel.self)> for migration from version model \"\(migrationManager.sourceModel)\" to version model \"\(migrationManager.destinationModel)\".")
GCDQueue.Main.async {
completion(PersistentStoreResult(.MappingModelNotFound))
}
return
}
let temporaryFileURL = NSURL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)!.URLByAppendingPathComponent(NSProcessInfo().globallyUniqueString)
var migrationError: NSError?
if !migrationManager.migrateStoreFromURL(
fileURL,
type: NSSQLiteStoreType,
options: nil,
withMappingModel: mappingModel,
toDestinationURL: temporaryFileURL,
destinationType: NSSQLiteStoreType,
destinationOptions: nil,
error: &migrationError
) {
CoreStore.handleError(
migrationError ?? NSError(coreStoreErrorCode: .UnknownError),
"Failed to prepare for migration from version model \"\(migrationManager.sourceModel)\" to version model \"\(migrationManager.destinationModel)\".")
let destinationModel = coordinator.managedObjectModel
if destinationModel.isConfiguration(
configuration,
compatibleWithStoreMetadata: metadata) {
GCDQueue.Main.async {
completion(PersistentStoreResult(.MigrationFailed))
completion(MigrationResult(.None))
}
return
return .None
}
let sourceModel = NSManagedObjectModel(
byMergingModels: [destinationModel],
forStoreMetadata: metadata
)!
if let mappingModel = NSMappingModel(
fromBundles: sourceBundles,
forSourceModel: sourceModel,
destinationModel: destinationModel) {
self.startMigrationForSQLiteStore(
fileURL,
sourceModel: sourceModel,
destinationModel: destinationModel,
mappingModel: mappingModel,
migrationType: .Heavyweight,
completion: completion
)
return .Heavyweight
}
if let mappingModel = NSMappingModel.inferredMappingModelForSourceModel(
sourceModel,
destinationModel: destinationModel,
error: nil) {
self.startMigrationForSQLiteStore(
fileURL,
sourceModel: sourceModel,
destinationModel: destinationModel,
mappingModel: mappingModel,
migrationType: .Lightweight,
completion: completion
)
return .Lightweight
}
CoreStore.handleError(
NSError(coreStoreErrorCode: .UnknownError),
"Failed to load an <\(NSMappingModel.self)> for migration from version model \"\(sourceModel)\" to version model \"\(destinationModel)\"."
)
GCDQueue.Main.async {
completion(MigrationResult(.MappingModelNotFound))
}
return nil
}
/**
@@ -236,7 +231,7 @@ public extension DataStack {
:param: 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.
:param: 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.
*/
public func addSQLiteStore(fileName: String, configuration: String? = nil, completion: (PersistentStoreResult) -> Void) {
public func addSQLiteStore(fileName: String, configuration: String? = nil, sourceBundles: [NSBundle]? = nil, completion: (PersistentStoreResult) -> Void) {
self.addSQLiteStore(
fileURL: applicationSupportDirectory.URLByAppendingPathComponent(
@@ -244,6 +239,7 @@ public extension DataStack {
isDirectory: false
),
configuration: configuration,
sourceBundles: sourceBundles,
completion: completion
)
}
@@ -255,25 +251,29 @@ public extension DataStack {
:param: 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.
:param: 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.
*/
public func addSQLiteStore(fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, completion: (PersistentStoreResult) -> Void) {
public func addSQLiteStore(fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, sourceBundles: [NSBundle]? = nil, completion: (PersistentStoreResult) -> Void) {
var error: NSError?
let metadata = NSPersistentStoreCoordinator.metadataForPersistentStoreOfType(
NSSQLiteStoreType,
URL: fileURL,
error: &error
)
if metadata == nil {
if NSFileManager.defaultManager().fileExistsAtPath(fileURL.path!) {
CoreStore.handleError(
error ?? NSError(coreStoreErrorCode: .UnknownError),
"Failed to load SQLite <\(NSPersistentStore.self)> metadata at \"\(fileURL)\".")
GCDQueue.Main.async {
var error: NSError?
let metadata = NSPersistentStoreCoordinator.metadataForPersistentStoreOfType(
NSSQLiteStoreType,
URL: fileURL,
error: &error
)
if metadata == nil {
completion(PersistentStoreResult(.UnknownError))
CoreStore.handleError(
error ?? NSError(coreStoreErrorCode: .UnknownError),
"Failed to load SQLite <\(NSPersistentStore.self)> metadata at \"\(fileURL)\"."
)
GCDQueue.Main.async {
completion(PersistentStoreResult(.UnknownError))
}
return
}
return
}
let coordinator = self.coordinator;
@@ -294,7 +294,8 @@ public extension DataStack {
CoreStore.handleError(
NSError(coreStoreErrorCode: .DifferentPersistentStoreExistsAtURL),
"Failed to add SQLite <\(NSPersistentStore.self)> at \"\(fileURL)\" because a different <\(NSPersistentStore.self)> at that URL already exists.")
"Failed to add SQLite <\(NSPersistentStore.self)> at \"\(fileURL)\" because a different <\(NSPersistentStore.self)> at that URL already exists."
)
GCDQueue.Main.async {
@@ -313,7 +314,9 @@ public extension DataStack {
CoreStore.handleError(
directoryError ?? NSError(coreStoreErrorCode: .UnknownError),
"Failed to create directory for SQLite store at \"\(fileURL)\".")
"Failed to create directory for SQLite store at \"\(fileURL)\"."
)
GCDQueue.Main.async {
completion(PersistentStoreResult(directoryError!))
@@ -347,11 +350,105 @@ public extension DataStack {
CoreStore.handleError(
persistentStoreError ?? NSError(coreStoreErrorCode: .UnknownError),
"Failed to add SQLite <\(NSPersistentStore.self)> at \"\(fileURL)\".")
"Failed to add SQLite <\(NSPersistentStore.self)> at \"\(fileURL)\"."
)
completion(PersistentStoreResult(.UnknownError))
}
}
}
}
// MARK: Private
private func startMigrationForSQLiteStore(fileURL: NSURL, sourceModel: NSManagedObjectModel, destinationModel: NSManagedObjectModel, mappingModel: NSMappingModel, migrationType: MigrationType, completion: (MigrationResult) -> Void) {
let migrationManager = NSMigrationManager(
sourceModel: sourceModel,
destinationModel: destinationModel
)
self.migrationQueue.async {
var lastReportedProgress: Float = -1
let timer = GCDTimer.createSuspended(
.Main,
interval: 0.1,
eventHandler: { (timer) -> Void in
let progress = migrationManager.migrationProgress
if progress > lastReportedProgress {
// TODO: progress
CoreStore.log(.Trace, message: "migration progress: \(progress)")
lastReportedProgress = progress
}
}
)
let temporaryFileURL = NSURL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)!.URLByAppendingPathComponent(NSProcessInfo().globallyUniqueString)
var migrationError: NSError?
let migrationCompleted = migrationManager.migrateStoreFromURL(
fileURL,
type: NSSQLiteStoreType,
options: nil,
withMappingModel: mappingModel,
toDestinationURL: temporaryFileURL,
destinationType: NSSQLiteStoreType,
destinationOptions: nil,
error: &migrationError
)
timer.suspend()
let fileManager = NSFileManager.defaultManager()
if !migrationCompleted {
fileManager.removeItemAtURL(temporaryFileURL, error: nil)
let error = migrationError ?? NSError(coreStoreErrorCode: .UnknownError)
CoreStore.handleError(
error,
"Failed to migrate from version model \"\(migrationManager.sourceModel)\" to version model \"\(migrationManager.destinationModel)\"."
)
GCDQueue.Main.async {
completion(MigrationResult(error))
}
return
}
var replaceError: NSError?
if !fileManager.replaceItemAtURL(
fileURL,
withItemAtURL: temporaryFileURL,
backupItemName: nil,
options: .allZeros,
resultingItemURL: nil,
error: &replaceError) {
fileManager.removeItemAtURL(temporaryFileURL, error: nil)
let error = replaceError ?? NSError(coreStoreErrorCode: .UnknownError)
CoreStore.handleError(
error,
"Failed to save store after migrating from version model \"\(migrationManager.sourceModel)\" to version model \"\(migrationManager.destinationModel)\"."
)
GCDQueue.Main.async {
completion(MigrationResult(error))
}
return
}
GCDQueue.Main.async {
completion(MigrationResult(migrationType))
}
}
}
}

View File

@@ -0,0 +1,129 @@
//
// MigrationChain.swift
// CoreStore
//
// Copyright (c) 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: - MigrationChain
public struct MigrationChain: NilLiteralConvertible, StringLiteralConvertible, DictionaryLiteralConvertible, ArrayLiteralConvertible {
// MARK: Public
public func contains(version: String) -> Bool {
return self.latestVersion == version
|| find(self.versionTree.keys, version) != nil
}
// MARK: NilLiteralConvertible
public init(nilLiteral: ()) {
self.latestVersion = nil
self.versionTree = [:]
}
// MARK: StringLiteralConvertible
public init(stringLiteral value: String) {
self.latestVersion = value
self.versionTree = [:]
}
// MARK: ExtendedGraphemeClusterLiteralConvertible
public init(extendedGraphemeClusterLiteral value: String) {
self.latestVersion = value
self.versionTree = [:]
}
// MARK: UnicodeScalarLiteralConvertible
public init(unicodeScalarLiteral value: String) {
self.latestVersion = value
self.versionTree = [:]
}
// MARK: DictionaryLiteralConvertible
public init(dictionaryLiteral elements: (String, String)...) {
let versionTree = elements.reduce([String: String]()) { (var versionTree, tuple: (String, String)) -> [String: String] in
versionTree[tuple.0] = tuple.1
return versionTree
}
let latestVersions = elements.filter { (tuple: (String, String)) -> Bool in
return versionTree[tuple.1] == nil
}
CoreStore.assert(latestVersions.count == 1, "\(typeName(MigrationChain))'s migration chain could not be resolved due to multiple leaf versions.")
self.latestVersion = latestVersions.first?.1
self.versionTree = versionTree
}
// MARK: ArrayLiteralConvertible
public init(arrayLiteral elements: String...) {
CoreStore.assert(Set(elements).count == elements.count, "\(typeName(MigrationChain))'s migration chain could not be created due to duplicate version strings.")
var lastVersion: String?
var versionTree = [String: String]()
for version in elements {
if let lastVersion = lastVersion {
versionTree[lastVersion] = version
}
lastVersion = version
}
self.versionTree = versionTree
self.latestVersion = elements.last
}
// MARK: Internal
internal let latestVersion: String?
// MARK: Private
private let versionTree: [String: String]
}

View File

@@ -0,0 +1,151 @@
//
// MigrationResult.swift
// CoreStore
//
// Copyright (c) 2014 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
// MARK: - MigrationType
/**
The `MigrationType` specifies the type of migration required for a store.
*/
public enum MigrationType: BooleanType {
// MARK: Public
/**
Indicates that the persistent store matches the latest model version and no migration is needed
*/
case None
/**
Indicates that the persistent store does not match the latest model version but Core Data can infer the mapping model, so a lightweight migration is needed
*/
case Lightweight
/**
Indicates that the persistent store does not match the latest model version and Core Data could not infer a mapping model, so a custom migration is needed
*/
case Heavyweight
// MARK: BooleanType
public var boolValue: Bool {
switch self {
case .None: return false
case .Lightweight: return true
case .Heavyweight: return true
}
}
}
// MARK: - MigrationResult
/**
The `MigrationResult` indicates the result of a migration.
The `MigrationResult` can be treated as a boolean:
CoreStore.upgradeSQLiteStoreIfNeeded { transaction in
// ...
let result = transaction.commit()
if result {
// succeeded
}
else {
// failed
}
}
or as an `enum`, where the resulting associated object can also be inspected:
CoreStore.beginAsynchronous { transaction in
// ...
let result = transaction.commit()
switch result {
case .Success(let hasChanges):
// hasChanges indicates if there were changes or not
case .Failure(let error):
// error is the NSError instance for the failure
}
}
```
*/
public enum MigrationResult {
// MARK: Public
/**
`MigrationResult.Success` indicates that the `commit()` for the transaction succeeded, either because the save succeeded or because there were no changes to save. The associated value `hasChanges` indicates if there were saved changes or not.
*/
case Success(MigrationType)
/**
`SaveResult.Failure` indicates that the `commit()` for the transaction failed. The associated object for this value is the related `NSError` instance.
*/
case Failure(NSError)
// MARK: Internal
internal init(_ migrationType: MigrationType) {
self = .Success(migrationType)
}
internal init(_ error: NSError) {
self = .Failure(error)
}
internal init(_ errorCode: CoreStoreErrorCode) {
self.init(errorCode, userInfo: nil)
}
internal init(_ errorCode: CoreStoreErrorCode, userInfo: [NSObject: AnyObject]?) {
self.init(NSError(
coreStoreErrorCode: errorCode,
userInfo: userInfo))
}
}
// MARK: - MigrationResult: BooleanType
extension MigrationResult: BooleanType {
public var boolValue: Bool {
switch self {
case .Success: return true
case .Failure: return false
}
}
}

View File

@@ -54,11 +54,6 @@ public enum CoreStoreErrorCode: Int {
An `NSMappingModel` could not be found for a specific source and destination model versions.
*/
case MappingModelNotFound
/**
An `NSMigrationManager` prepared to migrate the store.
*/
case MigrationFailed
}

View File

@@ -34,63 +34,63 @@ public extension CoreStore {
// MARK: Public
/**
Using the `defaultStack`, creates a `ManagedObjectController` for the specified `NSManagedObject`. Multiple `ManagedObjectObserver`'s may then register themselves to be notified when changes are made to the `NSManagedObject`.
Using the `defaultStack`, creates a `ObjectMonitor` for the specified `NSManagedObject`. Multiple `ObjectObserver`s may then register themselves to be notified when changes are made to the `NSManagedObject`.
:param: object the `NSManagedObject` to observe changes from
:returns: a `ManagedObjectController` that monitors changes to `object`
:returns: a `ObjectMonitor` that monitors changes to `object`
*/
public static func observeObject<T: NSManagedObject>(object: T) -> ManagedObjectController<T> {
public static func monitorObject<T: NSManagedObject>(object: T) -> ObjectMonitor<T> {
return self.defaultStack.observeObject(object)
return self.defaultStack.monitorObject(object)
}
/**
Using the `defaultStack`, creates a `ManagedObjectListController` for a list of `NSManagedObject`'s that satisfy the specified fetch clauses. Multiple `ManagedObjectListObserver`'s may then register themselves to be notified when changes are made to the list.
Using the `defaultStack`, creates a `ListMonitor` for a list of `NSManagedObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list.
:param: from a `From` clause indicating the entity type
:param: fetchClauses a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: a `ManagedObjectListController` instance that monitors changes to the list
:returns: a `ListMonitor` instance that monitors changes to the list
*/
public static func observeObjectList<T: NSManagedObject>(from: From<T>, _ groupBy: GroupBy? = nil, _ queryClauses: FetchClause...) -> ManagedObjectListController<T> {
public static func monitorList<T: NSManagedObject>(from: From<T>, _ groupBy: GroupBy? = nil, _ queryClauses: FetchClause...) -> ListMonitor<T> {
return self.defaultStack.observeObjectList(from, queryClauses)
return self.defaultStack.monitorList(from, queryClauses)
}
/**
Using the `defaultStack`, creates a `ManagedObjectListController` for a list of `NSManagedObject`'s that satisfy the specified fetch clauses. Multiple `ManagedObjectListObserver`'s may then register themselves to be notified when changes are made to the list.
Using the `defaultStack`, creates a `ListMonitor` for a list of `NSManagedObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list.
:param: from a `From` clause indicating the entity type
:param: fetchClauses a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: a `ManagedObjectListController` instance that monitors changes to the list
:returns: a `ListMonitor` instance that monitors changes to the list
*/
public static func observeObjectList<T: NSManagedObject>(from: From<T>, _ groupBy: GroupBy? = nil, _ queryClauses: [FetchClause]) -> ManagedObjectListController<T> {
public static func monitorList<T: NSManagedObject>(from: From<T>, _ groupBy: GroupBy? = nil, _ queryClauses: [FetchClause]) -> ListMonitor<T> {
return self.defaultStack.observeObjectList(from, queryClauses)
return self.defaultStack.monitorList(from, queryClauses)
}
/**
Using the `defaultStack`, creates a `ManagedObjectListController` for a sectioned list of `NSManagedObject`'s that satisfy the specified fetch clauses. Multiple `ManagedObjectListObserver`'s may then register themselves to be notified when changes are made to the list.
Using the `defaultStack`, creates a `ListMonitor` for a sectioned list of `NSManagedObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list.
:param: from a `From` clause indicating the entity type
:param: sectionedBy a `SectionedBy` clause indicating the keyPath for the attribute to use when sorting the list into sections.
:param: sectionBy a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections.
:param: fetchClauses a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: a `ManagedObjectListController` instance that monitors changes to the list
:returns: a `ListMonitor` instance that monitors changes to the list
*/
public static func observeSectionedList<T: NSManagedObject>(from: From<T>, _ sectionedBy: SectionedBy, _ fetchClauses: FetchClause...) -> ManagedObjectListController<T> {
public static func monitorSectionedList<T: NSManagedObject>(from: From<T>, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> ListMonitor<T> {
return self.defaultStack.observeSectionedList(from, sectionedBy, fetchClauses)
return self.defaultStack.monitorSectionedList(from, sectionBy, fetchClauses)
}
/**
Using the `defaultStack`, creates a `ManagedObjectListController` for a sectioned list of `NSManagedObject`'s that satisfy the specified fetch clauses. Multiple `ManagedObjectListObserver`'s may then register themselves to be notified when changes are made to the list.
Using the `defaultStack`, creates a `ListMonitor` for a sectioned list of `NSManagedObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list.
:param: from a `From` clause indicating the entity type
:param: sectionedBy a `SectionedBy` clause indicating the keyPath for the attribute to use when sorting the list into sections.
:param: sectionBy a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections.
:param: fetchClauses a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: a `ManagedObjectListController` instance that monitors changes to the list
:returns: a `ListMonitor` instance that monitors changes to the list
*/
public static func observeSectionedList<T: NSManagedObject>(from: From<T>, _ sectionedBy: SectionedBy, _ fetchClauses: [FetchClause]) -> ManagedObjectListController<T> {
public static func monitorSectionedList<T: NSManagedObject>(from: From<T>, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> ListMonitor<T> {
return self.defaultStack.observeSectionedList(from, sectionedBy, fetchClauses)
return self.defaultStack.monitorSectionedList(from, sectionBy, fetchClauses)
}
}

View File

@@ -35,81 +35,81 @@ public extension DataStack {
// MARK: Public
/**
Creates a `ManagedObjectController` for the specified `NSManagedObject`. Multiple `ManagedObjectObserver`'s may then register themselves to be notified when changes are made to the `NSManagedObject`.
Creates a `ObjectMonitor` for the specified `NSManagedObject`. Multiple `ObjectObserver`s may then register themselves to be notified when changes are made to the `NSManagedObject`.
:param: object the `NSManagedObject` to observe changes from
:returns: a `ManagedObjectController` that monitors changes to `object`
:returns: a `ObjectMonitor` that monitors changes to `object`
*/
public func observeObject<T: NSManagedObject>(object: T) -> ManagedObjectController<T> {
public func monitorObject<T: NSManagedObject>(object: T) -> ObjectMonitor<T> {
CoreStore.assert(NSThread.isMainThread(), "Attempted to observe objects from \(typeName(self)) outside the main thread.")
return ManagedObjectController(
return ObjectMonitor(
dataStack: self,
object: object
)
}
/**
Creates a `ManagedObjectListController` for a list of `NSManagedObject`'s that satisfy the specified fetch clauses. Multiple `ManagedObjectListObserver`'s may then register themselves to be notified when changes are made to the list.
Creates a `ListMonitor` for a list of `NSManagedObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list.
:param: from a `From` clause indicating the entity type
:param: fetchClauses a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: a `ManagedObjectListController` instance that monitors changes to the list
:returns: a `ListMonitor` instance that monitors changes to the list
*/
public func observeObjectList<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> ManagedObjectListController<T> {
public func monitorList<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> ListMonitor<T> {
return self.observeObjectList(from, fetchClauses)
return self.monitorList(from, fetchClauses)
}
/**
Creates a `ManagedObjectListController` for a list of `NSManagedObject`'s that satisfy the specified fetch clauses. Multiple `ManagedObjectListObserver`'s may then register themselves to be notified when changes are made to the list.
Creates a `ListMonitor` for a list of `NSManagedObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list.
:param: from a `From` clause indicating the entity type
:param: fetchClauses a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: a `ManagedObjectListController` instance that monitors changes to the list
:returns: a `ListMonitor` instance that monitors changes to the list
*/
public func observeObjectList<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> ManagedObjectListController<T> {
public func monitorList<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> ListMonitor<T> {
CoreStore.assert(NSThread.isMainThread(), "Attempted to observe objects from \(typeName(self)) outside the main thread.")
return ManagedObjectListController(
return ListMonitor(
dataStack: self,
from: from,
sectionedBy: nil,
sectionBy: nil,
fetchClauses: fetchClauses
)
}
/**
Creates a `ManagedObjectListController` for a sectioned list of `NSManagedObject`'s that satisfy the specified fetch clauses. Multiple `ManagedObjectListObserver`'s may then register themselves to be notified when changes are made to the list.
Creates a `ListMonitor` for a sectioned list of `NSManagedObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list.
:param: from a `From` clause indicating the entity type
:param: sectionedBy a `SectionedBy` clause indicating the keyPath for the attribute to use when sorting the list into sections.
:param: sectionBy a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections.
:param: fetchClauses a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: a `ManagedObjectListController` instance that monitors changes to the list
:returns: a `ListMonitor` instance that monitors changes to the list
*/
public func observeSectionedList<T: NSManagedObject>(from: From<T>, _ sectionedBy: SectionedBy, _ fetchClauses: FetchClause...) -> ManagedObjectListController<T> {
public func monitorSectionedList<T: NSManagedObject>(from: From<T>, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> ListMonitor<T> {
return self.observeSectionedList(from, sectionedBy, fetchClauses)
return self.monitorSectionedList(from, sectionBy, fetchClauses)
}
/**
Creates a `ManagedObjectListController` for a sectioned list of `NSManagedObject`'s that satisfy the specified fetch clauses. Multiple `ManagedObjectListObserver`'s may then register themselves to be notified when changes are made to the list.
Creates a `ListMonitor` for a sectioned list of `NSManagedObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list.
:param: from a `From` clause indicating the entity type
:param: sectionedBy a `SectionedBy` clause indicating the keyPath for the attribute to use when sorting the list into sections.
:param: sectionBy a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections.
:param: fetchClauses a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
:returns: a `ManagedObjectListController` instance that monitors changes to the list
:returns: a `ListMonitor` instance that monitors changes to the list
*/
public func observeSectionedList<T: NSManagedObject>(from: From<T>, _ sectionedBy: SectionedBy, _ fetchClauses: [FetchClause]) -> ManagedObjectListController<T> {
public func monitorSectionedList<T: NSManagedObject>(from: From<T>, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> ListMonitor<T> {
CoreStore.assert(NSThread.isMainThread(), "Attempted to observe objects from \(typeName(self)) outside the main thread.")
return ManagedObjectListController(
return ListMonitor(
dataStack: self,
from: from,
sectionedBy: sectionedBy,
sectionBy: sectionBy,
fetchClauses: fetchClauses
)
}

View File

@@ -1,5 +1,5 @@
//
// ManagedObjectListController.swift
// ListMonitor.swift
// CoreStore
//
// Copyright (c) 2015 John Rommel Estropia
@@ -28,96 +28,51 @@ import CoreData
import GCDKit
// MARK: - SectionedBy
// MARK: - ListMonitor
/**
The `SectionedBy` clause indicates the key path to use to group the `ManagedObjectListController` objects into sections. An optional closure can also be provided to transform the value into an appropriate section name:
The `ListMonitor` monitors changes to a list of `NSManagedObject` instances. Observers that implement the `ListObserver` protocol may then register themselves to the `ListMonitor`'s `addObserver(_:)` method:
let listController = CoreStore.observeSectionedList(
From(MyPersonEntity),
SectionedBy("age") { "Age \($0)" },
OrderBy(.Ascending("lastName"))
)
*/
public struct SectionedBy {
// MARK: Public
/**
Initializes a `SectionedBy` clause with the key path to use to group `ManagedObjectListController` objects into sections
:param: sectionKeyPath the key path to use to group the objects into sections
*/
public init(_ sectionKeyPath: KeyPath) {
self.init(sectionKeyPath, { $0 })
}
/**
Initializes a `SectionedBy` clause with the key path to use to group `ManagedObjectListController` objects into sections, and a closure to transform the value for the key path to an appropriate section name
:param: sectionKeyPath the key path to use to group the objects into sections
:param: sectionIndexTransformer a closure to transform the value for the key path to an appropriate section name
*/
public init(_ sectionKeyPath: KeyPath, _ sectionIndexTransformer: (sectionName: String?) -> String?) {
self.sectionKeyPath = sectionKeyPath
self.sectionIndexTransformer = sectionIndexTransformer
}
// MARK: Internal
internal let sectionKeyPath: KeyPath
internal let sectionIndexTransformer: (sectionName: KeyPath?) -> String?
}
// MARK: - ManagedObjectListController
/**
The `ManagedObjectListController` monitors changes to a list of `NSManagedObject` instances. Observers that implement the `ManagedObjectListChangeObserver` protocol may then register themselves to the `ManagedObjectListController`'s `addObserver(_:)` method:
let listController = CoreStore.observeObjectList(
let monitor = CoreStore.monitorList(
From(MyPersonEntity),
Where("title", isEqualTo: "Engineer"),
OrderBy(.Ascending("lastName"))
)
listController.addObserver(self)
monitor.addObserver(self)
The `ManagedObjectListController` instance needs to be held on (retained) for as long as the list needs to be observed.
Observers registered via `addObserver(_:)` are not retained. `ManagedObjectListController` only keeps a `weak` reference to all observers, thus keeping itself free from retain-cycles.
The `ListMonitor` instance needs to be held on (retained) for as long as the list needs to be observed.
Observers registered via `addObserver(_:)` are not retained. `ListMonitor` only keeps a `weak` reference to all observers, thus keeping itself free from retain-cycles.
Lists created with `observeObjectList(...)` keep a single-section list of objects, where each object can be accessed by index:
Lists created with `monitorList(...)` keep a single-section list of objects, where each object can be accessed by index:
let firstPerson: MyPersonEntity = listController[0]
let firstPerson: MyPersonEntity = monitor[0]
Accessing the list with an index above the valid range will throw an exception.
Creating a sectioned-list is also possible with the `observeSectionedList(...)` method:
Creating a sectioned-list is also possible with the `monitorSectionedList(...)` method:
let listController = CoreStore.observeSectionedList(
let monitor = CoreStore.monitorSectionedList(
From(MyPersonEntity),
SectionedBy("age") { "Age \($0)" },
SectionBy("age") { "Age \($0)" },
Where("title", isEqualTo: "Engineer"),
OrderBy(.Ascending("lastName"))
)
listController.addObserver(self)
monitor.addObserver(self)
Objects from `ManagedObjectListController`'s created this way can be accessed either by an `NSIndexPath` or a tuple:
Objects from `ListMonitor`s created this way can be accessed either by an `NSIndexPath` or a tuple:
let indexPath = NSIndexPath(forItem: 3, inSection: 2)
let person1 = listController[indexPath]
let person2 = listController[2, 3]
let person1 = monitor[indexPath]
let person2 = monitor[2, 3]
In the example above, both `person1` and `person2` will contain the object at section=2, index=3.
*/
public final class ManagedObjectListController<T: NSManagedObject> {
public final class ListMonitor<T: NSManagedObject> {
// MARK: Public
/**
Accesses the object at the given index within the first section. This subscript indexer is typically used for `ManagedObjectListController`'s created with `addObserver(_:)`.
Accesses the object at the given index within the first section. This subscript indexer is typically used for `ListMonitor`s created with `addObserver(_:)`.
:param: index the index of the object. Using an index above the valid range will throw an exception.
*/
@@ -127,7 +82,7 @@ public final class ManagedObjectListController<T: NSManagedObject> {
}
/**
Accesses the object at the given `NSIndexPath`. This subscript indexer is typically used for `ManagedObjectListController`'s created with `observeSectionedList(_:)`.
Accesses the object at the given `NSIndexPath`. This subscript indexer is typically used for `ListMonitor`s created with `monitorSectionedList(_:)`.
:param: indexPath the `NSIndexPath` for the object. Using an `indexPath` with an invalid range will throw an exception.
*/
@@ -137,7 +92,7 @@ public final class ManagedObjectListController<T: NSManagedObject> {
}
/**
Accesses the object at the given `sectionIndex` and `itemIndex`. This subscript indexer is typically used for `ManagedObjectListController`'s created with `observeSectionedList(_:)`.
Accesses the object at the given `sectionIndex` and `itemIndex`. This subscript indexer is typically used for `ListMonitor`s created with `monitorSectionedList(_:)`.
:param: sectionIndex the section index for the object. Using a `sectionIndex` with an invalid range will throw an exception.
:param: itemIndex the index for the object within the section. Using an `itemIndex` with an invalid range will throw an exception.
@@ -176,17 +131,17 @@ public final class ManagedObjectListController<T: NSManagedObject> {
}
/**
Registers a `ManagedObjectListChangeObserver` to be notified when changes to the receiver's list occur.
Registers a `ListObserver` to be notified when changes to the receiver's list occur.
To prevent retain-cycles, `ManagedObjectListController` only keeps `weak` references to its observers.
To prevent retain-cycles, `ListMonitor` only keeps `weak` references to its observers.
For thread safety, this method needs to be called from the main thread. An assertion failure will occur (on debug builds only) if called from any thread other than the main thread.
Calling `addObserver(_:)` multiple times on the same observer is safe, as `ManagedObjectListController` unregisters previous notifications to the observer before re-registering them.
Calling `addObserver(_:)` multiple times on the same observer is safe, as `ListMonitor` unregisters previous notifications to the observer before re-registering them.
:param: observer a `ManagedObjectListChangeObserver` to send change notifications to
:param: observer a `ListObserver` to send change notifications to
*/
public func addObserver<U: ManagedObjectListChangeObserver where U.EntityType == T>(observer: U) {
public func addObserver<U: ListObserver where U.EntityType == T>(observer: U) {
CoreStore.assert(NSThread.isMainThread(), "Attempted to add an observer of type \(typeName(observer)) outside the main thread.")
@@ -194,42 +149,42 @@ public final class ManagedObjectListController<T: NSManagedObject> {
self.registerChangeNotification(
&NotificationKey.willChangeList,
name: ManagedObjectListControllerWillChangeListNotification,
name: ListMonitorWillChangeListNotification,
toObserver: observer,
callback: { [weak observer] (listController) -> Void in
callback: { [weak observer] (monitor) -> Void in
if let observer = observer {
observer.managedObjectListWillChange(listController)
observer.listMonitorWillChange(monitor)
}
}
)
self.registerChangeNotification(
&NotificationKey.didChangeList,
name: ManagedObjectListControllerDidChangeListNotification,
name: ListMonitorDidChangeListNotification,
toObserver: observer,
callback: { [weak observer] (listController) -> Void in
callback: { [weak observer] (monitor) -> Void in
if let observer = observer {
observer.managedObjectListDidChange(listController)
observer.listMonitorDidChange(monitor)
}
}
)
}
/**
Registers a `ManagedObjectListObjectObserver` to be notified when changes to the receiver's list occur.
Registers a `ListObjectObserver` to be notified when changes to the receiver's list occur.
To prevent retain-cycles, `ManagedObjectListController` only keeps `weak` references to its observers.
To prevent retain-cycles, `ListMonitor` only keeps `weak` references to its observers.
For thread safety, this method needs to be called from the main thread. An assertion failure will occur (on debug builds only) if called from any thread other than the main thread.
Calling `addObserver(_:)` multiple times on the same observer is safe, as `ManagedObjectListController` unregisters previous notifications to the observer before re-registering them.
Calling `addObserver(_:)` multiple times on the same observer is safe, as `ListMonitor` unregisters previous notifications to the observer before re-registering them.
:param: observer a `ManagedObjectListObjectObserver` to send change notifications to
:param: observer a `ListObjectObserver` to send change notifications to
*/
public func addObserver<U: ManagedObjectListObjectObserver where U.EntityType == T>(observer: U) {
public func addObserver<U: ListObjectObserver where U.EntityType == T>(observer: U) {
CoreStore.assert(NSThread.isMainThread(), "Attempted to add an observer of type \(typeName(observer)) outside the main thread.")
@@ -237,39 +192,39 @@ public final class ManagedObjectListController<T: NSManagedObject> {
self.registerChangeNotification(
&NotificationKey.willChangeList,
name: ManagedObjectListControllerWillChangeListNotification,
name: ListMonitorWillChangeListNotification,
toObserver: observer,
callback: { [weak observer] (listController) -> Void in
callback: { [weak observer] (monitor) -> Void in
if let observer = observer {
observer.managedObjectListWillChange(listController)
observer.listMonitorWillChange(monitor)
}
}
)
self.registerChangeNotification(
&NotificationKey.didChangeList,
name: ManagedObjectListControllerDidChangeListNotification,
name: ListMonitorDidChangeListNotification,
toObserver: observer,
callback: { [weak observer] (listController) -> Void in
callback: { [weak observer] (monitor) -> Void in
if let observer = observer {
observer.managedObjectListDidChange(listController)
observer.listMonitorDidChange(monitor)
}
}
)
self.registerObjectNotification(
&NotificationKey.didInsertObject,
name: ManagedObjectListControllerDidInsertObjectNotification,
name: ListMonitorDidInsertObjectNotification,
toObserver: observer,
callback: { [weak observer] (listController, object, indexPath, newIndexPath) -> Void in
callback: { [weak observer] (monitor, object, indexPath, newIndexPath) -> Void in
if let observer = observer {
observer.managedObjectList(
listController,
observer.listMonitor(
monitor,
didInsertObject: object,
toIndexPath: newIndexPath!
)
@@ -278,14 +233,14 @@ public final class ManagedObjectListController<T: NSManagedObject> {
)
self.registerObjectNotification(
&NotificationKey.didDeleteObject,
name: ManagedObjectListControllerDidDeleteObjectNotification,
name: ListMonitorDidDeleteObjectNotification,
toObserver: observer,
callback: { [weak observer] (listController, object, indexPath, newIndexPath) -> Void in
callback: { [weak observer] (monitor, object, indexPath, newIndexPath) -> Void in
if let observer = observer {
observer.managedObjectList(
listController,
observer.listMonitor(
monitor,
didDeleteObject: object,
fromIndexPath: indexPath!
)
@@ -294,14 +249,14 @@ public final class ManagedObjectListController<T: NSManagedObject> {
)
self.registerObjectNotification(
&NotificationKey.didUpdateObject,
name: ManagedObjectListControllerDidUpdateObjectNotification,
name: ListMonitorDidUpdateObjectNotification,
toObserver: observer,
callback: { [weak observer] (listController, object, indexPath, newIndexPath) -> Void in
callback: { [weak observer] (monitor, object, indexPath, newIndexPath) -> Void in
if let observer = observer {
observer.managedObjectList(
listController,
observer.listMonitor(
monitor,
didUpdateObject: object,
atIndexPath: indexPath!
)
@@ -310,14 +265,14 @@ public final class ManagedObjectListController<T: NSManagedObject> {
)
self.registerObjectNotification(
&NotificationKey.didMoveObject,
name: ManagedObjectListControllerDidMoveObjectNotification,
name: ListMonitorDidMoveObjectNotification,
toObserver: observer,
callback: { [weak observer] (listController, object, indexPath, newIndexPath) -> Void in
callback: { [weak observer] (monitor, object, indexPath, newIndexPath) -> Void in
if let observer = observer {
observer.managedObjectList(
listController,
observer.listMonitor(
monitor,
didMoveObject: object,
fromIndexPath: indexPath!,
toIndexPath: newIndexPath!
@@ -328,17 +283,17 @@ public final class ManagedObjectListController<T: NSManagedObject> {
}
/**
Registers a `ManagedObjectListSectionObserver` to be notified when changes to the receiver's list occur.
Registers a `ListSectionObserver` to be notified when changes to the receiver's list occur.
To prevent retain-cycles, `ManagedObjectListController` only keeps `weak` references to its observers.
To prevent retain-cycles, `ListMonitor` only keeps `weak` references to its observers.
For thread safety, this method needs to be called from the main thread. An assertion failure will occur (on debug builds only) if called from any thread other than the main thread.
Calling `addObserver(_:)` multiple times on the same observer is safe, as `ManagedObjectListController` unregisters previous notifications to the observer before re-registering them.
Calling `addObserver(_:)` multiple times on the same observer is safe, as `ListMonitor` unregisters previous notifications to the observer before re-registering them.
:param: observer a `ManagedObjectListSectionObserver` to send change notifications to
:param: observer a `ListSectionObserver` to send change notifications to
*/
public func addObserver<U: ManagedObjectListSectionObserver where U.EntityType == T>(observer: U) {
public func addObserver<U: ListSectionObserver where U.EntityType == T>(observer: U) {
CoreStore.assert(NSThread.isMainThread(), "Attempted to add an observer of type \(typeName(observer)) outside the main thread.")
@@ -346,39 +301,39 @@ public final class ManagedObjectListController<T: NSManagedObject> {
self.registerChangeNotification(
&NotificationKey.willChangeList,
name: ManagedObjectListControllerWillChangeListNotification,
name: ListMonitorWillChangeListNotification,
toObserver: observer,
callback: { [weak observer] (listController) -> Void in
callback: { [weak observer] (monitor) -> Void in
if let observer = observer {
observer.managedObjectListWillChange(listController)
observer.listMonitorWillChange(monitor)
}
}
)
self.registerChangeNotification(
&NotificationKey.didChangeList,
name: ManagedObjectListControllerDidChangeListNotification,
name: ListMonitorDidChangeListNotification,
toObserver: observer,
callback: { [weak observer] (listController) -> Void in
callback: { [weak observer] (monitor) -> Void in
if let observer = observer {
observer.managedObjectListDidChange(listController)
observer.listMonitorDidChange(monitor)
}
}
)
self.registerObjectNotification(
&NotificationKey.didInsertObject,
name: ManagedObjectListControllerDidInsertObjectNotification,
name: ListMonitorDidInsertObjectNotification,
toObserver: observer,
callback: { [weak observer] (listController, object, indexPath, newIndexPath) -> Void in
callback: { [weak observer] (monitor, object, indexPath, newIndexPath) -> Void in
if let observer = observer {
observer.managedObjectList(
listController,
observer.listMonitor(
monitor,
didInsertObject: object,
toIndexPath: newIndexPath!
)
@@ -387,14 +342,14 @@ public final class ManagedObjectListController<T: NSManagedObject> {
)
self.registerObjectNotification(
&NotificationKey.didDeleteObject,
name: ManagedObjectListControllerDidDeleteObjectNotification,
name: ListMonitorDidDeleteObjectNotification,
toObserver: observer,
callback: { [weak observer] (listController, object, indexPath, newIndexPath) -> Void in
callback: { [weak observer] (monitor, object, indexPath, newIndexPath) -> Void in
if let observer = observer {
observer.managedObjectList(
listController,
observer.listMonitor(
monitor,
didDeleteObject: object,
fromIndexPath: indexPath!
)
@@ -403,14 +358,14 @@ public final class ManagedObjectListController<T: NSManagedObject> {
)
self.registerObjectNotification(
&NotificationKey.didUpdateObject,
name: ManagedObjectListControllerDidUpdateObjectNotification,
name: ListMonitorDidUpdateObjectNotification,
toObserver: observer,
callback: { [weak observer] (listController, object, indexPath, newIndexPath) -> Void in
callback: { [weak observer] (monitor, object, indexPath, newIndexPath) -> Void in
if let observer = observer {
observer.managedObjectList(
listController,
observer.listMonitor(
monitor,
didUpdateObject: object,
atIndexPath: indexPath!
)
@@ -419,14 +374,14 @@ public final class ManagedObjectListController<T: NSManagedObject> {
)
self.registerObjectNotification(
&NotificationKey.didMoveObject,
name: ManagedObjectListControllerDidMoveObjectNotification,
name: ListMonitorDidMoveObjectNotification,
toObserver: observer,
callback: { [weak observer] (listController, object, indexPath, newIndexPath) -> Void in
callback: { [weak observer] (monitor, object, indexPath, newIndexPath) -> Void in
if let observer = observer {
observer.managedObjectList(
listController,
observer.listMonitor(
monitor,
didMoveObject: object,
fromIndexPath: indexPath!,
toIndexPath: newIndexPath!
@@ -437,14 +392,14 @@ public final class ManagedObjectListController<T: NSManagedObject> {
self.registerSectionNotification(
&NotificationKey.didInsertSection,
name: ManagedObjectListControllerDidInsertSectionNotification,
name: ListMonitorDidInsertSectionNotification,
toObserver: observer,
callback: { [weak observer] (listController, sectionInfo, sectionIndex) -> Void in
callback: { [weak observer] (monitor, sectionInfo, sectionIndex) -> Void in
if let observer = observer {
observer.managedObjectList(
listController,
observer.listMonitor(
monitor,
didInsertSection: sectionInfo,
toSectionIndex: sectionIndex
)
@@ -453,14 +408,14 @@ public final class ManagedObjectListController<T: NSManagedObject> {
)
self.registerSectionNotification(
&NotificationKey.didDeleteSection,
name: ManagedObjectListControllerDidDeleteSectionNotification,
name: ListMonitorDidDeleteSectionNotification,
toObserver: observer,
callback: { [weak observer] (listController, sectionInfo, sectionIndex) -> Void in
callback: { [weak observer] (monitor, sectionInfo, sectionIndex) -> Void in
if let observer = observer {
observer.managedObjectList(
listController,
observer.listMonitor(
monitor,
didDeleteSection: sectionInfo,
fromSectionIndex: sectionIndex
)
@@ -470,13 +425,13 @@ public final class ManagedObjectListController<T: NSManagedObject> {
}
/**
Unregisters a `ManagedObjectListChangeObserver` from receiving notifications for changes to the receiver's list.
Unregisters a `ListObserver` from receiving notifications for changes to the receiver's list.
For thread safety, this method needs to be called from the main thread. An assertion failure will occur (on debug builds only) if called from any thread other than the main thread.
:param: observer a `ManagedObjectListChangeObserver` to unregister notifications to
:param: observer a `ListObserver` to unregister notifications to
*/
public func removeObserver<U: ManagedObjectListChangeObserver where U.EntityType == T>(observer: U) {
public func removeObserver<U: ListObserver where U.EntityType == T>(observer: U) {
CoreStore.assert(NSThread.isMainThread(), "Attempted to remove an observer of type \(typeName(observer)) outside the main thread.")
@@ -496,7 +451,7 @@ public final class ManagedObjectListController<T: NSManagedObject> {
// MARK: Internal
internal init(dataStack: DataStack, from: From<T>, sectionedBy: SectionedBy?, fetchClauses: [FetchClause]) {
internal init(dataStack: DataStack, from: From<T>, sectionBy: SectionBy?, fetchClauses: [FetchClause]) {
let context = dataStack.mainContext
@@ -514,7 +469,7 @@ public final class ManagedObjectListController<T: NSManagedObject> {
let fetchedResultsController = NSFetchedResultsController(
fetchRequest: fetchRequest,
managedObjectContext: context,
sectionNameKeyPath: sectionedBy?.sectionKeyPath,
sectionNameKeyPath: sectionBy?.sectionKeyPath,
cacheName: nil
)
@@ -524,7 +479,7 @@ public final class ManagedObjectListController<T: NSManagedObject> {
self.fetchedResultsControllerDelegate = fetchedResultsControllerDelegate
self.parentStack = dataStack
if let sectionIndexTransformer = sectionedBy?.sectionIndexTransformer {
if let sectionIndexTransformer = sectionBy?.sectionIndexTransformer {
self.sectionIndexTransformer = sectionIndexTransformer
}
@@ -554,7 +509,7 @@ public final class ManagedObjectListController<T: NSManagedObject> {
private let sectionIndexTransformer: (sectionName: KeyPath?) -> String?
private weak var parentStack: DataStack?
private func registerChangeNotification(notificationKey: UnsafePointer<Void>, name: String, toObserver observer: AnyObject, callback: (listController: ManagedObjectListController<T>) -> Void) {
private func registerChangeNotification(notificationKey: UnsafePointer<Void>, name: String, toObserver observer: AnyObject, callback: (monitor: ListMonitor<T>) -> Void) {
setAssociatedRetainedObject(
NotificationObserver(
@@ -564,7 +519,7 @@ public final class ManagedObjectListController<T: NSManagedObject> {
if let strongSelf = self {
callback(listController: strongSelf)
callback(monitor: strongSelf)
}
}
),
@@ -573,7 +528,7 @@ public final class ManagedObjectListController<T: NSManagedObject> {
)
}
private func registerObjectNotification(notificationKey: UnsafePointer<Void>, name: String, toObserver observer: AnyObject, callback: (listController: ManagedObjectListController<T>, object: T, indexPath: NSIndexPath?, newIndexPath: NSIndexPath?) -> Void) {
private func registerObjectNotification(notificationKey: UnsafePointer<Void>, name: String, toObserver observer: AnyObject, callback: (monitor: ListMonitor<T>, object: T, indexPath: NSIndexPath?, newIndexPath: NSIndexPath?) -> Void) {
setAssociatedRetainedObject(
NotificationObserver(
@@ -586,7 +541,7 @@ public final class ManagedObjectListController<T: NSManagedObject> {
let object = userInfo[UserInfoKeyObject] as? T {
callback(
listController: strongSelf,
monitor: strongSelf,
object: object,
indexPath: userInfo[UserInfoKeyIndexPath] as? NSIndexPath,
newIndexPath: userInfo[UserInfoKeyNewIndexPath] as? NSIndexPath
@@ -599,7 +554,7 @@ public final class ManagedObjectListController<T: NSManagedObject> {
)
}
private func registerSectionNotification(notificationKey: UnsafePointer<Void>, name: String, toObserver observer: AnyObject, callback: (listController: ManagedObjectListController<T>, sectionInfo: NSFetchedResultsSectionInfo, sectionIndex: Int) -> Void) {
private func registerSectionNotification(notificationKey: UnsafePointer<Void>, name: String, toObserver observer: AnyObject, callback: (monitor: ListMonitor<T>, sectionInfo: NSFetchedResultsSectionInfo, sectionIndex: Int) -> Void) {
setAssociatedRetainedObject(
NotificationObserver(
@@ -613,7 +568,7 @@ public final class ManagedObjectListController<T: NSManagedObject> {
let sectionIndex = (userInfo[UserInfoKeySectionIndex] as? NSNumber)?.integerValue {
callback(
listController: strongSelf,
monitor: strongSelf,
sectionInfo: sectionInfo,
sectionIndex: sectionIndex
)
@@ -627,9 +582,9 @@ public final class ManagedObjectListController<T: NSManagedObject> {
}
// MARK: - ManagedObjectListController: FetchedResultsControllerHandler
// MARK: - ListMonitor: FetchedResultsControllerHandler
extension ManagedObjectListController: FetchedResultsControllerHandler {
extension ListMonitor: FetchedResultsControllerHandler {
// MARK: FetchedResultsControllerHandler
@@ -639,7 +594,7 @@ extension ManagedObjectListController: FetchedResultsControllerHandler {
case .Insert:
NSNotificationCenter.defaultCenter().postNotificationName(
ManagedObjectListControllerDidInsertObjectNotification,
ListMonitorDidInsertObjectNotification,
object: self,
userInfo: [
UserInfoKeyObject: anObject,
@@ -649,7 +604,7 @@ extension ManagedObjectListController: FetchedResultsControllerHandler {
case .Delete:
NSNotificationCenter.defaultCenter().postNotificationName(
ManagedObjectListControllerDidDeleteObjectNotification,
ListMonitorDidDeleteObjectNotification,
object: self,
userInfo: [
UserInfoKeyObject: anObject,
@@ -659,7 +614,7 @@ extension ManagedObjectListController: FetchedResultsControllerHandler {
case .Update:
NSNotificationCenter.defaultCenter().postNotificationName(
ManagedObjectListControllerDidUpdateObjectNotification,
ListMonitorDidUpdateObjectNotification,
object: self,
userInfo: [
UserInfoKeyObject: anObject,
@@ -669,7 +624,7 @@ extension ManagedObjectListController: FetchedResultsControllerHandler {
case .Move:
NSNotificationCenter.defaultCenter().postNotificationName(
ManagedObjectListControllerDidMoveObjectNotification,
ListMonitorDidMoveObjectNotification,
object: self,
userInfo: [
UserInfoKeyObject: anObject,
@@ -686,7 +641,7 @@ extension ManagedObjectListController: FetchedResultsControllerHandler {
case .Insert:
NSNotificationCenter.defaultCenter().postNotificationName(
ManagedObjectListControllerDidInsertSectionNotification,
ListMonitorDidInsertSectionNotification,
object: self,
userInfo: [
UserInfoKeySectionInfo: sectionInfo,
@@ -696,7 +651,7 @@ extension ManagedObjectListController: FetchedResultsControllerHandler {
case .Delete:
NSNotificationCenter.defaultCenter().postNotificationName(
ManagedObjectListControllerDidDeleteSectionNotification,
ListMonitorDidDeleteSectionNotification,
object: self,
userInfo: [
UserInfoKeySectionInfo: sectionInfo,
@@ -712,7 +667,7 @@ extension ManagedObjectListController: FetchedResultsControllerHandler {
private func controllerWillChangeContent(controller: NSFetchedResultsController) {
NSNotificationCenter.defaultCenter().postNotificationName(
ManagedObjectListControllerWillChangeListNotification,
ListMonitorWillChangeListNotification,
object: self
)
}
@@ -720,7 +675,7 @@ extension ManagedObjectListController: FetchedResultsControllerHandler {
private func controllerDidChangeContent(controller: NSFetchedResultsController) {
NSNotificationCenter.defaultCenter().postNotificationName(
ManagedObjectListControllerDidChangeListNotification,
ListMonitorDidChangeListNotification,
object: self
)
}
@@ -799,16 +754,16 @@ private final class FetchedResultsControllerDelegate: NSFetchedResultsController
}
private let ManagedObjectListControllerWillChangeListNotification = "ManagedObjectListControllerWillChangeListNotification"
private let ManagedObjectListControllerDidChangeListNotification = "ManagedObjectListControllerDidChangeListNotification"
private let ListMonitorWillChangeListNotification = "ListMonitorWillChangeListNotification"
private let ListMonitorDidChangeListNotification = "ListMonitorDidChangeListNotification"
private let ManagedObjectListControllerDidInsertObjectNotification = "ManagedObjectListControllerDidInsertObjectNotification"
private let ManagedObjectListControllerDidDeleteObjectNotification = "ManagedObjectListControllerDidDeleteObjectNotification"
private let ManagedObjectListControllerDidUpdateObjectNotification = "ManagedObjectListControllerDidUpdateObjectNotification"
private let ManagedObjectListControllerDidMoveObjectNotification = "ManagedObjectListControllerDidMoveObjectNotification"
private let ListMonitorDidInsertObjectNotification = "ListMonitorDidInsertObjectNotification"
private let ListMonitorDidDeleteObjectNotification = "ListMonitorDidDeleteObjectNotification"
private let ListMonitorDidUpdateObjectNotification = "ListMonitorDidUpdateObjectNotification"
private let ListMonitorDidMoveObjectNotification = "ListMonitorDidMoveObjectNotification"
private let ManagedObjectListControllerDidInsertSectionNotification = "ManagedObjectListControllerDidInsertSectionNotification"
private let ManagedObjectListControllerDidDeleteSectionNotification = "ManagedObjectListControllerDidDeleteSectionNotification"
private let ListMonitorDidInsertSectionNotification = "ListMonitorDidInsertSectionNotification"
private let ListMonitorDidDeleteSectionNotification = "ListMonitorDidDeleteSectionNotification"
private let UserInfoKeyObject = "UserInfoKeyObject"
private let UserInfoKeyIndexPath = "UserInfoKeyIndexPath"

View File

@@ -0,0 +1,147 @@
//
// ListObserver.swift
// CoreStore
//
// Copyright (c) 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: - ListObserver
/**
Implement the `ListObserver` protocol to observe changes to a list of `NSManagedObject`s. `ListObserver`s may register themselves to a `ListMonitor`'s `addObserver(_:)` method:
let monitor = CoreStore.monitorList(
From(MyPersonEntity),
OrderBy(.Ascending("lastName"))
)
monitor.addObserver(self)
*/
public protocol ListObserver: class {
/**
The `NSManagedObject` type for the observed list
*/
typealias EntityType: NSManagedObject
/**
Handles processing just before a change to the observed list occurs
:param: monitor the `ListMonitor` monitoring the list being observed
*/
func listMonitorWillChange(monitor: ListMonitor<EntityType>)
/**
Handles processing right after a change to the observed list occurs
:param: monitor the `ListMonitor` monitoring the object being observed
*/
func listMonitorDidChange(monitor: ListMonitor<EntityType>)
}
// MARK: - ListObjectObserver
/**
Implement the `ListObjectObserver` protocol to observe detailed changes to a list's object. `ListObjectObserver`s may register themselves to a `ListMonitor`'s `addObserver(_:)` method:
let monitor = CoreStore.monitorList(
From(MyPersonEntity),
OrderBy(.Ascending("lastName"))
)
monitor.addObserver(self)
*/
public protocol ListObjectObserver: ListObserver {
/**
Notifies that an object was inserted to the specified `NSIndexPath` in the list
:param: monitor the `ListMonitor` monitoring the list being observed
:param: object the entity type for the inserted object
:param: indexPath the new `NSIndexPath` for the inserted object
*/
func listMonitor(monitor: ListMonitor<EntityType>, didInsertObject object: EntityType, toIndexPath indexPath: NSIndexPath)
/**
Notifies that an object was deleted from the specified `NSIndexPath` in the list
:param: monitor the `ListMonitor` monitoring the list being observed
:param: object the entity type for the deleted object
:param: indexPath the `NSIndexPath` for the deleted object
*/
func listMonitor(monitor: ListMonitor<EntityType>, didDeleteObject object: EntityType, fromIndexPath indexPath: NSIndexPath)
/**
Notifies that an object at the specified `NSIndexPath` was updated
:param: monitor the `ListMonitor` monitoring the list being observed
:param: object the entity type for the updated object
:param: indexPath the `NSIndexPath` for the updated object
*/
func listMonitor(monitor: ListMonitor<EntityType>, didUpdateObject object: EntityType, atIndexPath indexPath: NSIndexPath)
/**
Notifies that an object's index changed
:param: monitor the `ListMonitor` monitoring the list being observed
:param: object the entity type for the moved object
:param: fromIndexPath the previous `NSIndexPath` for the moved object
:param: toIndexPath the new `NSIndexPath` for the moved object
*/
func listMonitor(monitor: ListMonitor<EntityType>, didMoveObject object: EntityType, fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath)
}
// MARK: - ListSectionObserver
/**
Implement the `ListSectionObserver` protocol to observe changes to a list's section info. `ListSectionObserver`s may register themselves to a `ListMonitor`'s `addObserver(_:)` method:
let monitor = CoreStore.monitorSectionedList(
From(MyPersonEntity),
SectionBy("age") { "Age \($0)" },
OrderBy(.Ascending("lastName"))
)
monitor.addObserver(self)
*/
public protocol ListSectionObserver: ListObjectObserver {
/**
Notifies that a section was inserted at the specified index
:param: monitor the `ListMonitor` monitoring the list being observed
:param: sectionInfo the `NSFetchedResultsSectionInfo` for the inserted section
:param: sectionIndex the new section index for the new section
*/
func listMonitor(monitor: ListMonitor<EntityType>, didInsertSection sectionInfo: NSFetchedResultsSectionInfo, toSectionIndex sectionIndex: Int)
/**
Notifies that a section was inserted at the specified index
:param: monitor the `ListMonitor` monitoring the list being observed
:param: sectionInfo the `NSFetchedResultsSectionInfo` for the deleted section
:param: sectionIndex the previous section index for the deleted section
*/
func listMonitor(monitor: ListMonitor<EntityType>, didDeleteSection sectionInfo: NSFetchedResultsSectionInfo, fromSectionIndex sectionIndex: Int)
}

View File

@@ -1,147 +0,0 @@
//
// ManagedObjectListObserver.swift
// CoreStore
//
// Copyright (c) 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: - ManagedObjectListChangeObserver
/**
Implement the `ManagedObjectListChangeObserver` protocol to observe changes to a list of `NSManagedObject`'s. `ManagedObjectListChangeObserver`'s may register themselves to a `ManagedObjectListController`'s `addObserver(_:)` method:
let listController = CoreStore.observeObjectList(
From(MyPersonEntity),
OrderBy(.Ascending("lastName"))
)
listController.addObserver(self)
*/
public protocol ManagedObjectListChangeObserver: class {
/**
The `NSManagedObject` type for the observed list
*/
typealias EntityType: NSManagedObject
/**
Handles processing just before a change to the observed list occurs
:param: listController the `ManagedObjectListController` monitoring the list being observed
*/
func managedObjectListWillChange(listController: ManagedObjectListController<EntityType>)
/**
Handles processing right after a change to the observed list occurs
:param: listController the `ManagedObjectListController` monitoring the object being observed
*/
func managedObjectListDidChange(listController: ManagedObjectListController<EntityType>)
}
// MARK: - ManagedObjectListObjectObserver
/**
Implement the `ManagedObjectListObjectObserver` protocol to observe detailed changes to a list's object. `ManagedObjectListObjectObserver`'s may register themselves to a `ManagedObjectListController`'s `addObserver(_:)` method:
let listController = CoreStore.observeObjectList(
From(MyPersonEntity),
OrderBy(.Ascending("lastName"))
)
listController.addObserver(self)
*/
public protocol ManagedObjectListObjectObserver: ManagedObjectListChangeObserver {
/**
Notifies that an object was inserted to the specified `NSIndexPath` in the list
:param: listController the `ManagedObjectListController` monitoring the list being observed
:param: object the entity type for the inserted object
:param: indexPath the new `NSIndexPath` for the inserted object
*/
func managedObjectList(listController: ManagedObjectListController<EntityType>, didInsertObject object: EntityType, toIndexPath indexPath: NSIndexPath)
/**
Notifies that an object was deleted from the specified `NSIndexPath` in the list
:param: listController the `ManagedObjectListController` monitoring the list being observed
:param: object the entity type for the deleted object
:param: indexPath the `NSIndexPath` for the deleted object
*/
func managedObjectList(listController: ManagedObjectListController<EntityType>, didDeleteObject object: EntityType, fromIndexPath indexPath: NSIndexPath)
/**
Notifies that an object at the specified `NSIndexPath` was updated
:param: listController the `ManagedObjectListController` monitoring the list being observed
:param: object the entity type for the updated object
:param: indexPath the `NSIndexPath` for the updated object
*/
func managedObjectList(listController: ManagedObjectListController<EntityType>, didUpdateObject object: EntityType, atIndexPath indexPath: NSIndexPath)
/**
Notifies that an object's index changed
:param: listController the `ManagedObjectListController` monitoring the list being observed
:param: object the entity type for the moved object
:param: fromIndexPath the previous `NSIndexPath` for the moved object
:param: toIndexPath the new `NSIndexPath` for the moved object
*/
func managedObjectList(listController: ManagedObjectListController<EntityType>, didMoveObject object: EntityType, fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath)
}
// MARK: - ManagedObjectListSectionObserver
/**
Implement the `ManagedObjectListSectionObserver` protocol to observe changes to a list's section info. `ManagedObjectListSectionObserver`'s may register themselves to a `ManagedObjectListController`'s `addObserver(_:)` method:
let listController = CoreStore.observeSectionedList(
From(MyPersonEntity),
SectionedBy("age") { "Age \($0)" },
OrderBy(.Ascending("lastName"))
)
listController.addObserver(self)
*/
public protocol ManagedObjectListSectionObserver: ManagedObjectListObjectObserver {
/**
Notifies that a section was inserted at the specified index
:param: listController the `ManagedObjectListController` monitoring the list being observed
:param: sectionInfo the `NSFetchedResultsSectionInfo` for the inserted section
:param: sectionIndex the new section index for the new section
*/
func managedObjectList(listController: ManagedObjectListController<EntityType>, didInsertSection sectionInfo: NSFetchedResultsSectionInfo, toSectionIndex sectionIndex: Int)
/**
Notifies that a section was inserted at the specified index
:param: listController the `ManagedObjectListController` monitoring the list being observed
:param: sectionInfo the `NSFetchedResultsSectionInfo` for the deleted section
:param: sectionIndex the previous section index for the deleted section
*/
func managedObjectList(listController: ManagedObjectListController<EntityType>, didDeleteSection sectionInfo: NSFetchedResultsSectionInfo, fromSectionIndex sectionIndex: Int)
}

View File

@@ -1,5 +1,5 @@
//
// ManagedObjectController.swift
// ObjectMonitor.swift
// CoreStore
//
// Copyright (c) 2015 John Rommel Estropia
@@ -28,9 +28,9 @@ import CoreData
import GCDKit
private let ManagedObjectListControllerWillChangeObjectNotification = "ManagedObjectListControllerWillChangeObjectNotification"
private let ManagedObjectListControllerDidDeleteObjectNotification = "ManagedObjectListControllerDidDeleteObjectNotification"
private let ManagedObjectListControllerDidUpdateObjectNotification = "ManagedObjectListControllerDidUpdateObjectNotification"
private let ObjectMonitorWillChangeObjectNotification = "ObjectMonitorWillChangeObjectNotification"
private let ObjectMonitorDidDeleteObjectNotification = "ObjectMonitorDidDeleteObjectNotification"
private let ObjectMonitorDidUpdateObjectNotification = "ObjectMonitorDidUpdateObjectNotification"
private let UserInfoKeyObject = "UserInfoKeyObject"
@@ -42,19 +42,19 @@ private struct NotificationKey {
}
// MARK: - ManagedObjectController
// MARK: - ObjectMonitor
/**
The `ManagedObjectController` monitors changes to a single `NSManagedObject` instance. Observers that implement the `ManagedObjectObserver` protocol may then register themselves to the `ManagedObjectController`'s `addObserver(_:)` method:
The `ObjectMonitor` monitors changes to a single `NSManagedObject` instance. Observers that implement the `ObjectObserver` protocol may then register themselves to the `ObjectMonitor`'s `addObserver(_:)` method:
let objectController = CoreStore.observeObject(object)
objectController.addObserver(self)
let monitor = CoreStore.monitorObject(object)
monitor.addObserver(self)
The created `ManagedObjectController` instance needs to be held on (retained) for as long as the object needs to be observed.
The created `ObjectMonitor` instance needs to be held on (retained) for as long as the object needs to be observed.
Observers registered via `addObserver(_:)` are not retained. `ManagedObjectController` only keeps a `weak` reference to all observers, thus keeping itself free from retain-cycles.
Observers registered via `addObserver(_:)` are not retained. `ObjectMonitor` only keeps a `weak` reference to all observers, thus keeping itself free from retain-cycles.
*/
public final class ManagedObjectController<T: NSManagedObject> {
public final class ObjectMonitor<T: NSManagedObject> {
// MARK: Public
@@ -75,17 +75,17 @@ public final class ManagedObjectController<T: NSManagedObject> {
}
/**
Registers a `ManagedObjectObserver` to be notified when changes to the receiver's `object` are made.
Registers an `ObjectObserver` to be notified when changes to the receiver's `object` are made.
To prevent retain-cycles, `ManagedObjectController` only keeps `weak` references to its observers.
To prevent retain-cycles, `ObjectMonitor` only keeps `weak` references to its observers.
For thread safety, this method needs to be called from the main thread. An assertion failure will occur (on debug builds only) if called from any thread other than the main thread.
Calling `addObserver(_:)` multiple times on the same observer is safe, as `ManagedObjectController` unregisters previous notifications to the observer before re-registering them.
Calling `addObserver(_:)` multiple times on the same observer is safe, as `ObjectMonitor` unregisters previous notifications to the observer before re-registering them.
:param: observer a `ManagedObjectObserver` to send change notifications to
:param: observer an `ObjectObserver` to send change notifications to
*/
public func addObserver<U: ManagedObjectObserver where U.EntityType == T>(observer: U) {
public func addObserver<U: ObjectObserver where U.EntityType == T>(observer: U) {
CoreStore.assert(NSThread.isMainThread(), "Attempted to add an observer of type \(typeName(observer)) outside the main thread.")
@@ -93,33 +93,33 @@ public final class ManagedObjectController<T: NSManagedObject> {
self.registerChangeNotification(
&NotificationKey.willChangeObject,
name: ManagedObjectListControllerWillChangeObjectNotification,
name: ObjectMonitorWillChangeObjectNotification,
toObserver: observer,
callback: { [weak self, weak observer] (objectController) -> Void in
callback: { [weak self, weak observer] (monitor) -> Void in
if let strongSelf = self, let object = strongSelf.object, let observer = observer {
observer.managedObjectWillUpdate(objectController, object: object)
observer.objectMonitor(monitor, willUpdateObject: object)
}
}
)
self.registerObjectNotification(
&NotificationKey.didDeleteObject,
name: ManagedObjectListControllerDidDeleteObjectNotification,
name: ObjectMonitorDidDeleteObjectNotification,
toObserver: observer,
callback: { [weak self, weak observer] (objectController, object) -> Void in
callback: { [weak self, weak observer] (monitor, object) -> Void in
if let strongSelf = self, let observer = observer {
observer.managedObjectWasDeleted(objectController, object: object)
observer.objectMonitor(monitor, didDeleteObject: object)
}
}
)
self.registerObjectNotification(
&NotificationKey.didUpdateObject,
name: ManagedObjectListControllerDidUpdateObjectNotification,
name: ObjectMonitorDidUpdateObjectNotification,
toObserver: observer,
callback: { [weak self, weak observer] (objectController, object) -> Void in
callback: { [weak self, weak observer] (monitor, object) -> Void in
if let strongSelf = self, let observer = observer {
@@ -136,9 +136,9 @@ public final class ManagedObjectController<T: NSManagedObject> {
}
strongSelf.lastCommittedAttributes = currentCommitedAttributes
observer.managedObjectWasUpdated(
objectController,
object: object,
observer.objectMonitor(
monitor,
didUpdateObject: object,
changedPersistentKeys: changedKeys
)
}
@@ -147,13 +147,13 @@ public final class ManagedObjectController<T: NSManagedObject> {
}
/**
Unregisters a `ManagedObjectObserver` from receiving notifications for changes to the receiver's `object`.
Unregisters an `ObjectObserver` from receiving notifications for changes to the receiver's `object`.
For thread safety, this method needs to be called from the main thread. An assertion failure will occur (on debug builds only) if called from any thread other than the main thread.
:param: observer a `ManagedObjectObserver` to unregister notifications to
:param: observer an `ObjectObserver` to unregister notifications to
*/
public func removeObserver<U: ManagedObjectObserver where U.EntityType == T>(observer: U) {
public func removeObserver<U: ObjectObserver where U.EntityType == T>(observer: U) {
CoreStore.assert(NSThread.isMainThread(), "Attempted to remove an observer of type \(typeName(observer)) outside the main thread.")
@@ -171,7 +171,8 @@ public final class ManagedObjectController<T: NSManagedObject> {
let context = dataStack.mainContext
let fetchRequest = NSFetchRequest()
fetchRequest.entity = context.entityDescriptionForEntityClass(T.self)
fetchRequest.entity = object.entity
fetchRequest.fetchLimit = 1
fetchRequest.resultType = .ManagedObjectResultType
fetchRequest.sortDescriptors = []
@@ -216,7 +217,7 @@ public final class ManagedObjectController<T: NSManagedObject> {
private var lastCommittedAttributes = [NSString: NSObject]()
private weak var parentStack: DataStack?
private func registerChangeNotification(notificationKey: UnsafePointer<Void>, name: String, toObserver observer: AnyObject, callback: (objectController: ManagedObjectController<T>) -> Void) {
private func registerChangeNotification(notificationKey: UnsafePointer<Void>, name: String, toObserver observer: AnyObject, callback: (monitor: ObjectMonitor<T>) -> Void) {
setAssociatedRetainedObject(
NotificationObserver(
@@ -226,7 +227,7 @@ public final class ManagedObjectController<T: NSManagedObject> {
if let strongSelf = self {
callback(objectController: strongSelf)
callback(monitor: strongSelf)
}
}
),
@@ -235,7 +236,7 @@ public final class ManagedObjectController<T: NSManagedObject> {
)
}
private func registerObjectNotification(notificationKey: UnsafePointer<Void>, name: String, toObserver observer: AnyObject, callback: (objectController: ManagedObjectController<T>, object: T) -> Void) {
private func registerObjectNotification(notificationKey: UnsafePointer<Void>, name: String, toObserver observer: AnyObject, callback: (monitor: ObjectMonitor<T>, object: T) -> Void) {
setAssociatedRetainedObject(
NotificationObserver(
@@ -248,7 +249,7 @@ public final class ManagedObjectController<T: NSManagedObject> {
let object = userInfo[UserInfoKeyObject] as? T {
callback(
objectController: strongSelf,
monitor: strongSelf,
object: object
)
}
@@ -261,9 +262,9 @@ public final class ManagedObjectController<T: NSManagedObject> {
}
// MARK: - ManagedObjectController: FetchedResultsControllerHandler
// MARK: - ObjectMonitor: FetchedResultsControllerHandler
extension ManagedObjectController: FetchedResultsControllerHandler {
extension ObjectMonitor: FetchedResultsControllerHandler {
// MARK: FetchedResultsControllerHandler
@@ -273,14 +274,14 @@ extension ManagedObjectController: FetchedResultsControllerHandler {
case .Delete:
NSNotificationCenter.defaultCenter().postNotificationName(
ManagedObjectListControllerDidDeleteObjectNotification,
ObjectMonitorDidDeleteObjectNotification,
object: self,
userInfo: [UserInfoKeyObject: anObject]
)
case .Update:
NSNotificationCenter.defaultCenter().postNotificationName(
ManagedObjectListControllerDidUpdateObjectNotification,
ObjectMonitorDidUpdateObjectNotification,
object: self,
userInfo: [UserInfoKeyObject: anObject]
)
@@ -293,7 +294,7 @@ extension ManagedObjectController: FetchedResultsControllerHandler {
private func controllerWillChangeContent(controller: NSFetchedResultsController) {
NSNotificationCenter.defaultCenter().postNotificationName(
ManagedObjectListControllerWillChangeObjectNotification,
ObjectMonitorWillChangeObjectNotification,
object: self
)
}

View File

@@ -1,5 +1,5 @@
//
// ManagedObjectObserver.swift
// ObjectObserver.swift
// CoreStore
//
// Copyright (c) 2015 John Rommel Estropia
@@ -27,15 +27,15 @@ import Foundation
import CoreData
// MARK: - ManagedObjectObserver
// MARK: - ObjectObserver
/**
Implement the `ManagedObjectObserver` protocol to observe changes to a single `NSManagedObject` instance. `ManagedObjectObserver`'s may register themselves to a `ManagedObjectController`'s `addObserver(_:)` method:
Implement the `ObjectObserver` protocol to observe changes to a single `NSManagedObject` instance. `ObjectObserver`s may register themselves to a `ObjectMonitor`'s `addObserver(_:)` method:
let objectController = CoreStore.observeObject(object)
objectController.addObserver(self)
let monitor = CoreStore.monitorObject(object)
monitor.addObserver(self)
*/
public protocol ManagedObjectObserver: class {
public protocol ObjectObserver: class {
/**
The `NSManagedObject` type for the observed object
@@ -45,25 +45,25 @@ public protocol ManagedObjectObserver: class {
/**
Handles processing just before a change to the observed `object` occurs
:param: objectController the `ManagedObjectController` monitoring the object being observed
:param: monitor the `ObjectMonitor` monitoring the object being observed
:param: object the `NSManagedObject` instance being observed
*/
func managedObjectWillUpdate(objectController: ManagedObjectController<EntityType>, object: EntityType)
func objectMonitor(monitor: ObjectMonitor<EntityType>, willUpdateObject object: EntityType)
/**
Handles processing right after a change to the observed `object` occurs
:param: objectController the `ManagedObjectController` monitoring the object being observed
:param: monitor the `ObjectMonitor` monitoring the object being observed
:param: object the `NSManagedObject` instance being observed
:param: changedPersistentKeys a `Set` of key paths for the attributes that were changed. Note that `changedPersistentKeys` only contains keys for attributes/relationships present in the persistent store, thus transient properties will not be reported.
*/
func managedObjectWasUpdated(objectController: ManagedObjectController<EntityType>, object: EntityType, changedPersistentKeys: Set<KeyPath>)
func objectMonitor(monitor: ObjectMonitor<EntityType>, didUpdateObject object: EntityType, changedPersistentKeys: Set<KeyPath>)
/**
Handles processing right after `object` is deleted
:param: objectController the `ManagedObjectController` monitoring the object being observed
:param: monitor the `ObjectMonitor` monitoring the object being observed
:param: object the `NSManagedObject` instance being observed
*/
func managedObjectWasDeleted(objectController: ManagedObjectController<EntityType>, object: EntityType)
func objectMonitor(monitor: ObjectMonitor<EntityType>, didDeleteObject object: EntityType)
}

View File

@@ -0,0 +1,72 @@
//
// SectionBy.swift
// CoreStore
//
// Copyright (c) 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: - SectionBy
/**
The `SectionBy` clause indicates the key path to use to group the `ListMonitor` objects into sections. An optional closure can also be provided to transform the value into an appropriate section name:
let monitor = CoreStore.monitorSectionedList(
From(MyPersonEntity),
SectionBy("age") { "Age \($0)" },
OrderBy(.Ascending("lastName"))
)
*/
public struct SectionBy {
// MARK: Public
/**
Initializes a `SectionBy` clause with the key path to use to group `ListMonitor` objects into sections
:param: sectionKeyPath the key path to use to group the objects into sections
*/
public init(_ sectionKeyPath: KeyPath) {
self.init(sectionKeyPath, { $0 })
}
/**
Initializes a `SectionBy` clause with the key path to use to group `ListMonitor` objects into sections, and a closure to transform the value for the key path to an appropriate section name
:param: sectionKeyPath the key path to use to group the objects into sections
:param: sectionIndexTransformer a closure to transform the value for the key path to an appropriate section name
*/
public init(_ sectionKeyPath: KeyPath, _ sectionIndexTransformer: (sectionName: String?) -> String?) {
self.sectionKeyPath = sectionKeyPath
self.sectionIndexTransformer = sectionIndexTransformer
}
// MARK: Internal
internal let sectionKeyPath: KeyPath
internal let sectionIndexTransformer: (sectionName: KeyPath?) -> String?
}

View File

@@ -143,7 +143,7 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
}
/**
Deletes the specified `NSManagedObject`'s.
Deletes the specified `NSManagedObject`s.
:param: object1 the `NSManagedObject` type to be deleted
:param: object2 another `NSManagedObject` type to be deleted
@@ -157,9 +157,9 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
}
/**
Deletes the specified `NSManagedObject`'s.
Deletes the specified `NSManagedObject`s.
:param: objects the `NSManagedObject`'s type to be deleted
:param: objects the `NSManagedObject`s type to be deleted
*/
public override func delete(objects: [NSManagedObject?]) {

View File

@@ -28,91 +28,6 @@ import CoreData
import GCDKit
// MARK: - Into
/**
A `Into` clause contains the destination entity and destination persistent store for a `create(...)` method. A common usage is to just indicate the entity:
let person = transaction.create(Into(MyPersonEntity))
For cases where multiple `NSPersistentStore`'s contain the same entity, the destination configuration's name needs to be specified as well:
let person = transaction.create(Into<MyPersonEntity>("Configuration1"))
This helps the `NSManagedObjectContext` to determine which
*/
public struct Into<T: NSManagedObject> {
// MARK: Public
internal static var defaultConfigurationName: String {
return "PF_DEFAULT_CONFIGURATION_NAME"
}
/**
Initializes an `Into` clause.
Sample Usage:
let person = transaction.create(Into<MyPersonEntity>())
*/
public init(){
self.configuration = nil
self.inferStoreIfPossible = true
}
/**
Initializes an `Into` clause with the specified entity type.
Sample Usage:
let person = transaction.create(Into(MyPersonEntity))
:param: entity the `NSManagedObject` type to be created
*/
public init(_ entity: T.Type) {
self.configuration = nil
self.inferStoreIfPossible = true
}
/**
Initializes an `Into` clause with the specified configuration.
Sample Usage:
let person = transaction.create(Into<MyPersonEntity>("Configuration1"))
:param: configuration the `NSPersistentStore` configuration name to associate the object to. This parameter is required if multiple configurations contain the created `NSManagedObject`'s entity type. Set to `nil` to use the default configuration.
*/
public init(_ configuration: String?) {
self.configuration = configuration
self.inferStoreIfPossible = false
}
/**
Initializes an `Into` clause with the specified entity type and configuration.
Sample Usage:
let person = transaction.create(Into(MyPersonEntity.self, "Configuration1"))
:param: entity the `NSManagedObject` type to be created
:param: configuration the `NSPersistentStore` configuration name to associate the object to. This parameter is required if multiple configurations contain the created `NSManagedObject`'s entity type. Set to `nil` to use the default configuration.
*/
public init(_ entity: T.Type, _ configuration: String?) {
self.configuration = configuration
self.inferStoreIfPossible = false
}
// MARK: Internal
internal let configuration: String?
internal let inferStoreIfPossible: Bool
}
// MARK: - BaseDataTransaction
/**
@@ -141,25 +56,26 @@ public /*abstract*/ class BaseDataTransaction {
CoreStore.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to create an entity of type <\(T.self)> outside its designated queue.")
let context = self.context
let object = T.createInContext(context)
let entityClass = (into.entityClass as! NSManagedObject.Type)
let object = entityClass.createInContext(context) as! T
if into.inferStoreIfPossible {
switch context.parentStack!.persistentStoreForEntityClass(T.self, configuration: nil, inferStoreIfPossible: true) {
switch context.parentStack!.persistentStoreForEntityClass(entityClass, configuration: nil, inferStoreIfPossible: true) {
case (.Some(let persistentStore), _):
context.assignObject(object, toPersistentStore: persistentStore)
case (.None, true):
CoreStore.assert(false, "Attempted to create an entity of type \(typeName(object)) with ambiguous destination persistent store, but the configuration name was not specified.")
CoreStore.assert(false, "Attempted to create an entity of type <\(entityClass)> with ambiguous destination persistent store, but the configuration name was not specified.")
default:
CoreStore.assert(false, "Attempted to create an entity of type \(typeName(object)), but a destination persistent store containing the entity type could not be found.")
CoreStore.assert(false, "Attempted to create an entity of type <\(entityClass)>, but a destination persistent store containing the entity type could not be found.")
}
}
else {
switch context.parentStack!.persistentStoreForEntityClass(T.self, configuration: into.configuration, inferStoreIfPossible: false) {
switch context.parentStack!.persistentStoreForEntityClass(entityClass, configuration: into.configuration, inferStoreIfPossible: false) {
case (.Some(let persistentStore), _):
context.assignObject(object, toPersistentStore: persistentStore)
@@ -167,11 +83,11 @@ public /*abstract*/ class BaseDataTransaction {
default:
if let configuration = into.configuration {
CoreStore.assert(false, "Attempted to create an entity of type \(typeName(object)) into the configuration \"\(configuration)\", which it doesn't belong to.")
CoreStore.assert(false, "Attempted to create an entity of type <\(entityClass)> into the configuration \"\(configuration)\", which it doesn't belong to.")
}
else {
CoreStore.assert(false, "Attempted to create an entity of type \(typeName(object)) into the default configuration, which it doesn't belong to.")
CoreStore.assert(false, "Attempted to create an entity of type <\(entityClass)> into the default configuration, which it doesn't belong to.")
}
}
}
@@ -204,13 +120,13 @@ public /*abstract*/ class BaseDataTransaction {
CoreStore.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to update an entity of type <\(T.self)> outside its designated queue.")
CoreStore.assert(into.inferStoreIfPossible || (into.configuration ?? Into.defaultConfigurationName) == objectID.persistentStore?.configurationName, "Attempted to update an entity of type <\(T.self)> but the specified persistent store do not match the `NSManagedObjectID`.")
return T.inContext(self.context, withObjectID: objectID)
return (into.entityClass as! NSManagedObject.Type).inContext(self.context, withObjectID: objectID) as? T
}
/**
Deletes a specified `NSManagedObject`.
:param: object the `NSManagedObject` type to be deleted
:param: object the `NSManagedObject` to be deleted
*/
public func delete(object: NSManagedObject?) {
@@ -220,11 +136,11 @@ public /*abstract*/ class BaseDataTransaction {
}
/**
Deletes the specified `NSManagedObject`'s.
Deletes the specified `NSManagedObject`s.
:param: object1 the `NSManagedObject` type to be deleted
:param: object2 another `NSManagedObject` type to be deleted
:param: objects other `NSManagedObject`s type to be deleted
:param: object1 the `NSManagedObject` to be deleted
:param: object2 another `NSManagedObject` to be deleted
:param: objects other `NSManagedObject`s to be deleted
*/
public func delete(object1: NSManagedObject?, _ object2: NSManagedObject?, _ objects: NSManagedObject?...) {
@@ -232,9 +148,9 @@ public /*abstract*/ class BaseDataTransaction {
}
/**
Deletes the specified `NSManagedObject`'s.
Deletes the specified `NSManagedObject`s.
:param: objects the `NSManagedObject`'s type to be deleted
:param: objects the `NSManagedObject`s to be deleted
*/
public func delete(objects: [NSManagedObject?]) {

View File

@@ -0,0 +1,145 @@
//
// Into.swift
// CoreStore
//
// Copyright (c) 2014 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: - Into
/**
A `Into` clause contains the destination entity and destination persistent store for a `create(...)` method. A common usage is to just indicate the entity:
let person = transaction.create(Into(MyPersonEntity))
For cases where multiple `NSPersistentStore`s contain the same entity, the destination configuration's name needs to be specified as well:
let person = transaction.create(Into<MyPersonEntity>("Configuration1"))
This helps the `NSManagedObjectContext` to determine which
*/
public struct Into<T: NSManagedObject> {
// MARK: Public
internal static var defaultConfigurationName: String {
return "PF_DEFAULT_CONFIGURATION_NAME"
}
/**
Initializes an `Into` clause.
Sample Usage:
let person = transaction.create(Into<MyPersonEntity>())
*/
public init(){
self.configuration = nil
self.inferStoreIfPossible = true
self.entityClass = T.self
}
/**
Initializes an `Into` clause with the specified entity type.
Sample Usage:
let person = transaction.create(Into(MyPersonEntity))
:param: entity the `NSManagedObject` type to be created
*/
public init(_ entity: T.Type) {
self.configuration = nil
self.inferStoreIfPossible = true
self.entityClass = entity
}
/**
Initializes an `Into` clause with the specified entity class.
:param: entityClass the `NSManagedObject` class type to be created
*/
public init(_ entityClass: AnyClass) {
self.configuration = nil
self.inferStoreIfPossible = true
self.entityClass = entityClass
}
/**
Initializes an `Into` clause with the specified configuration.
Sample Usage:
let person = transaction.create(Into<MyPersonEntity>("Configuration1"))
:param: configuration the `NSPersistentStore` configuration name to associate the object to. This parameter is required if multiple configurations contain the created `NSManagedObject`'s entity type. Set to `nil` to use the default configuration.
*/
public init(_ configuration: String?) {
self.configuration = configuration
self.inferStoreIfPossible = false
self.entityClass = T.self
}
/**
Initializes an `Into` clause with the specified entity type and configuration.
Sample Usage:
let person = transaction.create(Into(MyPersonEntity.self, "Configuration1"))
:param: entity the `NSManagedObject` type to be created
:param: configuration the `NSPersistentStore` configuration name to associate the object to. This parameter is required if multiple configurations contain the created `NSManagedObject`'s entity type. Set to `nil` to use the default configuration.
*/
public init(_ entity: T.Type, _ configuration: String?) {
self.configuration = configuration
self.inferStoreIfPossible = false
self.entityClass = entity
}
/**
Initializes an `Into` clause with the specified entity class and configuration.
Sample Usage:
let person = transaction.create(Into(MyPersonEntity.self, "Configuration1"))
:param: entityClass the `NSManagedObject` class type to be created
:param: configuration the `NSPersistentStore` configuration name to associate the object to. This parameter is required if multiple configurations contain the created `NSManagedObject`'s entity type. Set to `nil` to use the default configuration.
*/
public init(_ entityClass: AnyClass, _ configuration: String?) {
self.configuration = configuration
self.inferStoreIfPossible = false
self.entityClass = entityClass
}
// MARK: Internal
internal let entityClass: AnyClass
internal let configuration: String?
internal let inferStoreIfPossible: Bool
}

View File

@@ -122,11 +122,11 @@ public final class SynchronousDataTransaction: BaseDataTransaction {
}
/**
Deletes the specified `NSManagedObject`'s.
Deletes the specified `NSManagedObject`s.
:param: object1 the `NSManagedObject` type to be deleted
:param: object2 another `NSManagedObject` type to be deleted
:param: objects other `NSManagedObject`s type to be deleted
:param: object1 the `NSManagedObject` to be deleted
:param: object2 another `NSManagedObject` to be deleted
:param: objects other `NSManagedObject`s to be deleted
*/
public override func delete(object1: NSManagedObject?, _ object2: NSManagedObject?, _ objects: NSManagedObject?...) {
@@ -136,9 +136,9 @@ public final class SynchronousDataTransaction: BaseDataTransaction {
}
/**
Deletes the specified `NSManagedObject`'s.
Deletes the specified `NSManagedObject`s.
:param: objects the `NSManagedObject`'s type to be deleted
:param: objects the `NSManagedObject`s to be deleted
*/
public override func delete(objects: [NSManagedObject?]) {

View File

@@ -45,42 +45,32 @@ public final class DataStack {
// MARK: Public
/**
Initializes a `DataStack` from a model created by merging all the models found in all bundles.
*/
public convenience init() {
let mergedModel: NSManagedObjectModel! = NSManagedObjectModel.mergedModelFromBundles(NSBundle.allBundles())
CoreStore.assert(mergedModel != nil, "Could not create a merged <\(NSManagedObjectModel.self)> from all bundles.")
self.init(managedObjectModel: mergedModel)
}
Initializes a `DataStack` from an `NSManagedObjectModel`.
/**
Initializes a `DataStack` from the specified model name.
:param: modelName the name of the (.xcdatamodeld) model file.
:param: modelName the name of the (.xcdatamodeld) model file. If not specified, the application name will be used
:param: sourceBundle an optional bundle to load models from. If not specified, the main bundle will be used.
:param: modelVersions the `MigrationChain` that indicates the heirarchy of the model's version names. If not specified, will default to a non-migrating data stack.
*/
public convenience init(modelName: String) {
public required init(modelName: String = applicationName, sourceBundle: NSBundle = NSBundle.mainBundle(), modelVersions: MigrationChain = nil) {
let modelFilePath: String! = NSBundle.mainBundle().pathForResource(modelName, ofType: "momd")
let modelFilePath: String! = sourceBundle.pathForResource(
modelName,
ofType: "momd"
)
CoreStore.assert(modelFilePath != nil, "Could not find a \"momd\" resource from the main bundle.")
let managedObjectModel: NSManagedObjectModel! = NSManagedObjectModel(contentsOfURL: NSURL(fileURLWithPath: modelFilePath)!)
CoreStore.assert(managedObjectModel != nil, "Could not create an <\(NSManagedObjectModel.self)> from the resource at path \"\(modelFilePath)\".")
self.init(managedObjectModel: managedObjectModel)
}
/**
Initializes a `DataStack` from an `NSManagedObjectModel`.
:param: managedObjectModel the `NSManagedObjectModel` of the (.xcdatamodeld) model file.
*/
public required init(managedObjectModel: NSManagedObjectModel) {
CoreStore.assert(
managedObjectModel != nil,
"Could not create an <\(NSManagedObjectModel.self)> from the resource at path \"\(modelFilePath)\"."
)
// TODO: assert existence of all model versions in the migrationChain
self.coordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)
self.rootSavingContext = NSManagedObjectContext.rootSavingContextForCoordinator(self.coordinator)
self.mainContext = NSManagedObjectContext.mainContextForRootContext(self.rootSavingContext)
self.sourceBundle = sourceBundle
self.modelVersions = modelVersions
var entityNameMapping = [EntityClassNameType: EntityNameType]()
var entityConfigurationsMapping = [EntityClassNameType: Set<String>]()
@@ -279,14 +269,17 @@ public final class DataStack {
internal let coordinator: NSPersistentStoreCoordinator
internal let rootSavingContext: NSManagedObjectContext
internal let mainContext: NSManagedObjectContext
internal let sourceBundle: NSBundle
internal let modelVersions: MigrationChain
internal let childTransactionQueue: GCDQueue = .createSerial("com.corestore.datastack.childtransactionqueue")
internal let migrationQueue: GCDQueue = .createSerial("com.corestore.datastack.migrationqueue")
internal func entityNameForEntityClass(entityClass: NSManagedObject.Type) -> String? {
internal func entityNameForEntityClass(entityClass: AnyClass) -> String? {
return self.entityNameMapping[NSStringFromClass(entityClass)]
}
internal func persistentStoresForEntityClass(entityClass: NSManagedObject.Type) -> [NSPersistentStore]? {
internal func persistentStoresForEntityClass(entityClass: AnyClass) -> [NSPersistentStore]? {
var returnValue: [NSPersistentStore]? = nil
self.storeMetadataUpdateQueue.barrierSync {
@@ -300,7 +293,7 @@ public final class DataStack {
return returnValue
}
internal func persistentStoreForEntityClass(entityClass: NSManagedObject.Type, configuration: String?, inferStoreIfPossible: Bool) -> (store: NSPersistentStore?, isAmbiguous: Bool) {
internal func persistentStoreForEntityClass(entityClass: AnyClass, configuration: String?, inferStoreIfPossible: Bool) -> (store: NSPersistentStore?, isAmbiguous: Bool) {
var returnValue: (store: NSPersistentStore?, isAmbiguous: Bool) = (store: nil, isAmbiguous: false)
self.storeMetadataUpdateQueue.barrierSync {