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

Elevate performAndWaitOrThrow to public API #59

Merged
merged 4 commits into from
Dec 3, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions CoreDataStack.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
/* Begin PBXBuildFile section */
6979C2F11B8F58FA006E521B /* SaveTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6979C2EF1B8F58BB006E521B /* SaveTests.swift */; };
69FC00381B7DBFB80002C1AC /* TempDirectoryTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 69FC00371B7DBFB80002C1AC /* TempDirectoryTestCase.m */; };
DB2E9B751C0F8D8D00DAFF8F /* NSManagedObjectContext+AsyncHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB2E9B741C0F8D8D00DAFF8F /* NSManagedObjectContext+AsyncHelpers.swift */; };
DB2E9B771C0F8E9F00DAFF8F /* NSPersistentStoreCoordinator+AsyncHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB2E9B761C0F8E9F00DAFF8F /* NSPersistentStoreCoordinator+AsyncHelpers.swift */; };
E41764DB1AEE854C00D89DBE /* Author.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41764DA1AEE854C00D89DBE /* Author.swift */; };
E41764DD1AEE854D00D89DBE /* Book.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41764DC1AEE854D00D89DBE /* Book.swift */; };
E43A3C5F1BFCCB660019D6B4 /* EntityMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E43A3C5E1BFCCB660019D6B4 /* EntityMonitor.swift */; };
Expand Down Expand Up @@ -66,6 +68,8 @@
6979C2EF1B8F58BB006E521B /* SaveTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SaveTests.swift; sourceTree = "<group>"; };
69FC00361B7DBFB80002C1AC /* TempDirectoryTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TempDirectoryTestCase.h; sourceTree = "<group>"; };
69FC00371B7DBFB80002C1AC /* TempDirectoryTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TempDirectoryTestCase.m; sourceTree = "<group>"; };
DB2E9B741C0F8D8D00DAFF8F /* NSManagedObjectContext+AsyncHelpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+AsyncHelpers.swift"; sourceTree = "<group>"; };
DB2E9B761C0F8E9F00DAFF8F /* NSPersistentStoreCoordinator+AsyncHelpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSPersistentStoreCoordinator+AsyncHelpers.swift"; sourceTree = "<group>"; };
E41764DA1AEE854C00D89DBE /* Author.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Author.swift; sourceTree = "<group>"; };
E41764DC1AEE854D00D89DBE /* Book.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Book.swift; sourceTree = "<group>"; };
E41AF91E1AF82046004EB4B8 /* CoreDataStackTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CoreDataStackTests-Bridging-Header.h"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -273,8 +277,10 @@
E4C9BC521AEA851F00A6BD1B /* Extensions */ = {
isa = PBXGroup;
children = (
DB2E9B741C0F8D8D00DAFF8F /* NSManagedObjectContext+AsyncHelpers.swift */,
E4C9BC4C1AEA850600A6BD1B /* NSManagedObjectContext+SaveHelpers.swift */,
E4C6F39B1AFD05E1004E3F1D /* NSPersistentStoreCoordinator+SQLiteHelpers.swift */,
DB2E9B761C0F8E9F00DAFF8F /* NSPersistentStoreCoordinator+AsyncHelpers.swift */,
);
name = Extensions;
sourceTree = "<group>";
Expand Down Expand Up @@ -449,10 +455,12 @@
buildActionMask = 2147483647;
files = (
E4C9BC4D1AEA850600A6BD1B /* CoreDataStack.swift in Sources */,
DB2E9B771C0F8E9F00DAFF8F /* NSPersistentStoreCoordinator+AsyncHelpers.swift in Sources */,
E43A3C671BFCD81A0019D6B4 /* CoreDataModelable.swift in Sources */,
E43A3C5F1BFCCB660019D6B4 /* EntityMonitor.swift in Sources */,
E4C9BC4F1AEA850600A6BD1B /* NSManagedObjectContext+SaveHelpers.swift in Sources */,
E43A3C691BFCDDC50019D6B4 /* FetchedResultsController.swift in Sources */,
DB2E9B751C0F8D8D00DAFF8F /* NSManagedObjectContext+AsyncHelpers.swift in Sources */,
E4C6F39C1AFD05E1004E3F1D /* NSPersistentStoreCoordinator+SQLiteHelpers.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
33 changes: 7 additions & 26 deletions CoreDataStack/CoreDataStack.swift
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ public extension CoreDataStack {
resetCallback(.Failure(CoreDataStackError.InMemoryStoreMissing))
break
}
try self.persistentStoreCoordinator.performAndWait() {
try self.persistentStoreCoordinator.performAndWaitOrThrow {
try self.persistentStoreCoordinator.removePersistentStore(store)
try self.persistentStoreCoordinator.addPersistentStoreWithType(NSInMemoryStoreType, configuration: nil, URL: nil, options: nil)
}
Expand All @@ -249,13 +249,15 @@ public extension CoreDataStack {
if #available(iOS 9, OSX 10.11, *) {
try coordinator.destroyPersistentStoreAtURL(storeURL, withType: NSSQLiteStoreType, options: nil)
} else {
try coordinator.performAndWait() {
let fm = NSFileManager()
try coordinator.performAndWaitOrThrow {
try coordinator.removePersistentStore(store)
try NSFileManager.defaultManager().removeItemAtURL(storeURL)
try fm.removeItemAtURL(storeURL)

// Remove journal files if present
let _ = try? NSFileManager.defaultManager().removeItemAtURL(storeURL.URLByAppendingPathComponent("-shm"))
let _ = try? NSFileManager.defaultManager().removeItemAtURL(storeURL.URLByAppendingPathComponent("-wal"))
// Eat the error because different versions of SQLite might have different journal files
let _ = try? fm.removeItemAtURL(storeURL.URLByAppendingPathComponent("-shm"))
let _ = try? fm.removeItemAtURL(storeURL.URLByAppendingPathComponent("-wal"))
}
}
} catch let resetError {
Expand Down Expand Up @@ -368,24 +370,3 @@ private extension NSBundle {
return NSManagedObjectModel(contentsOfURL: URL)!
}
}

private extension NSPersistentStoreCoordinator {
private func performAndWait<Return>(body: () throws -> Return) throws -> Return {
var value: Return!
var error: ErrorType?

performBlockAndWait {
do {
value = try body()
} catch let theError {
error = theError
}
}

if let error = error {
throw error
} else {
return value
}
}
}
43 changes: 43 additions & 0 deletions CoreDataStack/NSManagedObjectContext+AsyncHelpers.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//
// NSManagedObjectContext+AsyncHelpers.swift
// CoreDataStack
//
// Created by Zachary Waldowski on 12/2/15.
// Copyright © 2015 Big Nerd Ranch. All rights reserved.
//

import CoreData
import Swift

extension NSManagedObjectContext {
/**
Synchronously exexcutes a given function on the receiver’s queue.

You use this method to safely address managed objects on a concurrent
queue.

- attention: This method may safely be called reentrantly.
- parameter body: The method body to perform on the reciever.
- returns: The value returned from the inner function.
- throws: Any error thrown by the inner function. This method should be
technically `rethrows`, but cannot be due to Swift limitations.
**/
public func performAndWaitOrThrow<Return>(body: () throws -> Return) throws -> Return {
var result: Return!
var thrown: ErrorType?

performBlockAndWait {
do {
result = try body()
} catch {
thrown = error
}
}

if let thrown = thrown {
throw thrown
} else {
return result
}
}
}
33 changes: 9 additions & 24 deletions CoreDataStack/NSManagedObjectContext+SaveHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,12 @@ public extension NSManagedObjectContext {
- throws: Errors produced by the `save()` function on the `NSManagedObjectContext`
*/
public func saveContextAndWait() throws {
var saveError: ErrorType?
switch concurrencyType {
case .ConfinementConcurrencyType:
try sharedSaveFlow()
case .MainQueueConcurrencyType,
.PrivateQueueConcurrencyType:
self.performBlockAndWait {
do {
try self.sharedSaveFlow()
} catch let error {
saveError = error
}
}
}

if let saveError = saveError {
throw saveError
try performAndWaitOrThrow(sharedSaveFlow)
}
}

Expand All @@ -50,9 +39,9 @@ public extension NSManagedObjectContext {
- parameter completion: Completion closure with a `SaveResult` to be executed upon the completion of the save operation.
*/
public func saveContext(completion: CoreDataStackSaveCompletion? = nil) {
let saveFlow: (CoreDataStackSaveCompletion?) -> () = { completion in
func saveFlow() {
do {
try self.sharedSaveFlow()
try sharedSaveFlow()
completion?(.Success)
} catch let saveError {
completion?(.Failure(saveError))
Expand All @@ -61,22 +50,18 @@ public extension NSManagedObjectContext {

switch concurrencyType {
case .ConfinementConcurrencyType:
saveFlow(completion)
saveFlow()
case .PrivateQueueConcurrencyType,
.MainQueueConcurrencyType:
self.performBlock {
saveFlow(completion)
}
performBlock(saveFlow)
}
}

private func sharedSaveFlow() throws {
if hasChanges {
do {
try save()
} catch let error {
throw error
}
guard hasChanges else {
return
}

try save()
}
}
40 changes: 40 additions & 0 deletions CoreDataStack/NSPersistentStoreCoordinator+AsyncHelpers.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//
// NSPersistentStoreCoordinator+AsyncHelpers.swift
// CoreDataStack
//
// Created by Zachary Waldowski on 12/2/15.
// Copyright © 2015 Big Nerd Ranch. All rights reserved.
//

import CoreData

extension NSPersistentStoreCoordinator {
/**
Synchronously exexcutes a given function on the coordinator's internal
queue.

- attention: This method may safely be called reentrantly.
- parameter body: The method body to perform on the reciever.
- returns: The value returned from the inner function.
- throws: Any error thrown by the inner function. This method should be
technically `rethrows`, but cannot be due to Swift limitations.
**/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing here for method docs

public func performAndWaitOrThrow<Return>(body: () throws -> Return) throws -> Return {
var result: Return!
var thrown: ErrorType?

performBlockAndWait {
do {
result = try body()
} catch {
thrown = error
}
}

if let thrown = thrown {
throw thrown
} else {
return result
}
}
}
21 changes: 10 additions & 11 deletions Example/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,20 +60,19 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
}

let moc = stack.newBackgroundWorkerMOC()
moc.performBlockAndWait() {
do {
do {
try moc.performAndWaitOrThrow {
let existingBooks = try Book.allInContext(moc)
if existingBooks.count == 0 {
let books = StubbedBookData.books
for bookTitle in books {
let book = Book(managedObjectContext: moc)
book.title = bookTitle
}
try moc.saveContextAndWait()
guard existingBooks.isEmpty else { return }
let books = StubbedBookData.books
for bookTitle in books {
let book = Book(managedObjectContext: moc)
book.title = bookTitle
}
} catch {
print("Error creating inital data: \(error)")
try moc.saveContextAndWait()
}
} catch {
print("Error creating inital data: \(error)")
}
}
}
Expand Down