Skip to content
This repository has been archived by the owner on Jun 2, 2018. It is now read-only.

Type safe fetched results controller #55

Merged
merged 28 commits into from
Dec 1, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
5874302
Add the initial type safe fetched results controller
rcedwards Nov 20, 2015
ee42392
Add an arbitrary delay to the UI setup in the example app
rcedwards Nov 23, 2015
bf73ec9
Add sample fetched results controller to example project
rcedwards Nov 23, 2015
41342b0
Remove NSManagedObject requirement from FetchedResultsObjectChange enum
rcedwards Nov 24, 2015
8f4ebd4
Move testing data from Authors to Books
rcedwards Nov 24, 2015
8b150b1
Add a test for the initial fetch of the FRC
rcedwards Nov 24, 2015
06437a4
Add a test for object deletion in the FRC
rcedwards Nov 24, 2015
260229d
Add a test for object moves in an FRC
rcedwards Nov 24, 2015
486c608
Add test for an Update to an object in a FRC
rcedwards Nov 24, 2015
8622fce
Modify insert test to match other new tests
rcedwards Nov 24, 2015
5249170
Add willChange and didChange count assertions to the tests
rcedwards Nov 24, 2015
ebc1d62
Add a section insert test
rcedwards Nov 24, 2015
cff7949
Add a test for deleting a section
rcedwards Nov 24, 2015
35d810b
Add an icon to the sample app
rcedwards Nov 24, 2015
6b66452
Update markdown of docs to include highlighted types and define what …
rcedwards Nov 24, 2015
49997b6
Add documentation for the EntityMonitorDelegate
rcedwards Nov 24, 2015
cda79f5
Ensure didAccessValue is called always
rcedwards Nov 24, 2015
93bc5a2
Add documentation on the typealias within EntityMonitorDelegate
rcedwards Nov 24, 2015
b323fac
Reorganize FetchedResultsController class and document public API
rcedwards Nov 24, 2015
8e536bf
Update the example usage of the FetchedResultsController
rcedwards Nov 24, 2015
e18ec53
Fix typo in the docs
rcedwards Nov 30, 2015
56b1865
Capture the delegate object strongly within the BooksTableViewController
rcedwards Nov 30, 2015
18d2323
Update comment on issue in Swift compiler
rcedwards Nov 30, 2015
5b83ae9
Remove un-used custom sorted initializer
rcedwards Nov 30, 2015
81f5a5c
Syntactical update to sections mapping
rcedwards Nov 30, 2015
10b1cfe
Modify switch statement to ensure correct index paths are returned fo…
rcedwards Nov 30, 2015
abf9d63
Add John and Zach as authors to the pod spec
rcedwards Nov 30, 2015
f6abfa6
Handle inserts that have a newIndexPath
rcedwards Dec 1, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion BNRCoreDataStack.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ DESC
# s.license = { :type => "MIT", :file => "FILE_LICENSE" }


s.authors = ["Robert Edwards", "Brian Hardy"]
s.authors = ["Robert Edwards", "John Gallagher", "Brian Hardy", "Zachary Waldowski"]

s.ios.deployment_target = "8.0"
s.osx.deployment_target = "10.10"
Expand Down
24 changes: 24 additions & 0 deletions CoreDataStack.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,18 @@
E43A3C5F1BFCCB660019D6B4 /* EntityMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E43A3C5E1BFCCB660019D6B4 /* EntityMonitor.swift */; };
E43A3C651BFCD0F00019D6B4 /* EntityMonitorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E43A3C641BFCD0F00019D6B4 /* EntityMonitorTests.swift */; };
E43A3C671BFCD81A0019D6B4 /* CoreDataModelable.swift in Sources */ = {isa = PBXBuildFile; fileRef = E43A3C661BFCD81A0019D6B4 /* CoreDataModelable.swift */; };
E43A3C691BFCDDC50019D6B4 /* FetchedResultsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E43A3C681BFCDDC50019D6B4 /* FetchedResultsController.swift */; };
E4598B3A1B7CF80200F7DAD9 /* TestModel.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = E4C9BC531AEA857E00A6BD1B /* TestModel.xcdatamodeld */; };
E4598B3B1B7CF80800F7DAD9 /* Author.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41764DA1AEE854C00D89DBE /* Author.swift */; };
E4598B3C1B7CF80800F7DAD9 /* Book.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41764DC1AEE854D00D89DBE /* Book.swift */; };
E4598B3E1B7CF86800F7DAD9 /* TestModel.sqlite in Resources */ = {isa = PBXBuildFile; fileRef = E4598B3D1B7CF86800F7DAD9 /* TestModel.sqlite */; };
E4598B411B7CF8BF00F7DAD9 /* ModelMigrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4598B401B7CF8BF00F7DAD9 /* ModelMigrationTests.swift */; };
E45DE9DE1BFD33B8000A2F01 /* CoreDataModelableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E45DE9DD1BFD33B8000A2F01 /* CoreDataModelableTests.swift */; };
E47A83E01C038871001A047E /* BooksTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47A83DE1C038871001A047E /* BooksTableViewController.swift */; };
E47A83E91C03A4CD001A047E /* BooksTableViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = E47A83E81C03A4CD001A047E /* BooksTableViewController.xib */; };
E47A83EA1C03A4CD001A047E /* BooksTableViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = E47A83E81C03A4CD001A047E /* BooksTableViewController.xib */; };
E47A83EC1C03AE6A001A047E /* BooksTestData.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47A83EB1C03AE6A001A047E /* BooksTestData.swift */; };
E47A83ED1C03AE6A001A047E /* BooksTestData.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47A83EB1C03AE6A001A047E /* BooksTestData.swift */; };
E4B23F011B5EAE6C00A4F218 /* BatchOperationContextTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B23F001B5EAE6C00A4F218 /* BatchOperationContextTests.swift */; };
E4B99C691B740321005B7E1A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B99C681B740321005B7E1A /* AppDelegate.swift */; };
E4B99C6E1B740321005B7E1A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E4B99C6C1B740321005B7E1A /* Main.storyboard */; };
Expand All @@ -33,6 +39,7 @@
E4C9BC401AEA848600A6BD1B /* InitializationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C9BC3F1AEA848600A6BD1B /* InitializationTests.swift */; };
E4C9BC4D1AEA850600A6BD1B /* CoreDataStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C9BC4A1AEA850600A6BD1B /* CoreDataStack.swift */; };
E4C9BC4F1AEA850600A6BD1B /* NSManagedObjectContext+SaveHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C9BC4C1AEA850600A6BD1B /* NSManagedObjectContext+SaveHelpers.swift */; };
E4DB61441BFE771300BD3E6B /* FetchedResultsControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4DB61431BFE771300BD3E6B /* FetchedResultsControllerTests.swift */; };
E4DB61461BFFBB4400BD3E6B /* bnr-logo-square.png in Resources */ = {isa = PBXBuildFile; fileRef = E4DB61451BFFBB4400BD3E6B /* bnr-logo-square.png */; };
E4DB61481BFFBBD200BD3E6B /* MyCoreDataConnectedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4DB61471BFFBBD200BD3E6B /* MyCoreDataConnectedViewController.swift */; };
E4F59A461B505AE700C61516 /* StoreTeardownTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F59A451B505AE700C61516 /* StoreTeardownTests.swift */; };
Expand Down Expand Up @@ -65,10 +72,14 @@
E43A3C5E1BFCCB660019D6B4 /* EntityMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = EntityMonitor.swift; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
E43A3C641BFCD0F00019D6B4 /* EntityMonitorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = EntityMonitorTests.swift; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
E43A3C661BFCD81A0019D6B4 /* CoreDataModelable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreDataModelable.swift; sourceTree = "<group>"; };
E43A3C681BFCDDC50019D6B4 /* FetchedResultsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchedResultsController.swift; sourceTree = "<group>"; };
E4598B391B7CF43600F7DAD9 /* TestModel 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "TestModel 2.xcdatamodel"; sourceTree = "<group>"; };
E4598B3D1B7CF86800F7DAD9 /* TestModel.sqlite */ = {isa = PBXFileReference; lastKnownFileType = file; path = TestModel.sqlite; sourceTree = "<group>"; };
E4598B401B7CF8BF00F7DAD9 /* ModelMigrationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ModelMigrationTests.swift; sourceTree = "<group>"; };
E45DE9DD1BFD33B8000A2F01 /* CoreDataModelableTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreDataModelableTests.swift; sourceTree = "<group>"; };
E47A83DE1C038871001A047E /* BooksTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BooksTableViewController.swift; sourceTree = "<group>"; };
E47A83E81C03A4CD001A047E /* BooksTableViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = BooksTableViewController.xib; sourceTree = "<group>"; };
E47A83EB1C03AE6A001A047E /* BooksTestData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BooksTestData.swift; path = CoreDataStackTests/BooksTestData.swift; sourceTree = SOURCE_ROOT; };
E4B23F001B5EAE6C00A4F218 /* BatchOperationContextTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BatchOperationContextTests.swift; sourceTree = "<group>"; };
E4B99C661B740321005B7E1A /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
E4B99C681B740321005B7E1A /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
Expand All @@ -86,6 +97,7 @@
E4C9BC4A1AEA850600A6BD1B /* CoreDataStack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreDataStack.swift; sourceTree = "<group>"; };
E4C9BC4C1AEA850600A6BD1B /* NSManagedObjectContext+SaveHelpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+SaveHelpers.swift"; sourceTree = "<group>"; };
E4C9BC541AEA857E00A6BD1B /* TestModel.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = TestModel.xcdatamodel; sourceTree = "<group>"; };
E4DB61431BFE771300BD3E6B /* FetchedResultsControllerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchedResultsControllerTests.swift; sourceTree = "<group>"; };
E4DB61451BFFBB4400BD3E6B /* bnr-logo-square.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bnr-logo-square.png"; sourceTree = "<group>"; };
E4DB61471BFFBBD200BD3E6B /* MyCoreDataConnectedViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MyCoreDataConnectedViewController.swift; sourceTree = "<group>"; };
E4F59A451B505AE700C61516 /* StoreTeardownTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoreTeardownTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -121,6 +133,7 @@
E41764DE1AEE856F00D89DBE /* Test Model */ = {
isa = PBXGroup;
children = (
E47A83EB1C03AE6A001A047E /* BooksTestData.swift */,
E4598B3F1B7CF86F00F7DAD9 /* Migration Samples */,
E41764DA1AEE854C00D89DBE /* Author.swift */,
E41764DC1AEE854D00D89DBE /* Book.swift */,
Expand All @@ -134,6 +147,7 @@
isa = PBXGroup;
children = (
E43A3C5E1BFCCB660019D6B4 /* EntityMonitor.swift */,
E43A3C681BFCDDC50019D6B4 /* FetchedResultsController.swift */,
);
name = "Type Safe Monitors";
path = CoreDataStack;
Expand Down Expand Up @@ -165,6 +179,7 @@
isa = PBXGroup;
children = (
E43A3C641BFCD0F00019D6B4 /* EntityMonitorTests.swift */,
E4DB61431BFE771300BD3E6B /* FetchedResultsControllerTests.swift */,
);
name = "Type Safe Monitors Tests";
sourceTree = "<group>";
Expand All @@ -182,6 +197,8 @@
children = (
E4B99C681B740321005B7E1A /* AppDelegate.swift */,
E4DB61471BFFBBD200BD3E6B /* MyCoreDataConnectedViewController.swift */,
E47A83DE1C038871001A047E /* BooksTableViewController.swift */,
E47A83E81C03A4CD001A047E /* BooksTableViewController.xib */,
E4B99C6C1B740321005B7E1A /* Main.storyboard */,
E4B99C711B740321005B7E1A /* LaunchScreen.storyboard */,
E4DB61491BFFBD4E00BD3E6B /* Supporting Files */,
Expand Down Expand Up @@ -386,6 +403,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
E47A83EA1C03A4CD001A047E /* BooksTableViewController.xib in Resources */,
E4DB61461BFFBB4400BD3E6B /* bnr-logo-square.png in Resources */,
E4B99C731B740321005B7E1A /* LaunchScreen.storyboard in Resources */,
E4B99C701B740321005B7E1A /* Assets.xcassets in Resources */,
Expand All @@ -404,6 +422,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
E47A83E91C03A4CD001A047E /* BooksTableViewController.xib in Resources */,
E4598B3E1B7CF86800F7DAD9 /* TestModel.sqlite in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand All @@ -417,8 +436,10 @@
files = (
E4DB61481BFFBBD200BD3E6B /* MyCoreDataConnectedViewController.swift in Sources */,
E4598B3A1B7CF80200F7DAD9 /* TestModel.xcdatamodeld in Sources */,
E47A83E01C038871001A047E /* BooksTableViewController.swift in Sources */,
E4598B3B1B7CF80800F7DAD9 /* Author.swift in Sources */,
E4598B3C1B7CF80800F7DAD9 /* Book.swift in Sources */,
E47A83ED1C03AE6A001A047E /* BooksTestData.swift in Sources */,
E4B99C691B740321005B7E1A /* AppDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand All @@ -431,6 +452,7 @@
E43A3C671BFCD81A0019D6B4 /* CoreDataModelable.swift in Sources */,
E43A3C5F1BFCCB660019D6B4 /* EntityMonitor.swift in Sources */,
E4C9BC4F1AEA850600A6BD1B /* NSManagedObjectContext+SaveHelpers.swift in Sources */,
E43A3C691BFCDDC50019D6B4 /* FetchedResultsController.swift in Sources */,
E4C6F39C1AFD05E1004E3F1D /* NSPersistentStoreCoordinator+SQLiteHelpers.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand All @@ -445,6 +467,8 @@
69FC00381B7DBFB80002C1AC /* TempDirectoryTestCase.m in Sources */,
E4598B411B7CF8BF00F7DAD9 /* ModelMigrationTests.swift in Sources */,
E43A3C651BFCD0F00019D6B4 /* EntityMonitorTests.swift in Sources */,
E4DB61441BFE771300BD3E6B /* FetchedResultsControllerTests.swift in Sources */,
E47A83EC1C03AE6A001A047E /* BooksTestData.swift in Sources */,
E4F59A461B505AE700C61516 /* StoreTeardownTests.swift in Sources */,
E45DE9DE1BFD33B8000A2F01 /* CoreDataModelableTests.swift in Sources */,
E41764DD1AEE854D00D89DBE /* Book.swift in Sources */,
Expand Down
50 changes: 29 additions & 21 deletions CoreDataStack/CoreDataModelable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,32 @@
import CoreData

/**
Protocol to be conformed to by NSManagedObject subclasses that allow for convenience
Protocol to be conformed to by `NSManagedObject` subclasses that allow for convenience
methods that make fetching, inserting, deleting, and change management easier.
*/
public protocol CoreDataModelable {
/**
The name of your NSManagedObject's entity within the XCDataModel.
The name of your `NSManagedObject`'s entity within the `XCDataModel`.

- returns: String: Entity's name in XCDataModel
- returns: String: Entity's name in `XCDataModel`
*/
static var entityName: String { get }
}

/**
Extension to CoreDataModelable with convenience methods for
creating, deleting, and fetching entities from a specific NSManagedObjectContext.
Extension to `CoreDataModelable` with convenience methods for
creating, deleting, and fetching entities from a specific `NSManagedObjectContext`.
*/
public extension CoreDataModelable where Self: NSManagedObject {

// MARK: - Creating Objects

/**
Creates a new instance of the Entity within the specified NSManagedObjectContext.
Creates a new instance of the Entity within the specified `NSManagedObjectContext`.

- parameter context: NSManagedObjectContext to create the object within.
- parameter context: `NSManagedObjectContext` to create the object within.

- returns: Self: The newly created entity.
- returns: `Self`: The newly created entity.
*/
init(managedObjectContext context: NSManagedObjectContext) {
self.init(entity: Self.entityInContext(context), insertIntoManagedObjectContext: context)
Expand All @@ -51,12 +51,14 @@ public extension CoreDataModelable where Self: NSManagedObject {
// MARK: - Finding Objects

/**
Fetches the first Entity that matches the optional predicate within the specified NSManagedObjectContext.
Fetches the first Entity that matches the optional predicate within the specified `NSManagedObjectContext`.

- parameter predicate: An optional NSPredicate for filtering
- parameter context: NSManagedObjectContext to find the entities within.
- parameter predicate: An optional `NSPredicate` for filtering
- parameter context: `NSManagedObjectContext` to find the entities within.

- throws: Any error produced from `executeFetchRequest`

- returns: Self?: The first entity that matches the optional predicate or nil.
- returns: `Self?`: The first entity that matches the optional predicate or `nil`.
*/
static public func findFirst(predicate: NSPredicate?, context: NSManagedObjectContext) throws -> Self? {
let fetchRequest = fetchRequestForEntity(inContext: context)
Expand All @@ -68,12 +70,14 @@ public extension CoreDataModelable where Self: NSManagedObject {
}

/**
Fetches all Entities within the specified NSManagedObjectContext.
Fetches all Entities within the specified `NSManagedObjectContext`.

- parameter context: NSManagedObjectContext to find the entities within.
- parameter sortDescriptors: Optional array of NSSortDescriptors to apply to the fetch
- parameter context: `NSManagedObjectContext` to find the entities within.
- parameter sortDescriptors: Optional array of `NSSortDescriptors` to apply to the fetch

- returns: [Self]: The array of matching entities.
- throws: Any error produced from `executeFetchRequest`

- returns: `[Self]`: The array of matching entities.
*/
static public func allInContext(context: NSManagedObjectContext, sortDescriptors: [NSSortDescriptor]? = nil) throws -> [Self] {
let fetchRequest = fetchRequestForEntity(inContext: context)
Expand All @@ -84,20 +88,24 @@ public extension CoreDataModelable where Self: NSManagedObject {
// MARK: - Removing Objects

/**
Removes all entities from within the specified NSManagedObjectContext.
Removes all entities from within the specified `NSManagedObjectContext`.

- parameter context: NSManagedObjectContext to remove the entities from.
- parameter context: `NSManagedObjectContext` to remove the entities from.

- throws: Any error produced from `executeFetchRequest`
*/
static public func removeAll(context: NSManagedObjectContext) throws {
let fetchRequest = fetchRequestForEntity(inContext: context)
try removeAllObjectsReturnedByRequest(fetchRequest, inContext: context)
}

/**
Removes all entities from within the specified NSManagedObjectContext excluding a supplied array of entities.
Removes all entities from within the specified `NSManagedObjectContext` excluding a supplied array of entities.

- parameter toKeep: An Array of `NSManagedObjects` belonging to the `NSManagedObjectContext` to exclude from deletion.
- parameter inContext: The `NSManagedObjectContext` to remove the Entities from.

- parameter toKeep: An Array of NSManagedObjects belonging to the NSManagedObjectContext to exclude from deletion.
- parameter inContext: The NSManagedObjectContext to remove the Entities from.
- throws: Any error produced from `executeFetchRequest`
*/
static public func removeAllExcept(toKeep: [Self], inContext context: NSManagedObjectContext) throws {
let fetchRequest = fetchRequestForEntity(inContext: context)
Expand Down
Loading