Skip to content
This repository has been archived by the owner on Feb 28, 2020. It is now read-only.

Commit

Permalink
Merge branch 'release/6.0.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
vmartinelli committed Jun 30, 2018
2 parents 5132107 + 1585c26 commit 726c24f
Show file tree
Hide file tree
Showing 15 changed files with 382 additions and 143 deletions.
2 changes: 1 addition & 1 deletion AlecrimCoreData.podspec
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|

s.name = "AlecrimCoreData"
s.version = "6.0"
s.version = "6.0.1"
s.summary = "A powerful and elegant Core Data framework for Swift."
s.homepage = "https://www.alecrim.com/AlecrimCoreData"

Expand Down
16 changes: 12 additions & 4 deletions Source/AlecrimCoreData.xcodeproj/project.pbxproj
Expand Up @@ -11,8 +11,10 @@
1454786620B247F300831016 /* PersistentContainerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1454786520B247F300831016 /* PersistentContainerType.swift */; };
1454786820B2484900831016 /* PersistentContainerAuxiliarTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1454786720B2484800831016 /* PersistentContainerAuxiliarTypes.swift */; };
1454786A20B2487300831016 /* CustomPersistentContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1454786920B2487300831016 /* CustomPersistentContainer.swift */; };
1462C65F20C8E63A00A7A4E6 /* EntityObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1462C65E20C8E63A00A7A4E6 /* EntityObserver.swift */; };
146B042F208AEAE3002091BF /* FetchRequestController+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 146B042E208AEAE3002091BF /* FetchRequestController+Extensions.swift */; };
14B9460020759D0D00A7CFFD /* NSTableView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14B945FF20759D0D00A7CFFD /* NSTableView+Extensions.swift */; };
14C2028F20C8DBAB00821A79 /* ManagedObjectContextType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14C2028E20C8DBAB00821A79 /* ManagedObjectContextType.swift */; };
14CC3374205B28CA00BA682A /* NSArrayController+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14CC335E205B28CA00BA682A /* NSArrayController+Extensions.swift */; };
14CC3375205B28CA00BA682A /* UITableView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14CC335F205B28CA00BA682A /* UITableView+Extensions.swift */; };
14CC3376205B28CA00BA682A /* UICollectionView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14CC3360205B28CA00BA682A /* UICollectionView+Extensions.swift */; };
Expand Down Expand Up @@ -40,8 +42,10 @@
1454786520B247F300831016 /* PersistentContainerType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersistentContainerType.swift; sourceTree = "<group>"; };
1454786720B2484800831016 /* PersistentContainerAuxiliarTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersistentContainerAuxiliarTypes.swift; sourceTree = "<group>"; };
1454786920B2487300831016 /* CustomPersistentContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomPersistentContainer.swift; sourceTree = "<group>"; };
1462C65E20C8E63A00A7A4E6 /* EntityObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntityObserver.swift; sourceTree = "<group>"; };
146B042E208AEAE3002091BF /* FetchRequestController+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FetchRequestController+Extensions.swift"; sourceTree = "<group>"; };
14B945FF20759D0D00A7CFFD /* NSTableView+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTableView+Extensions.swift"; sourceTree = "<group>"; };
14C2028E20C8DBAB00821A79 /* ManagedObjectContextType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManagedObjectContextType.swift; sourceTree = "<group>"; };
14CC335E205B28CA00BA682A /* NSArrayController+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSArrayController+Extensions.swift"; sourceTree = "<group>"; };
14CC335F205B28CA00BA682A /* UITableView+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITableView+Extensions.swift"; sourceTree = "<group>"; };
14CC3360205B28CA00BA682A /* UICollectionView+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UICollectionView+Extensions.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -112,12 +116,14 @@
14CC335D205B28CA00BA682A /* Convenience */ = {
isa = PBXGroup;
children = (
1462C65E20C8E63A00A7A4E6 /* EntityObserver.swift */,
146B042E208AEAE3002091BF /* FetchRequestController+Extensions.swift */,
14CC3360205B28CA00BA682A /* UICollectionView+Extensions.swift */,
14CC335F205B28CA00BA682A /* UITableView+Extensions.swift */,
14C2028E20C8DBAB00821A79 /* ManagedObjectContextType.swift */,
14CC335E205B28CA00BA682A /* NSArrayController+Extensions.swift */,
14CC3361205B28CA00BA682A /* NSCollectionView+Extensions.swift */,
14B945FF20759D0D00A7CFFD /* NSTableView+Extensions.swift */,
14CC3360205B28CA00BA682A /* UICollectionView+Extensions.swift */,
14CC335F205B28CA00BA682A /* UITableView+Extensions.swift */,
);
path = Convenience;
sourceTree = "<group>";
Expand Down Expand Up @@ -261,13 +267,15 @@
14CC3374205B28CA00BA682A /* NSArrayController+Extensions.swift in Sources */,
14CC3385205B28CA00BA682A /* FetchedResultsControllerDelegate.swift in Sources */,
14CC337C205B28CA00BA682A /* Expression.swift in Sources */,
14C2028F20C8DBAB00821A79 /* ManagedObjectContextType.swift in Sources */,
14CC3380205B28CA00BA682A /* Config.swift in Sources */,
14CC3377205B28CA00BA682A /* NSCollectionView+Extensions.swift in Sources */,
1454786820B2484900831016 /* PersistentContainerAuxiliarTypes.swift in Sources */,
14CC3378205B28CA00BA682A /* PersistentContainer.swift in Sources */,
1454786620B247F300831016 /* PersistentContainerType.swift in Sources */,
14CC337B205B28CA00BA682A /* Query.swift in Sources */,
14CC3384205B28CA00BA682A /* FetchedResultsSectionInfo.swift in Sources */,
1462C65F20C8E63A00A7A4E6 /* EntityObserver.swift in Sources */,
14CC3375205B28CA00BA682A /* UITableView+Extensions.swift in Sources */,
14CC3379205B28CA00BA682A /* ManagedObjectContext.swift in Sources */,
14CC3383205B28CA00BA682A /* FetchRequestController.swift in Sources */,
Expand Down Expand Up @@ -412,7 +420,7 @@
CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1815;
CURRENT_PROJECT_VERSION = 1837;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
Expand Down Expand Up @@ -444,7 +452,7 @@
CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1815;
CURRENT_PROJECT_VERSION = 1837;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
Expand Down
45 changes: 45 additions & 0 deletions Source/AlecrimCoreData/Convenience/EntityObserver.swift
@@ -0,0 +1,45 @@
//
// EntityObserver.swift
// AlecrimCoreData
//
// Created by Vanderlei Martinelli on 07/06/18.
// Copyright © 2018 Alecrim. All rights reserved.
//

import Foundation

// MARK: -

/// A fetch request controller wrapper for one entity only. Can be used as observer for an entity detail presentation, for example.
public final class EntityObserver<EntityType: ManagedObject> {

private let frc: FetchRequestController<EntityType>

fileprivate init(entity: EntityType, propertyName: String, updateHandler didChangeContentClosure: @escaping () -> Void, context: ManagedObjectContext) {
self.frc = Query<EntityType>(in: context)
.batchSize(0)
.filtered(using: Predicate(format: "SELF == %@", argumentArray: [entity]))
.sorted(by: SortDescriptor(key: propertyName, ascending: true))
.toFetchRequestController()

self.frc.didChangeContent(closure: didChangeContentClosure)
}

deinit {
self.frc.removeAllBindings()
}

}

// MARK: -

extension PersistentContainerType {
public func observer<EntityType: ManagedObject>(for entity: EntityType, updateHandler: @escaping () -> Void) -> EntityObserver<EntityType> {
// using any property here is fine, but there must be at least one property
guard let propertyName = entity.entity.properties.first(where: { $0.isTransient == false })?.name else {
fatalError("No property found.")
}

return EntityObserver(entity: entity, propertyName: propertyName, updateHandler: updateHandler, context: self.viewContext)
}
}
133 changes: 133 additions & 0 deletions Source/AlecrimCoreData/Convenience/ManagedObjectContextType.swift
@@ -0,0 +1,133 @@
//
// ManagedObjectContextType.swift
// AlecrimCoreData
//
// Created by Vanderlei Martinelli on 07/06/18.
// Copyright © 2018 Alecrim. All rights reserved.
//

import Foundation

// MARK: -

extension ManagedObjectContext: ManagedObjectContextType {}

// MARK: -

public protocol ManagedObjectContextType {
func perform(_ block: @escaping () -> Void)
func performAndWait(_ block: () -> Void)
}

// MARK: -

extension ManagedObjectContextType {

public func async<Value>(execute closure: @escaping (Self) throws -> Value, completion: @escaping ((Value?, Error?) -> Void)) {
let context = self

context.perform {
do {
let value = try closure(context)
completion(value, nil)
}
catch {
completion(nil, error)
}
}
}

public func async<Value>(execute closure: @escaping (Self) -> Value, completion: ((Value) -> Void)? = nil) {
let context = self

context.perform {
let value = closure(context)
completion?(value)
}
}

@discardableResult
public func sync<Value>(execute closure: (Self) throws -> Value) throws -> Value {
var value: Value?
var outError: Error?

let context = self

context.performAndWait {
do {
value = try closure(context)
}
catch {
outError = error
}
}

if let outError = outError {
throw outError
}

return value!
}

@discardableResult
public func sync<Value>(execute closure: (Self) -> Value) -> Value {
var value: Value?

let context = self

context.performAndWait {
value = closure(context)
}

return value!
}

}

// MARK: -

extension PersistentContainer {

public func async<Value>(execute closure: @escaping (ManagedObjectContext) throws -> Value, completion: @escaping ((Value?, Error?) -> Void)) {
return self.backgroundContext.async(execute: closure, completion: completion)
}

public func async<Value>(execute closure: @escaping (ManagedObjectContext) -> Value, completion: ((Value) -> Void)? = nil) {
return self.backgroundContext.async(execute: closure, completion: completion)
}

@discardableResult
public func sync<Value>(execute closure: (ManagedObjectContext) throws -> Value) throws -> Value {
return try self.backgroundContext.sync(execute: closure)
}

@discardableResult
public func sync<Value>(execute closure: (ManagedObjectContext) -> Value) -> Value {
return self.backgroundContext.sync(execute: closure)
}

}

// MARK: -

extension CustomPersistentContainer {

public func async<Value>(execute closure: @escaping (Context) throws -> Value, completion: @escaping ((Value?, Error?) -> Void)) {
return self.backgroundContext.async(execute: closure, completion: completion)
}

public func async<Value>(execute closure: @escaping (Context) -> Value, completion: ((Value) -> Void)? = nil) {
return self.backgroundContext.async(execute: closure, completion: completion)
}

@discardableResult
public func sync<Value>(execute closure: (Context) throws -> Value) throws -> Value {
return try self.backgroundContext.sync(execute: closure)
}

@discardableResult
public func sync<Value>(execute closure: (Context) -> Value) -> Value {
return self.backgroundContext.sync(execute: closure)
}

}
Expand Up @@ -126,6 +126,12 @@ extension FetchRequestController {

case .move(let oldIndexPath, let newIndexPath):
collectionView.moveItem(at: oldIndexPath, to: newIndexPath)

// workaround to be sure that cells will be refreshed
// note: this only works when using a cell configuration handler
if itemConfigurationHandler != nil {
updatedIndexPaths.append(newIndexPath)
}
}
}
}, completionHandler: { _ in
Expand Down
Expand Up @@ -122,6 +122,12 @@ extension FetchRequestController {

case .move(let oldIndexPath, let newIndexPath):
collectionView.moveItem(at: oldIndexPath, to: newIndexPath)

// workaround to be sure that cells will be refreshed
// note: this only works when using a cell configuration handler
if cellConfigurationHandler != nil {
updatedIndexPaths.append(newIndexPath)
}
}
}
}, completion: { _ in
Expand Down
Expand Up @@ -12,62 +12,26 @@ import CoreData
open class CustomPersistentContainer<Context: NSManagedObjectContext> {

// MARK: -

fileprivate final class HelperPersistentContainer<Context: NSManagedObjectContext>: PersistentContainer {

private lazy var _viewContext: NSManagedObjectContext = {
let context = Context(concurrencyType: .mainQueueConcurrencyType)

context.persistentStoreCoordinator = self.persistentStoreCoordinator

context.automaticallyMergesChangesFromParent = true
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy

return context
}()

fileprivate override var viewContext: NSManagedObjectContext { return self._viewContext }

fileprivate override func newBackgroundContext() -> NSManagedObjectContext {
let context = Context(concurrencyType: .privateQueueConcurrencyType)

context.persistentStoreCoordinator = self.persistentStoreCoordinator

context.automaticallyMergesChangesFromParent = true
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy

return context
}

fileprivate override func performBackgroundTask(_ block: @escaping (NSManagedObjectContext) -> Void) {
super.performBackgroundTask { context in
//
context.persistentStoreCoordinator = self.persistentStoreCoordinator

//
context.automaticallyMergesChangesFromParent = true
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy

//
block(context)
}
}

}


public private(set) lazy var backgroundContext: Context = self.newBackgroundContext()

// MARK: -

fileprivate let rawValue: NSPersistentContainer

// MARK: -

public convenience init() {
try! self.init(storageType: .disk, managedObjectModel: type(of: self).managedObjectModel(), persistentStoreURL: type(of: self).persistentStoreURL(), ubiquitousConfiguration: nil)
/// Use caution when using this initializer.
public init(name: String? = nil) {
self.rawValue = HelperPersistentContainer<Context>(name: name)
}

public init(storageType: PersistentContainerStorageType = .disk, managedObjectModel: NSManagedObjectModel, persistentStoreURL: URL, ubiquitousConfiguration: PersistentContainerUbiquitousConfiguration? = nil) throws {
self.rawValue = try HelperPersistentContainer<Context>(storageType: storageType, managedObjectModel: managedObjectModel, persistentStoreURL: persistentStoreURL, ubiquitousConfiguration: ubiquitousConfiguration)

public init(name: String? = nil, managedObjectModel: NSManagedObjectModel, storageType: PersistentContainerStorageType, persistentStoreURL: URL, persistentStoreDescriptionOptions: [String : NSObject]? = nil, ubiquitousConfiguration: PersistentContainerUbiquitousConfiguration? = nil) throws {
self.rawValue = try HelperPersistentContainer<Context>(name: name, managedObjectModel: managedObjectModel, storageType: storageType, persistentStoreURL: persistentStoreURL, persistentStoreDescriptionOptions: persistentStoreDescriptionOptions, ubiquitousConfiguration: ubiquitousConfiguration)
}

public init(name: String, managedObjectModel: NSManagedObjectModel, persistentStoreDescription: NSPersistentStoreDescription, completionHandler: @escaping (NSPersistentContainer, NSPersistentStoreDescription, Error?) -> Void) throws {
self.rawValue = try HelperPersistentContainer<Context>(name: name, managedObjectModel: managedObjectModel, persistentStoreDescription: persistentStoreDescription, completionHandler: completionHandler)
}

// MARK: -
Expand All @@ -87,3 +51,34 @@ open class CustomPersistentContainer<Context: NSManagedObjectContext> {
}

}

// MARK: -

extension CustomPersistentContainer {

fileprivate final class HelperPersistentContainer<Context: NSManagedObjectContext>: PersistentContainer {

private lazy var _viewContext: NSManagedObjectContext = {
let context = Context(concurrencyType: .mainQueueConcurrencyType)
self.configureManagedObjectContext(context)

return context
}()

fileprivate override var viewContext: NSManagedObjectContext { return self._viewContext }

fileprivate override func newBackgroundContext() -> NSManagedObjectContext {
let context = Context(concurrencyType: .privateQueueConcurrencyType)
self.configureManagedObjectContext(context)

return context
}

fileprivate override func configureManagedObjectContext(_ context: NSManagedObjectContext) {
context.persistentStoreCoordinator = self.persistentStoreCoordinator
super.configureManagedObjectContext(context)
}

}

}

0 comments on commit 726c24f

Please sign in to comment.