Added setup and transactions demo

This commit is contained in:
John Rommel Estropia
2015-05-26 01:44:49 +09:00
parent cb867c07ab
commit 1cc4f21336
32 changed files with 1184 additions and 334 deletions

View File

@@ -665,6 +665,7 @@
);
INFOPLIST_FILE = HardcoreData/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
OTHER_SWIFT_FLAGS = "-D DEBUG";
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -688,6 +689,7 @@
);
INFOPLIST_FILE = HardcoreData/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;

View File

@@ -34,6 +34,125 @@ A `Form` clause binds the `NSManagedObject` entity type to the generics type sys
*/
public struct From<T: NSManagedObject> {
public init(){ }
public init(_ entity: T.Type) { }
// MARK: Public
public init(){
self.findPersistentStores = { _ in nil }
}
public init(_ entity: T.Type) {
self.findPersistentStores = { _ in nil }
}
public init(_ configurations: String...) {
self.init(configurations: configurations)
}
public init(_ configurations: [String]) {
self.init(configurations: configurations)
}
public init(_ entity: T.Type, _ configurations: String...) {
self.init(configurations: configurations)
}
public init(_ entity: T.Type, _ configurations: [String]) {
self.init(configurations: configurations)
}
public init(_ storeURLs: NSURL...) {
self.init(storeURLs: storeURLs)
}
public init(_ storeURLs: [NSURL]) {
self.init(storeURLs: storeURLs)
}
public init(_ entity: T.Type, _ storeURLs: NSURL...) {
self.init(storeURLs: storeURLs)
}
public init(_ entity: T.Type, _ storeURLs: [NSURL]) {
self.init(storeURLs: storeURLs)
}
public init(_ persistentStores: NSPersistentStore...) {
self.init(persistentStores: persistentStores)
}
public init(_ persistentStores: [NSPersistentStore]) {
self.init(persistentStores: persistentStores)
}
public init(_ entity: T.Type, _ persistentStores: NSPersistentStore...) {
self.init(persistentStores: persistentStores)
}
public init(_ entity: T.Type, _ persistentStores: [NSPersistentStore]) {
self.init(persistentStores: persistentStores)
}
// MARK: Internal
internal func applyToFetchRequest(fetchRequest: NSFetchRequest, context: NSManagedObjectContext) {
fetchRequest.entity = context.entityDescriptionForEntityClass(T.self)
fetchRequest.affectedStores = self.findPersistentStores(context: context)
}
// MARK: Private
private let findPersistentStores: (context: NSManagedObjectContext) -> [NSPersistentStore]?
private init(configurations: [String]) {
let configurationsSet = Set(configurations)
self.findPersistentStores = { (context: NSManagedObjectContext) -> [NSPersistentStore]? in
return context.parentStack?.persistentStoresForEntityClass(T.self)?.filter {
return configurationsSet.contains($0.configurationName)
}
}
}
private init(storeURLs: [NSURL]) {
let storeURLsSet = Set(storeURLs)
self.findPersistentStores = { (context: NSManagedObjectContext) -> [NSPersistentStore]? in
return context.parentStack?.persistentStoresForEntityClass(T.self)?.filter {
return $0.URL != nil && storeURLsSet.contains($0.URL!)
}
}
}
private init(persistentStores: [NSPersistentStore]) {
let persistentStores = Set(persistentStores)
self.findPersistentStores = { (context: NSManagedObjectContext) -> [NSPersistentStore]? in
return context.parentStack?.persistentStoresForEntityClass(T.self)?.filter {
return persistentStores.contains($0)
}
}
}
}

View File

@@ -43,7 +43,7 @@ public extension DataStack {
*/
public func fetchOne<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> T? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to fetch from a \(typeName(self)) outside the main thread.")
return self.mainContext.fetchOne(from, fetchClauses)
}
@@ -57,7 +57,7 @@ public extension DataStack {
*/
public func fetchOne<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> T? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to fetch from a \(typeName(self)) outside the main thread.")
return self.mainContext.fetchOne(from, fetchClauses)
}
@@ -71,7 +71,7 @@ public extension DataStack {
*/
public func fetchAll<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> [T]? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to fetch from a \(typeName(self)) outside the main thread.")
return self.mainContext.fetchAll(from, fetchClauses)
}
@@ -85,7 +85,7 @@ public extension DataStack {
*/
public func fetchAll<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> [T]? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to fetch from a \(typeName(self)) outside the main thread.")
return self.mainContext.fetchAll(from, fetchClauses)
}
@@ -99,7 +99,7 @@ public extension DataStack {
*/
public func fetchCount<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> Int? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to fetch from a \(typeName(self)) outside the main thread.")
return self.mainContext.fetchCount(from, fetchClauses)
}
@@ -113,7 +113,7 @@ public extension DataStack {
*/
public func fetchCount<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> Int? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to fetch from a \(typeName(self)) outside the main thread.")
return self.mainContext.fetchCount(from, fetchClauses)
}
@@ -127,7 +127,7 @@ public extension DataStack {
*/
public func fetchObjectID<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> NSManagedObjectID? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to fetch from a \(typeName(self)) outside the main thread.")
return self.mainContext.fetchObjectID(from, fetchClauses)
}
@@ -141,7 +141,7 @@ public extension DataStack {
*/
public func fetchObjectID<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> NSManagedObjectID? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to fetch from a \(typeName(self)) outside the main thread.")
return self.mainContext.fetchObjectID(from, fetchClauses)
}
@@ -155,7 +155,7 @@ public extension DataStack {
*/
public func fetchObjectIDs<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> [NSManagedObjectID]? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to fetch from a \(typeName(self)) outside the main thread.")
return self.mainContext.fetchObjectIDs(from, fetchClauses)
}
@@ -169,39 +169,11 @@ public extension DataStack {
*/
public func fetchObjectIDs<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> [NSManagedObjectID]? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to fetch from a \(typeName(self)) outside the main thread.")
return self.mainContext.fetchObjectIDs(from, fetchClauses)
}
/**
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
*/
public func deleteAll<T: NSManagedObject>(from: From<T>, _ deleteClauses: DeleteClause...) -> Int? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to delete from a \(typeName(self)) outside the main queue.")
return self.mainContext.deleteAll(from, deleteClauses)
}
/**
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
*/
public func deleteAll<T: NSManagedObject>(from: From<T>, _ deleteClauses: [DeleteClause]) -> Int? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to delete from a \(typeName(self)) outside the main queue.")
return self.mainContext.deleteAll(from, deleteClauses)
}
/**
Queries aggregate values as specified by the `QueryClause`'s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
@@ -214,7 +186,7 @@ public extension DataStack {
*/
public func queryValue<T: NSManagedObject, U: SelectValueResultType>(from: From<T>, _ selectClause: Select<U>, _ queryClauses: QueryClause...) -> U? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to query from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to query from a \(typeName(self)) outside the main thread.")
return self.mainContext.queryValue(from, selectClause, queryClauses)
}
@@ -231,7 +203,7 @@ public extension DataStack {
*/
public func queryValue<T: NSManagedObject, U: SelectValueResultType>(from: From<T>, _ selectClause: Select<U>, _ queryClauses: [QueryClause]) -> U? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to query from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to query from a \(typeName(self)) outside the main thread.")
return self.mainContext.queryValue(from, selectClause, queryClauses)
}
@@ -248,7 +220,7 @@ public extension DataStack {
*/
public func queryAttributes<T: NSManagedObject>(from: From<T>, _ selectClause: Select<NSDictionary>, _ queryClauses: QueryClause...) -> [[NSString: AnyObject]]? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to query from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to query from a \(typeName(self)) outside the main thread.")
return self.mainContext.queryAttributes(from, selectClause, queryClauses)
}
@@ -265,7 +237,7 @@ public extension DataStack {
*/
public func queryAttributes<T: NSManagedObject>(from: From<T>, _ selectClause: Select<NSDictionary>, _ queryClauses: [QueryClause]) -> [[NSString: AnyObject]]? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to query from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to query from a \(typeName(self)) outside the main thread.")
return self.mainContext.queryAttributes(from, selectClause, queryClauses)
}

View File

@@ -151,30 +151,6 @@ public extension HardcoreData {
return self.defaultStack.fetchObjectIDs(from, fetchClauses)
}
/**
Using the `defaultStack`, 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
*/
public static func deleteAll<T: NSManagedObject>(from: From<T>, _ deleteClauses: DeleteClause...) -> Int? {
return self.defaultStack.deleteAll(from, deleteClauses)
}
/**
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
*/
public static func deleteAll<T: NSManagedObject>(from: From<T>, _ deleteClauses: [DeleteClause]) -> Int? {
return self.defaultStack.deleteAll(from, deleteClauses)
}
/**
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.

View File

@@ -41,7 +41,8 @@ internal extension NSManagedObjectContext {
internal func fetchOne<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> T? {
let fetchRequest = NSFetchRequest()
fetchRequest.entity = self.entityDescriptionForEntityClass(T)
from.applyToFetchRequest(fetchRequest, context: self)
fetchRequest.fetchLimit = 1
fetchRequest.resultType = .ManagedObjectResultType
@@ -75,7 +76,8 @@ internal extension NSManagedObjectContext {
internal func fetchAll<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> [T]? {
let fetchRequest = NSFetchRequest()
fetchRequest.entity = self.entityDescriptionForEntityClass(T)
from.applyToFetchRequest(fetchRequest, context: self)
fetchRequest.fetchLimit = 0
fetchRequest.resultType = .ManagedObjectResultType
@@ -109,7 +111,7 @@ internal extension NSManagedObjectContext {
internal func fetchCount<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> Int? {
let fetchRequest = NSFetchRequest()
fetchRequest.entity = self.entityDescriptionForEntityClass(T)
from.applyToFetchRequest(fetchRequest, context: self)
for clause in fetchClauses {
@@ -141,7 +143,8 @@ internal extension NSManagedObjectContext {
internal func fetchObjectID<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> NSManagedObjectID? {
let fetchRequest = NSFetchRequest()
fetchRequest.entity = self.entityDescriptionForEntityClass(T)
from.applyToFetchRequest(fetchRequest, context: self)
fetchRequest.fetchLimit = 1
fetchRequest.resultType = .ManagedObjectIDResultType
@@ -175,7 +178,8 @@ internal extension NSManagedObjectContext {
internal func fetchObjectIDs<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> [NSManagedObjectID]? {
let fetchRequest = NSFetchRequest()
fetchRequest.entity = self.entityDescriptionForEntityClass(T)
from.applyToFetchRequest(fetchRequest, context: self)
fetchRequest.fetchLimit = 0
fetchRequest.resultType = .ManagedObjectIDResultType
@@ -209,7 +213,8 @@ internal extension NSManagedObjectContext {
internal func deleteAll<T: NSManagedObject>(from: From<T>, _ deleteClauses: [DeleteClause]) -> Int? {
let fetchRequest = NSFetchRequest()
fetchRequest.entity = self.entityDescriptionForEntityClass(T)
from.applyToFetchRequest(fetchRequest, context: self)
fetchRequest.fetchLimit = 0
fetchRequest.resultType = .ManagedObjectResultType
fetchRequest.returnsObjectsAsFaults = true
@@ -254,7 +259,8 @@ internal extension NSManagedObjectContext {
internal func queryValue<T: NSManagedObject, U: SelectValueResultType>(from: From<T>, _ selectClause: Select<U>, _ queryClauses: [QueryClause]) -> U? {
let fetchRequest = NSFetchRequest()
fetchRequest.entity = self.entityDescriptionForEntityClass(T)
from.applyToFetchRequest(fetchRequest, context: self)
fetchRequest.fetchLimit = 0
selectClause.applyToFetchRequest(fetchRequest)
@@ -294,7 +300,8 @@ internal extension NSManagedObjectContext {
internal func queryAttributes<T: NSManagedObject>(from: From<T>, _ selectClause: Select<NSDictionary>, _ queryClauses: [QueryClause]) -> [[NSString: AnyObject]]? {
let fetchRequest = NSFetchRequest()
fetchRequest.entity = self.entityDescriptionForEntityClass(T)
from.applyToFetchRequest(fetchRequest, context: self)
fetchRequest.fetchLimit = 0
selectClause.applyToFetchRequest(fetchRequest)

View File

@@ -42,7 +42,7 @@ public extension DataStack {
*/
public func observeObject<T: NSManagedObject>(object: T) -> ManagedObjectController<T> {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to observe objects from \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to observe objects from \(typeName(self)) outside the main thread.")
return ManagedObjectController(
dataStack: self,
@@ -71,11 +71,11 @@ public extension DataStack {
*/
public func observeObjectList<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> ManagedObjectListController<T> {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to observe objects from \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to observe objects from \(typeName(self)) outside the main thread.")
return ManagedObjectListController(
dataStack: self,
entity: T.self,
from: from,
sectionedBy: nil,
fetchClauses: fetchClauses
)
@@ -104,11 +104,11 @@ public extension DataStack {
*/
public func observeSectionedList<T: NSManagedObject>(from: From<T>, _ sectionedBy: SectionedBy, _ fetchClauses: [FetchClause]) -> ManagedObjectListController<T> {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to observe objects from \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to observe objects from \(typeName(self)) outside the main thread.")
return ManagedObjectListController(
dataStack: self,
entity: T.self,
from: from,
sectionedBy: sectionedBy,
fetchClauses: fetchClauses
)

View File

@@ -87,7 +87,7 @@ public final class ManagedObjectController<T: NSManagedObject> {
*/
public func addObserver<U: ManagedObjectObserver where U.EntityType == T>(observer: U) {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to add a \(typeName(observer)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to add an observer of type \(typeName(observer)) outside the main thread.")
self.removeObserver(observer)
@@ -155,7 +155,7 @@ public final class ManagedObjectController<T: NSManagedObject> {
*/
public func removeObserver<U: ManagedObjectObserver where U.EntityType == T>(observer: U) {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to remove a \(typeName(observer)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to remove an observer of type \(typeName(observer)) outside the main thread.")
let nilValue: AnyObject? = nil
setAssociatedRetainedObject(nilValue, forKey: &NotificationKey.willChangeObject, inObject: observer)

View File

@@ -188,7 +188,7 @@ public final class ManagedObjectListController<T: NSManagedObject> {
*/
public func addObserver<U: ManagedObjectListChangeObserver where U.EntityType == T>(observer: U) {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to add a \(typeName(observer)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to add an observer of type \(typeName(observer)) outside the main thread.")
self.removeObserver(observer)
@@ -231,7 +231,7 @@ public final class ManagedObjectListController<T: NSManagedObject> {
*/
public func addObserver<U: ManagedObjectListObjectObserver where U.EntityType == T>(observer: U) {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to add a \(typeName(observer)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to add an observer of type \(typeName(observer)) outside the main thread.")
self.removeObserver(observer)
@@ -340,7 +340,7 @@ public final class ManagedObjectListController<T: NSManagedObject> {
*/
public func addObserver<U: ManagedObjectListSectionObserver where U.EntityType == T>(observer: U) {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to add a \(typeName(observer)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to add an observer of type \(typeName(observer)) outside the main thread.")
self.removeObserver(observer)
@@ -478,7 +478,7 @@ public final class ManagedObjectListController<T: NSManagedObject> {
*/
public func removeObserver<U: ManagedObjectListChangeObserver where U.EntityType == T>(observer: U) {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to remove a \(typeName(observer)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to remove an observer of type \(typeName(observer)) outside the main thread.")
let nilValue: AnyObject? = nil
setAssociatedRetainedObject(nilValue, forKey: &NotificationKey.willChangeList, inObject: observer)
@@ -496,12 +496,13 @@ public final class ManagedObjectListController<T: NSManagedObject> {
// MARK: Internal
internal init(dataStack: DataStack, entity: T.Type, sectionedBy: SectionedBy?, fetchClauses: [FetchClause]) {
internal init(dataStack: DataStack, from: From<T>, sectionedBy: SectionedBy?, fetchClauses: [FetchClause]) {
let context = dataStack.mainContext
let fetchRequest = NSFetchRequest()
fetchRequest.entity = context.entityDescriptionForEntityClass(entity)
from.applyToFetchRequest(fetchRequest, context: context)
fetchRequest.fetchLimit = 0
fetchRequest.resultType = .ManagedObjectResultType

View File

@@ -91,16 +91,16 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
// MARK: BaseDataTransaction
/**
Creates a new `NSManagedObject` with the specified entity type. This method should not be used after the `commit()` method was already called once.
Creates a new `NSManagedObject` with the specified entity type.
:param: entity the `NSManagedObject` type to be created
:param: into the `Into` clause indicating the destination `NSManagedObject` entity type and the destination configuration
:returns: a new `NSManagedObject` instance of the specified entity type.
*/
public override func create<T: NSManagedObject>(entity: T.Type) -> T {
public override func create<T: NSManagedObject>(into: Into<T>) -> T {
HardcoreData.assert(!self.isCommitted, "Attempted to create an entity of type <\(entity)> from an already committed \(typeName(self)).")
HardcoreData.assert(!self.isCommitted, "Attempted to create an entity of type <\(T.self)> from an already committed \(typeName(self)).")
return super.create(entity)
return super.create(into)
}
/**

View File

@@ -28,6 +28,86 @@ 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
/**
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
/**
@@ -48,14 +128,50 @@ public /*abstract*/ class BaseDataTransaction {
/**
Creates a new `NSManagedObject` with the specified entity type.
:param: entity the `NSManagedObject` type to be created
:param: into the `Into` clause indicating the destination `NSManagedObject` entity type and the destination configuration
:returns: a new `NSManagedObject` instance of the specified entity type.
*/
public func create<T: NSManagedObject>(entity: T.Type) -> T {
public func create<T: NSManagedObject>(into: Into<T>) -> T {
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to create an entity of type <\(entity)> outside its designated queue.")
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to create an entity of type <\(T.self)> outside its designated queue.")
return T.createInContext(self.context)
let context = self.context
let object = T.createInContext(context)
if into.inferStoreIfPossible {
switch context.parentStack!.persistentStoreForEntityClass(T.self, configuration: nil, inferStoreIfPossible: true) {
case (.Some(let persistentStore), _):
context.assignObject(object, toPersistentStore: persistentStore)
case (.None, true):
HardcoreData.assert(false, "Attempted to create an entity of type \(typeName(object)) with ambiguous destination persistent store, but the configuration name was not specified.")
default:
HardcoreData.assert(false, "Attempted to create an entity of type \(typeName(object)), 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) {
case (.Some(let persistentStore), _):
context.assignObject(object, toPersistentStore: persistentStore)
default:
if let configuration = into.configuration {
HardcoreData.assert(false, "Attempted to create an entity of type \(typeName(object)) into the configuration \"\(configuration)\", which it doesn't belong to.")
}
else {
HardcoreData.assert(false, "Attempted to create an entity of type \(typeName(object)) into the default configuration, which it doesn't belong to.")
}
}
}
return object
}
/**

View File

@@ -41,7 +41,7 @@ public extension DataStack {
*/
public func beginAsynchronous(closure: (transaction: AsynchronousDataTransaction) -> Void) {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to begin a transaction from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to begin a transaction from a \(typeName(self)) outside the main thread.")
AsynchronousDataTransaction(
mainContext: self.rootSavingContext,
@@ -57,7 +57,7 @@ public extension DataStack {
*/
public func beginSynchronous(closure: (transaction: SynchronousDataTransaction) -> Void) -> SaveResult? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to begin a transaction from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to begin a transaction from a \(typeName(self)) outside the main thread.")
return SynchronousDataTransaction(
mainContext: self.rootSavingContext,
@@ -72,7 +72,7 @@ public extension DataStack {
*/
public func beginDetached() -> DetachedDataTransaction {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to begin a transaction from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(NSThread.isMainThread(), "Attempted to begin a transaction from a \(typeName(self)) outside the main thread.")
return DetachedDataTransaction(
mainContext: self.rootSavingContext,

View File

@@ -70,16 +70,16 @@ public final class SynchronousDataTransaction: BaseDataTransaction {
// MARK: BaseDataTransaction
/**
Creates a new `NSManagedObject` with the specified entity type. This method should not be used after the `commit()` method was already called once.
Creates a new `NSManagedObject` with the specified entity type.
:param: entity the `NSManagedObject` type to be created
:param: into the `Into` clause indicating the destination `NSManagedObject` entity type and the destination configuration
:returns: a new `NSManagedObject` instance of the specified entity type.
*/
public override func create<T: NSManagedObject>(entity: T.Type) -> T {
public override func create<T: NSManagedObject>(into: Into<T>) -> T {
HardcoreData.assert(!self.isCommitted, "Attempted to create an entity of type <\(entity)> from an already committed \(typeName(self)).")
HardcoreData.assert(!self.isCommitted, "Attempted to create an entity of type <\(T.self)> from an already committed \(typeName(self)).")
return super.create(entity)
return super.create(into)
}
/**

View File

@@ -28,6 +28,8 @@ import CoreData
import GCDKit
private let defaultConfigurationName = "PF_DEFAULT_CONFIGURATION_NAME"
private let applicationSupportDirectory = NSFileManager.defaultManager().URLsForDirectory(.ApplicationSupportDirectory, inDomains: .UserDomainMask).first as! NSURL
private let applicationName = ((NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleName") as? String) ?? "CoreData")
@@ -81,14 +83,20 @@ public final class DataStack {
self.coordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)
self.rootSavingContext = NSManagedObjectContext.rootSavingContextForCoordinator(self.coordinator)
self.mainContext = NSManagedObjectContext.mainContextForRootContext(self.rootSavingContext)
self.entityNameMapping = (managedObjectModel.entities as! [NSEntityDescription]).reduce([EntityClassNameType: EntityNameType]()) { (var mapping, entityDescription) in
var entityNameMapping = [EntityClassNameType: EntityNameType]()
var entityConfigurationsMapping = [EntityClassNameType: Set<String>]()
for entityDescription in managedObjectModel.entities as! [NSEntityDescription] {
let managedObjectClassName = entityDescription.managedObjectClassName
entityConfigurationsMapping[managedObjectClassName] = []
if let entityName = entityDescription.name {
mapping[entityDescription.managedObjectClassName] = entityName
entityNameMapping[managedObjectClassName] = entityName
}
return mapping
}
self.entityNameMapping = entityNameMapping
self.entityConfigurationsMapping = entityConfigurationsMapping
self.rootSavingContext.parentStack = self
}
@@ -117,6 +125,7 @@ public final class DataStack {
if let store = store {
self.updateMetadataForPersistentStore(store)
return PersistentStoreResult(store)
}
@@ -140,7 +149,7 @@ public final class DataStack {
Adds to the stack an SQLite store from the given SQLite file name.
:param: fileName the local filename for the SQLite persistent store in the "Application Support" directory. A new SQLite file will be created if it does not exist. Note that if you have multiple configurations, you will need to specify a different `fileName` explicitly for each of them.
:param: configuration an optional configuration name from the model file. If not specified, defaults to nil. Note that if you have multiple configurations, you will need to specify a different `fileName` explicitly for each of them.
: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: automigrating Set to true to configure Core Data auto-migration, or false to disable. If not specified, defaults to true.
:param: resetStoreOnMigrationFailure Set to true to delete the store on migration failure; or set to false to throw exceptions on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false
:returns: a `PersistentStoreResult` indicating success or failure.
@@ -162,7 +171,7 @@ public final class DataStack {
Adds to the stack an SQLite store from the given SQLite file URL.
:param: fileURL the local file URL for the SQLite persistent store. A new SQLite file will be created if it does not exist. If not specified, defaults to a file URL pointing to a "<Application name>.sqlite" file in the "Application Support" directory. Note that if you have multiple configurations, you will need to specify a different `fileURL` explicitly for each of them.
:param: configuration an optional configuration name from the model file. If not specified, defaults to nil. Note that if you have multiple configurations, you will need to specify a different `fileURL` explicitly for each of them.
: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: automigrating Set to true to configure Core Data auto-migration, or false to disable. If not specified, defaults to true.
:param: resetStoreOnMigrationFailure Set to true to delete the store on migration failure; or set to false to throw exceptions on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false.
:returns: a `PersistentStoreResult` indicating success or failure.
@@ -176,7 +185,7 @@ public final class DataStack {
if store.type == NSSQLiteStoreType
&& isExistingStoreAutomigrating == automigrating
&& store.configurationName == (configuration ?? "PF_DEFAULT_CONFIGURATION_NAME") {
&& store.configurationName == (configuration ?? defaultConfigurationName) {
return PersistentStoreResult(store)
}
@@ -218,6 +227,7 @@ public final class DataStack {
if let store = store {
self.updateMetadataForPersistentStore(store)
return PersistentStoreResult(store)
}
@@ -253,6 +263,7 @@ public final class DataStack {
if let store = store {
self.updateMetadataForPersistentStore(store)
return PersistentStoreResult(store)
}
}
@@ -276,12 +287,77 @@ public final class DataStack {
return self.entityNameMapping[NSStringFromClass(entityClass)]
}
internal func persistentStoresForEntityClass(entityClass: NSManagedObject.Type) -> [NSPersistentStore]? {
var returnValue: [NSPersistentStore]? = nil
self.storeMetadataUpdateQueue.barrierSync {
let configurationsForEntity = self.entityConfigurationsMapping[NSStringFromClass(entityClass)] ?? []
returnValue = map(configurationsForEntity) {
return self.configurationStoreMapping[$0]!
}
}
return returnValue
}
internal func persistentStoreForEntityClass(entityClass: NSManagedObject.Type, configuration: String?, inferStoreIfPossible: Bool) -> (store: NSPersistentStore?, isAmbiguous: Bool) {
var returnValue: (store: NSPersistentStore?, isAmbiguous: Bool) = (store: nil, isAmbiguous: false)
self.storeMetadataUpdateQueue.barrierSync {
let configurationsForEntity = self.entityConfigurationsMapping[NSStringFromClass(entityClass)] ?? []
if let configuration = configuration {
if configurationsForEntity.contains(configuration) {
returnValue = (store: self.configurationStoreMapping[configuration], isAmbiguous: false)
return
}
else if !inferStoreIfPossible {
return
}
}
switch configurationsForEntity.count {
case 0:
return
case 1 where inferStoreIfPossible:
returnValue = (store: self.configurationStoreMapping[configurationsForEntity.first!], isAmbiguous: false)
default:
returnValue = (store: nil, isAmbiguous: true)
}
}
return returnValue
}
// MARK: Private
private typealias EntityClassNameType = String
private typealias EntityNameType = String
private typealias ConfigurationNameType = String
private let coordinator: NSPersistentStoreCoordinator
private let entityNameMapping: [EntityClassNameType: EntityNameType]
private let storeMetadataUpdateQueue = GCDQueue.createConcurrent("com.hardcoreData.persistentStoreBarrierQueue")
private var configurationStoreMapping = [ConfigurationNameType: NSPersistentStore]()
private var entityConfigurationsMapping = [EntityClassNameType: Set<String>]()
private func updateMetadataForPersistentStore(persistentStore: NSPersistentStore) {
self.storeMetadataUpdateQueue.barrierAsync {
let configurationName = persistentStore.configurationName
self.configurationStoreMapping[configurationName] = persistentStore
for entityDescription in (self.coordinator.managedObjectModel.entitiesForConfiguration(configurationName) as? [NSEntityDescription] ?? []) {
self.entityConfigurationsMapping[entityDescription.managedObjectClassName]?.insert(configurationName)
}
}
}
}

View File

@@ -11,24 +11,25 @@
B503FAE01AFDC71700F90881 /* ObjectObserverDemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B503FADC1AFDC71700F90881 /* ObjectObserverDemoViewController.swift */; };
B503FAE11AFDC71700F90881 /* Palette.swift in Sources */ = {isa = PBXBuildFile; fileRef = B503FADD1AFDC71700F90881 /* Palette.swift */; };
B503FAE21AFDC71700F90881 /* PaletteTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B503FADE1AFDC71700F90881 /* PaletteTableViewCell.swift */; };
B52977D91B120B80003D50A5 /* ObserversViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52977D81B120B80003D50A5 /* ObserversViewController.swift */; };
B52977DD1B120F3B003D50A5 /* TransactionsDemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52977DC1B120F3B003D50A5 /* TransactionsDemoViewController.swift */; };
B52977DF1B120F83003D50A5 /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B52977DE1B120F83003D50A5 /* MapKit.framework */; };
B52977E11B120F8A003D50A5 /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B52977E01B120F8A003D50A5 /* CoreLocation.framework */; };
B52977E41B121635003D50A5 /* Place.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52977E31B121635003D50A5 /* Place.swift */; };
B54AAD4F1AF4D26E00848AE0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54AAD4E1AF4D26E00848AE0 /* AppDelegate.swift */; };
B54AAD521AF4D26E00848AE0 /* HardcoreDataDemo.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = B54AAD501AF4D26E00848AE0 /* HardcoreDataDemo.xcdatamodeld */; };
B54AAD591AF4D26E00848AE0 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B54AAD571AF4D26E00848AE0 /* Main.storyboard */; };
B54AAD5B1AF4D26E00848AE0 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B54AAD5A1AF4D26E00848AE0 /* Images.xcassets */; };
B54AAD5E1AF4D26E00848AE0 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = B54AAD5C1AF4D26E00848AE0 /* LaunchScreen.xib */; };
B54AAD6A1AF4D26E00848AE0 /* HardcoreDataDemoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54AAD691AF4D26E00848AE0 /* HardcoreDataDemoTests.swift */; };
B566E32A1B117B1F00F4F0C6 /* StackSetupDemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B566E3291B117B1F00F4F0C6 /* StackSetupDemoViewController.swift */; };
B566E3321B11DF3200F4F0C6 /* UserAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = B566E3311B11DF3200F4F0C6 /* UserAccount.swift */; };
B583A9201AF5F542001F76AF /* HardcoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B583A91B1AF5F4F4001F76AF /* HardcoreData.framework */; };
B583A9211AF5F542001F76AF /* HardcoreData.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B583A91B1AF5F4F4001F76AF /* HardcoreData.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
B5E7240F1B11F993006FB83F /* TwitterAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E7240E1B11F993006FB83F /* TwitterAccount.swift */; };
B5E724111B11F994006FB83F /* FacebookAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E724101B11F994006FB83F /* FacebookAccount.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
B54AAD641AF4D26E00848AE0 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = B54AAD411AF4D26E00848AE0 /* Project object */;
proxyType = 1;
remoteGlobalIDString = B54AAD481AF4D26E00848AE0;
remoteInfo = HardcoreDataDemo;
};
B583A91A1AF5F4F4001F76AF /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = B583A9141AF5F4F3001F76AF /* HardcoreData.xcodeproj */;
@@ -78,6 +79,11 @@
B503FADC1AFDC71700F90881 /* ObjectObserverDemoViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjectObserverDemoViewController.swift; sourceTree = "<group>"; };
B503FADD1AFDC71700F90881 /* Palette.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Palette.swift; sourceTree = "<group>"; };
B503FADE1AFDC71700F90881 /* PaletteTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaletteTableViewCell.swift; sourceTree = "<group>"; };
B52977D81B120B80003D50A5 /* ObserversViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObserversViewController.swift; sourceTree = "<group>"; };
B52977DC1B120F3B003D50A5 /* TransactionsDemoViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionsDemoViewController.swift; sourceTree = "<group>"; };
B52977DE1B120F83003D50A5 /* MapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MapKit.framework; path = System/Library/Frameworks/MapKit.framework; sourceTree = SDKROOT; };
B52977E01B120F8A003D50A5 /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = System/Library/Frameworks/CoreLocation.framework; sourceTree = SDKROOT; };
B52977E31B121635003D50A5 /* Place.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Place.swift; sourceTree = "<group>"; };
B54AAD491AF4D26E00848AE0 /* HardcoreDataDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HardcoreDataDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
B54AAD4D1AF4D26E00848AE0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
B54AAD4E1AF4D26E00848AE0 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
@@ -85,11 +91,12 @@
B54AAD581AF4D26E00848AE0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
B54AAD5A1AF4D26E00848AE0 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
B54AAD5D1AF4D26E00848AE0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
B54AAD631AF4D26E00848AE0 /* HardcoreDataDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HardcoreDataDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
B54AAD681AF4D26E00848AE0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
B54AAD691AF4D26E00848AE0 /* HardcoreDataDemoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HardcoreDataDemoTests.swift; sourceTree = "<group>"; };
B566E3291B117B1F00F4F0C6 /* StackSetupDemoViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StackSetupDemoViewController.swift; sourceTree = "<group>"; };
B566E3311B11DF3200F4F0C6 /* UserAccount.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserAccount.swift; sourceTree = "<group>"; };
B583A9141AF5F4F3001F76AF /* HardcoreData.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = HardcoreData.xcodeproj; path = ../HardcoreData.xcodeproj; sourceTree = "<group>"; };
B583A9251AF5F547001F76AF /* GCDKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; name = GCDKit.framework; path = "/Users/johnestropia/Library/Developer/Xcode/DerivedData/HardcoreDataDemo-ftknhsqfpsthfogvisxisgpbbhsj/Build/Products/Debug-iphoneos/GCDKit.framework"; sourceTree = "<absolute>"; };
B5E7240E1B11F993006FB83F /* TwitterAccount.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TwitterAccount.swift; sourceTree = "<group>"; };
B5E724101B11F994006FB83F /* FacebookAccount.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FacebookAccount.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -97,23 +104,19 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
B52977E11B120F8A003D50A5 /* CoreLocation.framework in Frameworks */,
B52977DF1B120F83003D50A5 /* MapKit.framework in Frameworks */,
B583A9201AF5F542001F76AF /* HardcoreData.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
B54AAD601AF4D26E00848AE0 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
B503FADA1AFDC71700F90881 /* List and Object Observers Demo */ = {
isa = PBXGroup;
children = (
B52977D81B120B80003D50A5 /* ObserversViewController.swift */,
B503FADB1AFDC71700F90881 /* ObjectListObserverDemoViewController.swift */,
B503FADC1AFDC71700F90881 /* ObjectObserverDemoViewController.swift */,
B503FADD1AFDC71700F90881 /* Palette.swift */,
@@ -122,13 +125,31 @@
path = "List and Object Observers Demo";
sourceTree = "<group>";
};
B52977DB1B120F2C003D50A5 /* Transactions Demo */ = {
isa = PBXGroup;
children = (
B52977E31B121635003D50A5 /* Place.swift */,
B52977DC1B120F3B003D50A5 /* TransactionsDemoViewController.swift */,
);
path = "Transactions Demo";
sourceTree = "<group>";
};
B52977E21B120F90003D50A5 /* Frameworks */ = {
isa = PBXGroup;
children = (
B583A9141AF5F4F3001F76AF /* HardcoreData.xcodeproj */,
B52977E01B120F8A003D50A5 /* CoreLocation.framework */,
B52977DE1B120F83003D50A5 /* MapKit.framework */,
B583A9251AF5F547001F76AF /* GCDKit.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
B54AAD401AF4D26E00848AE0 = {
isa = PBXGroup;
children = (
B583A9251AF5F547001F76AF /* GCDKit.framework */,
B583A9141AF5F4F3001F76AF /* HardcoreData.xcodeproj */,
B52977E21B120F90003D50A5 /* Frameworks */,
B54AAD4B1AF4D26E00848AE0 /* HardcoreDataDemo */,
B54AAD661AF4D26E00848AE0 /* HardcoreDataDemoTests */,
B54AAD4A1AF4D26E00848AE0 /* Products */,
);
sourceTree = "<group>";
@@ -137,7 +158,6 @@
isa = PBXGroup;
children = (
B54AAD491AF4D26E00848AE0 /* HardcoreDataDemo.app */,
B54AAD631AF4D26E00848AE0 /* HardcoreDataDemoTests.xctest */,
);
name = Products;
sourceTree = "<group>";
@@ -146,7 +166,9 @@
isa = PBXGroup;
children = (
B54AAD4E1AF4D26E00848AE0 /* AppDelegate.swift */,
B566E3271B117AE700F4F0C6 /* Stack Setup Demo */,
B503FADA1AFDC71700F90881 /* List and Object Observers Demo */,
B52977DB1B120F2C003D50A5 /* Transactions Demo */,
B54AAD571AF4D26E00848AE0 /* Main.storyboard */,
B54AAD5A1AF4D26E00848AE0 /* Images.xcassets */,
B54AAD5C1AF4D26E00848AE0 /* LaunchScreen.xib */,
@@ -164,21 +186,15 @@
name = "Supporting Files";
sourceTree = "<group>";
};
B54AAD661AF4D26E00848AE0 /* HardcoreDataDemoTests */ = {
B566E3271B117AE700F4F0C6 /* Stack Setup Demo */ = {
isa = PBXGroup;
children = (
B54AAD691AF4D26E00848AE0 /* HardcoreDataDemoTests.swift */,
B54AAD671AF4D26E00848AE0 /* Supporting Files */,
B5E724101B11F994006FB83F /* FacebookAccount.swift */,
B5E7240E1B11F993006FB83F /* TwitterAccount.swift */,
B566E3311B11DF3200F4F0C6 /* UserAccount.swift */,
B566E3291B117B1F00F4F0C6 /* StackSetupDemoViewController.swift */,
);
path = HardcoreDataDemoTests;
sourceTree = "<group>";
};
B54AAD671AF4D26E00848AE0 /* Supporting Files */ = {
isa = PBXGroup;
children = (
B54AAD681AF4D26E00848AE0 /* Info.plist */,
);
name = "Supporting Files";
path = "Stack Setup Demo";
sourceTree = "<group>";
};
B583A9151AF5F4F3001F76AF /* Products */ = {
@@ -213,24 +229,6 @@
productReference = B54AAD491AF4D26E00848AE0 /* HardcoreDataDemo.app */;
productType = "com.apple.product-type.application";
};
B54AAD621AF4D26E00848AE0 /* HardcoreDataDemoTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = B54AAD701AF4D26E00848AE0 /* Build configuration list for PBXNativeTarget "HardcoreDataDemoTests" */;
buildPhases = (
B54AAD5F1AF4D26E00848AE0 /* Sources */,
B54AAD601AF4D26E00848AE0 /* Frameworks */,
B54AAD611AF4D26E00848AE0 /* Resources */,
);
buildRules = (
);
dependencies = (
B54AAD651AF4D26E00848AE0 /* PBXTargetDependency */,
);
name = HardcoreDataDemoTests;
productName = HardcoreDataDemoTests;
productReference = B54AAD631AF4D26E00848AE0 /* HardcoreDataDemoTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@@ -243,10 +241,6 @@
B54AAD481AF4D26E00848AE0 = {
CreatedOnToolsVersion = 6.3;
};
B54AAD621AF4D26E00848AE0 = {
CreatedOnToolsVersion = 6.3;
TestTargetID = B54AAD481AF4D26E00848AE0;
};
};
};
buildConfigurationList = B54AAD441AF4D26E00848AE0 /* Build configuration list for PBXProject "HardcoreDataDemo" */;
@@ -269,7 +263,6 @@
projectRoot = "";
targets = (
B54AAD481AF4D26E00848AE0 /* HardcoreDataDemo */,
B54AAD621AF4D26E00848AE0 /* HardcoreDataDemoTests */,
);
};
/* End PBXProject section */
@@ -302,13 +295,6 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
B54AAD611AF4D26E00848AE0 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@@ -316,7 +302,14 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B52977DD1B120F3B003D50A5 /* TransactionsDemoViewController.swift in Sources */,
B52977E41B121635003D50A5 /* Place.swift in Sources */,
B503FAE01AFDC71700F90881 /* ObjectObserverDemoViewController.swift in Sources */,
B52977D91B120B80003D50A5 /* ObserversViewController.swift in Sources */,
B566E32A1B117B1F00F4F0C6 /* StackSetupDemoViewController.swift in Sources */,
B5E724111B11F994006FB83F /* FacebookAccount.swift in Sources */,
B5E7240F1B11F993006FB83F /* TwitterAccount.swift in Sources */,
B566E3321B11DF3200F4F0C6 /* UserAccount.swift in Sources */,
B54AAD521AF4D26E00848AE0 /* HardcoreDataDemo.xcdatamodeld in Sources */,
B503FAE11AFDC71700F90881 /* Palette.swift in Sources */,
B503FAE21AFDC71700F90881 /* PaletteTableViewCell.swift in Sources */,
@@ -325,22 +318,9 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
B54AAD5F1AF4D26E00848AE0 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B54AAD6A1AF4D26E00848AE0 /* HardcoreDataDemoTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
B54AAD651AF4D26E00848AE0 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = B54AAD481AF4D26E00848AE0 /* HardcoreDataDemo */;
targetProxy = B54AAD641AF4D26E00848AE0 /* PBXContainerItemProxy */;
};
B583A91F1AF5F512001F76AF /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = HardcoreData;
@@ -463,6 +443,7 @@
"$(USER_LIBRARY_DIR)/Developer/Xcode/DerivedData/HardcoreDataDemo-ftknhsqfpsthfogvisxisgpbbhsj/Build/Products/Debug-iphoneos",
);
INFOPLIST_FILE = HardcoreDataDemo/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
};
@@ -477,45 +458,12 @@
"$(USER_LIBRARY_DIR)/Developer/Xcode/DerivedData/HardcoreDataDemo-ftknhsqfpsthfogvisxisgpbbhsj/Build/Products/Debug-iphoneos",
);
INFOPLIST_FILE = HardcoreDataDemo/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
B54AAD711AF4D26E00848AE0 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
);
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
INFOPLIST_FILE = HardcoreDataDemoTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/HardcoreDataDemo.app/HardcoreDataDemo";
};
name = Debug;
};
B54AAD721AF4D26E00848AE0 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
);
INFOPLIST_FILE = HardcoreDataDemoTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/HardcoreDataDemo.app/HardcoreDataDemo";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
@@ -537,15 +485,6 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
B54AAD701AF4D26E00848AE0 /* Build configuration list for PBXNativeTarget "HardcoreDataDemoTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
B54AAD711AF4D26E00848AE0 /* Debug */,
B54AAD721AF4D26E00848AE0 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
/* Begin XCVersionGroup section */

View File

@@ -7,7 +7,6 @@
//
import UIKit
import HardcoreData
// MARK: - AppDelegate
@@ -21,7 +20,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
HardcoreData.addSQLiteStore(resetStoreOnMigrationFailure: true)
return true
}
}

View File

@@ -6,6 +6,87 @@
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
</dependencies>
<scenes>
<!--SNS Accounts-->
<scene sceneID="3If-81-mNf">
<objects>
<tableViewController id="AW4-lY-JNk" customClass="StackSetupDemoViewController" customModule="HardcoreDataDemo" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="S3A-lm-AuA">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<view key="tableHeaderView" contentMode="scaleToFill" id="yud-WH-MPa">
<rect key="frame" x="0.0" y="64" width="600" height="150"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="sns" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="czc-nd-es9">
<rect key="frame" x="20" y="20" width="560" height="21.5"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="18"/>
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="name" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1eh-91-O2N">
<rect key="frame" x="20" y="65" width="42.5" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="friends" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="p2y-0T-hQs">
<rect key="frame" x="536" y="67" width="44" height="17"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="1eh-91-O2N" firstAttribute="top" secondItem="czc-nd-es9" secondAttribute="bottom" constant="23.5" id="F6q-Jt-glI"/>
<constraint firstItem="p2y-0T-hQs" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="1eh-91-O2N" secondAttribute="trailing" constant="20" id="GVS-6o-rmj"/>
<constraint firstItem="p2y-0T-hQs" firstAttribute="trailing" secondItem="czc-nd-es9" secondAttribute="trailing" id="P5Z-WP-UCj"/>
<constraint firstAttribute="trailing" secondItem="czc-nd-es9" secondAttribute="trailing" constant="20" id="adf-Th-qDC"/>
<constraint firstItem="p2y-0T-hQs" firstAttribute="centerY" secondItem="1eh-91-O2N" secondAttribute="centerY" constant="0.25" id="ccw-e2-zjE"/>
<constraint firstItem="czc-nd-es9" firstAttribute="top" secondItem="yud-WH-MPa" secondAttribute="top" constant="20" id="un5-6w-1o5"/>
<constraint firstItem="czc-nd-es9" firstAttribute="leading" secondItem="yud-WH-MPa" secondAttribute="leading" constant="20" id="xsd-bs-BQL"/>
<constraint firstItem="1eh-91-O2N" firstAttribute="leading" secondItem="czc-nd-es9" secondAttribute="leading" id="ye6-S5-Yil"/>
</constraints>
</view>
<prototypes>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="UITableViewCell" textLabel="8b8-lM-Krq" detailTextLabel="hR1-Zb-BOk" style="IBUITableViewCellStyleValue1" id="dMt-nx-EO5">
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="dMt-nx-EO5" id="gdK-VV-zNb">
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Title" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="8b8-lM-Krq">
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Subtitle" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="hR1-Zb-BOk">
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" red="0.55686274509803924" green="0.55686274509803924" blue="0.57647058823529407" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
</prototypes>
<connections>
<outlet property="dataSource" destination="AW4-lY-JNk" id="VFi-3E-pTo"/>
<outlet property="delegate" destination="AW4-lY-JNk" id="7WG-Pm-8Xz"/>
</connections>
</tableView>
<navigationItem key="navigationItem" title="SNS Accounts" id="JVD-bl-r7d"/>
<connections>
<outlet property="accountTypeLabel" destination="czc-nd-es9" id="R9X-mn-dRl"/>
<outlet property="friendsLabel" destination="p2y-0T-hQs" id="4dM-fL-Jau"/>
<outlet property="nameLabel" destination="1eh-91-O2N" id="0wU-tN-YFO"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="cd4-SX-KET" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="3694" y="650"/>
</scene>
<!--HardcoreData Demos-->
<scene sceneID="0Be-vc-h1W">
<objects>
@@ -22,7 +103,7 @@
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="AXm-KE-45G" id="9te-Wx-hkf">
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Q3n-Df-v1t">
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Accounts" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Q3n-Df-v1t">
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
@@ -36,6 +117,9 @@
</label>
</subviews>
</tableViewCellContentView>
<connections>
<segue destination="AW4-lY-JNk" kind="show" id="u5L-No-Yuk"/>
</connections>
</tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="vpt-cT-gMo" detailTextLabel="ou9-TZ-8bf" style="IBUITableViewCellStyleSubtitle" id="fsb-zw-8Ii">
<autoresizingMask key="autoresizingMask"/>
@@ -65,7 +149,7 @@
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="ekW-PJ-mbo" id="CYq-mg-PVS">
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="UbU-Kd-yrY">
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Placemark" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="UbU-Kd-yrY">
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
@@ -79,6 +163,9 @@
</label>
</subviews>
</tableViewCellContentView>
<connections>
<segue destination="jPl-fH-NlD" kind="show" id="g1J-LG-mxf"/>
</connections>
</tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="ZfY-Aq-Ykq" detailTextLabel="QzD-9b-k1j" style="IBUITableViewCellStyleSubtitle" id="wyK-rk-3tI">
<autoresizingMask key="autoresizingMask"/>
@@ -261,7 +348,7 @@
<!--Colors-->
<scene sceneID="3lD-lX-hIc">
<objects>
<viewController automaticallyAdjustsScrollViewInsets="NO" hidesBottomBarWhenPushed="YES" id="YOI-b7-Nxn" sceneMemberID="viewController">
<viewController automaticallyAdjustsScrollViewInsets="NO" hidesBottomBarWhenPushed="YES" id="YOI-b7-Nxn" customClass="ObserversViewController" customModule="HardcoreDataDemo" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="IML-3o-caw"/>
<viewControllerLayoutGuide type="bottom" id="LNL-mj-D7l"/>
@@ -461,6 +548,43 @@
</objects>
<point key="canvasLocation" x="4404" y="1546"/>
</scene>
<!--Placemark-->
<scene sceneID="LRD-q1-hw1">
<objects>
<viewController id="jPl-fH-NlD" customClass="TransactionsDemoViewController" customModule="HardcoreDataDemo" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="1of-hY-qOU"/>
<viewControllerLayoutGuide type="bottom" id="RZg-hi-T8O"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="k4s-iL-Krh">
<rect key="frame" x="0.0" y="0.0" width="600" height="536"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<mapView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" misplaced="YES" mapType="standard" translatesAutoresizingMaskIntoConstraints="NO" id="V2U-0R-Ts0">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<connections>
<outlet property="delegate" destination="jPl-fH-NlD" id="Sjn-YC-haS"/>
</connections>
</mapView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="RZg-hi-T8O" firstAttribute="top" secondItem="V2U-0R-Ts0" secondAttribute="bottom" id="GcS-Jz-Wcm"/>
<constraint firstItem="V2U-0R-Ts0" firstAttribute="top" secondItem="k4s-iL-Krh" secondAttribute="top" id="S5Z-Da-V6J"/>
<constraint firstAttribute="trailing" secondItem="V2U-0R-Ts0" secondAttribute="trailing" id="YPc-RK-5ib"/>
<constraint firstItem="V2U-0R-Ts0" firstAttribute="leading" secondItem="k4s-iL-Krh" secondAttribute="leading" id="hk5-Rz-FyU"/>
</constraints>
</view>
<extendedEdge key="edgesForExtendedLayout" bottom="YES"/>
<navigationItem key="navigationItem" title="Placemark" id="s5y-WX-W5w"/>
<connections>
<outlet property="mapView" destination="V2U-0R-Ts0" id="X6a-Bp-psg"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="YnG-TD-zxQ" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="3694" y="2020"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="3ih-RN-P43">
<objects>

View File

@@ -7,7 +7,32 @@
<attribute name="saturation" optional="YES" attributeType="Float" defaultValueString="0.0" syncable="YES"/>
<userInfo/>
</entity>
<entity name="Place" representedClassName="HardcoreDataDemo.Place" syncable="YES">
<attribute name="latitude" optional="YES" attributeType="Double" defaultValueString="0.0" syncable="YES"/>
<attribute name="longitude" optional="YES" attributeType="Double" defaultValueString="0.0" syncable="YES"/>
<attribute name="subtitle" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="title" optional="YES" attributeType="String" syncable="YES"/>
</entity>
<entity name="UserAccount" representedClassName="HardcoreDataDemo.UserAccount" syncable="YES">
<attribute name="accountType" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="friends" optional="YES" attributeType="Integer 32" defaultValueString="0" syncable="YES"/>
<attribute name="name" optional="YES" attributeType="String" syncable="YES"/>
</entity>
<configuration name="ObservingDemo">
<memberEntity name="Palette"/>
</configuration>
<configuration name="SetupDemo_Jane">
<memberEntity name="UserAccount"/>
</configuration>
<configuration name="SetupDemo_John">
<memberEntity name="UserAccount"/>
</configuration>
<configuration name="TransactionsDemo">
<memberEntity name="Place"/>
</configuration>
<elements>
<element name="Palette" positionX="261" positionY="189" width="128" height="105"/>
<element name="UserAccount" positionX="261" positionY="216" width="128" height="90"/>
<element name="Place" positionX="261" positionY="225" width="128" height="105"/>
</elements>
</model>

View File

@@ -10,13 +10,22 @@ import UIKit
import HardcoreData
struct Shared {
private struct Static {
static let palettes = HardcoreData.observeSectionedList(
From(Palette),
SectionedBy("colorName"),
OrderBy(.Ascending("hue"))
)
static let palettes: ManagedObjectListController<Palette> = {
HardcoreData.addSQLiteStore(
"ColorsDemo.sqlite",
configuration: "ObservingDemo",
resetStoreOnMigrationFailure: true
)
return HardcoreData.observeSectionedList(
From(Palette),
SectionedBy("colorName"),
OrderBy(.Ascending("hue"))
)
}()
}
@@ -28,7 +37,7 @@ class ObjectListObserverDemoViewController: UITableViewController, ManagedObject
deinit {
Shared.palettes.removeObserver(self)
Static.palettes.removeObserver(self)
}
@@ -52,7 +61,7 @@ class ObjectListObserverDemoViewController: UITableViewController, ManagedObject
action: "addBarButtonItemTouched:"
)
Shared.palettes.addObserver(self)
Static.palettes.addObserver(self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
@@ -74,19 +83,19 @@ class ObjectListObserverDemoViewController: UITableViewController, ManagedObject
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return Shared.palettes.numberOfSections()
return Static.palettes.numberOfSections()
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Shared.palettes.numberOfObjectsInSection(section)
return Static.palettes.numberOfObjectsInSection(section)
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("PaletteTableViewCell") as! PaletteTableViewCell
let palette = Shared.palettes[indexPath]
let palette = Static.palettes[indexPath]
cell.colorView?.backgroundColor = palette.color
cell.label?.text = palette.colorText
@@ -102,7 +111,7 @@ class ObjectListObserverDemoViewController: UITableViewController, ManagedObject
self.performSegueWithIdentifier(
"ObjectObserverDemoViewController",
sender: Shared.palettes[indexPath]
sender: Static.palettes[indexPath]
)
}
@@ -111,7 +120,7 @@ class ObjectListObserverDemoViewController: UITableViewController, ManagedObject
switch editingStyle {
case .Delete:
let palette = Shared.palettes[indexPath]
let palette = Static.palettes[indexPath]
HardcoreData.beginAsynchronous{ (transaction) -> Void in
transaction.delete(palette)
@@ -125,7 +134,7 @@ class ObjectListObserverDemoViewController: UITableViewController, ManagedObject
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return Shared.palettes.sectionInfoAtIndex(section).name
return Static.palettes.sectionInfoAtIndex(section).name
}
@@ -158,7 +167,7 @@ class ObjectListObserverDemoViewController: UITableViewController, ManagedObject
if let cell = self.tableView.cellForRowAtIndexPath(indexPath) as? PaletteTableViewCell {
let palette = Shared.palettes[indexPath]
let palette = Static.palettes[indexPath]
cell.colorView?.backgroundColor = palette.color
cell.label?.text = palette.colorText
}
@@ -199,7 +208,7 @@ class ObjectListObserverDemoViewController: UITableViewController, ManagedObject
HardcoreData.beginAsynchronous { (transaction) -> Void in
let palette = transaction.create(Palette)
let palette = transaction.create(Into(Palette))
palette.setInitialValues()
transaction.commit()

View File

@@ -53,7 +53,7 @@ class ObjectObserverDemoViewController: UIViewController, ManagedObjectObserver
HardcoreData.beginSynchronous { (transaction) -> Void in
let palette = transaction.create(Palette)
let palette = transaction.create(Into(Palette))
palette.setInitialValues()
transaction.commit()

View File

@@ -0,0 +1,30 @@
//
// ObserversViewController.swift
// HardcoreDataDemo
//
// Created by John Rommel Estropia on 2015/05/24.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved.
//
import UIKit
// MARK: - ObserversViewController
class ObserversViewController: UIViewController {
// MARK: UIViewController
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
let alert = UIAlertController(
title: "Observers Demo",
message: "This demo shows how to observe changes to a list of objects. The top and bottom view controllers both observe a single shared \"ManagedObjectListController\" instance.\n\nTap on a row to see another demo that shows how to observe changes to a single object using a \"ManagedObjectController\".",
preferredStyle: .Alert
)
alert.addAction(UIAlertAction(title: "OK", style: .Cancel, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
}

View File

@@ -0,0 +1,15 @@
//
// FacebookAccount.swift
// HardcoreDataDemo
//
// Created by John Rommel Estropia on 2015/05/24.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved.
//
import Foundation
import CoreData
// MARK: - FacebookAccount
class FacebookAccount: TwitterAccount { }

View File

@@ -0,0 +1,190 @@
//
// StackSetupDemoViewController.swift
// HardcoreDataDemo
//
// Created by John Rommel Estropia on 2015/05/24.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved.
//
import UIKit
import HardcoreData
private struct Static {
static let johnConfiguration = "SetupDemo_John"
static let janeConfiguration = "SetupDemo_Jane"
static let facebookStack: DataStack = {
let dataStack = DataStack(modelName: "HardcoreDataDemo")
dataStack.addSQLiteStore(
"AccountsDemo_FB_John.sqlite",
configuration: johnConfiguration,
resetStoreOnMigrationFailure: true
)
dataStack.addSQLiteStore(
"AccountsDemo_FB_Jane.sqlite",
configuration: janeConfiguration,
resetStoreOnMigrationFailure: true
)
dataStack.beginSynchronous { (transaction) -> Void in
transaction.deleteAll(From<UserAccount>(johnConfiguration))
transaction.deleteAll(From<UserAccount>(janeConfiguration))
let account1 = transaction.create(Into<UserAccount>(johnConfiguration))
account1.accountType = "Facebook"
account1.name = "John Smith HCD"
account1.friends = 42
let account2 = transaction.create(Into<UserAccount>(janeConfiguration))
account2.accountType = "Facebook"
account2.name = "Jane Doe HCD"
account2.friends = 314
transaction.commit()
}
return dataStack
}()
static let twitterStack: DataStack = {
let dataStack = DataStack(modelName: "HardcoreDataDemo")
dataStack.addSQLiteStore(
"AccountsDemo_TW_John.sqlite",
configuration: johnConfiguration,
resetStoreOnMigrationFailure: true
)
dataStack.addSQLiteStore(
"AccountsDemo_TW_Jane.sqlite",
configuration: janeConfiguration,
resetStoreOnMigrationFailure: true
)
dataStack.beginSynchronous { (transaction) -> Void in
transaction.deleteAll(From<UserAccount>(johnConfiguration))
transaction.deleteAll(From<UserAccount>(janeConfiguration))
let account1 = transaction.create(Into<UserAccount>(johnConfiguration))
account1.accountType = "Twitter"
account1.name = "#johnsmith_hcd"
account1.friends = 7
let account2 = transaction.create(Into<UserAccount>(janeConfiguration))
account2.accountType = "Twitter"
account2.name = "#janedoe_hcd"
account2.friends = 100
transaction.commit()
}
return dataStack
}()
}
// MARK: - StackSetupDemoViewController
class StackSetupDemoViewController: UITableViewController {
let accounts = [
[
Static.facebookStack.fetchOne(From<UserAccount>(Static.johnConfiguration))!,
Static.facebookStack.fetchOne(From<UserAccount>(Static.janeConfiguration))!
],
[
Static.twitterStack.fetchOne(From<UserAccount>(Static.johnConfiguration))!,
Static.twitterStack.fetchOne(From<UserAccount>(Static.janeConfiguration))!
]
]
// MARK: UIViewController
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.tableView.reloadData()
let indexPath = NSIndexPath(forRow: 0, inSection: 0)
self.tableView.selectRowAtIndexPath(indexPath, animated: false, scrollPosition: .None)
self.updateDetailsWithAccount(self.accounts[indexPath.section][indexPath.row])
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
let alert = UIAlertController(
title: "Setup Demo",
message: "This demo shows how to initialize 2 DataStacks with 2 configurations each, for a total of 4 SQLite files, each with 1 instance of a \"UserAccount\" entity.",
preferredStyle: .Alert
)
alert.addAction(UIAlertAction(title: "OK", style: .Cancel, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
// MARK: UITableViewDataSource
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return self.accounts.count
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.accounts[section].count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("UITableViewCell") as! UITableViewCell
let account = self.accounts[indexPath.section][indexPath.row]
cell.textLabel?.text = account.name
cell.detailTextLabel?.text = "\(account.friends) friends"
return cell
}
// MARK: UITableViewDelegate
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let account = self.accounts[indexPath.section][indexPath.row]
self.updateDetailsWithAccount(account)
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
switch section {
case 0: return "Facebook Accounts"
case 1: return "Twitter Accounts"
default: return nil
}
}
// MARK: Private
@IBOutlet weak var accountTypeLabel: UILabel?
@IBOutlet weak var nameLabel: UILabel?
@IBOutlet weak var friendsLabel: UILabel?
func updateDetailsWithAccount(account: UserAccount) {
self.accountTypeLabel?.text = account.accountType
self.nameLabel?.text = account.name
self.friendsLabel?.text = "\(account.friends) friends"
}
}

View File

@@ -0,0 +1,15 @@
//
// TwitterAccount.swift
// HardcoreDataDemo
//
// Created by John Rommel Estropia on 2015/05/24.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved.
//
import Foundation
import CoreData
// MARK: - TwitterAccount
class TwitterAccount: UserAccount { }

View File

@@ -0,0 +1,20 @@
//
// UserAccount.swift
// HardcoreDataDemo
//
// Created by John Rommel Estropia on 2015/05/24.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved.
//
import Foundation
import CoreData
// MARK: - UserAccount
class UserAccount: NSManagedObject {
@NSManaged var accountType: String?
@NSManaged var name: String?
@NSManaged var friends: Int32
}

View File

@@ -0,0 +1,50 @@
//
// Place.swift
// HardcoreDataDemo
//
// Created by John Rommel Estropia on 2015/05/24.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved.
//
import Foundation
import CoreData
import MapKit
// MARK: - Place
class Place: NSManagedObject, MKAnnotation {
@NSManaged var latitude: Double
@NSManaged var longitude: Double
@NSManaged var title: String?
@NSManaged var subtitle: String?
func setInitialValues() {
self.latitude = Double(arc4random_uniform(180)) - 90
self.longitude = Double(arc4random_uniform(360)) - 180
self.title = "\(self.latitude), \(self.longitude)"
self.subtitle = nil
}
// MARK: MKAnnotation
var coordinate: CLLocationCoordinate2D {
get {
return CLLocationCoordinate2DMake(
self.latitude,
self.longitude
)
}
set {
self.latitude = newValue.latitude
self.longitude = newValue.longitude
self.title = "\(self.latitude), \(self.longitude)"
self.subtitle = nil
}
}
}

View File

@@ -0,0 +1,208 @@
//
// TransactionsDemoViewController.swift
// HardcoreDataDemo
//
// Created by John Rommel Estropia on 2015/05/24.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved.
//
import UIKit
import CoreLocation
import MapKit
import AddressBookUI
import HardcoreData
import GCDKit
private struct Static {
static let placeController: ManagedObjectController<Place> = {
HardcoreData.addSQLiteStore(
"PlaceDemo.sqlite",
configuration: "TransactionsDemo",
resetStoreOnMigrationFailure: true
)
var place = HardcoreData.fetchOne(From(Place))
if place == nil {
HardcoreData.beginSynchronous { (transaction) -> Void in
let place = transaction.create(Into(Place))
place.setInitialValues()
transaction.commit()
}
place = HardcoreData.fetchOne(From(Place))
}
return HardcoreData.observeObject(place!)
}()
}
// MARK: - TransactionsDemoViewController
class TransactionsDemoViewController: UIViewController, MKMapViewDelegate, ManagedObjectObserver {
// MARK: NSObject
deinit {
Static.placeController.removeObserver(self)
}
// MARK: UIViewController
override func viewDidLoad() {
super.viewDidLoad()
let longPressGesture = UILongPressGestureRecognizer(target: self, action: "longPressGestureRecognized:")
self.mapView?.addGestureRecognizer(longPressGesture)
Static.placeController.addObserver(self)
self.navigationItem.rightBarButtonItem = UIBarButtonItem(
barButtonSystemItem: .Refresh,
target: self,
action: "refreshButtonTapped:"
)
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
let alert = UIAlertController(
title: "Observers Demo",
message: "This demo shows how to use the 3 types of transactions to save updates: synchronous, asynchronous, and detached. Long-tap on the map to change the pin location.",
preferredStyle: .Alert
)
alert.addAction(UIAlertAction(title: "OK", style: .Cancel, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
if let mapView = self.mapView, let place = Static.placeController.object {
mapView.addAnnotation(place)
mapView.setCenterCoordinate(place.coordinate, animated: false)
mapView.selectAnnotation(place, animated: false)
}
}
// MARK: MKMapViewDelegate
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
let identifier = "MKAnnotationView"
var annotationView: MKPinAnnotationView! = mapView.dequeueReusableAnnotationViewWithIdentifier(identifier) as? MKPinAnnotationView
if annotationView == nil {
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
annotationView.enabled = true
annotationView.canShowCallout = true
annotationView.animatesDrop = true
}
else {
annotationView.annotation = annotation
}
return annotationView
}
// MARK: ManagedObjectObserver
func managedObjectWillUpdate(objectController: ManagedObjectController<Place>, object: Place) {
// none
}
func managedObjectWasUpdated(objectController: ManagedObjectController<Place>, object: Place, changedPersistentKeys: Set<KeyPath>) {
if let mapView = self.mapView {
mapView.removeAnnotations(mapView.annotations ?? [])
mapView.addAnnotation(object)
mapView.setCenterCoordinate(object.coordinate, animated: true)
mapView.selectAnnotation(object, animated: true)
if changedPersistentKeys.contains("latitude") || changedPersistentKeys.contains("longitude") {
self.geocodePlace(object)
}
}
}
func managedObjectWasDeleted(objectController: ManagedObjectController<Place>, object: Place) {
// none
}
// MARK: Private
var geocoder: CLGeocoder?
@IBOutlet weak var mapView: MKMapView?
@IBAction dynamic func longPressGestureRecognized(sender: AnyObject?) {
if let mapView = self.mapView, let gesture = sender as? UILongPressGestureRecognizer where gesture.state == .Began {
HardcoreData.beginAsynchronous { (transaction) -> Void in
let place = transaction.fetch(Static.placeController.object)
place?.coordinate = mapView.convertPoint(
gesture.locationInView(mapView),
toCoordinateFromView: mapView
)
transaction.commit { (_) -> Void in }
}
}
}
@IBAction dynamic func refreshButtonTapped(sender: AnyObject?) {
HardcoreData.beginSynchronous { (transaction) -> Void in
let place = transaction.fetch(Static.placeController.object)
place?.setInitialValues()
transaction.commit()
}
}
func geocodePlace(place: Place) {
let transaction = HardcoreData.beginDetached()
self.geocoder?.cancelGeocode()
var geocoder = CLGeocoder()
self.geocoder = geocoder
geocoder.reverseGeocodeLocation(
CLLocation(latitude: place.latitude, longitude: place.longitude),
completionHandler: { [weak self] (placemarks, error) -> Void in
if let strongSelf = self, let placemark = (placemarks as? [CLPlacemark])?.first {
let place = transaction.fetch(Static.placeController.object)
place?.title = placemark.name
place?.subtitle = ABCreateStringWithAddressDictionary(placemark.addressDictionary, true)
transaction.commit { (_) -> Void in }
}
self?.geocoder = nil
}
)
}
}

View File

@@ -1,36 +0,0 @@
//
// HardcoreDataDemoTests.swift
// HardcoreDataDemoTests
//
// Created by John Rommel Estropia on 2015/05/02.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved.
//
import UIKit
import XCTest
class HardcoreDataDemoTests: XCTestCase {
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testExample() {
// This is an example of a functional test case.
XCTAssert(true, "Pass")
}
func testPerformanceExample() {
// This is an example of a performance test case.
self.measureBlock() {
// Put the code you want to measure the time of here.
}
}
}

View File

@@ -1,24 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>com.johnestropia.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@@ -70,31 +70,31 @@ class HardcoreDataTests: XCTestCase {
let createExpectation = self.expectationWithDescription("Entity creation")
HardcoreData.beginAsynchronous { (transaction) -> Void in
let obj1 = transaction.create(TestEntity1)
let obj1 = transaction.create(Into(TestEntity1))
obj1.testEntityID = 1
obj1.testString = "lololol"
obj1.testNumber = 42
obj1.testDate = NSDate()
let count = transaction.queryValue(
From(TestEntity1),
From<TestEntity1>(),
Select<Int>(.Count("testNumber"))
)
XCTAssertTrue(count == 0, "count == 0 (actual: \(count))") // counts only objects in store
let obj2 = transaction.create(TestEntity2)
let obj2 = transaction.create(Into<TestEntity2>())
obj2.testEntityID = 2
obj2.testString = "hahaha"
obj2.testNumber = 100
obj2.testDate = NSDate()
let obj3 = transaction.create(TestEntity2)
let obj3 = transaction.create(Into<TestEntity2>("Config2"))
obj3.testEntityID = 3
obj3.testString = "hahaha"
obj3.testNumber = 90
obj3.testDate = NSDate()
let obj4 = transaction.create(TestEntity2)
let obj4 = transaction.create(Into(TestEntity2.self, "Config2"))
obj4.testEntityID = 5
obj4.testString = "hohoho"
obj4.testNumber = 80
@@ -103,14 +103,14 @@ class HardcoreDataTests: XCTestCase {
transaction.beginSynchronous { (transaction) -> Void in
let obj4 = transaction.create(TestEntity2)
let obj4 = transaction.create(Into<TestEntity2>())
obj4.testEntityID = 4
obj4.testString = "hehehehe"
obj4.testNumber = 80
obj4.testDate = NSDate()
let objs4test = transaction.fetchOne(
From(TestEntity2),
From<TestEntity2>("Config2"),
Where("testEntityID", isEqualTo: 4),
Tweak { (fetchRequest) -> Void in
@@ -239,7 +239,7 @@ class HardcoreDataTests: XCTestCase {
let detachedExpectation = self.expectationWithDescription("Query creation")
let obj5 = detachedTransaction.create(TestEntity1)
let obj5 = detachedTransaction.create(Into<TestEntity1>("Config1"))
obj5.testEntityID = 5
obj5.testString = "hihihi"
obj5.testNumber = 70
@@ -259,7 +259,7 @@ class HardcoreDataTests: XCTestCase {
)
XCTAssertTrue(count == 1, "count == 1 (actual: \(count))")
let obj6 = detachedTransaction.create(TestEntity1)
let obj6 = detachedTransaction.create(Into<TestEntity1>())
obj6.testEntityID = 6
obj6.testString = "huehuehue"
obj6.testNumber = 130

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="7517.1" systemVersion="14C109" minimumToolsVersion="Xcode 4.3" macOSVersion="Automatic" iOSVersion="Automatic">
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="7701" systemVersion="14D136" minimumToolsVersion="Xcode 4.3" macOSVersion="Automatic" iOSVersion="Automatic">
<entity name="TestEntity1AAA" representedClassName="HardcoreDataTests.TestEntity1" syncable="YES">
<attribute name="testDate" optional="YES" attributeType="Date" syncable="YES"/>
<attribute name="testEntityID" attributeType="Integer 64" syncable="YES"/>

View File

@@ -4,6 +4,9 @@
[![License](https://img.shields.io/cocoapods/l/HardcoreData.svg?style=flat)](http://cocoadocs.org/docsets/HardcoreData)
Simple, elegant, and smart Core Data programming with Swift
(Swift only, iOS 8+ only)
## Features
- Supports multiple persistent stores per *data stack*, just the way .xcdatamodeld files are supposed to. HardcoreData will also manage one *data stack* by default, but you can create and manage as many as you need.
@@ -18,7 +21,7 @@ Simple, elegant, and smart Core Data programming with Swift
Quick-setup:
```swift
HardcoreData.defaultStack.addSQLiteStore("MyStore.sqlite")
HardcoreData.addSQLiteStore("MyStore.sqlite")
```
Simple transactions:
@@ -61,8 +64,19 @@ let count = HardcoreData.queryValue(
```
## Quick jumps
## Architecture
- [Architecture](#architecture)
- [Setting up](#setup)
- [Saving and processing transactions](#transactions)
- [Fetching and querying](#fetch_query)
- [Logging and error handling](#logging)
- [Observing changes and notifications](#observing)
- [Importing data](#importing)
## <a name="architecture">Architecture</a>
For maximum safety and performance, HardcoreData will enforce coding patterns and practices it was designed for. (Don't worry, it's not as scary as it sounds.) But it is advisable to understand the "magic" of HardcoreData before you use it in your apps.
If you are already familiar with the inner workings of CoreData, here is a mapping of `HardcoreData` abstractions:
@@ -73,7 +87,7 @@ If you are already familiar with the inner workings of CoreData, here is a mappi
| `NSPersistentStore`<br />("Configuration"s in the .xcdatamodeld file) | `DataStack` configuration<br />(multiple sqlite / in-memory stores per stack) |
| `NSManagedObjectContext` | `BaseDataTransaction` subclasses<br />(`SynchronousDataTransaction`, `AsynchronousDataTransaction`, `DetachedDataTransaction`) |
RestKit and MagicalRecord set up their `NSManagedObjectContext`s this way:
Popular libraries [RestKit](https://github.com/RestKit/RestKit) and [MagicalRecord](https://github.com/magicalpanda/MagicalRecord) set up their `NSManagedObjectContext`s this way:
<img src="https://cloud.githubusercontent.com/assets/3029684/6734049/40579660-ce99-11e4-9d38-829877386afb.png" alt="nested contexts" height=271 />
@@ -85,7 +99,7 @@ This allows for a butter-smooth main thread, while still taking advantage of saf
## Setting up
## <a name="setup">Setting up</a>
The simplest way to initialize HardcoreData is to add a default store to the default stack:
```swift
HardcoreData.defaultStack.addSQLiteStore()
@@ -154,23 +168,27 @@ Check out the *HardcoreData.swift* and *DataStack.swift files* if you want to ex
## Saving and processing transactions
## <a name="transactions">Saving and processing transactions</a>
(implemented; README pending)
## Fetching and querying
## <a name="fetch_query">Fetching and querying</a>
(implemented; README pending)
## Logging and error handling
## <a name="logging">Logging and error handling</a>
(implemented; README pending)
## Observing changes and notifications
## <a name="observing">Observing changes and notifications</a>
(implemented; README pending)
## Importing data
## <a name="importing">Importing data</a>
(currently implementing)