diff --git a/README.md b/README.md index 77c05b83..2efa5963 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,6 @@ Choose the right one depending ton the configuration you need for you app. ```ruby pod "SugarRecord/CoreData" -pod "SugarRecord/CoreData+iCloud" ``` ### [Carthage](https://github.com/carthage) @@ -71,21 +70,6 @@ func coreDataStorage() -> CoreDataDefaultStorage { } ``` -##### Creating an iCloud Storage - -SugarRecord supports the integration of CoreData with iCloud. It's very easy to setup since it's implemented in its own storage that you can use from your app, `CoreDataiCloudStorage`: - -```swift -// Initializes the CoreDataiCloudStorage -func icloudStorage() -> CoreDataiCloudStorage { - let bundle = Bundle(for: self.classForCoder) - let model = CoreDataObjectModel.merged([bundle]) - let icloudConfig = CoreDataiCloudConfig(ubiquitousContentName: "MyDb", ubiquitousContentURL: "Path/", ubiquitousContainerIdentifier: "com.company.MyApp.anothercontainer") - let icloudStorage = try! CoreDataiCloudStorage(model: model, iCloud: icloudConfig) - return icloudStorage -} -``` - #### Contexts Storages offer multiple kind of contexts that are the entry points to the database. For curious developers, in case of CoreData a context is a wrapper around `NSManagedObjectContext`. The available contexts are: @@ -209,7 +193,6 @@ class Presenter { - [Nimble](https://github.com/quick/nimble) - [CoreData and threads with GCD](http://www.cimgf.com/2011/05/04/core-data-and-threads-without-the-headache/) - [Jazzy](https://github.com/realm/jazzy) -- [iCloud + CoreData (objc.io)](http://www.objc.io/issue-10/icloud-core-data.html) ## Contributors diff --git a/SugarRecord.podspec b/SugarRecord.podspec index 24f7c841..abbee26a 100644 --- a/SugarRecord.podspec +++ b/SugarRecord.podspec @@ -9,7 +9,7 @@ Pod::Spec.new do |s| s.social_media_url = 'https://twitter.com/carambalabs' s.requires_arc = true - s.ios.deployment_target = "8.0" + s.ios.deployment_target = "9.0" s.osx.deployment_target = "10.10" coredata_dependencies = lambda do |spec| @@ -17,7 +17,7 @@ Pod::Spec.new do |s| end foundation_dependencies = lambda do |spec| - spec.dependency "Result", "~> 3.0" + spec.dependency "Result", "~> 5.0" end all_platforms = lambda do |spec| @@ -27,22 +27,12 @@ Pod::Spec.new do |s| spec.tvos.deployment_target = '9.0' end - excluded_icloud_files = ['SugarRecord/Source/CoreData/Entities/iCloudConfig.swift', 'SugarRecord/Source/CoreData/Storages/CoreDataiCloudStorage.swift'] - s.subspec "CoreData" do |spec| source_files = ['SugarRecord/Source/Foundation/**/*.{swift}', 'SugarRecord/Source/CoreData/**/*.{swift}'] spec.source_files = source_files - spec.exclude_files = excluded_icloud_files coredata_dependencies.call(spec) foundation_dependencies.call(spec) all_platforms.call(spec) end - s.subspec "CoreData+iCloud" do |spec| - source_files = ['SugarRecord/Source/Foundation/**/*.{swift}', 'SugarRecord/Source/CoreData/**/*.{swift}'] - spec.source_files = source_files - coredata_dependencies.call(spec) - foundation_dependencies.call(spec) - end - end diff --git a/SugarRecord/Source/CoreData/Entities/CoreDataObservable.swift b/SugarRecord/Source/CoreData/Entities/CoreDataObservable.swift index 6f0365b9..d27d34e0 100644 --- a/SugarRecord/Source/CoreData/Entities/CoreDataObservable.swift +++ b/SugarRecord/Source/CoreData/Entities/CoreDataObservable.swift @@ -2,7 +2,7 @@ import Foundation import CoreData @available(OSX 10.12, *) -public class CoreDataObservable: RequestObservable, NSFetchedResultsControllerDelegate where T:Equatable { +public class CoreDataObservable: RequestObservable, NSFetchedResultsControllerDelegate { // MARK: - Attributes diff --git a/SugarRecord/Source/CoreData/Entities/CoreDataiCloudConfig.swift b/SugarRecord/Source/CoreData/Entities/CoreDataiCloudConfig.swift deleted file mode 100644 index 3d15e599..00000000 --- a/SugarRecord/Source/CoreData/Entities/CoreDataiCloudConfig.swift +++ /dev/null @@ -1,30 +0,0 @@ -import Foundation - -public struct CoreDataiCloudConfig { - - // MARK: - Attributes - - internal let ubiquitousContentName: String - internal let ubiquitousContentURL: String - internal let ubiquitousContainerIdentifier: String - internal let ubiquitousPeerTokenOption: String? - internal let removeUbiquitousMetadataOption: Bool? - internal let rebuildFromUbiquitousContentOption: Bool? - - - // MARK: - Init - - public init(ubiquitousContentName: String, - ubiquitousContentURL: String, - ubiquitousContainerIdentifier: String, - ubiquitousPeerTokenOption: String? = nil, - removeUbiquitousMetadataOption: Bool? = nil, - rebuildFromUbiquitousContentOption: Bool? = nil) { - self.ubiquitousContentName = ubiquitousContentName - self.ubiquitousContentURL = ubiquitousContentURL - self.ubiquitousPeerTokenOption = ubiquitousPeerTokenOption - self.removeUbiquitousMetadataOption = removeUbiquitousMetadataOption - self.ubiquitousContainerIdentifier = ubiquitousContainerIdentifier - self.rebuildFromUbiquitousContentOption = rebuildFromUbiquitousContentOption - } -} diff --git a/SugarRecord/Source/CoreData/Extensions/NSManagedObjectContext.swift b/SugarRecord/Source/CoreData/Extensions/NSManagedObjectContext.swift index 88ad93ca..0a1f4ce0 100644 --- a/SugarRecord/Source/CoreData/Extensions/NSManagedObjectContext.swift +++ b/SugarRecord/Source/CoreData/Extensions/NSManagedObjectContext.swift @@ -18,6 +18,115 @@ extension NSManagedObjectContext: Context { return typedResults } + public func fetchOne(_ request: FetchRequest) throws -> T? { + guard let entity = T.self as? NSManagedObject.Type else { throw StorageError.invalidType } + let fetchRequest: NSFetchRequest = NSFetchRequest(entityName: entity.entityName) + fetchRequest.predicate = request.predicate + fetchRequest.sortDescriptors = request.sortDescriptor.map {[$0]} + fetchRequest.fetchOffset = request.fetchOffset + fetchRequest.fetchLimit = 1 + + if let results = try? self.fetch(fetchRequest), results.count > 0, let item = results.first as? T { + return item + } + + return nil + } + + public func query(_ request: FetchRequest, attributes: [String]) throws -> [[String: Any]] { + guard let entity = T.self as? NSManagedObject.Type else { throw StorageError.invalidType } + let fetchRequest: NSFetchRequest = NSFetchRequest(entityName: entity.entityName) + fetchRequest.predicate = request.predicate + fetchRequest.sortDescriptors = request.sortDescriptor.map {[$0]} + fetchRequest.fetchOffset = request.fetchOffset + fetchRequest.fetchLimit = request.fetchLimit + + fetchRequest.propertiesToFetch = attributes + fetchRequest.resultType = .dictionaryResultType + + let results = try self.fetch(fetchRequest) + let typedResults = results.compactMap { $0 as? [String: Any] } + return typedResults + } + + public func query(_ request: FetchRequest, attribute: String) throws -> [String]? { + guard let entity = T.self as? NSManagedObject.Type else { throw StorageError.invalidType } + let fetchRequest: NSFetchRequest = NSFetchRequest(entityName: entity.entityName) + fetchRequest.predicate = request.predicate + fetchRequest.sortDescriptors = request.sortDescriptor.map {[$0]} + fetchRequest.fetchOffset = request.fetchOffset + fetchRequest.fetchLimit = request.fetchLimit + + fetchRequest.propertiesToFetch = [attribute] + fetchRequest.resultType = .dictionaryResultType + + let results = try self.fetch(fetchRequest) + + var elements = [String]() + results.compactMap { $0 as? [String: Any] }.forEach { + if let value = $0[attribute] as? String { + elements.append(value) + } + } + + return elements + } + + public func querySet(_ request: FetchRequest, attribute: String) throws -> Set? { + guard let entity = T.self as? NSManagedObject.Type else { throw StorageError.invalidType } + let fetchRequest: NSFetchRequest = NSFetchRequest(entityName: entity.entityName) + fetchRequest.predicate = request.predicate + fetchRequest.sortDescriptors = request.sortDescriptor.map {[$0]} + fetchRequest.fetchOffset = request.fetchOffset + fetchRequest.fetchLimit = request.fetchLimit + + fetchRequest.propertiesToFetch = [attribute] + fetchRequest.resultType = .dictionaryResultType + + let results = try self.fetch(fetchRequest) + + var ids = Set() + + if let currentResults = results as? [[String: Any]] { + for item in currentResults { + if let id = item[attribute] as? String { + ids.insert(id) + } + } + } + + return ids + } + + public func queryOne(_ request: FetchRequest, attributes: [String]) throws -> [String: Any]? { + guard let entity = T.self as? NSManagedObject.Type else { throw StorageError.invalidType } + let fetchRequest: NSFetchRequest = NSFetchRequest(entityName: entity.entityName) + fetchRequest.predicate = request.predicate + fetchRequest.sortDescriptors = request.sortDescriptor.map {[$0]} + fetchRequest.fetchOffset = request.fetchOffset + fetchRequest.fetchLimit = 1 + + fetchRequest.propertiesToFetch = attributes + fetchRequest.resultType = .dictionaryResultType + + let results = try self.fetch(fetchRequest) + let typedResults = results.compactMap { $0 as? [String: Any] } + return typedResults.first + } + + public func count(_ request: FetchRequest) -> Int { + guard let entity = T.self as? NSManagedObject.Type else { return 0 } + let fetchRequest: NSFetchRequest = NSFetchRequest(entityName: entity.entityName) + fetchRequest.predicate = request.predicate + + if let count = try? self.count(for: fetchRequest) { + return count + } + + return 0 + } + + public func insert(_ entity: T) throws {} public func new() throws -> T { @@ -42,6 +151,44 @@ extension NSManagedObjectContext: Context { throw StorageError.invalidOperation("-removeAll not available in NSManagedObjectContext. Remove the store instead") } + public func saveToPersistentStore(_ completion: ((Swift.Result) -> Void)? = nil) { + + self.performAndWait { + do { + try self.save() + + if let parentContext = self.parent { + parentContext.saveToPersistentStore(completion) + } else { + DispatchQueue.main.async { + completion?(.success(nil)) + } + } + } catch { + DispatchQueue.main.async { + completion?(.failure(error)) + } + } + } + } + + // MARK: - Batch Actions + public func batchUpdate(entityName: String, propertiesToUpdate: [AnyHashable : Any]?, predicate: NSPredicate?) { + let request = NSBatchUpdateRequest(entityName: entityName) + request.propertiesToUpdate = propertiesToUpdate + request.resultType = .updatedObjectsCountResultType + request.predicate = predicate + + _ = try? self.execute(request) + } + + public func batchDelete(entityName: String, predicate: NSPredicate?) { + let fetch = NSFetchRequest(entityName: entityName) + fetch.predicate = predicate + let request = NSBatchDeleteRequest(fetchRequest: fetch) + + _ = try? self.execute(request) + } } diff --git a/SugarRecord/Source/CoreData/Storages/CoreDataDefaultStorage.swift b/SugarRecord/Source/CoreData/Storages/CoreDataDefaultStorage.swift index c445ad8c..74998267 100644 --- a/SugarRecord/Source/CoreData/Storages/CoreDataDefaultStorage.swift +++ b/SugarRecord/Source/CoreData/Storages/CoreDataDefaultStorage.swift @@ -20,7 +20,6 @@ public class CoreDataDefaultStorage: Storage { } } - public var type: StorageType = .coreData public var mainContext: Context! private var _saveContext: Context! public var saveContext: Context! { @@ -110,28 +109,20 @@ public class CoreDataDefaultStorage: Storage { // MARK: - Init - - public convenience init(store: CoreDataStore, model: CoreDataObjectModel, migrate: Bool = true) throws { - try self.init(store: store, model: model, migrate: migrate, versionController: VersionController()) - } - - internal init(store: CoreDataStore, model: CoreDataObjectModel, migrate: Bool = true, versionController: VersionController) throws { - self.store = store + public init(store: CoreDataStore, model: CoreDataObjectModel, migrate: Bool = true) throws { + self.store = store self.objectModel = model.model()! self.persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: objectModel) self.persistentStore = try cdInitializeStore(store: store, storeCoordinator: persistentStoreCoordinator, migrate: migrate) self.rootSavingContext = cdContext(withParent: .coordinator(self.persistentStoreCoordinator), concurrencyType: .privateQueueConcurrencyType, inMemory: false) self.mainContext = cdContext(withParent: .context(self.rootSavingContext), concurrencyType: .mainQueueConcurrencyType, inMemory: false) - #if DEBUG - versionController.check() - #endif } // MARK: - Public @available(OSX 10.12, *) - public func observable(request: FetchRequest) -> RequestObservable where T:Equatable { + public func observable(request: FetchRequest) -> RequestObservable { return CoreDataObservable(request: request, context: self.mainContext as! NSManagedObjectContext) } diff --git a/SugarRecord/Source/CoreData/Storages/CoreDataiCloudStorage.swift b/SugarRecord/Source/CoreData/Storages/CoreDataiCloudStorage.swift deleted file mode 100644 index 7501e3b9..00000000 --- a/SugarRecord/Source/CoreData/Storages/CoreDataiCloudStorage.swift +++ /dev/null @@ -1,166 +0,0 @@ -import Foundation -import CoreData - -public class CoreDataiCloudStorage: Storage { - - // MARK: - Attributes - - internal let store: CoreDataStore - internal var objectModel: NSManagedObjectModel! = nil - internal var persistentStore: NSPersistentStore! = nil - internal var persistentStoreCoordinator: NSPersistentStoreCoordinator! = nil - internal var rootSavingContext: NSManagedObjectContext! = nil - - - // MARK: - Storage - - public var description: String { - get { - return "CoreDataiCloudStorage" - } - } - public var type: StorageType = .coreData - - public var mainContext: Context! - - public var saveContext: Context! { - get { - let context = cdContext(withParent: .context(self.rootSavingContext), concurrencyType: .privateQueueConcurrencyType, inMemory: false) - context.observe(inMainThread: true) { [weak self] (notification) -> Void in - (self?.mainContext as? NSManagedObjectContext)?.mergeChanges(fromContextDidSave: notification as Notification) - } - return context - } - } - - public var memoryContext: Context! { - get { - let context = cdContext(withParent: .context(self.rootSavingContext), concurrencyType: .privateQueueConcurrencyType, inMemory: true) - return context - } - } - - public func operation(_ operation: @escaping (_ context: Context, _ save: @escaping () -> Void) throws -> T) throws -> T { - let context: NSManagedObjectContext = (self.saveContext as? NSManagedObjectContext)! - var _error: Error! - - var returnedObject: T! - - context.performAndWait { - do { - returnedObject = try operation(context, { () -> Void in - do { - try context.save() - } - catch { - _error = error - } - if self.rootSavingContext.hasChanges { - self.rootSavingContext.performAndWait { - do { - try self.rootSavingContext.save() - } - catch { - _error = error - } - } - } - }) - } - catch { - _error = error - } - } - if let error = _error { - throw error - } - - return returnedObject - } - - public func backgroundOperation(_ operation: @escaping (_ context: Context, _ save: @escaping () -> Void) -> (), completion: @escaping (Error?) -> ()) { - let context: NSManagedObjectContext = self.saveContext as! NSManagedObjectContext - var _error: Error! - context.perform { - operation(context, { () -> Void in - do { - try context.save() - } - catch { - _error = error - } - self.rootSavingContext.perform { - if self.rootSavingContext.hasChanges { - do { - try self.rootSavingContext.save() - } - catch { - _error = error - } - } - completion(_error) - } - }) - } - } - - public func removeStore() throws { - try FileManager.default.removeItem(at: store.path() as URL) - } - - - // MARK: - Init - - public convenience init(model: CoreDataObjectModel, iCloud: CoreDataiCloudConfig) throws { - try self.init(model: model, iCloud: iCloud, versionController: VersionController()) - } - - internal init(model: CoreDataObjectModel, iCloud: CoreDataiCloudConfig, versionController: VersionController) throws { - self.objectModel = model.model()! - self.persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: objectModel) - let result = try! cdiCloudInitializeStore(storeCoordinator: persistentStoreCoordinator, iCloud: iCloud) - self.store = result.0 - self.persistentStore = result.1 - self.rootSavingContext = cdContext(withParent: .coordinator(self.persistentStoreCoordinator), concurrencyType: .privateQueueConcurrencyType, inMemory: false) - self.mainContext = cdContext(withParent: .context(self.rootSavingContext), concurrencyType: .mainQueueConcurrencyType, inMemory: false) - self.observeiCloudChangesInCoordinator() - #if DEBUG - versionController.check() - #endif - } - - - // MARK: - Public - -#if os(iOS) || os(tvOS) || os(watchOS) - - public func observable(request: FetchRequest) -> RequestObservable where T:Equatable { - return CoreDataObservable(request: request, context: self.mainContext as! NSManagedObjectContext) - } - -#endif - - // MARK: - Private - - private func observeiCloudChangesInCoordinator() { - NotificationCenter - .default - .addObserver(forName: NSNotification.Name.NSPersistentStoreDidImportUbiquitousContentChanges, object: self.persistentStoreCoordinator, queue: nil) { [weak self] (notification) -> Void in - self?.rootSavingContext.perform { - self?.rootSavingContext.mergeChanges(fromContextDidSave: notification) - } - } - } - -} - -internal func cdiCloudInitializeStore(storeCoordinator: NSPersistentStoreCoordinator, iCloud: CoreDataiCloudConfig) throws -> (CoreDataStore, NSPersistentStore?) { - let storeURL = FileManager.default - .url(forUbiquityContainerIdentifier: iCloud.ubiquitousContainerIdentifier)! - .appendingPathComponent(iCloud.ubiquitousContentURL) - var options = CoreDataOptions.migration.dict() - options[NSPersistentStoreUbiquitousContentURLKey] = storeURL as AnyObject? - options[NSPersistentStoreUbiquitousContentNameKey] = iCloud.ubiquitousContentName as AnyObject? - let store = CoreDataStore.url(storeURL) - return try (store, cdAddPersistentStore(store: store, storeCoordinator: storeCoordinator, options: options)) -} diff --git a/SugarRecord/Source/Foundation/Entities/Request.swift b/SugarRecord/Source/Foundation/Entities/Request.swift index 02147d9a..7026c68c 100644 --- a/SugarRecord/Source/Foundation/Entities/Request.swift +++ b/SugarRecord/Source/Foundation/Entities/Request.swift @@ -21,6 +21,14 @@ public struct FetchRequest: Equatable { self.fetchLimit = fetchLimit } + public init(_ context: Context, predicate: NSPredicate? = nil, fetchOffset: Int = 0, fetchLimit: Int = 0) { + self.context = context + self.sortDescriptor = nil + self.predicate = predicate + self.fetchOffset = fetchOffset + self.fetchLimit = fetchLimit + } + // MARK: - Public Fetching Methods @@ -33,6 +41,27 @@ public struct FetchRequest: Equatable { } + public func query(attributes: [String]) throws -> [[String: Any]] { + return try context!.query(self, attributes: attributes) + } + + public func queryOne(attributes: [String]) throws -> [String: Any]? { + return try context!.queryOne(self, attributes: attributes) + } + + public func querySet(attribute: String) throws -> Set? { + return try context!.querySet(self, attribute: attribute) + } + + public func fetchOne() throws -> T? { + return try context!.fetchOne(self) + } + + public func count() -> Int { + return context!.count(self) + } + + // MARK: - Public Builder Methods public func filtered(with predicate: NSPredicate) -> FetchRequest { @@ -54,7 +83,7 @@ public struct FetchRequest: Equatable { return self .request(withPredicate: NSPredicate(format: "NOT (\(key) IN %@)", value)) } - + public func sorted(with sortDescriptor: NSSortDescriptor) -> FetchRequest { return self @@ -94,5 +123,5 @@ public struct FetchRequest: Equatable { public func == (lhs: FetchRequest, rhs: FetchRequest) -> Bool { return lhs.sortDescriptor == rhs.sortDescriptor && - lhs.predicate == rhs.predicate + lhs.predicate == rhs.predicate } diff --git a/SugarRecord/Source/Foundation/Entities/Storage.swift b/SugarRecord/Source/Foundation/Entities/Storage.swift index bad0fd36..fb754200 100644 --- a/SugarRecord/Source/Foundation/Entities/Storage.swift +++ b/SugarRecord/Source/Foundation/Entities/Storage.swift @@ -1,14 +1,10 @@ import Foundation -public enum StorageType { - case coreData -} typealias StorageOperation = ((_ context: Context, _ save: () -> Void) throws -> Void) throws -> Void public protocol Storage: CustomStringConvertible, Requestable { - var type: StorageType { get } var mainContext: Context! { get } var saveContext: Context! { get } var memoryContext: Context! { get } diff --git a/SugarRecord/Source/Foundation/Extensions/RequestExtension.swift b/SugarRecord/Source/Foundation/Extensions/RequestExtension.swift deleted file mode 100644 index b9041f8b..00000000 --- a/SugarRecord/Source/Foundation/Extensions/RequestExtension.swift +++ /dev/null @@ -1,22 +0,0 @@ -import Foundation - - -// MARK: Request extension (NSPredicateConvertible) - -extension FetchRequest: NSPredicateConvertible { - - public init(predicate: NSPredicate) { - self = FetchRequest(predicate: predicate) - } -} - - -// MARK: Request extension (NSSortDescriptorConvertible) - -extension FetchRequest: NSSortDescriptorConvertible { - - public init(sortDescriptor: NSSortDescriptor) { - self = FetchRequest(sortDescriptor: sortDescriptor) - } - -} diff --git a/SugarRecord/Source/Foundation/Logger/Logger.swift b/SugarRecord/Source/Foundation/Logger/Logger.swift deleted file mode 100644 index c39d89dd..00000000 --- a/SugarRecord/Source/Foundation/Logger/Logger.swift +++ /dev/null @@ -1,11 +0,0 @@ -import Foundation - -internal class Logger { - - // MARK: - Internal - - internal func log(_ message: String) { - print("🍬 SugarRecord: \(message)") - } - -} diff --git a/SugarRecord/Source/Foundation/Protocols/Context.swift b/SugarRecord/Source/Foundation/Protocols/Context.swift index cf9f3ebc..01d5d898 100644 --- a/SugarRecord/Source/Foundation/Protocols/Context.swift +++ b/SugarRecord/Source/Foundation/Protocols/Context.swift @@ -3,26 +3,35 @@ import Foundation public protocol Context: Requestable { func fetch(_ request: FetchRequest) throws -> [T] + func fetchOne(_ request: FetchRequest) throws -> T? func insert(_ entity: T) throws func new() throws -> T + func query(_ request: FetchRequest, attributes: [String]) throws -> [[String: Any]] + func query(_ request: FetchRequest, attribute: String) throws -> [String]? + func querySet(_ request: FetchRequest, attribute: String) throws -> Set? + func queryOne(_ request: FetchRequest, attributes: [String]) throws -> [String: Any]? + func count(_ request: FetchRequest) -> Int func create() throws -> T func remove(_ objects: [T]) throws func remove(_ object: T) throws func removeAll() throws + func saveToPersistentStore(_ completion: ((Swift.Result) -> Void)?) + func batchUpdate(entityName: String, propertiesToUpdate: [AnyHashable : Any]?, predicate: NSPredicate?) + func batchDelete(entityName: String, predicate: NSPredicate?) } // MARK: - Extension of Context implementing convenience methods. public extension Context { - - public func create() throws -> T { + + func create() throws -> T { let instance: T = try self.new() try self.insert(instance) return instance } - - public func remove(_ object: T) throws { + + func remove(_ object: T) throws { return try self.remove([object]) } diff --git a/SugarRecord/Source/Foundation/Version/VersionController.swift b/SugarRecord/Source/Foundation/Version/VersionController.swift deleted file mode 100644 index 60a59479..00000000 --- a/SugarRecord/Source/Foundation/Version/VersionController.swift +++ /dev/null @@ -1,31 +0,0 @@ -import Foundation - -internal class VersionController { - - // MARK: - Attributes - - fileprivate let provider: VersionProvider - fileprivate let logger: Logger - - - // MARK: - Init - - internal init(provider: VersionProvider = VersionProvider(), - logger: Logger = Logger()) { - self.provider = provider - self.logger = logger - } - - - // MARK: - Internal - - internal func check() { - let frameworkVersion = self.provider.framework() - self.provider.github { [weak self] (githubVersion) in - if frameworkVersion != githubVersion { - self?.logger.log("There's a new version available, \(githubVersion)") - } - } - } - -} diff --git a/SugarRecord/Source/Foundation/Version/VersionProvider.swift b/SugarRecord/Source/Foundation/Version/VersionProvider.swift deleted file mode 100644 index aa3e457a..00000000 --- a/SugarRecord/Source/Foundation/Version/VersionProvider.swift +++ /dev/null @@ -1,33 +0,0 @@ -import Foundation - -internal class VersionProvider: NSObject { - - // MARK: - Constants - - internal static let apiReleasesUrl: String = "https://api.github.com/repos/carambalabs/sugarrecord/releases" - - - // MARK: - Internal - - internal func framework() -> String! { - if let version = Bundle(for: VersionProvider.classForCoder()).object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String { - return version - } - return nil - } - - internal func github(_ completion: @escaping (String) -> Void) { - let request: URLRequest = URLRequest(url: URL(string: VersionProvider.apiReleasesUrl)!) - let session = URLSession(configuration: URLSessionConfiguration.default) - session.dataTask(with: request, completionHandler: { (data, response, error) in - if let data = data { - let json: AnyObject? = try! JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as AnyObject? - if let array = json as? [[String: AnyObject]], let lastVersion = array.first, let versionTag: String = lastVersion["tag_name"] as? String { - completion(versionTag) - } - } - }).resume() - session.finishTasksAndInvalidate() - } - -}