From 66a1c645b5a92496d8163a3e5771655bae80ad2e Mon Sep 17 00:00:00 2001 From: Ondrej Rafaj Date: Wed, 9 May 2018 23:38:51 +0100 Subject: [PATCH 1/3] ideal API? --- Classes/Protocols/Entity.swift | 6 +++++- Classes/Protocols/QueryExecutable.swift | 5 +++++ Reloaded.podspec | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Classes/Protocols/Entity.swift b/Classes/Protocols/Entity.swift index 7cf42ae..2c7f7cc 100644 --- a/Classes/Protocols/Entity.swift +++ b/Classes/Protocols/Entity.swift @@ -32,7 +32,11 @@ extension Entity where Self: NSManagedObject { /// Create new query public static var query: Query { - print(self) + return Query(self) + } + + /// Create new query + public static func query(on: DispatchQueue) -> Query { return Query(self) } diff --git a/Classes/Protocols/QueryExecutable.swift b/Classes/Protocols/QueryExecutable.swift index f6da214..ce9b839 100644 --- a/Classes/Protocols/QueryExecutable.swift +++ b/Classes/Protocols/QueryExecutable.swift @@ -43,6 +43,7 @@ extension QueryExecutable { extension QueryExecutable where EntityType: NSManagedObject { + /// Return all data based on your query public func all(on context: NSManagedObjectContext = CoreData.managedContext) throws -> [EntityType] { guard let data = try context.fetch(fetchRequest()) as? [EntityType] else { return [] @@ -50,12 +51,14 @@ extension QueryExecutable where EntityType: NSManagedObject { return data } + /// Delete all data captured by your query public func delete(on context: NSManagedObjectContext = CoreData.managedContext) throws { for object in try all(on: context) { try object.delete(on: context) } try context.save() + // TODO: Fix the following batch request! // let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest()) // do { // let batchDeleteResult = try context.execute(deleteRequest) as! NSBatchDeleteResult @@ -66,10 +69,12 @@ extension QueryExecutable where EntityType: NSManagedObject { // } } + /// Count the number of items in your query public func count(on context: NSManagedObjectContext = CoreData.managedContext) throws -> Int { return try context.count(for: fetchRequest()) } + /// Get first result of your query public func first(on context: NSManagedObjectContext = CoreData.managedContext) throws -> EntityType? { return try context.fetch(fetchRequest()).first as? EntityType } diff --git a/Reloaded.podspec b/Reloaded.podspec index a698584..0ac7d00 100644 --- a/Reloaded.podspec +++ b/Reloaded.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = 'Reloaded' - s.version = '1.0.1' + s.version = '1.1.0' s.summary = 'Reloaded! Swift "ORM like" abstraction layer for CoreData' s.swift_version = '4.0' From 3a06f33aeebf2e7490b6fa2518c94c48827be5f4 Mon Sep 17 00:00:00 2001 From: Ondrej Rafaj Date: Thu, 10 May 2018 19:58:02 +0100 Subject: [PATCH 2/3] multithreading API mockup 2 --- .../QueryExecutable+ManagedObject.swift | 51 +++++++++++++++++++ .../QueryExecutable+Threading.swift | 41 +++++++++++++++ Classes/Libs/CoreDataPromise.swift | 14 +++++ Classes/Protocols/Entity.swift | 5 -- Classes/Protocols/QueryExecutable.swift | 40 --------------- Reloaded.xcodeproj/project.pbxproj | 48 ++++++++++------- 6 files changed, 136 insertions(+), 63 deletions(-) create mode 100644 Classes/Extensions/QueryExecutable+ManagedObject.swift create mode 100644 Classes/Extensions/QueryExecutable+Threading.swift create mode 100644 Classes/Libs/CoreDataPromise.swift diff --git a/Classes/Extensions/QueryExecutable+ManagedObject.swift b/Classes/Extensions/QueryExecutable+ManagedObject.swift new file mode 100644 index 0000000..24e40f2 --- /dev/null +++ b/Classes/Extensions/QueryExecutable+ManagedObject.swift @@ -0,0 +1,51 @@ +// +// QueryExecutable+ManagedObject.swift +// Reloaded +// +// Created by Ondrej Rafaj on 10/05/2018. +// Copyright © 2018 LiveUI. All rights reserved. +// + +import Foundation +import CoreData + + +extension QueryExecutable where EntityType: NSManagedObject { + + /// Return all data based on your query + public func all(on context: NSManagedObjectContext = CoreData.managedContext) throws -> [EntityType] { + guard let data = try context.fetch(fetchRequest()) as? [EntityType] else { + return [] + } + return data + } + + /// Delete all data captured by your query + public func delete(on context: NSManagedObjectContext = CoreData.managedContext) throws { + for object in try all(on: context) { + try object.delete(on: context) + } + try context.save() + + // TODO: Fix the following batch request! + // let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest()) + // do { + // let batchDeleteResult = try context.execute(deleteRequest) as! NSBatchDeleteResult + // print("The batch delete request has deleted \(batchDeleteResult.result!) records.") + // } catch { + // let updateError = error as NSError + // print("\(updateError), \(updateError.userInfo)") + // } + } + + /// Count the number of items in your query + public func count(on context: NSManagedObjectContext = CoreData.managedContext) throws -> Int { + return try context.count(for: fetchRequest()) + } + + /// Get first result of your query + public func first(on context: NSManagedObjectContext = CoreData.managedContext) throws -> EntityType? { + return try context.fetch(fetchRequest()).first as? EntityType + } + +} diff --git a/Classes/Extensions/QueryExecutable+Threading.swift b/Classes/Extensions/QueryExecutable+Threading.swift new file mode 100644 index 0000000..45fca79 --- /dev/null +++ b/Classes/Extensions/QueryExecutable+Threading.swift @@ -0,0 +1,41 @@ +// +// QueryExecutable+Threading.swift +// Reloaded +// +// Created by Ondrej Rafaj on 10/05/2018. +// Copyright © 2018 LiveUI. All rights reserved. +// + +import Foundation +import CoreData + + +extension QueryExecutable where EntityType: NSManagedObject { + + /// Return all data based on your query + public func all(on queue: DispatchQueue) -> CoreDataPromise<[EntityType]> { + // Create or get the right context for the desired queue + // Execute query on the thread for the context + // Fulfill promise + // Dispose of the context if no-one is using it anymore + fatalError() + } + + /// Delete all data captured by your query + public func delete(on queue: DispatchQueue) -> CoreDataPromise { + // All above + + // Update all contexts + fatalError() + } + + /// Count the number of items in your query + public func count(on queue: DispatchQueue) -> CoreDataPromise{ + fatalError() + } + + /// Get first result of your query + public func first(on queue: DispatchQueue) -> CoreDataPromise { + fatalError() + } + +} diff --git a/Classes/Libs/CoreDataPromise.swift b/Classes/Libs/CoreDataPromise.swift new file mode 100644 index 0000000..8490e16 --- /dev/null +++ b/Classes/Libs/CoreDataPromise.swift @@ -0,0 +1,14 @@ +// +// CoreDataPromise.swift +// Reloaded +// +// Created by Ondrej Rafaj on 10/05/2018. +// Copyright © 2018 LiveUI. All rights reserved. +// + +import Foundation + + +public class CoreDataPromise { + +} diff --git a/Classes/Protocols/Entity.swift b/Classes/Protocols/Entity.swift index 2c7f7cc..4ba12ac 100644 --- a/Classes/Protocols/Entity.swift +++ b/Classes/Protocols/Entity.swift @@ -35,11 +35,6 @@ extension Entity where Self: NSManagedObject { return Query(self) } - /// Create new query - public static func query(on: DispatchQueue) -> Query { - return Query(self) - } - /// Basic fetch request public static var fetchRequest: Request { let fetch = Request(entityName: entityName) diff --git a/Classes/Protocols/QueryExecutable.swift b/Classes/Protocols/QueryExecutable.swift index ce9b839..6e4846c 100644 --- a/Classes/Protocols/QueryExecutable.swift +++ b/Classes/Protocols/QueryExecutable.swift @@ -40,43 +40,3 @@ extension QueryExecutable { } } - -extension QueryExecutable where EntityType: NSManagedObject { - - /// Return all data based on your query - public func all(on context: NSManagedObjectContext = CoreData.managedContext) throws -> [EntityType] { - guard let data = try context.fetch(fetchRequest()) as? [EntityType] else { - return [] - } - return data - } - - /// Delete all data captured by your query - public func delete(on context: NSManagedObjectContext = CoreData.managedContext) throws { - for object in try all(on: context) { - try object.delete(on: context) - } - try context.save() - - // TODO: Fix the following batch request! -// let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest()) -// do { -// let batchDeleteResult = try context.execute(deleteRequest) as! NSBatchDeleteResult -// print("The batch delete request has deleted \(batchDeleteResult.result!) records.") -// } catch { -// let updateError = error as NSError -// print("\(updateError), \(updateError.userInfo)") -// } - } - - /// Count the number of items in your query - public func count(on context: NSManagedObjectContext = CoreData.managedContext) throws -> Int { - return try context.count(for: fetchRequest()) - } - - /// Get first result of your query - public func first(on context: NSManagedObjectContext = CoreData.managedContext) throws -> EntityType? { - return try context.fetch(fetchRequest()).first as? EntityType - } - -} diff --git a/Reloaded.xcodeproj/project.pbxproj b/Reloaded.xcodeproj/project.pbxproj index 5e26d12..d93047e 100644 --- a/Reloaded.xcodeproj/project.pbxproj +++ b/Reloaded.xcodeproj/project.pbxproj @@ -28,6 +28,18 @@ 15115DA7206E1E5000BC08D3 /* QuerySort.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15115DA3206E1E5000BC08D3 /* QuerySort.swift */; }; 15115DED206E5AF400BC08D3 /* Setup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15B75910206D36460080AB5E /* Setup.swift */; }; 15115DEE206E5AFC00BC08D3 /* ReloadedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15B75878206D30FD0080AB5E /* ReloadedTests.swift */; }; + 15A7BD1320A4CB4D000BAD54 /* QueryExecutable+ManagedObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15A7BD1220A4CB4D000BAD54 /* QueryExecutable+ManagedObject.swift */; }; + 15A7BD1420A4CB4D000BAD54 /* QueryExecutable+ManagedObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15A7BD1220A4CB4D000BAD54 /* QueryExecutable+ManagedObject.swift */; }; + 15A7BD1520A4CB4D000BAD54 /* QueryExecutable+ManagedObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15A7BD1220A4CB4D000BAD54 /* QueryExecutable+ManagedObject.swift */; }; + 15A7BD1620A4CB4D000BAD54 /* QueryExecutable+ManagedObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15A7BD1220A4CB4D000BAD54 /* QueryExecutable+ManagedObject.swift */; }; + 15A7BD1820A4CB72000BAD54 /* QueryExecutable+Threading.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15A7BD1720A4CB72000BAD54 /* QueryExecutable+Threading.swift */; }; + 15A7BD1920A4CB72000BAD54 /* QueryExecutable+Threading.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15A7BD1720A4CB72000BAD54 /* QueryExecutable+Threading.swift */; }; + 15A7BD1A20A4CB73000BAD54 /* QueryExecutable+Threading.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15A7BD1720A4CB72000BAD54 /* QueryExecutable+Threading.swift */; }; + 15A7BD1B20A4CB73000BAD54 /* QueryExecutable+Threading.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15A7BD1720A4CB72000BAD54 /* QueryExecutable+Threading.swift */; }; + 15A7BD1D20A4CBF8000BAD54 /* CoreDataPromise.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15A7BD1C20A4CBF8000BAD54 /* CoreDataPromise.swift */; }; + 15A7BD1E20A4CBF8000BAD54 /* CoreDataPromise.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15A7BD1C20A4CBF8000BAD54 /* CoreDataPromise.swift */; }; + 15A7BD1F20A4CBF8000BAD54 /* CoreDataPromise.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15A7BD1C20A4CBF8000BAD54 /* CoreDataPromise.swift */; }; + 15A7BD2020A4CBF8000BAD54 /* CoreDataPromise.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15A7BD1C20A4CBF8000BAD54 /* CoreDataPromise.swift */; }; 15B7587B206D30FD0080AB5E /* Reloaded.h in Headers */ = {isa = PBXBuildFile; fileRef = 15B7586D206D30FD0080AB5E /* Reloaded.h */; settings = {ATTRIBUTES = (Public, ); }; }; 15B75894206D316E0080AB5E /* Array+NSPredicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15B75886206D316E0080AB5E /* Array+NSPredicate.swift */; }; 15B75895206D316E0080AB5E /* Query.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15B75888206D316E0080AB5E /* Query.swift */; }; @@ -109,10 +121,12 @@ 15115DA3206E1E5000BC08D3 /* QuerySort.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuerySort.swift; sourceTree = ""; }; 15115DAF206E256800BC08D3 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; 15115DB0206E256800BC08D3 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; - 15115DC0206E548700BC08D3 /* CoreData+Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CoreData+Tests.swift"; sourceTree = ""; }; 15115DDC206E592A00BC08D3 /* ReloadedTests-iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "ReloadedTests-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 15115DE0206E592B00BC08D3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 15116A662070236100BC08D3 /* Reloaded.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = Reloaded.podspec; sourceTree = ""; }; + 15A7BD1220A4CB4D000BAD54 /* QueryExecutable+ManagedObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "QueryExecutable+ManagedObject.swift"; sourceTree = ""; }; + 15A7BD1720A4CB72000BAD54 /* QueryExecutable+Threading.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "QueryExecutable+Threading.swift"; sourceTree = ""; }; + 15A7BD1C20A4CBF8000BAD54 /* CoreDataPromise.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataPromise.swift; sourceTree = ""; }; 15B7586A206D30FD0080AB5E /* Reloaded.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Reloaded.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 15B7586D206D30FD0080AB5E /* Reloaded.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Reloaded.h; sourceTree = ""; }; 15B7586E206D30FD0080AB5E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -229,22 +243,6 @@ name = Other; sourceTree = ""; }; - 15115DBE206E543400BC08D3 /* ReloadedTestTools */ = { - isa = PBXGroup; - children = ( - 15115DBF206E545D00BC08D3 /* Extensions */, - ); - path = ReloadedTestTools; - sourceTree = ""; - }; - 15115DBF206E545D00BC08D3 /* Extensions */ = { - isa = PBXGroup; - children = ( - 15115DC0206E548700BC08D3 /* CoreData+Tests.swift */, - ); - path = Extensions; - sourceTree = ""; - }; 15115DC4206E564500BC08D3 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -266,7 +264,6 @@ 15115DAE206E255100BC08D3 /* Other */, 15115D72206D853800BC08D3 /* Demo-iOS */, 15B75884206D316E0080AB5E /* Classes */, - 15115DBE206E543400BC08D3 /* ReloadedTestTools */, 15B7586C206D30FD0080AB5E /* Reloaded-macOS */, 15B758A5206D32B00080AB5E /* Reloaded-iOS */, 15B758C1206D32E50080AB5E /* Reloaded-tvOS */, @@ -326,6 +323,8 @@ isa = PBXGroup; children = ( 15B75886206D316E0080AB5E /* Array+NSPredicate.swift */, + 15A7BD1220A4CB4D000BAD54 /* QueryExecutable+ManagedObject.swift */, + 15A7BD1720A4CB72000BAD54 /* QueryExecutable+Threading.swift */, ); path = Extensions; sourceTree = ""; @@ -337,6 +336,7 @@ 15B7588A206D316E0080AB5E /* QueryField.swift */, 15B7588B206D316E0080AB5E /* Filter */, 15115DA2206E1E2F00BC08D3 /* Sorting */, + 15A7BD1C20A4CBF8000BAD54 /* CoreDataPromise.swift */, ); path = Libs; sourceTree = ""; @@ -677,9 +677,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 15A7BD1D20A4CBF8000BAD54 /* CoreDataPromise.swift in Sources */, 15115D99206E1B4600BC08D3 /* QueryFilter.swift in Sources */, 15B7589A206D316E0080AB5E /* Filters.swift in Sources */, 15115DA4206E1E5000BC08D3 /* QuerySort.swift in Sources */, + 15A7BD1820A4CB72000BAD54 /* QueryExecutable+Threading.swift in Sources */, 15B75896206D316E0080AB5E /* Sorting.swift in Sources */, 15B75897206D316E0080AB5E /* QueryField.swift in Sources */, 15B75899206D316E0080AB5E /* QueryFilterType.swift in Sources */, @@ -690,6 +692,7 @@ 15B75895206D316E0080AB5E /* Query.swift in Sources */, 15B75894206D316E0080AB5E /* Array+NSPredicate.swift in Sources */, 15B7589D206D316E0080AB5E /* QueryExecutable.swift in Sources */, + 15A7BD1320A4CB4D000BAD54 /* QueryExecutable+ManagedObject.swift in Sources */, 15B75898206D316E0080AB5E /* QueryFilterValue.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -698,9 +701,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 15A7BD1E20A4CBF8000BAD54 /* CoreDataPromise.swift in Sources */, 15115D9A206E1B4600BC08D3 /* QueryFilter.swift in Sources */, 15B758DD206D330D0080AB5E /* Filters.swift in Sources */, 15115DA5206E1E5000BC08D3 /* QuerySort.swift in Sources */, + 15A7BD1920A4CB72000BAD54 /* QueryExecutable+Threading.swift in Sources */, 15B758D9206D330D0080AB5E /* Sorting.swift in Sources */, 15B758DA206D330D0080AB5E /* QueryField.swift in Sources */, 15B758DC206D330D0080AB5E /* QueryFilterType.swift in Sources */, @@ -711,6 +716,7 @@ 15B758D8206D330D0080AB5E /* Query.swift in Sources */, 15B758D7206D330D0080AB5E /* Array+NSPredicate.swift in Sources */, 15B758E0206D330D0080AB5E /* QueryExecutable.swift in Sources */, + 15A7BD1420A4CB4D000BAD54 /* QueryExecutable+ManagedObject.swift in Sources */, 15B758DB206D330D0080AB5E /* QueryFilterValue.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -719,9 +725,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 15A7BD1F20A4CBF8000BAD54 /* CoreDataPromise.swift in Sources */, 15115D9B206E1B4600BC08D3 /* QueryFilter.swift in Sources */, 15B758E8206D330E0080AB5E /* Filters.swift in Sources */, 15115DA6206E1E5000BC08D3 /* QuerySort.swift in Sources */, + 15A7BD1A20A4CB73000BAD54 /* QueryExecutable+Threading.swift in Sources */, 15B758E4206D330E0080AB5E /* Sorting.swift in Sources */, 15B758E5206D330E0080AB5E /* QueryField.swift in Sources */, 15B758E7206D330E0080AB5E /* QueryFilterType.swift in Sources */, @@ -732,6 +740,7 @@ 15B758E3206D330E0080AB5E /* Query.swift in Sources */, 15B758E2206D330E0080AB5E /* Array+NSPredicate.swift in Sources */, 15B758EB206D330E0080AB5E /* QueryExecutable.swift in Sources */, + 15A7BD1520A4CB4D000BAD54 /* QueryExecutable+ManagedObject.swift in Sources */, 15B758E6206D330E0080AB5E /* QueryFilterValue.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -740,9 +749,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 15A7BD2020A4CBF8000BAD54 /* CoreDataPromise.swift in Sources */, 15115D9C206E1B4600BC08D3 /* QueryFilter.swift in Sources */, 15B75900206D33720080AB5E /* Filters.swift in Sources */, 15115DA7206E1E5000BC08D3 /* QuerySort.swift in Sources */, + 15A7BD1B20A4CB73000BAD54 /* QueryExecutable+Threading.swift in Sources */, 15B758FC206D33720080AB5E /* Sorting.swift in Sources */, 15B758FD206D33720080AB5E /* QueryField.swift in Sources */, 15B758FF206D33720080AB5E /* QueryFilterType.swift in Sources */, @@ -753,6 +764,7 @@ 15B758FB206D33720080AB5E /* Query.swift in Sources */, 15B758FA206D33720080AB5E /* Array+NSPredicate.swift in Sources */, 15B75903206D33720080AB5E /* QueryExecutable.swift in Sources */, + 15A7BD1620A4CB4D000BAD54 /* QueryExecutable+ManagedObject.swift in Sources */, 15B758FE206D33720080AB5E /* QueryFilterValue.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; From d8444ff42097706db24030020c23647006a6e67d Mon Sep 17 00:00:00 2001 From: Ondrej Rafaj Date: Fri, 11 May 2018 16:27:45 +0100 Subject: [PATCH 3/3] wip --- Classes/CoreData.swift | 33 ++++++++++++++--- .../QueryExecutable+ManagedObject.swift | 30 ++++++++------- .../QueryExecutable+Threading.swift | 8 ++-- Classes/Libs/CoreDataPromise.swift | 37 ++++++++++++++++++- Demo-iOS/ViewController.swift | 3 ++ 5 files changed, 87 insertions(+), 24 deletions(-) diff --git a/Classes/CoreData.swift b/Classes/CoreData.swift index 77c9174..b032591 100644 --- a/Classes/CoreData.swift +++ b/Classes/CoreData.swift @@ -42,14 +42,35 @@ public class CoreData { // MARK: Basic methods + private var _privateManagedContext: NSManagedObjectContext? + private var _managedContext: NSManagedObjectContext? + + /// Private managed object context for this instance + public lazy var privateManagedContext: NSManagedObjectContext = { + guard let privateManagedContext = _privateManagedContext else { + // Create a background managed context as root context so the + // main thread should not be blocked on more expensive opersations + let privateManagedContext = persistentContainer.newBackgroundContext() + _privateManagedContext = privateManagedContext + return privateManagedContext + } + return privateManagedContext + }() + /// Managed context for this instance - public var managedContext: NSManagedObjectContext { - return persistentContainer.viewContext - } + public lazy var managedContext: NSManagedObjectContext = { + guard let managedContext = _managedContext else { + let managedContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType) + managedContext.parent = privateManagedContext + _managedContext = managedContext + return managedContext + } + return managedContext + }() /// Default managed context public static var managedContext: NSManagedObjectContext { - return CoreData.default.persistentContainer.viewContext + return CoreData.default.managedContext } /// Create new entry on the default context @@ -78,12 +99,12 @@ public class CoreData { /// Save managed context if it has changes public func saveContext() throws { - try CoreData.save(context: managedContext) + try CoreData.save(context: privateManagedContext) } /// Save default managed context if it has changes public static func saveContext() throws { - try CoreData.save(context: managedContext) + try CoreData.default.saveContext() } // MARK: Private interface diff --git a/Classes/Extensions/QueryExecutable+ManagedObject.swift b/Classes/Extensions/QueryExecutable+ManagedObject.swift index 24e40f2..106ba93 100644 --- a/Classes/Extensions/QueryExecutable+ManagedObject.swift +++ b/Classes/Extensions/QueryExecutable+ManagedObject.swift @@ -13,7 +13,8 @@ import CoreData extension QueryExecutable where EntityType: NSManagedObject { /// Return all data based on your query - public func all(on context: NSManagedObjectContext = CoreData.managedContext) throws -> [EntityType] { + public func all(on context: NSManagedObjectContext?) throws -> [EntityType] { + let context = context ?? CoreData.managedContext guard let data = try context.fetch(fetchRequest()) as? [EntityType] else { return [] } @@ -21,30 +22,33 @@ extension QueryExecutable where EntityType: NSManagedObject { } /// Delete all data captured by your query - public func delete(on context: NSManagedObjectContext = CoreData.managedContext) throws { + public func delete(on context: NSManagedObjectContext?) throws { + let context = context ?? CoreData.managedContext for object in try all(on: context) { try object.delete(on: context) } try context.save() - // TODO: Fix the following batch request! - // let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest()) - // do { - // let batchDeleteResult = try context.execute(deleteRequest) as! NSBatchDeleteResult - // print("The batch delete request has deleted \(batchDeleteResult.result!) records.") - // } catch { - // let updateError = error as NSError - // print("\(updateError), \(updateError.userInfo)") - // } +// TODO: Fix the following batch request! +// let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest()) +// do { +// let batchDeleteResult = try context.execute(deleteRequest) as! NSBatchDeleteResult +// print("The batch delete request has deleted \(batchDeleteResult.result!) records.") +// } catch { +// let updateError = error as NSError +// print("\(updateError), \(updateError.userInfo)") +// } } /// Count the number of items in your query - public func count(on context: NSManagedObjectContext = CoreData.managedContext) throws -> Int { + public func count(on context: NSManagedObjectContext?) throws -> Int { + let context = context ?? CoreData.managedContext return try context.count(for: fetchRequest()) } /// Get first result of your query - public func first(on context: NSManagedObjectContext = CoreData.managedContext) throws -> EntityType? { + public func first(on context: NSManagedObjectContext?) throws -> EntityType? { + let context = context ?? CoreData.managedContext return try context.fetch(fetchRequest()).first as? EntityType } diff --git a/Classes/Extensions/QueryExecutable+Threading.swift b/Classes/Extensions/QueryExecutable+Threading.swift index 45fca79..b03ed29 100644 --- a/Classes/Extensions/QueryExecutable+Threading.swift +++ b/Classes/Extensions/QueryExecutable+Threading.swift @@ -13,7 +13,7 @@ import CoreData extension QueryExecutable where EntityType: NSManagedObject { /// Return all data based on your query - public func all(on queue: DispatchQueue) -> CoreDataPromise<[EntityType]> { + public func all(on queue: DispatchQueue = .main) -> CoreDataPromise<[EntityType]> { // Create or get the right context for the desired queue // Execute query on the thread for the context // Fulfill promise @@ -22,19 +22,19 @@ extension QueryExecutable where EntityType: NSManagedObject { } /// Delete all data captured by your query - public func delete(on queue: DispatchQueue) -> CoreDataPromise { + public func delete(on queue: DispatchQueue = .main) -> CoreDataPromise { // All above + // Update all contexts fatalError() } /// Count the number of items in your query - public func count(on queue: DispatchQueue) -> CoreDataPromise{ + public func count(on queue: DispatchQueue = .main) -> CoreDataPromise{ fatalError() } /// Get first result of your query - public func first(on queue: DispatchQueue) -> CoreDataPromise { + public func first(on queue: DispatchQueue = .main) -> CoreDataPromise { fatalError() } diff --git a/Classes/Libs/CoreDataPromise.swift b/Classes/Libs/CoreDataPromise.swift index 8490e16..0e45eee 100644 --- a/Classes/Libs/CoreDataPromise.swift +++ b/Classes/Libs/CoreDataPromise.swift @@ -9,6 +9,41 @@ import Foundation -public class CoreDataPromise { +public class CoreDataPromise { + public internal(set) var expectation: Expectation? { + didSet { + if let expectation = expectation { + thenPromiseClosure?(expectation) + mapPromise?.expectation = mapPromiseClosure?(expectation) + } + } + } + + /// Then closure type + public typealias ThenPromise = ((Expectation) -> Void) + + /// Map closure type + public typealias MapPromise = ((Expectation) -> T) + + var thenPromiseClosure: ThenPromise? + + /// Executes when operation is finished + public func then(_ promise: @escaping ThenPromise) -> Self { + thenPromiseClosure = promise + return self + } + + var mapPromiseClosure: MapPromise? + var mapPromise: CoreDataPromise? + +// /// Executes when operation is finished to transform result to another type +// public func map(to: T.Type = T.self, _ promise: @escaping MapPromise) -> CoreDataPromise { +// mapPromiseClosure = promise +// +// let mapPromise = CoreDataPromise() +// +// return mapPromise +// } + } diff --git a/Demo-iOS/ViewController.swift b/Demo-iOS/ViewController.swift index 32d9a9d..6d77d16 100644 --- a/Demo-iOS/ViewController.swift +++ b/Demo-iOS/ViewController.swift @@ -31,6 +31,9 @@ class ViewController: UIViewController { } let all = try! Locomotive.query.filter("hasChimney" == true).filter(.or, "color" == "green", "color" == "black").sort(by: "color", direction: .orderedAscending).all() + all.forEach { (<#Collection.Element#>) in + <#code#> + } all.forEach { loco in print("Color: \(loco.color ?? "Unknown"); Chimney: \(Bool(loco.hasChimney))") }