ManagedObjectListController demo

This commit is contained in:
John Rommel Estropia
2015-05-06 01:06:22 +09:00
parent d043d64cb1
commit 8d42a4a885
24 changed files with 1566 additions and 453 deletions

View File

@@ -16,7 +16,6 @@
B52B68BC1AAB46BD00CE7F48 /* ManagedObjectListController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52B68BB1AAB46BD00CE7F48 /* ManagedObjectListController.swift */; };
B52B68BE1AAB484C00CE7F48 /* DataStack+Observing.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52B68BD1AAB484C00CE7F48 /* DataStack+Observing.swift */; };
B52B68C01AAB9DAD00CE7F48 /* ManagedObjectListObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52B68BF1AAB9DAD00CE7F48 /* ManagedObjectListObserver.swift */; };
B52B68C21AAF554600CE7F48 /* ManagedObjectListSectionInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52B68C11AAF554600CE7F48 /* ManagedObjectListSectionInfo.swift */; };
B5398AA21AA8938D00B66388 /* DetachedDataTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5398AA11AA8938D00B66388 /* DetachedDataTransaction.swift */; };
B54A9F071AA7654400AFEC05 /* DataStack+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54A9F061AA7654400AFEC05 /* DataStack+Querying.swift */; };
B54D53071AB3538500D55BA8 /* QueryClause.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54D53061AB3538500D55BA8 /* QueryClause.swift */; };
@@ -26,6 +25,7 @@
B595CAC41A9A11C1009A397F /* NSManagedObjectContext+Setup.swift in Sources */ = {isa = PBXBuildFile; fileRef = B595CAC31A9A11C1009A397F /* NSManagedObjectContext+Setup.swift */; };
B595CAC61A9A1260009A397F /* NSManagedObjectContext+Transaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B595CAC51A9A1260009A397F /* NSManagedObjectContext+Transaction.swift */; };
B595CAC81A9A161B009A397F /* WeakObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B595CAC71A9A161B009A397F /* WeakObject.swift */; };
B5BC26DD1AF78A8800276889 /* ManagedObjectObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BC26DC1AF78A8800276889 /* ManagedObjectObserver.swift */; };
B5CFD36E1A0775F000B7885F /* SaveResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CFD36D1A0775F000B7885F /* SaveResult.swift */; };
B5CFF23E19FD1D1C00D6DFC4 /* NSManagedObjectContext+HardcoreData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CFF23D19FD1D1C00D6DFC4 /* NSManagedObjectContext+HardcoreData.swift */; };
B5CFF24019FD383100D6DFC4 /* BaseDataTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CFF23F19FD383100D6DFC4 /* BaseDataTransaction.swift */; };
@@ -94,7 +94,6 @@
B52B68BB1AAB46BD00CE7F48 /* ManagedObjectListController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedObjectListController.swift; sourceTree = "<group>"; };
B52B68BD1AAB484C00CE7F48 /* DataStack+Observing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DataStack+Observing.swift"; sourceTree = "<group>"; };
B52B68BF1AAB9DAD00CE7F48 /* ManagedObjectListObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedObjectListObserver.swift; sourceTree = "<group>"; };
B52B68C11AAF554600CE7F48 /* ManagedObjectListSectionInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedObjectListSectionInfo.swift; sourceTree = "<group>"; };
B5398AA11AA8938D00B66388 /* DetachedDataTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetachedDataTransaction.swift; sourceTree = "<group>"; };
B54A9F061AA7654400AFEC05 /* DataStack+Querying.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DataStack+Querying.swift"; sourceTree = "<group>"; };
B54D53061AB3538500D55BA8 /* QueryClause.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QueryClause.swift; sourceTree = "<group>"; };
@@ -104,6 +103,7 @@
B595CAC31A9A11C1009A397F /* NSManagedObjectContext+Setup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+Setup.swift"; sourceTree = "<group>"; };
B595CAC51A9A1260009A397F /* NSManagedObjectContext+Transaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+Transaction.swift"; sourceTree = "<group>"; };
B595CAC71A9A161B009A397F /* WeakObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WeakObject.swift; sourceTree = "<group>"; };
B5BC26DC1AF78A8800276889 /* ManagedObjectObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedObjectObserver.swift; sourceTree = "<group>"; };
B5CFD36D1A0775F000B7885F /* SaveResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SaveResult.swift; sourceTree = "<group>"; };
B5CFF23D19FD1D1C00D6DFC4 /* NSManagedObjectContext+HardcoreData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+HardcoreData.swift"; sourceTree = "<group>"; };
B5CFF23F19FD383100D6DFC4 /* BaseDataTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseDataTransaction.swift; sourceTree = "<group>"; };
@@ -238,10 +238,10 @@
B52B68B61AAB45FB00CE7F48 /* Observing */ = {
isa = PBXGroup;
children = (
B52B68BF1AAB9DAD00CE7F48 /* ManagedObjectListObserver.swift */,
B52B68BB1AAB46BD00CE7F48 /* ManagedObjectListController.swift */,
B52B68BF1AAB9DAD00CE7F48 /* ManagedObjectListObserver.swift */,
B52B68B91AAB46AD00CE7F48 /* ManagedObjectController.swift */,
B52B68C11AAF554600CE7F48 /* ManagedObjectListSectionInfo.swift */,
B5BC26DC1AF78A8800276889 /* ManagedObjectObserver.swift */,
B52B68BD1AAB484C00CE7F48 /* DataStack+Observing.swift */,
);
name = Observing;
@@ -527,6 +527,7 @@
B5D8081A1A3495BD00A44484 /* AssociatedObjects.swift in Sources */,
B595CAC41A9A11C1009A397F /* NSManagedObjectContext+Setup.swift in Sources */,
B5D19BFF1AA14351001D1A99 /* SynchronousDataTransaction.swift in Sources */,
B5BC26DD1AF78A8800276889 /* ManagedObjectObserver.swift in Sources */,
B5D10DC21AB4590F004B4EEA /* Select.swift in Sources */,
B5E209E01A0726460089C9D4 /* NSManagedObject+Transaction.swift in Sources */,
B582DF821A98B0E7003F09C6 /* HardcoreData+Querying.swift in Sources */,
@@ -540,7 +541,6 @@
B5D399F119FC818E000E91BB /* DataStack.swift in Sources */,
B5D808161A34947300A44484 /* NotificationObserver.swift in Sources */,
B52B68BE1AAB484C00CE7F48 /* DataStack+Observing.swift in Sources */,
B52B68C21AAF554600CE7F48 /* ManagedObjectListSectionInfo.swift in Sources */,
B57078B01A50392D007E33F2 /* FetchClause.swift in Sources */,
B5F409ED1A8B200700A228EA /* NSManagedObjectContext+Querying.swift in Sources */,
B5D10DC01AB42C85004B4EEA /* GroupBy.swift in Sources */,

View File

@@ -77,7 +77,7 @@ public /*abstract*/ class BaseDataTransaction {
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to delete an entity of type \(typeName(object)) outside its designated queue.")
object.deleteFromContext()
object.inContext(self.context)?.deleteFromContext()
}
// MARK: Saving changes

View File

@@ -34,15 +34,25 @@ public extension DataStack {
// MARK: Public
public func observeObjectList<T: NSManagedObject>(from: From<T>, _ queryClauses: FetchClause...) -> ManagedObjectListController<T> {
public func observeObject<T: NSManagedObject>(object: T) -> ManagedObjectController<T> {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a \(typeName(self)) outside the main queue.")
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to observe objects from \(typeName(self)) outside the main queue.")
return ManagedObjectController(
dataStack: self,
object: object
)
}
public func observeObjectList<T: NSManagedObject>(from: From<T>, _ groupBy: GroupBy? = nil, _ queryClauses: FetchClause...) -> ManagedObjectListController<T> {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to observe objects from \(typeName(self)) outside the main queue.")
// TODO: sectionNameKeyPath and cacheResults
return ManagedObjectListController(
dataStack: self,
entity: T.self,
sectionNameKeyPath: nil,
sectionNameKeyPath: groupBy?.keyPaths.first,
cacheResults: false,
queryClauses: queryClauses
)

View File

@@ -25,3 +25,279 @@
import Foundation
import CoreData
import GCDKit
private let ManagedObjectListControllerWillChangeListNotification = "ManagedObjectListControllerWillChangeListNotification"
private let ManagedObjectListControllerDidChangeListNotification = "ManagedObjectListControllerDidChangeListNotification"
private let ManagedObjectListControllerDidDeleteObjectNotification = "ManagedObjectListControllerDidDeleteObjectNotification"
private let ManagedObjectListControllerDidUpdateObjectNotification = "ManagedObjectListControllerDidUpdateObjectNotification"
private let UserInfoKeyObject = "UserInfoKeyObject"
private struct NotificationKey {
static var willChangeList: Void?
static var didChangeList: Void?
static var didDeleteObject: Void?
static var didUpdateObject: Void?
}
// MARK: - ManagedObjectController
public final class ManagedObjectController<T: NSManagedObject>: FetchedResultsControllerHandler {
// MARK: Public
public var object: T? {
return self.fetchedResultsController.fetchedObjects?.first as? T
}
public var isObjectDeleted: Bool {
return self.object?.managedObjectContext == nil
}
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.")
self.registerChangeNotification(
&NotificationKey.willChangeList,
name: ManagedObjectListControllerWillChangeListNotification,
toObserver: observer,
callback: { [weak observer, weak self] (objectController) -> Void in
if let observer = observer, let object = self?.object {
observer.managedObjectWillUpdate(objectController, object: object)
}
}
)
self.registerObjectNotification(
&NotificationKey.didDeleteObject,
name: ManagedObjectListControllerDidDeleteObjectNotification,
toObserver: observer,
callback: { [weak observer] (objectController, object) -> Void in
if let observer = observer {
observer.managedObjectWasDeleted(objectController, object: object)
}
}
)
self.registerObjectNotification(
&NotificationKey.didUpdateObject,
name: ManagedObjectListControllerDidUpdateObjectNotification,
toObserver: observer,
callback: { [weak observer] (objectController, object) -> Void in
if let observer = observer {
observer.managedObjectWasUpdated(objectController, object: object)
}
}
)
}
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.")
let nilValue: AnyObject? = nil
setAssociatedRetainedObject(nilValue, forKey: &NotificationKey.willChangeList, inObject: observer)
setAssociatedRetainedObject(nilValue, forKey: &NotificationKey.didDeleteObject, inObject: observer)
setAssociatedRetainedObject(nilValue, forKey: &NotificationKey.didUpdateObject, inObject: observer)
}
// MARK: FetchedResultsControllerHandler
private func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
switch type {
case .Delete:
NSNotificationCenter.defaultCenter().postNotificationName(
ManagedObjectListControllerDidDeleteObjectNotification,
object: self,
userInfo: [UserInfoKeyObject: anObject]
)
case .Update:
NSNotificationCenter.defaultCenter().postNotificationName(
ManagedObjectListControllerDidUpdateObjectNotification,
object: self,
userInfo: [UserInfoKeyObject: anObject]
)
default:
break
}
}
private func controllerWillChangeContent(controller: NSFetchedResultsController) {
NSNotificationCenter.defaultCenter().postNotificationName(
ManagedObjectListControllerWillChangeListNotification,
object: self
)
}
private func controllerDidChangeContent(controller: NSFetchedResultsController) {
NSNotificationCenter.defaultCenter().postNotificationName(
ManagedObjectListControllerDidChangeListNotification,
object: self
)
}
// MARK: Internal
internal init(dataStack: DataStack, object: T) {
let context = dataStack.mainContext
let fetchRequest = NSFetchRequest()
fetchRequest.entity = context.entityDescriptionForEntityClass(T.self)
fetchRequest.fetchLimit = 1
fetchRequest.resultType = .ManagedObjectResultType
Where("SELF", isEqualTo: object).applyToFetchRequest(fetchRequest)
SortedBy(.Ascending("objectID")).applyToFetchRequest(fetchRequest)
let fetchedResultsController = NSFetchedResultsController(
fetchRequest: fetchRequest,
managedObjectContext: context,
sectionNameKeyPath: nil,
cacheName: nil
)
let fetchedResultsControllerDelegate = FetchedResultsControllerDelegate()
self.fetchedResultsController = fetchedResultsController
self.fetchedResultsControllerDelegate = fetchedResultsControllerDelegate
self.parentStack = dataStack
fetchedResultsControllerDelegate.handler = self
fetchedResultsControllerDelegate.fetchedResultsController = fetchedResultsController
var error: NSError?
if !fetchedResultsController.performFetch(&error) {
HardcoreData.handleError(
error ?? NSError(hardcoreDataErrorCode: .UnknownError),
"Failed to perform fetch on <\(NSFetchedResultsController.self)>.")
}
}
// MARK: Private
private let fetchedResultsController: NSFetchedResultsController
private let fetchedResultsControllerDelegate: FetchedResultsControllerDelegate
private weak var parentStack: DataStack?
private func registerChangeNotification(notificationKey: UnsafePointer<Void>, name: String, toObserver observer: AnyObject, callback: (objectController: ManagedObjectController<T>) -> Void) {
setAssociatedRetainedObject(
NotificationObserver(
notificationName: name,
object: self,
closure: { [weak self] (note) -> Void in
if let strongSelf = self {
callback(objectController: strongSelf)
}
}
),
forKey: notificationKey,
inObject: observer
)
}
private func registerObjectNotification(notificationKey: UnsafePointer<Void>, name: String, toObserver observer: AnyObject, callback: (objectController: ManagedObjectController<T>, object: T) -> Void) {
setAssociatedRetainedObject(
NotificationObserver(
notificationName: name,
object: self,
closure: { [weak self] (note) -> Void in
if let strongSelf = self,
let userInfo = note.userInfo,
let object = userInfo[UserInfoKeyObject] as? T {
callback(
objectController: strongSelf,
object: object
)
}
}
),
forKey: notificationKey,
inObject: observer
)
}
}
// MARK: - FetchedResultsControllerHandler
private protocol FetchedResultsControllerHandler: class {
func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?)
func controllerWillChangeContent(controller: NSFetchedResultsController)
func controllerDidChangeContent(controller: NSFetchedResultsController)
}
// MARK: - FetchedResultsControllerDelegate
private final class FetchedResultsControllerDelegate: NSFetchedResultsControllerDelegate {
// MARK: NSFetchedResultsControllerDelegate
@objc func controllerWillChangeContent(controller: NSFetchedResultsController) {
self.handler?.controllerWillChangeContent(controller)
}
@objc func controllerDidChangeContent(controller: NSFetchedResultsController) {
self.handler?.controllerDidChangeContent(controller)
}
@objc func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
self.handler?.controller(controller, didChangeObject: anObject, atIndexPath: indexPath, forChangeType: type, newIndexPath: newIndexPath)
}
// MARK: Private
weak var handler: FetchedResultsControllerHandler?
weak var fetchedResultsController: NSFetchedResultsController? {
didSet {
oldValue?.delegate = nil
self.fetchedResultsController?.delegate = self
}
}
deinit {
self.fetchedResultsController?.delegate = nil
}
}

View File

@@ -30,25 +30,37 @@ import GCDKit
private let ManagedObjectListControllerWillChangeListNotification = "ManagedObjectListControllerWillChangeListNotification"
private let ManagedObjectListControllerDidChangeListNotification = "ManagedObjectListControllerDidChangeListNotification"
private let ManagedObjectListControllerDidInsertObjectNotification = "ManagedObjectListControllerDidInsertObjectNotification"
private let ManagedObjectListControllerDidDeleteObjectNotification = "ManagedObjectListControllerDidDeleteObjectNotification"
private let ManagedObjectListControllerDidUpdateObjectNotification = "ManagedObjectListControllerDidUpdateObjectNotification"
private let ManagedObjectListControllerDidMoveObjectNotification = "ManagedObjectListControllerDidMoveObjectNotification"
private let ManagedObjectListControllerDidInsertSectionNotification = "ManagedObjectListControllerDidInsertSectionNotification"
private let ManagedObjectListControllerDidDeleteSectionNotification = "ManagedObjectListControllerDidDeleteSectionNotification"
private let UserInfoKeyObject = "UserInfoKeyObject"
private let UserInfoKeyIndexPath = "UserInfoKeyIndexPath"
private let UserInfoKeyNewIndexPath = "UserInfoKeyNewIndexPath"
private let UserInfoKeySectionInfo = "UserInfoKeySectionInfo"
private let UserInfoKeySectionIndex = "UserInfoKeySectionIndex"
private struct NotificationKey {
static var willChangeList: Void?
static var didChangeList: Void?
static var didInsertObject: Void?
static var didDeleteObject: Void?
static var didUpdateObject: Void?
static var didMoveObject: Void?
static var didInsertSection: Void?
static var didDeleteSection: Void?
}
// MARK: - ManagedObjectListController
public final class ManagedObjectListController<T: NSManagedObject>: FetchedResultsControllerHandler {
@@ -65,153 +77,289 @@ public final class ManagedObjectListController<T: NSManagedObject>: FetchedResul
return self.fetchedResultsController.sections?.count ?? 0
}
public func numberOfItemsInSection(section: Int) -> Int {
public func numberOfObjectsInSection(section: Int) -> Int {
return (self.fetchedResultsController.sections?[section] as? NSFetchedResultsSectionInfo)?.numberOfObjects ?? 0
}
public func addObserver<U: ManagedObjectListObserver where U.EntityType == T>(observer: U) {
public func sectionInfoAtIndex(section: Int) -> NSFetchedResultsSectionInfo {
return self.fetchedResultsController.sections![section] as! NSFetchedResultsSectionInfo
}
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.")
setAssociatedRetainedObject(
NotificationObserver(
notificationName: ManagedObjectListControllerWillChangeListNotification,
object: self,
closure: { [weak self, weak observer] (note) -> Void in
self.registerChangeNotification(
&NotificationKey.willChangeList,
name: ManagedObjectListControllerWillChangeListNotification,
toObserver: observer,
callback: { [weak observer] (listController) -> Void in
if let observer = observer {
if let strongSelf = self, let strongObserver = observer {
strongObserver.managedObjectListWillChange(strongSelf)
}
observer.managedObjectListWillChange(listController)
}
),
forKey: &NotificationKey.willChangeList,
inObject: observer
}
)
setAssociatedRetainedObject(
NotificationObserver(
notificationName: ManagedObjectListControllerDidChangeListNotification,
object: self,
closure: { [weak self, weak observer] (note) -> Void in
self.registerChangeNotification(
&NotificationKey.didChangeList,
name: ManagedObjectListControllerDidChangeListNotification,
toObserver: observer,
callback: { [weak observer] (listController) -> Void in
if let observer = observer {
if let strongSelf = self, let strongObserver = observer {
strongObserver.managedObjectListDidChange(strongSelf)
}
observer.managedObjectListDidChange(listController)
}
),
forKey: &NotificationKey.willChangeList,
inObject: observer
)
setAssociatedRetainedObject(
NotificationObserver(
notificationName: ManagedObjectListControllerDidInsertObjectNotification,
object: self,
closure: { [weak self, weak observer] (note) -> Void in
if let strongSelf = self,
let strongObserver = observer,
let userInfo = note.userInfo,
let object = userInfo[UserInfoKeyObject] as? T,
let newIndexPath = userInfo[UserInfoKeyNewIndexPath] as? NSIndexPath {
strongObserver.managedObjectList(
strongSelf,
didInsertObject: object,
toIndexPath: newIndexPath
)
}
}
),
forKey: &NotificationKey.didInsertObject,
inObject: observer
)
setAssociatedRetainedObject(
NotificationObserver(
notificationName: ManagedObjectListControllerDidDeleteObjectNotification,
object: self,
closure: { [weak self, weak observer] (note) -> Void in
if let strongSelf = self,
let strongObserver = observer,
let userInfo = note.userInfo,
let object = userInfo[UserInfoKeyObject] as? T,
let indexPath = userInfo[UserInfoKeyIndexPath] as? NSIndexPath {
strongObserver.managedObjectList(
strongSelf,
didDeleteObject: object,
fromIndexPath: indexPath
)
}
}
),
forKey: &NotificationKey.didDeleteObject,
inObject: observer
)
setAssociatedRetainedObject(
NotificationObserver(
notificationName: ManagedObjectListControllerDidUpdateObjectNotification,
object: self,
closure: { [weak self, weak observer] (note) -> Void in
if let strongSelf = self,
let strongObserver = observer,
let userInfo = note.userInfo,
let object = userInfo[UserInfoKeyObject] as? T,
let indexPath = userInfo[UserInfoKeyIndexPath] as? NSIndexPath {
strongObserver.managedObjectList(
strongSelf,
didUpdateObject: object,
atIndexPath: indexPath
)
}
}
),
forKey: &NotificationKey.didUpdateObject,
inObject: observer
)
setAssociatedRetainedObject(
NotificationObserver(
notificationName: ManagedObjectListControllerDidMoveObjectNotification,
object: self,
closure: { [weak self, weak observer] (note) -> Void in
if let strongSelf = self,
let strongObserver = observer,
let userInfo = note.userInfo,
let object = userInfo[UserInfoKeyObject] as? T,
let indexPath = userInfo[UserInfoKeyIndexPath] as? NSIndexPath ,
let newIndexPath = userInfo[UserInfoKeyNewIndexPath] as? NSIndexPath {
strongObserver.managedObjectList(
strongSelf,
didMoveObject: object,
fromIndexPath: indexPath,
toIndexPath: newIndexPath
)
}
}
),
forKey: &NotificationKey.didMoveObject,
inObject: observer
}
)
}
public func removeObserver<U: ManagedObjectListObserver where U.EntityType == T>(observer: U) {
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.")
self.registerChangeNotification(
&NotificationKey.willChangeList,
name: ManagedObjectListControllerWillChangeListNotification,
toObserver: observer,
callback: { [weak observer] (listController) -> Void in
if let observer = observer {
observer.managedObjectListWillChange(listController)
}
}
)
self.registerChangeNotification(
&NotificationKey.didChangeList,
name: ManagedObjectListControllerDidChangeListNotification,
toObserver: observer,
callback: { [weak observer] (listController) -> Void in
if let observer = observer {
observer.managedObjectListDidChange(listController)
}
}
)
self.registerObjectNotification(
&NotificationKey.didInsertObject,
name: ManagedObjectListControllerDidInsertObjectNotification,
toObserver: observer,
callback: { [weak observer] (listController, object, indexPath, newIndexPath) -> Void in
if let observer = observer {
observer.managedObjectList(
listController,
didInsertObject: object,
toIndexPath: newIndexPath!
)
}
}
)
self.registerObjectNotification(
&NotificationKey.didDeleteObject,
name: ManagedObjectListControllerDidDeleteObjectNotification,
toObserver: observer,
callback: { [weak observer] (listController, object, indexPath, newIndexPath) -> Void in
if let observer = observer {
observer.managedObjectList(
listController,
didDeleteObject: object,
fromIndexPath: indexPath!
)
}
}
)
self.registerObjectNotification(
&NotificationKey.didUpdateObject,
name: ManagedObjectListControllerDidUpdateObjectNotification,
toObserver: observer,
callback: { [weak observer] (listController, object, indexPath, newIndexPath) -> Void in
if let observer = observer {
observer.managedObjectList(
listController,
didUpdateObject: object,
atIndexPath: indexPath!
)
}
}
)
self.registerObjectNotification(
&NotificationKey.didMoveObject,
name: ManagedObjectListControllerDidMoveObjectNotification,
toObserver: observer,
callback: { [weak observer] (listController, object, indexPath, newIndexPath) -> Void in
if let observer = observer {
observer.managedObjectList(
listController,
didMoveObject: object,
fromIndexPath: indexPath!,
toIndexPath: newIndexPath!
)
}
}
)
}
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.")
self.registerChangeNotification(
&NotificationKey.willChangeList,
name: ManagedObjectListControllerWillChangeListNotification,
toObserver: observer,
callback: { [weak observer] (listController) -> Void in
if let observer = observer {
observer.managedObjectListWillChange(listController)
}
}
)
self.registerChangeNotification(
&NotificationKey.didChangeList,
name: ManagedObjectListControllerDidChangeListNotification,
toObserver: observer,
callback: { [weak observer] (listController) -> Void in
if let observer = observer {
observer.managedObjectListDidChange(listController)
}
}
)
self.registerObjectNotification(
&NotificationKey.didInsertObject,
name: ManagedObjectListControllerDidInsertObjectNotification,
toObserver: observer,
callback: { [weak observer] (listController, object, indexPath, newIndexPath) -> Void in
if let observer = observer {
observer.managedObjectList(
listController,
didInsertObject: object,
toIndexPath: newIndexPath!
)
}
}
)
self.registerObjectNotification(
&NotificationKey.didDeleteObject,
name: ManagedObjectListControllerDidDeleteObjectNotification,
toObserver: observer,
callback: { [weak observer] (listController, object, indexPath, newIndexPath) -> Void in
if let observer = observer {
observer.managedObjectList(
listController,
didDeleteObject: object,
fromIndexPath: indexPath!
)
}
}
)
self.registerObjectNotification(
&NotificationKey.didUpdateObject,
name: ManagedObjectListControllerDidUpdateObjectNotification,
toObserver: observer,
callback: { [weak observer] (listController, object, indexPath, newIndexPath) -> Void in
if let observer = observer {
observer.managedObjectList(
listController,
didUpdateObject: object,
atIndexPath: indexPath!
)
}
}
)
self.registerObjectNotification(
&NotificationKey.didMoveObject,
name: ManagedObjectListControllerDidMoveObjectNotification,
toObserver: observer,
callback: { [weak observer] (listController, object, indexPath, newIndexPath) -> Void in
if let observer = observer {
observer.managedObjectList(
listController,
didMoveObject: object,
fromIndexPath: indexPath!,
toIndexPath: newIndexPath!
)
}
}
)
self.registerSectionNotification(
&NotificationKey.didInsertSection,
name: ManagedObjectListControllerDidInsertSectionNotification,
toObserver: observer,
callback: { [weak observer] (listController, sectionInfo, sectionIndex) -> Void in
if let observer = observer {
observer.managedObjectList(
listController,
didInsertSection: sectionInfo,
toSectionIndex: sectionIndex
)
}
}
)
self.registerSectionNotification(
&NotificationKey.didDeleteSection,
name: ManagedObjectListControllerDidDeleteSectionNotification,
toObserver: observer,
callback: { [weak observer] (listController, sectionInfo, sectionIndex) -> Void in
if let observer = observer {
observer.managedObjectList(
listController,
didDeleteSection: sectionInfo,
fromSectionIndex: sectionIndex
)
}
}
)
}
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.")
setAssociatedRetainedObject(nil as AnyObject?, forKey: &NotificationKey.willChangeList, inObject: observer)
setAssociatedRetainedObject(nil as AnyObject?, forKey: &NotificationKey.didChangeList, inObject: observer)
setAssociatedRetainedObject(nil as AnyObject?, forKey: &NotificationKey.didInsertObject, inObject: observer)
setAssociatedRetainedObject(nil as AnyObject?, forKey: &NotificationKey.didDeleteObject, inObject: observer)
setAssociatedRetainedObject(nil as AnyObject?, forKey: &NotificationKey.didUpdateObject, inObject: observer)
setAssociatedRetainedObject(nil as AnyObject?, forKey: &NotificationKey.didMoveObject, inObject: observer)
let nilValue: AnyObject? = nil
setAssociatedRetainedObject(nilValue, forKey: &NotificationKey.willChangeList, inObject: observer)
setAssociatedRetainedObject(nilValue, forKey: &NotificationKey.didChangeList, inObject: observer)
setAssociatedRetainedObject(nilValue, forKey: &NotificationKey.didInsertObject, inObject: observer)
setAssociatedRetainedObject(nilValue, forKey: &NotificationKey.didDeleteObject, inObject: observer)
setAssociatedRetainedObject(nilValue, forKey: &NotificationKey.didUpdateObject, inObject: observer)
setAssociatedRetainedObject(nilValue, forKey: &NotificationKey.didMoveObject, inObject: observer)
setAssociatedRetainedObject(nilValue, forKey: &NotificationKey.didInsertSection, inObject: observer)
setAssociatedRetainedObject(nilValue, forKey: &NotificationKey.didDeleteSection, inObject: observer)
}
// MARK: FetchedResultsControllerHandler
private func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
@@ -263,6 +411,31 @@ public final class ManagedObjectListController<T: NSManagedObject>: FetchedResul
private func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {
switch type {
case .Insert:
NSNotificationCenter.defaultCenter().postNotificationName(
ManagedObjectListControllerDidInsertSectionNotification,
object: self,
userInfo: [
UserInfoKeySectionInfo: sectionInfo,
UserInfoKeySectionIndex: NSNumber(integer: sectionIndex)
]
)
case .Delete:
NSNotificationCenter.defaultCenter().postNotificationName(
ManagedObjectListControllerDidDeleteSectionNotification,
object: self,
userInfo: [
UserInfoKeySectionInfo: sectionInfo,
UserInfoKeySectionIndex: NSNumber(integer: sectionIndex)
]
)
default:
break
}
}
private func controllerWillChangeContent(controller: NSFetchedResultsController) {
@@ -336,6 +509,77 @@ public final class ManagedObjectListController<T: NSManagedObject>: FetchedResul
private let fetchedResultsController: NSFetchedResultsController
private let fetchedResultsControllerDelegate: FetchedResultsControllerDelegate
private weak var parentStack: DataStack?
private func registerChangeNotification(notificationKey: UnsafePointer<Void>, name: String, toObserver observer: AnyObject, callback: (listController: ManagedObjectListController<T>) -> Void) {
setAssociatedRetainedObject(
NotificationObserver(
notificationName: name,
object: self,
closure: { [weak self] (note) -> Void in
if let strongSelf = self {
callback(listController: strongSelf)
}
}
),
forKey: notificationKey,
inObject: observer
)
}
private func registerObjectNotification(notificationKey: UnsafePointer<Void>, name: String, toObserver observer: AnyObject, callback: (listController: ManagedObjectListController<T>, object: T, indexPath: NSIndexPath?, newIndexPath: NSIndexPath?) -> Void) {
setAssociatedRetainedObject(
NotificationObserver(
notificationName: name,
object: self,
closure: { [weak self] (note) -> Void in
if let strongSelf = self,
let userInfo = note.userInfo,
let object = userInfo[UserInfoKeyObject] as? T {
callback(
listController: strongSelf,
object: object,
indexPath: userInfo[UserInfoKeyIndexPath] as? NSIndexPath,
newIndexPath: userInfo[UserInfoKeyNewIndexPath] as? NSIndexPath
)
}
}
),
forKey: notificationKey,
inObject: observer
)
}
private func registerSectionNotification(notificationKey: UnsafePointer<Void>, name: String, toObserver observer: AnyObject, callback: (listController: ManagedObjectListController<T>, sectionInfo: NSFetchedResultsSectionInfo, sectionIndex: Int) -> Void) {
setAssociatedRetainedObject(
NotificationObserver(
notificationName: name,
object: self,
closure: { [weak self] (note) -> Void in
if let strongSelf = self,
let userInfo = note.userInfo,
let sectionInfo = userInfo[UserInfoKeySectionInfo] as? NSFetchedResultsSectionInfo,
let sectionIndex = (userInfo[UserInfoKeySectionIndex] as? NSNumber)?.integerValue {
callback(
listController: strongSelf,
sectionInfo: sectionInfo,
sectionIndex: sectionIndex
)
}
}
),
forKey: notificationKey,
inObject: observer
)
}
}
@@ -361,16 +605,6 @@ private final class FetchedResultsControllerDelegate: NSFetchedResultsController
// MARK: NSFetchedResultsControllerDelegate
@objc func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
self.handler?.controller(controller, didChangeObject: anObject, atIndexPath: indexPath, forChangeType: type, newIndexPath: newIndexPath)
}
@objc func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {
self.handler?.controller(controller, didChangeSection: sectionInfo, atIndex: sectionIndex, forChangeType: type)
}
@objc func controllerWillChangeContent(controller: NSFetchedResultsController) {
self.handler?.controllerWillChangeContent(controller)
@@ -381,6 +615,16 @@ private final class FetchedResultsControllerDelegate: NSFetchedResultsController
self.handler?.controllerDidChangeContent(controller)
}
@objc func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
self.handler?.controller(controller, didChangeObject: anObject, atIndexPath: indexPath, forChangeType: type, newIndexPath: newIndexPath)
}
@objc func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {
self.handler?.controller(controller, didChangeSection: sectionInfo, atIndex: sectionIndex, forChangeType: type)
}
@objc func controller(controller: NSFetchedResultsController, sectionIndexTitleForSectionName sectionName: String?) -> String? {
return self.handler?.controller(controller, sectionIndexTitleForSectionName: sectionName)

View File

@@ -27,23 +27,21 @@ import Foundation
import CoreData
// MARK: - ManagedObjectListObserver
// MARK: - ManagedObjectListChangeObserver
public protocol ManagedObjectListObserver: class {
public protocol ManagedObjectListChangeObserver: class {
typealias EntityType: NSManagedObject
func managedObjectListWillChange(listController: ManagedObjectListController<EntityType>)
// func managedObjectList(listController: ManagedObjectListController<EntityType>, didInsertSection sectionInfo: NSFetchedResultsSectionInfo, toSectionIndex sectionIndex: Int)
//
// func managedObjectList(listController: ManagedObjectListController<EntityType>, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int)
//
// func managedObjectList(listController: ManagedObjectListController<EntityType>, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int)
//
// func managedObjectList(listController: ManagedObjectListController<EntityType>, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int)
func managedObjectListDidChange(listController: ManagedObjectListController<EntityType>)
}
// MARK: - ManagedObjectListObjectObserver
public protocol ManagedObjectListObjectObserver: ManagedObjectListChangeObserver {
func managedObjectList(listController: ManagedObjectListController<EntityType>, didInsertObject object: EntityType, toIndexPath indexPath: NSIndexPath)
@@ -54,11 +52,19 @@ public protocol ManagedObjectListObserver: class {
func managedObjectList(listController: ManagedObjectListController<EntityType>, didMoveObject object: EntityType, fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath)
func managedObjectListDidChange(listController: ManagedObjectListController<EntityType>)
//
// private func controller(controller: NSFetchedResultsController, sectionIndexTitleForSectionName sectionName: String?) -> String? {
//
// return nil
// }
}
// MARK: - ManagedObjectListSectionObserver
public protocol ManagedObjectListSectionObserver: ManagedObjectListObjectObserver {
func managedObjectList(listController: ManagedObjectListController<EntityType>, didInsertSection sectionInfo: NSFetchedResultsSectionInfo, toSectionIndex sectionIndex: Int)
func managedObjectList(listController: ManagedObjectListController<EntityType>, didDeleteSection sectionInfo: NSFetchedResultsSectionInfo, fromSectionIndex sectionIndex: Int)
}

View File

@@ -1,13 +0,0 @@
//
// ManagedObjectListSectionInfo.swift
// HardcoreData
//
// Created by John Rommel Estropia on 2015/03/11.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved.
//
import UIKit
class ManagedObjectListSectionInfo: NSObject {
}

View File

@@ -0,0 +1,41 @@
//
// ManagedObjectObserver.swift
// HardcoreData
//
// Copyright (c) 2015 John Rommel Estropia
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
import Foundation
import CoreData
// MARK: - ManagedObjectObserver
public protocol ManagedObjectObserver: class {
typealias EntityType: NSManagedObject
func managedObjectWillUpdate(objectController: ManagedObjectController<EntityType>, object: EntityType)
func managedObjectWasUpdated(objectController: ManagedObjectController<EntityType>, object: EntityType)
func managedObjectWasDeleted(objectController: ManagedObjectController<EntityType>, object: EntityType)
}

101
HardcoreData/README.md Normal file
View File

@@ -0,0 +1,101 @@
# HardcoreData
[![Version](https://img.shields.io/cocoapods/v/HardcoreData.svg?style=flat)](http://cocoadocs.org/docsets/HardcoreData)
[![Platform](https://img.shields.io/cocoapods/p/HardcoreData.svg?style=flat)](http://cocoadocs.org/docsets/HardcoreData)
[![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
## 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. (see "Setting up")
- Ability to plug-in your own logging framework (or your favorite 3rd-party logger). (see "Logging and error handling")
- Makes it hard to fall into common concurrency mistakes. All Core Data tasks are encapsulated into safer, higher-level abstractions without sacrificing flexibility and customizability. (see "Saving and processing transactions")
- Provides convenient API for common use cases. (see "Fetching and querying")
- Pleasant API designed around Swifts code elegance and type safety. (see "TL;DR sample codes")
#### TL;DR sample codes
Quick-setup:
```swift
HardcoreData.defaultStack.addSQLiteStore("MyStore.sqlite")
```
Simple transactions:
```swift
HardcoreData.beginAsynchronous { (transaction) -> Void in
let object = transaction.create(MyEntity)
object.entityID = 1
object.name = "test entity"
transaction.commit { (result) -> Void in
switch result {
case .Success(let hasChanges): println("success!")
case .Failure(let error): println(error)
}
}
}
```
Easy fetching:
```swift
let objects = HardcoreData.fetchAll(From(MyEntity))
```
```swift
let objects = HardcoreData.fetchAll(
From(MyEntity),
Where("entityID", isEqualTo: 1),
SortedBy(.Ascending("entityID"), .Descending("name")),
CustomizeFetch { (fetchRequest) -> Void in
fetchRequest.includesPendingChanges = true
}
)
```
Simple queries:
```swift
let count = HardcoreData.queryValue(
From(MyEntity),
Select<Int>(.Count("entityID"))
)
```
## Architecture
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:
| *Core Data* | *HardcoreData* |
| --- | --- |
| `NSManagedObjectModel` / `NSPersistentStoreCoordinator`<br />(.xcdatamodeld file) | `DataStack` |
| `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:
<img src="https://cloud.githubusercontent.com/assets/3029684/6734049/40579660-ce99-11e4-9d38-829877386afb.png" alt="nested contexts" height=271 />
This ensures maximum data integrity between contexts without blocking the main queue. But as <a href="http://floriankugler.com/2013/04/29/concurrent-core-data-stack-performance-shootout/">Florian Kugler's investigation</a> found out, merging contexts is still by far faster than saving nested contexts. HardcoreData's `DataStack` takes the best of both worlds by treating the main `NSManagedObjectContext` as a read-only context, and only allows changes to be made within *transactions*:
<img src="https://cloud.githubusercontent.com/assets/3029684/6734050/4078b642-ce99-11e4-95ea-c0c1d24fbe80.png" alt="nested contexts and merge hybrid" height=212 />
This allows for a butter-smooth main thread, while still taking advantage of safe nested contexts.
## Setting up
## Saving and processing transactions
## Fetching and querying
## Logging and error handling
## Observing changes and notifications (currently in the works)

View File

@@ -9,15 +9,15 @@
/* Begin PBXBuildFile section */
B54AAD4F1AF4D26E00848AE0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54AAD4E1AF4D26E00848AE0 /* AppDelegate.swift */; };
B54AAD521AF4D26E00848AE0 /* HardcoreDataDemo.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = B54AAD501AF4D26E00848AE0 /* HardcoreDataDemo.xcdatamodeld */; };
B54AAD541AF4D26E00848AE0 /* MasterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54AAD531AF4D26E00848AE0 /* MasterViewController.swift */; };
B54AAD561AF4D26E00848AE0 /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54AAD551AF4D26E00848AE0 /* DetailViewController.swift */; };
B54AAD541AF4D26E00848AE0 /* PalettesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54AAD531AF4D26E00848AE0 /* PalettesViewController.swift */; };
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 */; };
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, ); }; };
B583A92B1AF5FCE6001F76AF /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = B583A92A1AF5FCE6001F76AF /* Event.swift */; };
B5BC26E41AF8F67900276889 /* PaletteTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BC26E31AF8F67900276889 /* PaletteTableViewCell.swift */; };
B5BC26E81AF8FD9600276889 /* Palette.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BC26E71AF8FD9600276889 /* Palette.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -77,8 +77,7 @@
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>"; };
B54AAD511AF4D26E00848AE0 /* HardcoreDataDemo.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = HardcoreDataDemo.xcdatamodel; sourceTree = "<group>"; };
B54AAD531AF4D26E00848AE0 /* MasterViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterViewController.swift; sourceTree = "<group>"; };
B54AAD551AF4D26E00848AE0 /* DetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailViewController.swift; sourceTree = "<group>"; };
B54AAD531AF4D26E00848AE0 /* PalettesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PalettesViewController.swift; sourceTree = "<group>"; };
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>"; };
@@ -87,7 +86,8 @@
B54AAD691AF4D26E00848AE0 /* HardcoreDataDemoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HardcoreDataDemoTests.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>"; };
B583A92A1AF5FCE6001F76AF /* Event.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Event.swift; sourceTree = "<group>"; };
B5BC26E31AF8F67900276889 /* PaletteTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaletteTableViewCell.swift; sourceTree = "<group>"; };
B5BC26E71AF8FD9600276889 /* Palette.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Palette.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -133,12 +133,11 @@
isa = PBXGroup;
children = (
B54AAD4E1AF4D26E00848AE0 /* AppDelegate.swift */,
B54AAD531AF4D26E00848AE0 /* MasterViewController.swift */,
B54AAD551AF4D26E00848AE0 /* DetailViewController.swift */,
B5BC26E51AF8F67D00276889 /* DataSources */,
B5BC26E61AF8F68200276889 /* Entities */,
B54AAD571AF4D26E00848AE0 /* Main.storyboard */,
B54AAD5A1AF4D26E00848AE0 /* Images.xcassets */,
B54AAD5C1AF4D26E00848AE0 /* LaunchScreen.xib */,
B583A92A1AF5FCE6001F76AF /* Event.swift */,
B54AAD501AF4D26E00848AE0 /* HardcoreDataDemo.xcdatamodeld */,
B54AAD4C1AF4D26E00848AE0 /* Supporting Files */,
);
@@ -179,6 +178,23 @@
name = Products;
sourceTree = "<group>";
};
B5BC26E51AF8F67D00276889 /* DataSources */ = {
isa = PBXGroup;
children = (
B54AAD531AF4D26E00848AE0 /* PalettesViewController.swift */,
B5BC26E31AF8F67900276889 /* PaletteTableViewCell.swift */,
);
name = DataSources;
sourceTree = "<group>";
};
B5BC26E61AF8F68200276889 /* Entities */ = {
isa = PBXGroup;
children = (
B5BC26E71AF8FD9600276889 /* Palette.swift */,
);
name = Entities;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -307,9 +323,9 @@
files = (
B54AAD521AF4D26E00848AE0 /* HardcoreDataDemo.xcdatamodeld in Sources */,
B54AAD4F1AF4D26E00848AE0 /* AppDelegate.swift in Sources */,
B54AAD541AF4D26E00848AE0 /* MasterViewController.swift in Sources */,
B54AAD561AF4D26E00848AE0 /* DetailViewController.swift in Sources */,
B583A92B1AF5FCE6001F76AF /* Event.swift in Sources */,
B5BC26E41AF8F67900276889 /* PaletteTableViewCell.swift in Sources */,
B5BC26E81AF8FD9600276889 /* Palette.swift in Sources */,
B54AAD541AF4D26E00848AE0 /* PalettesViewController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@@ -10,6 +10,17 @@ import UIKit
import HardcoreData
let paletteList: ManagedObjectListController<Palette> = {
HardcoreData.defaultStack.addSQLiteStore()
return HardcoreData.defaultStack.observeObjectList(
From(Palette),
GroupBy("colorName"),
SortedBy(.Ascending("hue"), .Ascending("dateAdded"))
)
}()
// MARK: - AppDelegate
@UIApplicationMain

View File

@@ -1,102 +1,449 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="7702" systemVersion="14D136" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="rS3-R9-Ivy">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="7702" systemVersion="14D136" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="szK-z4-VnV">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7701"/>
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
</dependencies>
<scenes>
<!--Master-->
<scene sceneID="cUi-kZ-frf">
<!--Demo-->
<scene sceneID="0Be-vc-h1W">
<objects>
<navigationController title="Master" id="rS3-R9-Ivy" sceneMemberID="viewController">
<navigationBar key="navigationBar" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" id="yXu-0R-QUA">
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<connections>
<segue destination="pGg-6v-bdr" kind="relationship" relationship="rootViewController" id="RxB-wf-QIq"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="eq9-QA-ai8" sceneMemberID="firstResponder"/>
<tableViewController id="t0d-B0-B7U" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="uHB-Yr-ujV">
<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"/>
<sections>
<tableViewSection headerTitle="Observing Changes" id="wIP-Hn-YfF">
<cells>
<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"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="fsb-zw-8Ii" id="Upm-AO-Fw3">
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Observing Object Lists" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="vpt-cT-gMo">
<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="Using ManagedObjectListController and ManagedObjectListObserver" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="ou9-TZ-8bf">
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="11"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
<connections>
<segue destination="YOI-b7-Nxn" kind="show" id="29o-wO-3LK"/>
</connections>
</tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="quG-kg-ady" detailTextLabel="hPD-ed-MbJ" style="IBUITableViewCellStyleSubtitle" id="OnF-07-qx3">
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="OnF-07-qx3" id="3rZ-A6-AuF">
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Observing a Single Object" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="quG-kg-ady">
<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="Using ManagedObjectController" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="hPD-ed-MbJ">
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="11"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
<tableViewSection headerTitle="Observing Changes" id="tZI-Y2-NJK">
<cells>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="scO-Ch-nTl" detailTextLabel="OGH-Vy-Sma" style="IBUITableViewCellStyleSubtitle" id="Naz-cd-2YO">
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Naz-cd-2YO" id="NOk-yA-OEJ">
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Observing Object Lists" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="scO-Ch-nTl">
<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="Using ManagedObjectListController" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="OGH-Vy-Sma">
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="11"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="QeW-r4-AVT" detailTextLabel="pPh-68-9W2" style="IBUITableViewCellStyleSubtitle" id="7Gb-Xl-ua6">
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="7Gb-Xl-ua6" id="WDd-fK-sr9">
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Observing a Single Object" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="QeW-r4-AVT">
<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="Using ManagedObjectController" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="pPh-68-9W2">
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="11"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
<tableViewSection headerTitle="Observing Changes" id="aMb-6a-4MW">
<cells>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="SyX-c3-NzS" detailTextLabel="mSk-C8-oBH" style="IBUITableViewCellStyleSubtitle" id="3i5-Gb-RnY">
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="3i5-Gb-RnY" id="cah-Ag-byv">
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Observing Object Lists" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="SyX-c3-NzS">
<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="Using ManagedObjectListController" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="mSk-C8-oBH">
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="11"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="L8m-Y3-8LF" detailTextLabel="S8e-SS-C8R" style="IBUITableViewCellStyleSubtitle" id="mKK-mf-OYb">
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="mKK-mf-OYb" id="WR1-I2-8xr">
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Observing a Single Object" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="L8m-Y3-8LF">
<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="Using ManagedObjectController" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="S8e-SS-C8R">
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="11"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
</sections>
<connections>
<outlet property="dataSource" destination="t0d-B0-B7U" id="05l-tO-FOb"/>
<outlet property="delegate" destination="t0d-B0-B7U" id="Yta-gX-xFD"/>
</connections>
</tableView>
<navigationItem key="navigationItem" title="Demo" id="3bj-zE-UYZ"/>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Xgp-Zn-Sbp" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-148" y="64"/>
<point key="canvasLocation" x="2982" y="1325"/>
</scene>
<!--Master-->
<scene sceneID="VgW-fR-Quf">
<!--Observing Object List-->
<scene sceneID="3lD-lX-hIc">
<objects>
<tableViewController title="Master" id="pGg-6v-bdr" customClass="MasterViewController" customModule="HardcoreDataDemo" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="mLL-gJ-YKr">
<viewController hidesBottomBarWhenPushed="YES" id="YOI-b7-Nxn" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="IML-3o-caw"/>
<viewControllerLayoutGuide type="bottom" id="LNL-mj-D7l"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="6x3-vn-Egt">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="L5f-tW-lXf">
<rect key="frame" x="0.0" y="0.0" width="600" height="300"/>
<connections>
<segue destination="3AE-ED-0oj" kind="embed" id="YcI-2Z-ijV"/>
</connections>
</containerView>
<containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="6So-f3-4Gp">
<rect key="frame" x="0.0" y="300" width="600" height="300"/>
<connections>
<segue destination="sll-yo-mBc" kind="embed" id="AAl-HS-dq2"/>
</connections>
</containerView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="6So-f3-4Gp" firstAttribute="top" secondItem="L5f-tW-lXf" secondAttribute="bottom" id="3m8-tj-Nd4"/>
<constraint firstAttribute="trailingMargin" secondItem="6So-f3-4Gp" secondAttribute="trailing" constant="-16" id="4L8-wZ-F59"/>
<constraint firstItem="LNL-mj-D7l" firstAttribute="top" secondItem="6So-f3-4Gp" secondAttribute="bottom" constant="-49" id="8wL-zm-wnt"/>
<constraint firstItem="L5f-tW-lXf" firstAttribute="leading" secondItem="6x3-vn-Egt" secondAttribute="leadingMargin" constant="-16" id="CbE-2f-7wk"/>
<constraint firstAttribute="trailingMargin" secondItem="L5f-tW-lXf" secondAttribute="trailing" constant="-16" id="dso-2g-fgA"/>
<constraint firstItem="6So-f3-4Gp" firstAttribute="leading" secondItem="6x3-vn-Egt" secondAttribute="leadingMargin" constant="-16" id="eXM-D3-NLv"/>
<constraint firstItem="L5f-tW-lXf" firstAttribute="height" secondItem="6So-f3-4Gp" secondAttribute="height" id="xc9-x7-u66"/>
<constraint firstItem="L5f-tW-lXf" firstAttribute="top" secondItem="IML-3o-caw" secondAttribute="bottom" constant="-64" id="zJ5-sE-iJA"/>
</constraints>
</view>
<navigationItem key="navigationItem" title="Observing Object List" id="7Gd-Ad-Bzu"/>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="C9h-Ba-WoL" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="3694" y="1325"/>
</scene>
<!--Data Source-->
<scene sceneID="bjb-2c-ZyA">
<objects>
<tableViewController id="n8m-TO-mNX" customClass="PalettesViewController" customModule="HardcoreDataDemo" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="60" sectionHeaderHeight="22" sectionFooterHeight="22" id="Rtx-h6-8jn">
<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"/>
<prototypes>
<tableViewCell contentMode="scaleToFill" selectionStyle="blue" accessoryType="disclosureIndicator" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="UITableViewCell" textLabel="2pz-XF-uhl" style="IBUITableViewCellStyleDefault" id="m0d-ak-lc9">
<rect key="frame" x="0.0" y="86" width="320" height="44"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="PaletteTableViewCell" id="H3Q-dW-6zD" customClass="PaletteTableViewCell" customModule="HardcoreDataDemo" customModuleProvider="target">
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="m0d-ak-lc9" id="d3P-M7-ByW">
<rect key="frame" x="0.0" y="0.0" width="287" height="43"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="H3Q-dW-6zD" id="66x-XH-nr3">
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" text="Title" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="2pz-XF-uhl">
<rect key="frame" x="15" y="0.0" width="270" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="20"/>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Qce-fh-1hq">
<rect key="frame" x="8" y="8" width="43" height="43"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="width" secondItem="Qce-fh-1hq" secondAttribute="height" multiplier="1:1" id="vc2-65-W9v"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="iyX-f6-aWG">
<rect key="frame" x="61" y="8" width="46" height="43"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="18"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="iyX-f6-aWG" firstAttribute="leading" secondItem="Qce-fh-1hq" secondAttribute="trailing" constant="10" id="KfE-Ey-VHT"/>
<constraint firstAttribute="bottomMargin" secondItem="Qce-fh-1hq" secondAttribute="bottom" id="T2z-OS-Ewz"/>
<constraint firstItem="iyX-f6-aWG" firstAttribute="height" secondItem="Qce-fh-1hq" secondAttribute="height" id="Wzu-s3-3WB"/>
<constraint firstAttribute="centerY" secondItem="iyX-f6-aWG" secondAttribute="centerY" id="XrI-RK-gmT"/>
<constraint firstItem="Qce-fh-1hq" firstAttribute="top" secondItem="66x-XH-nr3" secondAttribute="topMargin" id="cqj-bh-MAd"/>
<constraint firstItem="Qce-fh-1hq" firstAttribute="leading" secondItem="66x-XH-nr3" secondAttribute="leadingMargin" id="xga-8N-Tg8"/>
</constraints>
</tableViewCellContentView>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<connections>
<segue destination="Ah7-4n-0Wa" kind="show" identifier="showDetail" id="jUr-3t-vfg"/>
<outlet property="colorView" destination="Qce-fh-1hq" id="p2C-6x-E23"/>
<outlet property="label" destination="iyX-f6-aWG" id="QQj-z6-zlJ"/>
</connections>
</tableViewCell>
</prototypes>
<sections/>
<connections>
<outlet property="dataSource" destination="pGg-6v-bdr" id="P41-gY-KXY"/>
<outlet property="delegate" destination="pGg-6v-bdr" id="Y6K-Cp-Qkv"/>
<outlet property="dataSource" destination="n8m-TO-mNX" id="mJd-Ez-IgP"/>
<outlet property="delegate" destination="n8m-TO-mNX" id="kCy-Z4-W6S"/>
</connections>
</tableView>
<navigationItem key="navigationItem" title="Master" id="tQt-TN-PWz"/>
<navigationItem key="navigationItem" title="Data Source" id="Rlh-3D-rPD"/>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="6Cn-md-YlS" sceneMemberID="firstResponder"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="Wss-ed-4dj" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="605" y="64"/>
<point key="canvasLocation" x="2982" y="608"/>
</scene>
<!--Detail-->
<scene sceneID="Cn3-H9-jdl">
<!--Tab Bar Controller-->
<scene sceneID="G36-km-fMb">
<objects>
<viewController title="Detail" id="Ah7-4n-0Wa" customClass="DetailViewController" customModule="HardcoreDataDemo" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="cIm-e0-J51"/>
<viewControllerLayoutGuide type="bottom" id="a0L-h9-sNL"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="MMQ-IT-qOo">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<label clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" text="Detail view content goes here" textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="Lei-5M-9Gs">
<rect key="frame" x="20" y="292" width="560" height="17"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<fontDescription key="fontDescription" type="system" size="system"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="Lei-5M-9Gs" firstAttribute="leading" secondItem="MMQ-IT-qOo" secondAttribute="leading" constant="20" symbolic="YES" id="62x-JV-TTJ"/>
<constraint firstItem="Lei-5M-9Gs" firstAttribute="centerY" secondItem="MMQ-IT-qOo" secondAttribute="centerY" id="JzS-HC-Rnl"/>
<constraint firstAttribute="trailing" secondItem="Lei-5M-9Gs" secondAttribute="trailing" constant="20" symbolic="YES" id="pXB-RP-Zz6"/>
</constraints>
</view>
<navigationItem key="navigationItem" title="Detail" id="cra-N8-TIN"/>
<tabBarController automaticallyAdjustsScrollViewInsets="NO" id="szK-z4-VnV" sceneMemberID="viewController">
<toolbarItems/>
<nil key="simulatedBottomBarMetrics"/>
<tabBar key="tabBar" contentMode="scaleToFill" id="PnD-bJ-rHY">
<autoresizingMask key="autoresizingMask"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</tabBar>
<connections>
<outlet property="detailDescriptionLabel" destination="Lei-5M-9Gs" id="sCT-F7-F6f"/>
<segue destination="EQn-RG-F8w" kind="relationship" relationship="viewControllers" id="LVq-O5-Dy8"/>
<segue destination="Ni8-QF-XHB" kind="relationship" relationship="viewControllers" id="W8q-TH-uRN"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="lrm-kH-fPn" sceneMemberID="firstResponder"/>
</tabBarController>
<placeholder placeholderIdentifier="IBFirstResponder" id="xkC-5P-6Tl" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1343" y="64"/>
<point key="canvasLocation" x="1408" y="902"/>
</scene>
<!--Demo-->
<scene sceneID="Ffr-kh-wmT">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="Ni8-QF-XHB" sceneMemberID="viewController">
<tabBarItem key="tabBarItem" title="Demo" image="second" id="3iQ-I2-4LW"/>
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="00L-5k-Eno">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="t0d-B0-B7U" kind="relationship" relationship="rootViewController" id="Wry-8Y-XLA"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="sm4-t7-KeT" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="2170" y="1325"/>
</scene>
<!--Data Source-->
<scene sceneID="gWT-4Y-Q7f">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="EQn-RG-F8w" sceneMemberID="viewController">
<tabBarItem key="tabBarItem" title="Data Source" image="first" id="RhG-RY-VGb"/>
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="rAn-Lz-7zg">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="n8m-TO-mNX" kind="relationship" relationship="rootViewController" id="XnX-7f-B7r"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="ST8-TK-asq" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="2170" y="608"/>
</scene>
<!--Palettes View Controller-->
<scene sceneID="gkX-bd-Rel">
<objects>
<tableViewController id="3AE-ED-0oj" customClass="PalettesViewController" customModule="HardcoreDataDemo" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="60" sectionHeaderHeight="22" sectionFooterHeight="22" id="DAz-BE-6Ca">
<rect key="frame" x="0.0" y="0.0" width="600" height="300"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<prototypes>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="PaletteTableViewCell" id="G3X-70-BCD" customClass="PaletteTableViewCell" customModule="HardcoreDataDemo" customModuleProvider="target">
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="G3X-70-BCD" id="aT8-nz-i5l">
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="uQX-PI-UWF">
<rect key="frame" x="8" y="8" width="43" height="43"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="width" secondItem="uQX-PI-UWF" secondAttribute="height" multiplier="1:1" id="9qA-iN-Neb"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="HJC-5w-lIN">
<rect key="frame" x="61" y="8" width="46" height="43"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="18"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="HJC-5w-lIN" firstAttribute="height" secondItem="uQX-PI-UWF" secondAttribute="height" id="8Py-Ub-gQD"/>
<constraint firstItem="uQX-PI-UWF" firstAttribute="top" secondItem="aT8-nz-i5l" secondAttribute="topMargin" id="9e4-wD-lwf"/>
<constraint firstAttribute="centerY" secondItem="HJC-5w-lIN" secondAttribute="centerY" id="AeC-j4-DBE"/>
<constraint firstItem="HJC-5w-lIN" firstAttribute="leading" secondItem="uQX-PI-UWF" secondAttribute="trailing" constant="10" id="S2k-mE-rvY"/>
<constraint firstItem="uQX-PI-UWF" firstAttribute="leading" secondItem="aT8-nz-i5l" secondAttribute="leadingMargin" id="iTc-PQ-LHf"/>
<constraint firstAttribute="bottomMargin" secondItem="uQX-PI-UWF" secondAttribute="bottom" id="wpc-Bf-dsb"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="colorView" destination="uQX-PI-UWF" id="Ihp-LO-19e"/>
<outlet property="label" destination="HJC-5w-lIN" id="Jrz-ok-NSe"/>
</connections>
</tableViewCell>
</prototypes>
<connections>
<outlet property="dataSource" destination="3AE-ED-0oj" id="aqz-Pr-nHv"/>
<outlet property="delegate" destination="3AE-ED-0oj" id="mQ8-xM-CER"/>
</connections>
</tableView>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="P5L-49-pOr" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="4404" y="1145"/>
</scene>
<!--Make Changes-->
<scene sceneID="iDB-TD-It9">
<objects>
<tableViewController id="lCE-i6-UCT" customClass="PalettesViewController" customModule="HardcoreDataDemo" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="60" sectionHeaderHeight="22" sectionFooterHeight="22" id="Zba-8M-Zd7">
<rect key="frame" x="0.0" y="0.0" width="600" height="300"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<prototypes>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="PaletteTableViewCell" id="zSO-3e-OVq" customClass="PaletteTableViewCell" customModule="HardcoreDataDemo" customModuleProvider="target">
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="zSO-3e-OVq" id="cHA-by-n4b">
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="5uq-Yi-XwH">
<rect key="frame" x="8" y="8" width="43" height="43"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="width" secondItem="5uq-Yi-XwH" secondAttribute="height" multiplier="1:1" id="oOe-HC-VyN"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Zyu-PC-WmO">
<rect key="frame" x="61" y="8" width="46" height="43"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="18"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstAttribute="bottomMargin" secondItem="5uq-Yi-XwH" secondAttribute="bottom" id="2Bf-dJ-VmS"/>
<constraint firstItem="Zyu-PC-WmO" firstAttribute="leading" secondItem="5uq-Yi-XwH" secondAttribute="trailing" constant="10" id="6uD-S1-gkb"/>
<constraint firstAttribute="centerY" secondItem="Zyu-PC-WmO" secondAttribute="centerY" id="KQ6-bN-Og6"/>
<constraint firstItem="5uq-Yi-XwH" firstAttribute="top" secondItem="cHA-by-n4b" secondAttribute="topMargin" id="NqW-uA-mqj"/>
<constraint firstItem="Zyu-PC-WmO" firstAttribute="height" secondItem="5uq-Yi-XwH" secondAttribute="height" id="Yd5-bt-R8q"/>
<constraint firstItem="5uq-Yi-XwH" firstAttribute="leading" secondItem="cHA-by-n4b" secondAttribute="leadingMargin" id="a2a-Ol-0HV"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="colorView" destination="5uq-Yi-XwH" id="tgl-W1-A55"/>
<outlet property="label" destination="Zyu-PC-WmO" id="Oda-gD-ElI"/>
</connections>
</tableViewCell>
</prototypes>
<connections>
<outlet property="dataSource" destination="lCE-i6-UCT" id="vTv-9f-P8U"/>
<outlet property="delegate" destination="lCE-i6-UCT" id="Ken-sI-O2f"/>
</connections>
</tableView>
<navigationItem key="navigationItem" title="Make Changes" id="koc-aK-cgD"/>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="QAS-su-ZdM" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="5085" y="1546"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="DTD-lH-nr5">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="sll-yo-mBc" sceneMemberID="viewController">
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="6XA-6M-yvZ">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="lCE-i6-UCT" kind="relationship" relationship="rootViewController" id="4Vz-ah-FHX"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="wJg-s1-Dpr" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="4404" y="1546"/>
</scene>
</scenes>
<resources>
<image name="first" width="30" height="30"/>
<image name="second" width="30" height="30"/>
</resources>
</document>

View File

@@ -1,45 +0,0 @@
//
// DetailViewController.swift
// HardcoreDataDemo
//
// Created by John Rommel Estropia on 2015/05/02.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved.
//
import UIKit
class DetailViewController: UIViewController {
@IBOutlet weak var detailDescriptionLabel: UILabel!
var detailItem: AnyObject? {
didSet {
// Update the view.
self.configureView()
}
}
func configureView() {
// Update the user interface for the detail item.
if let detail: AnyObject = self.detailItem {
if let label = self.detailDescriptionLabel {
label.text = detail.valueForKey("timeStamp")!.description
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.configureView()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

View File

@@ -1,16 +0,0 @@
//
// Event.swift
// HardcoreDataDemo
//
// Created by John Rommel Estropia on 2015/05/03.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved.
//
import Foundation
import CoreData
class Event: NSManagedObject {
@NSManaged var timeStamp: NSDate
}

View File

@@ -1,12 +1,16 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="7549" systemVersion="14D136" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
<entity name="Event" representedClassName="HardcoreDataDemo.Event">
<attribute name="timeStamp" optional="YES" attributeType="Date">
<entity name="Palette" representedClassName="HardcoreDataDemo.Palette">
<attribute name="brightness" optional="YES" attributeType="Float" defaultValueString="0.0" syncable="YES"/>
<attribute name="colorName" optional="YES" transient="YES" attributeType="String" syncable="YES"/>
<attribute name="dateAdded" optional="YES" attributeType="Date">
<userInfo/>
</attribute>
<attribute name="hue" optional="YES" attributeType="Integer 32" defaultValueString="0.0" syncable="YES"/>
<attribute name="saturation" optional="YES" attributeType="Float" defaultValueString="0.0" syncable="YES"/>
<userInfo/>
</entity>
<elements>
<element name="Event" positionX="261" positionY="189" width="128" height="60"/>
<element name="Palette" positionX="261" positionY="189" width="128" height="120"/>
</elements>
</model>

View File

@@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "first.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "second.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@@ -43,8 +43,6 @@
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

View File

@@ -1,137 +0,0 @@
//
// MasterViewController.swift
// HardcoreDataDemo
//
// Created by John Rommel Estropia on 2015/05/02.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved.
//
import UIKit
import HardcoreData
// MARK: - MasterViewController
class MasterViewController: UITableViewController, ManagedObjectListObserver {
// MARK: UIViewController
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.leftBarButtonItem = self.editButtonItem()
self.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: "addBarButtonItemTouched:")
self.list.addObserver(self)
}
// MARK: UITableViewController
required init!(coder aDecoder: NSCoder!) {
HardcoreData.defaultStack.addSQLiteStore()
self.list = HardcoreData.defaultStack.observeObjectList(
From(Event),
SortedBy(.Ascending("timeStamp"))
)
super.init(coder: aDecoder)
}
// MARK: UITableViewDataSource
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return self.list.numberOfSections()
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.list.numberOfItemsInSection(section)
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("UITableViewCell") as! UITableViewCell
cell.textLabel?.text = "\(self.list[indexPath].timeStamp)"
return cell
}
// MARK: UITableViewDelegate
// override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
//
// super.tableView(tableView, didSelectRowAtIndexPath: indexPath)
// }
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
switch editingStyle {
case .Delete:
let event = self.list[indexPath]
HardcoreData.beginAsynchronous{ (transaction) -> Void in
transaction.delete(transaction.fetch(event)!)
transaction.commit { (result) -> Void in }
}
default:
break
}
}
// optional func tableView(tableView: UITableView, moveRowAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath)
// MARK: ManagedObjectListObserver
func managedObjectListWillChange(listController: ManagedObjectListController<Event>) {
self.tableView.beginUpdates()
}
func managedObjectList(listController: ManagedObjectListController<Event>, didInsertObject object: Event, toIndexPath indexPath: NSIndexPath) {
self.tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
}
func managedObjectList(listController: ManagedObjectListController<Event>, didDeleteObject object: Event, fromIndexPath indexPath: NSIndexPath) {
self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
}
func managedObjectList(listController: ManagedObjectListController<Event>, didUpdateObject object: Event, atIndexPath indexPath: NSIndexPath) {
self.tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
}
func managedObjectList(listController: ManagedObjectListController<Event>, didMoveObject object: Event, fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
self.tableView.moveRowAtIndexPath(fromIndexPath, toIndexPath: toIndexPath)
}
func managedObjectListDidChange(listController: ManagedObjectListController<Event>) {
self.tableView.endUpdates()
}
// MARK: Private
let list: ManagedObjectListController<Event>
@objc dynamic func addBarButtonItemTouched(sender: AnyObject!) {
HardcoreData.beginAsynchronous { (transaction) -> Void in
let event = transaction.create(Event)
event.timeStamp = NSDate()
transaction.commit { (result) -> Void in }
}
}
}

View File

@@ -0,0 +1,58 @@
//
// Palette.swift
// HardcoreDataDemo
//
// Created by John Rommel Estropia on 2015/05/05.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved.
//
import Foundation
import CoreData
class Palette: NSManagedObject {
@NSManaged var dateAdded: NSDate
@NSManaged var hue: Int32
@NSManaged var saturation: Float
@NSManaged var brightness: Float
@objc dynamic var colorName: String {
get {
let key = "colorName"
self.willAccessValueForKey(key)
let value: AnyObject? = self.primitiveValueForKey(key)
self.didAccessValueForKey(key)
if let colorName = value as? String {
return colorName
}
let colorName: String
switch self.hue % 360 {
case 0 ..< 20: colorName = "Lower Reds"
case 20 ..< 57: colorName = "Oranges and Browns"
case 57 ..< 90: colorName = "Yellow-Greens"
case 90 ..< 159: colorName = "Greens"
case 159 ..< 197: colorName = "Blue-Greens"
case 197 ..< 241: colorName = "Blues"
case 241 ..< 297: colorName = "Violets"
case 297 ..< 331: colorName = "Magentas"
default: colorName = "Upper Reds"
}
self.setPrimitiveValue(colorName, forKey: key)
return colorName
}
set {
let key = "colorName"
self.willChangeValueForKey(key)
self.setPrimitiveValue(newValue, forKey: key)
self.didChangeValueForKey(key)
}
}
}

View File

@@ -0,0 +1,26 @@
//
// PaletteTableViewCell.swift
// HardcoreDataDemo
//
// Created by John Rommel Estropia on 2015/05/05.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved.
//
import UIKit
class PaletteTableViewCell: UITableViewCell {
@IBOutlet weak var colorView: UIView?
@IBOutlet weak var label: UILabel?
func setHue(hue: Int32, saturation: Float, brightness: Float) {
let color = UIColor(
hue: CGFloat(hue) / 360.0,
saturation: CGFloat(saturation),
brightness: CGFloat(brightness),
alpha: 1.0)
self.colorView?.backgroundColor = color
self.label?.text = "H: \(hue)˚, S: \(round(saturation * 100.0))%, B: \(round(brightness * 100.0))%"
}
}

View File

@@ -0,0 +1,162 @@
//
// PalettesViewController.swift
// HardcoreDataDemo
//
// Created by John Rommel Estropia on 2015/05/02.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved.
//
import UIKit
import HardcoreData
// MARK: - PalettesViewController
class PalettesViewController: UITableViewController, ManagedObjectListSectionObserver {
// MARK: NSObject
deinit {
paletteList.removeObserver(self)
}
// MARK: UIViewController
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.leftBarButtonItems = [
self.editButtonItem(),
UIBarButtonItem(barButtonSystemItem: .Trash, target: self, action: "resetBarButtonItemTouched:")
]
self.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: "addBarButtonItemTouched:")
paletteList.addObserver(self)
}
// MARK: UITableViewDataSource
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return paletteList.numberOfSections()
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return paletteList.numberOfObjectsInSection(section)
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("PaletteTableViewCell") as! PaletteTableViewCell
let palette = paletteList[indexPath]
cell.setHue(palette.hue, saturation: palette.saturation, brightness: palette.brightness)
return cell
}
// MARK: UITableViewDelegate
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
switch editingStyle {
case .Delete:
let palette = paletteList[indexPath]
HardcoreData.beginAsynchronous{ (transaction) -> Void in
transaction.delete(palette)
transaction.commit { (result) -> Void in }
}
default:
break
}
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return paletteList.sectionInfoAtIndex(section).name
}
// MARK: ManagedObjectListChangeObserver
func managedObjectListWillChange(listController: ManagedObjectListController<Palette>) {
self.tableView.beginUpdates()
}
func managedObjectListDidChange(listController: ManagedObjectListController<Palette>) {
self.tableView.endUpdates()
}
// MARK: ManagedObjectListObjectObserver
func managedObjectList(listController: ManagedObjectListController<Palette>, didInsertObject object: Palette, toIndexPath indexPath: NSIndexPath) {
self.tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
}
func managedObjectList(listController: ManagedObjectListController<Palette>, didDeleteObject object: Palette, fromIndexPath indexPath: NSIndexPath) {
self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
}
func managedObjectList(listController: ManagedObjectListController<Palette>, didUpdateObject object: Palette, atIndexPath indexPath: NSIndexPath) {
let cell = self.tableView.cellForRowAtIndexPath(indexPath) as! PaletteTableViewCell
let palette = paletteList[indexPath]
cell.setHue(palette.hue, saturation: palette.saturation, brightness: palette.brightness)
}
func managedObjectList(listController: ManagedObjectListController<Palette>, didMoveObject object: Palette, fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
self.tableView.moveRowAtIndexPath(fromIndexPath, toIndexPath: toIndexPath)
}
// MARK: ManagedObjectListSectionObserver
func managedObjectList(listController: ManagedObjectListController<Palette>, didInsertSection sectionInfo: NSFetchedResultsSectionInfo, toSectionIndex sectionIndex: Int) {
self.tableView.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Automatic)
}
func managedObjectList(listController: ManagedObjectListController<Palette>, didDeleteSection sectionInfo: NSFetchedResultsSectionInfo, fromSectionIndex sectionIndex: Int) {
self.tableView.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Automatic)
}
// MARK: Private
@IBAction dynamic func resetBarButtonItemTouched(sender: AnyObject?) {
HardcoreData.beginAsynchronous { (transaction) -> Void in
transaction.deleteAll(From(Palette))
transaction.commit { (result) -> Void in }
}
}
@IBAction dynamic func addBarButtonItemTouched(sender: AnyObject?) {
HardcoreData.beginAsynchronous { (transaction) -> Void in
let palette = transaction.create(Palette)
palette.hue = Int32(arc4random_uniform(360))
palette.saturation = 1.0
palette.brightness = 0.5
palette.dateAdded = NSDate()
transaction.commit { (result) -> Void in }
}
}
}