From d656c80534a4d6d92f222efd29a9103ccfed4a34 Mon Sep 17 00:00:00 2001 From: Miguel Angel Z Date: Fri, 30 Apr 2021 15:46:32 -0400 Subject: [PATCH 01/22] Remove unused test file --- Tests/CommonTests/AltairMDKTests.swift | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 Tests/CommonTests/AltairMDKTests.swift diff --git a/Tests/CommonTests/AltairMDKTests.swift b/Tests/CommonTests/AltairMDKTests.swift deleted file mode 100644 index 7026d33..0000000 --- a/Tests/CommonTests/AltairMDKTests.swift +++ /dev/null @@ -1,15 +0,0 @@ -import XCTest -@testable import AltairMDK - -final class AltairMDKTests: XCTestCase { - func testExample() { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct - // results. - XCTAssertEqual(AltairMDK().text, "Hello, World!") - } - - static var allTests = [ - ("testExample", testExample), - ] -} From ced61bdfd47c6d76c5905ab4e28d52b247597d0e Mon Sep 17 00:00:00 2001 From: Miguel Angel Z Date: Fri, 30 Apr 2021 15:47:13 -0400 Subject: [PATCH 02/22] Little improvements in trigger func of store --- Sources/Common/Protocols/Store.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Common/Protocols/Store.swift b/Sources/Common/Protocols/Store.swift index 6d25630..4b1032b 100644 --- a/Sources/Common/Protocols/Store.swift +++ b/Sources/Common/Protocols/Store.swift @@ -8,5 +8,5 @@ public protocol Store: ObservableObject { associatedtype State associatedtype Action var state: State { get } - func trigger(action: Action) + func trigger(_ action: Action) } From cafef4b12986f0db898560cc7ca7bcdf5b3f241d Mon Sep 17 00:00:00 2001 From: Miguel Angel Zapata Date: Fri, 30 Apr 2021 19:27:08 -0400 Subject: [PATCH 03/22] Add Mappers protocol that cannot fail --- Package.swift | 1 - Sources/Common/Protocols/Mappers.swift | 18 ++++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/Package.swift b/Package.swift index 391745b..b6715d0 100644 --- a/Package.swift +++ b/Package.swift @@ -10,7 +10,6 @@ let package = Package( products: [ .library( name: "Altair-MDK", - type: .dynamic, targets: [ "AltairMDKCommon", "AltairMDKProviders" diff --git a/Sources/Common/Protocols/Mappers.swift b/Sources/Common/Protocols/Mappers.swift index f2e5642..660334d 100644 --- a/Sources/Common/Protocols/Mappers.swift +++ b/Sources/Common/Protocols/Mappers.swift @@ -8,11 +8,25 @@ import Foundation public protocol ModelMapper { associatedtype Entity associatedtype Model - static var mapEntityToModel: (Entity) throws -> Model { get } + static var mapEntityToModel: (Entity) -> Model { get } } -/// Map from Data or Presentation Model to Entity +/// Map from Data or Presentation Model to Domain Entity public protocol EntityMapper { + associatedtype Model + associatedtype Entity + static var mapModelToEntity: (Model) -> Entity { get } +} + +/// Map from Domain Entity to Data or Presentation Model +public protocol ModelFailableMapper { + associatedtype Entity + associatedtype Model + static var mapEntityToModel: (Entity) throws -> Model { get } +} + +/// Map from Data or Presentation Model to Domain Entity +public protocol EntityFailableMapper { associatedtype Model associatedtype Entity static var mapModelToEntity: (Model) throws -> Entity { get } From 6b625e7b80f38b75411b8548eda3d6733b908b2d Mon Sep 17 00:00:00 2001 From: Miguel Angel Zapata Date: Sun, 2 May 2021 20:22:13 -0400 Subject: [PATCH 04/22] Add map function to Loadable that never fail --- Sources/Common/Types/Loadable.swift | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Sources/Common/Types/Loadable.swift b/Sources/Common/Types/Loadable.swift index ad81bba..c6fda17 100644 --- a/Sources/Common/Types/Loadable.swift +++ b/Sources/Common/Types/Loadable.swift @@ -14,12 +14,21 @@ public protocol Functor { associatedtype A associatedtype B: Functor = Self + func map(_ transform: (A) -> C) -> B where B.A == C func tryMap(_ transform: (A) throws -> C) throws -> B where B.A == C } extension Loadable: Functor { public typealias A = T + public func map(_ transform: (T) -> C) -> Loadable { + switch self { + case .neverLoaded: return .neverLoaded + case .loading: return .loading + case .loaded(let value): return .loaded(transform(value)) + } + } + public func tryMap(_ transform: (T) throws -> C) throws -> Loadable { switch self { case .neverLoaded: return .neverLoaded From 1d6b627253d229f7f1edd3b511cff636aa974d30 Mon Sep 17 00:00:00 2001 From: Miguel Angel Zapata Date: Wed, 5 May 2021 17:29:35 -0400 Subject: [PATCH 05/22] Add a method to make a bind and trigger an action --- Sources/Common/Protocols/Store.swift | 12 ++++++++++++ Sources/Common/Varios.swift | 10 ++++++++++ 2 files changed, 22 insertions(+) create mode 100644 Sources/Common/Varios.swift diff --git a/Sources/Common/Protocols/Store.swift b/Sources/Common/Protocols/Store.swift index 4b1032b..eface3a 100644 --- a/Sources/Common/Protocols/Store.swift +++ b/Sources/Common/Protocols/Store.swift @@ -2,6 +2,7 @@ // Created by Miguel Angel on 25-03-21. // +import SwiftUI import Foundation public protocol Store: ObservableObject { @@ -10,3 +11,14 @@ public protocol Store: ObservableObject { var state: State { get } func trigger(_ action: Action) } + +public extension Store { + + func binding(for keyPath: KeyPath, transform: @escaping (Value) -> Action) -> Binding { + Binding( + get: { self.state[keyPath: keyPath] }, + set: { self.trigger(transform($0)) } + ) + } + +} diff --git a/Sources/Common/Varios.swift b/Sources/Common/Varios.swift new file mode 100644 index 0000000..3b793ca --- /dev/null +++ b/Sources/Common/Varios.swift @@ -0,0 +1,10 @@ +// +// File.swift +// +// +// Created by Miguel Angel on 07-05-21. +// + +import Foundation +import SwiftUI + From 3c845fd69bb99291d5e795dbbf29d03483f753a9 Mon Sep 17 00:00:00 2001 From: Miguel Angel Zapata Date: Fri, 7 May 2021 22:21:58 -0400 Subject: [PATCH 06/22] Add some view modificators --- .../Extensions/SwiftUI/View+Navigation.swift | 66 +++++++++++++++++++ Sources/Common/Varios.swift | 10 --- .../ViewModifiers/PopoverModifier.swift | 30 +++++++++ .../Common/ViewModifiers/SheetModifier.swift | 30 +++++++++ 4 files changed, 126 insertions(+), 10 deletions(-) create mode 100644 Sources/Common/Extensions/SwiftUI/View+Navigation.swift delete mode 100644 Sources/Common/Varios.swift create mode 100644 Sources/Common/ViewModifiers/PopoverModifier.swift create mode 100644 Sources/Common/ViewModifiers/SheetModifier.swift diff --git a/Sources/Common/Extensions/SwiftUI/View+Navigation.swift b/Sources/Common/Extensions/SwiftUI/View+Navigation.swift new file mode 100644 index 0000000..4b5849b --- /dev/null +++ b/Sources/Common/Extensions/SwiftUI/View+Navigation.swift @@ -0,0 +1,66 @@ +// +// View+Navigation.swift +// +// +// Created by Miguel Angel on 07-05-21. +// + +import SwiftUI + +extension View { + + public func onNavigation(_ action: @escaping () -> Void) -> some View { + let isActive = Binding( + get: { false }, + set: { newValue in + if newValue { + action() + } + } + ) + return NavigationLink(destination: EmptyView(), isActive: isActive) { + self + } + } + + public func navigation(item: Binding, @ViewBuilder destination: (Item) -> Destination) -> some View { + let isActive = Binding( + get: { item.wrappedValue != nil }, + set: { value in + if !value { + item.wrappedValue = nil + } + } + ) + return navigation(isActive: isActive) { + item.wrappedValue.map(destination) + } + } + + public func navigation(isActive: Binding, @ViewBuilder destination: () -> Destination) -> some View { + overlay( + NavigationLink( + destination: isActive.wrappedValue ? destination() : nil, + isActive: isActive, + label: { EmptyView() } + ) + ) + } + +} + +extension NavigationLink { + + public init(item: Binding, @ViewBuilder destination: (T) -> D, @ViewBuilder label: () -> Label) where Destination == D? { + let isActive = Binding( + get: { item.wrappedValue != nil }, + set: { value in + if !value { + item.wrappedValue = nil + } + } + ) + self.init(destination: item.wrappedValue.map(destination), isActive: isActive, label: label) + } + +} diff --git a/Sources/Common/Varios.swift b/Sources/Common/Varios.swift deleted file mode 100644 index 3b793ca..0000000 --- a/Sources/Common/Varios.swift +++ /dev/null @@ -1,10 +0,0 @@ -// -// File.swift -// -// -// Created by Miguel Angel on 07-05-21. -// - -import Foundation -import SwiftUI - diff --git a/Sources/Common/ViewModifiers/PopoverModifier.swift b/Sources/Common/ViewModifiers/PopoverModifier.swift new file mode 100644 index 0000000..758ec56 --- /dev/null +++ b/Sources/Common/ViewModifiers/PopoverModifier.swift @@ -0,0 +1,30 @@ +// +// PopoverModifier.swift +// +// +// Created by Miguel Angel on 07-05-21. +// + +import SwiftUI + +public struct PopoverModifier: ViewModifier { + + // MARK: Stored Properties + + private let item: Binding + private let destination: (Item) -> Destination + + // MARK: Initialization + + public init(item: Binding, @ViewBuilder content: @escaping (Item) -> Destination) { + self.item = item + self.destination = content + } + + // MARK: Methods + + public func body(content: Content) -> some View { + content.popover(item: item, content: destination) + } + +} diff --git a/Sources/Common/ViewModifiers/SheetModifier.swift b/Sources/Common/ViewModifiers/SheetModifier.swift new file mode 100644 index 0000000..191a3af --- /dev/null +++ b/Sources/Common/ViewModifiers/SheetModifier.swift @@ -0,0 +1,30 @@ +// +// SheetModifier.swift +// +// +// Created by Miguel Angel on 07-05-21. +// + +import SwiftUI + +public struct SheetModifier: ViewModifier { + + // MARK: Stored Properties + + private let item: Binding + private let destination: (Item) -> Destination + + // MARK: Initialization + + public init(item: Binding, @ViewBuilder content: @escaping (Item) -> Destination) { + self.item = item + self.destination = content + } + + // MARK: Methods + + public func body(content: Content) -> some View { + content.sheet(item: item, content: destination) + } + +} From b150b39c93a6ede3bfa7b9a5a63f12ade9c3cdf2 Mon Sep 17 00:00:00 2001 From: Miguel Angel Zapata Date: Fri, 7 May 2021 22:37:47 -0400 Subject: [PATCH 07/22] Add LazyView --- .../Extensions/SwiftUI/View+Navigation.swift | 20 +++++++++++++++++- Sources/Common/Views/LazyView.swift | 21 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 Sources/Common/Views/LazyView.swift diff --git a/Sources/Common/Extensions/SwiftUI/View+Navigation.swift b/Sources/Common/Extensions/SwiftUI/View+Navigation.swift index 4b5849b..53a6e56 100644 --- a/Sources/Common/Extensions/SwiftUI/View+Navigation.swift +++ b/Sources/Common/Extensions/SwiftUI/View+Navigation.swift @@ -23,6 +23,24 @@ extension View { } } + /// ### Example of use + /// struct ListingScene: View { + /// @ObservedObject private(set) var viewModel: ListingViewModel + /// + /// @State private var destination: Destination? + /// + /// var body: some View { + /// NavigationView { + /// VStack { + /// Button("Nav") { + /// destination = .detail(id: "") + /// }.navigation(item: $destination, destination: viewModel.router.route(to:)) + /// } + /// } + /// .navigationViewStyle(StackNavigationViewStyle()) + /// } + /// } + /// public func navigation(item: Binding, @ViewBuilder destination: (Item) -> Destination) -> some View { let isActive = Binding( get: { item.wrappedValue != nil }, @@ -51,7 +69,7 @@ extension View { extension NavigationLink { - public init(item: Binding, @ViewBuilder destination: (T) -> D, @ViewBuilder label: () -> Label) where Destination == D? { + public init(item: Binding, @ViewBuilder destination: @escaping (T) -> D, @ViewBuilder label: () -> Label) where Destination == D? { let isActive = Binding( get: { item.wrappedValue != nil }, set: { value in diff --git a/Sources/Common/Views/LazyView.swift b/Sources/Common/Views/LazyView.swift new file mode 100644 index 0000000..98674e2 --- /dev/null +++ b/Sources/Common/Views/LazyView.swift @@ -0,0 +1,21 @@ +// +// LazyView.swift +// +// +// Created by Miguel Angel on 07-05-21. +// + +import SwiftUI + +public struct LazyView: View { + let build: () -> Content + + public init(_ build: @autoclosure @escaping () -> Content) { + self.build = build + } + + public var body: Content { + build() + } + +} From f07016e6b4672c472ccc52390804f82e631b311b Mon Sep 17 00:00:00 2001 From: Miguel Angel Zapata Date: Mon, 10 May 2021 21:54:18 -0400 Subject: [PATCH 08/22] Add Publisher extension NoRetain --- .../Combine/Publisher+NoRetain.swift | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 Sources/Common/Extensions/Combine/Publisher+NoRetain.swift diff --git a/Sources/Common/Extensions/Combine/Publisher+NoRetain.swift b/Sources/Common/Extensions/Combine/Publisher+NoRetain.swift new file mode 100644 index 0000000..4f5f1e5 --- /dev/null +++ b/Sources/Common/Extensions/Combine/Publisher+NoRetain.swift @@ -0,0 +1,26 @@ +// +// Publisher+NoRetain.swift +// +// +// Created by Miguel Angel on 10-05-21. +// + +import Combine + +extension Publisher where Self.Failure == Never { + + /// ### Example of use + /// store + /// .$state + /// .map { $0.exception } + /// .receive(on: DispatchQueue.main) + /// .assignNoRetain(to: \.exception, on: self) + /// .store(in: &bag) + /// + func assignNoRetain(to keyPath: ReferenceWritableKeyPath< Root, + Self.Output >, on object: Root) -> AnyCancellable where Root: AnyObject { + sink { [weak object] (value) in + object?[keyPath: keyPath] = value + } + } +} From 2c54a67709b10fab4de0f1c18a7de3e9f32d3277 Mon Sep 17 00:00:00 2001 From: Miguel Angel Zapata Date: Wed, 12 May 2021 20:07:50 -0400 Subject: [PATCH 09/22] Add Toggleable type --- Sources/Common/Types/Toggleable.swift | 37 +++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 Sources/Common/Types/Toggleable.swift diff --git a/Sources/Common/Types/Toggleable.swift b/Sources/Common/Types/Toggleable.swift new file mode 100644 index 0000000..2d0c9c2 --- /dev/null +++ b/Sources/Common/Types/Toggleable.swift @@ -0,0 +1,37 @@ +// +// Toggleable.swift +// +// +// Created by Miguel Angel on 12-05-21. +// + +public enum Toggleable: Equatable { + case on + case off + + public var isOn: Bool { + self == .on + } + + public var isOff: Bool { + self == .off + } + + public mutating func toggle() { + switch self { + case .on: + self = .off + case .off: + self = .on + } + } + + public var state: Bool { + switch self { + case .on: + return true + case .off: + return false + } + } +} From 927b99a7724a8985545a2c4ebd8e69b096f41707 Mon Sep 17 00:00:00 2001 From: Miguel Angel Zapata Date: Wed, 19 May 2021 20:27:51 -0400 Subject: [PATCH 10/22] Add variation to return the toggle result --- Sources/Common/Utils/Regex.swift | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 Sources/Common/Utils/Regex.swift diff --git a/Sources/Common/Utils/Regex.swift b/Sources/Common/Utils/Regex.swift new file mode 100644 index 0000000..8f19435 --- /dev/null +++ b/Sources/Common/Utils/Regex.swift @@ -0,0 +1,25 @@ +// +// RegexMatch.swift +// +// +// Created by Miguel Angel on 19-05-21. +// + +import Foundation + +public final class Regex { + + public static func matches(for regex: String, in text: String) -> [String] { + do { + let regex = try NSRegularExpression(pattern: regex) + let results = regex.matches(in: text, range: NSRange(text.startIndex..., in: text)) + return results.map { + String(text[Range($0.range, in: text)!]) + } + } catch let error { + print("Invalid Regex: \(error.localizedDescription)") + return [] + } + } + +} From dcee5ed020c42f65db45cd40529b41bc01c4644c Mon Sep 17 00:00:00 2001 From: Miguel Angel Zapata Date: Tue, 25 May 2021 20:13:35 -0400 Subject: [PATCH 11/22] Add Storage Agent definition --- Sources/Common/Protocols/Exception.swift | 1 + .../Storage/Base/ConfigurationType.swift | 20 ++++++ Sources/Providers/Storage/Base/Sorted.swift | 11 ++++ Sources/Providers/Storage/StorageAgent.swift | 62 +++++++++++++++++++ .../Providers/Storage/StorageProvider.swift | 8 +++ 5 files changed, 102 insertions(+) create mode 100644 Sources/Providers/Storage/Base/ConfigurationType.swift create mode 100644 Sources/Providers/Storage/Base/Sorted.swift create mode 100644 Sources/Providers/Storage/StorageAgent.swift create mode 100644 Sources/Providers/Storage/StorageProvider.swift diff --git a/Sources/Common/Protocols/Exception.swift b/Sources/Common/Protocols/Exception.swift index cc01c5b..ec2b971 100644 --- a/Sources/Common/Protocols/Exception.swift +++ b/Sources/Common/Protocols/Exception.swift @@ -13,5 +13,6 @@ public protocol Exception: LocalizedError { public enum ExceptionCategory { case feature case network + case storage case mappers } diff --git a/Sources/Providers/Storage/Base/ConfigurationType.swift b/Sources/Providers/Storage/Base/ConfigurationType.swift new file mode 100644 index 0000000..144f9c4 --- /dev/null +++ b/Sources/Providers/Storage/Base/ConfigurationType.swift @@ -0,0 +1,20 @@ +// +// ConfigurationType.swift +// +// +// Created by Miguel Angel on 25-05-21. +// + +import Foundation + +public enum ConfigurationType { + case basic(identifier: String) + case inMemory(identifier: String?) + + func identifier() -> String? { + switch self { + case .basic(let identifier): return identifier + case .inMemory(let identifier): return identifier + } + } +} diff --git a/Sources/Providers/Storage/Base/Sorted.swift b/Sources/Providers/Storage/Base/Sorted.swift new file mode 100644 index 0000000..53fbf5c --- /dev/null +++ b/Sources/Providers/Storage/Base/Sorted.swift @@ -0,0 +1,11 @@ +// +// Sorted.swift +// +// +// Created by Miguel Angel on 25-05-21. +// + +public struct Sorted { + var key: String + var ascending: Bool = true +} diff --git a/Sources/Providers/Storage/StorageAgent.swift b/Sources/Providers/Storage/StorageAgent.swift new file mode 100644 index 0000000..a523954 --- /dev/null +++ b/Sources/Providers/Storage/StorageAgent.swift @@ -0,0 +1,62 @@ +// +// StorageAgent.swift +// +// +// Created by Miguel Angel on 25-05-21. +// + +import Combine +import AltairMDKCommon + +public protocol StorageAgent: AnyObject { + associatedtype Storable + associatedtype Predicate + func insert(_ object: Storable) -> AnyPublisher + func insertAll(_ objects: [Storable]) -> AnyPublisher + func update(_ object: Storable) -> AnyPublisher + func delete(_ object: Storable) -> AnyPublisher + func deleteAll(_ model: Storable.Type, predicate: Predicate?) -> AnyPublisher + func readAll(_ model: Storable.Type, predicate: Predicate?, sorted: Sorted?) -> AnyPublisher<[Storable], StorageException> +} + +public enum StorageException { + case unknown(Error) + case notInitialized + case storePathNotFound + case dbModelFileNotFound + case dbModelCreationFail +} + +extension StorageException: Exception { + + public var category: ExceptionCategory { + .storage + } + + public var code: String { + switch self { + case .unknown: return "mdk.stg.01" + case .notInitialized: return "mdk.stg.02" + case .storePathNotFound: return "mdk.stg.03" + case .dbModelFileNotFound: return "mdk.stg.04" + case .dbModelCreationFail: return "mdk.stg.05" + } + } + + public var errorDescription: String? { + switch self { + case .unknown(let error): + return "Unknown Storage Error: \(error.localizedDescription)" + case .notInitialized: + return "DB not initialized" + case .storePathNotFound: + return "Gettings document directory fail" + case .dbModelFileNotFound: + return "DB File not found" + case .dbModelCreationFail: + return "Object model creation fail" + + } + } + +} diff --git a/Sources/Providers/Storage/StorageProvider.swift b/Sources/Providers/Storage/StorageProvider.swift new file mode 100644 index 0000000..2016c25 --- /dev/null +++ b/Sources/Providers/Storage/StorageProvider.swift @@ -0,0 +1,8 @@ +// +// File.swift +// +// +// Created by Miguel Angel on 25-05-21. +// + +import Foundation From 4beca4697dc239eaa519169080a941f88405afce Mon Sep 17 00:00:00 2001 From: Miguel Angel Zapata Date: Tue, 1 Jun 2021 13:16:17 -0400 Subject: [PATCH 12/22] Add configuration code for CoreDataAgent --- .../Agents/CoreData/CoreDataAgent.swift | 29 +++++++ .../CoreData/CoreDataStoreCoordinator.swift | 86 +++++++++++++++++++ .../Storage/Base/ConfigurationType.swift | 4 +- 3 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift create mode 100644 Sources/Providers/Storage/Agents/CoreData/CoreDataStoreCoordinator.swift diff --git a/Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift b/Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift new file mode 100644 index 0000000..81e709c --- /dev/null +++ b/Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift @@ -0,0 +1,29 @@ +// +// CoreDataAgent.swift +// +// +// Created by Miguel Angel on 01-06-21. +// + +import Combine +import CoreData + +final class CoreDataAgent { + + var managedContext: NSManagedObjectContext? + + required init(configuration: ConfigurationType) throws { + switch configuration { + case .basic: + try initDB(modelName: configuration.identifier(), storeType: .sqLiteStoreType) + case .inMemory: + try initDB(modelName: configuration.identifier(), storeType: .inMemoryStoreType) + } + } + + private func initDB(modelName: String, storeType: StoreType) throws { + let coordinator = try CoreDataStoreCoordinator.persistentStoreCoordinator(modelName: modelName, storeType: storeType) + self.managedContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType) + self.managedContext?.persistentStoreCoordinator = coordinator + } +} diff --git a/Sources/Providers/Storage/Agents/CoreData/CoreDataStoreCoordinator.swift b/Sources/Providers/Storage/Agents/CoreData/CoreDataStoreCoordinator.swift new file mode 100644 index 0000000..6b2a052 --- /dev/null +++ b/Sources/Providers/Storage/Agents/CoreData/CoreDataStoreCoordinator.swift @@ -0,0 +1,86 @@ +// +// CoreDataStoreCoordinator.swift +// +// +// Created by Miguel Angel on 31-05-21. +// + +import CoreData + +enum StoreType: String { + case sqLiteStoreType + case inMemoryStoreType +} + +class CoreDataStoreCoordinator { + + static func persistentStoreCoordinator(modelName: String, storeType: StoreType) throws -> NSPersistentStoreCoordinator { + return try NSPersistentStoreCoordinator.coordinator(modelName: modelName, storeType: storeType) + } + +} + +private extension NSPersistentStoreCoordinator { + + static func coordinator(modelName: String, storeType: StoreType) throws -> NSPersistentStoreCoordinator { + guard let modelURL = Bundle.main.url(forResource: modelName, withExtension: "momd") else { + throw StorageException.dbModelFileNotFound + } + + guard let managedObjectModel = NSManagedObjectModel(contentsOf: modelURL) else { + throw StorageException.dbModelCreationFail + } + + let persistentContainer = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel) + switch storeType { + case .sqLiteStoreType: + try persistentContainer.configureSQLiteStore(name: modelName) + case .inMemoryStoreType: + try persistentContainer.configureInMemoryStore() + } + return persistentContainer + } + +} + +private extension NSPersistentStoreCoordinator { + + func configureSQLiteStore(name: String) throws { + guard let documents = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last else { + throw StorageException.storePathNotFound + } + + do { + let url = documents.appendingPathComponent("\(name).sqlite") + let options = [ + NSMigratePersistentStoresAutomaticallyOption: true, + NSInferMappingModelAutomaticallyOption: true + ] + try addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: options) + } catch { + throw StorageException.unknown(error) + } + } + + func configureInMemoryStore() throws { + var memoryStoreError: Error? = .none + let description = NSPersistentStoreDescription() + description.type = NSInMemoryStoreType + description.shouldAddStoreAsynchronously = false + + self.addPersistentStore(with: description) { (description, error) in + precondition(description.type == NSInMemoryStoreType) + if let error = error { + memoryStoreError = error + print("Create an in-mem coordinator failed \(error)") + } + } + + if let error = memoryStoreError { + throw StorageException.unknown(error) + } + + } + +} + diff --git a/Sources/Providers/Storage/Base/ConfigurationType.swift b/Sources/Providers/Storage/Base/ConfigurationType.swift index 144f9c4..3fe6196 100644 --- a/Sources/Providers/Storage/Base/ConfigurationType.swift +++ b/Sources/Providers/Storage/Base/ConfigurationType.swift @@ -9,9 +9,9 @@ import Foundation public enum ConfigurationType { case basic(identifier: String) - case inMemory(identifier: String?) + case inMemory(identifier: String = "") - func identifier() -> String? { + func identifier() -> String { switch self { case .basic(let identifier): return identifier case .inMemory(let identifier): return identifier From 907a7d9c1d66d793af3451e767bbbd9e50e04f0f Mon Sep 17 00:00:00 2001 From: Miguel Angel Zapata Date: Tue, 1 Jun 2021 19:13:03 -0400 Subject: [PATCH 13/22] Add CoreDataAgents method concretions --- .../Agents/CoreData/CoreDataAgent.swift | 96 ++++++++++++++++++- .../Storage/Agents/NoneStorageAgent.swift | 42 ++++++++ Sources/Providers/Storage/Base/Storable.swift | 12 +++ Sources/Providers/Storage/StorageAgent.swift | 43 ++++++--- .../Providers/Storage/StorageProvider.swift | 32 ++++++- 5 files changed, 209 insertions(+), 16 deletions(-) create mode 100644 Sources/Providers/Storage/Agents/NoneStorageAgent.swift create mode 100644 Sources/Providers/Storage/Base/Storable.swift diff --git a/Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift b/Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift index 81e709c..e345fc5 100644 --- a/Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift +++ b/Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift @@ -7,9 +7,11 @@ import Combine import CoreData +import Foundation + +// TODO: Add Logger for track the exceptions and replace print - @mzapatae at 01/06/21 +final class CoreDataAgent: StorageAgent { -final class CoreDataAgent { - var managedContext: NSManagedObjectContext? required init(configuration: ConfigurationType) throws { @@ -26,4 +28,94 @@ final class CoreDataAgent { self.managedContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType) self.managedContext?.persistentStoreCoordinator = coordinator } + + func insert(object: Storable) -> AnyPublisher { + guard let managedObject = object as? NSManagedObject else { return Fail(error: .objectNotSupported).eraseToAnyPublisher() } + guard let context = managedContext else { return Fail(error: .notInitialized).eraseToAnyPublisher() } + do { + context.insert(managedObject) + try context.save() + return Just(()).setFailureType(to: StorageException.self).eraseToAnyPublisher() + } catch { + print("Insert object error: \(error)") + return Fail(error: .insertObjectFail).eraseToAnyPublisher() + } + } + + func insertAll(objects: [Storable]) -> AnyPublisher { + guard let managedObjects = objects as? [NSManagedObject] else { return Fail(error: .objectNotSupported).eraseToAnyPublisher() } + guard let context = managedContext else { return Fail(error: .notInitialized).eraseToAnyPublisher() } + do { + managedObjects.forEach { context.insert($0) } + try context.save() + return Just(()).setFailureType(to: StorageException.self).eraseToAnyPublisher() + } catch { + print("Insert all objects error: \(error)") + return Fail(error: .insertObjectFail).eraseToAnyPublisher() + } + } + + func update(object: Storable) -> AnyPublisher { + guard let managedObject = object as? NSManagedObject else { return Fail(error: .objectNotSupported).eraseToAnyPublisher() } + guard let context = managedContext else { return Fail(error: .notInitialized).eraseToAnyPublisher() } + do { + if managedObject.isUpdated { + try context.save() + return Just(()).setFailureType(to: StorageException.self).eraseToAnyPublisher() + } else { + print("Not exist and update for the object") + return Fail(error: .updateObjectFail).eraseToAnyPublisher() + } + } catch { + print("Update object error: \(error)") + return Fail(error: .updateObjectFail).eraseToAnyPublisher() + } + } + + func delete(object: Storable) -> AnyPublisher { + guard let managedObject = object as? NSManagedObject else { return Fail(error: .objectNotSupported).eraseToAnyPublisher() } + guard let context = managedContext else { return Fail(error: .notInitialized).eraseToAnyPublisher() } + do { + context.delete(managedObject) + try context.save() + return Just(()).setFailureType(to: StorageException.self).eraseToAnyPublisher() + } catch { + print("Delete object error: \(error)") + return Fail(error: .deleteObjectFail).eraseToAnyPublisher() + } + } + + func deleteAll(_ model: Storable.Type, predicate: NSPredicate?) -> AnyPublisher { + guard let type = model as? NSManagedObject.Type else { return Fail(error: .objectNotSupported).eraseToAnyPublisher() } + guard let context = managedContext else { return Fail(error: .notInitialized).eraseToAnyPublisher() } + do { + let fetchRequest = type.fetchRequest() as NSFetchRequest + fetchRequest.returnsObjectsAsFaults = false + fetchRequest.predicate = predicate + + let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest) + try context.execute(deleteRequest) + try context.save() + return Just(()).setFailureType(to: StorageException.self).eraseToAnyPublisher() + } catch { + print("Delete all objects error: \(error)") + return Fail(error: .deleteObjectFail).eraseToAnyPublisher() + } + } + + func readAll(_ model: T.Type, predicate: NSPredicate?, sorted: Sorted?) -> AnyPublisher<[T], StorageException> where T: Storable { + guard let type = model as? NSManagedObject.Type else { return Fail(error: .objectNotSupported).eraseToAnyPublisher() } + guard let context = managedContext else { return Fail(error: .notInitialized).eraseToAnyPublisher() } + do { + let fetchRequest = type.fetchRequest() as NSFetchRequest + fetchRequest.predicate = predicate + + let results = try context.fetch(fetchRequest) as? [T] ?? [] + return Just(results).setFailureType(to: StorageException.self).eraseToAnyPublisher() + } catch { + print("Read all objects error: \(error)") + return Fail(outputType: [T].self, failure: .readObjectFail).eraseToAnyPublisher() + } + } + } diff --git a/Sources/Providers/Storage/Agents/NoneStorageAgent.swift b/Sources/Providers/Storage/Agents/NoneStorageAgent.swift new file mode 100644 index 0000000..0ea30eb --- /dev/null +++ b/Sources/Providers/Storage/Agents/NoneStorageAgent.swift @@ -0,0 +1,42 @@ +// +// NoneAgent.swift +// +// +// Created by Miguel Angel on 01-06-21. +// + +import Combine +import Foundation + +final class NoneStorageAgent: StorageAgent { + + func insert(object: Storable) -> AnyPublisher { + print("Object is supposed to be inserted but nothing has happened") + return Just(()).setFailureType(to: StorageException.self).eraseToAnyPublisher() + } + + func insertAll(objects: [Storable]) -> AnyPublisher { + print("Objects is supposed to be inserted but nothing has happened") + return Just(()).setFailureType(to: StorageException.self).eraseToAnyPublisher() + } + + func update(object: Storable) -> AnyPublisher { + print("Object is supposed to be updated but nothing has happened") + return Just(()).setFailureType(to: StorageException.self).eraseToAnyPublisher() + } + + func delete(object: Storable) -> AnyPublisher { + print("Object is supposed to be deleted but nothing has happened") + return Just(()).setFailureType(to: StorageException.self).eraseToAnyPublisher() + } + + func deleteAll(_ model: Storable.Type, predicate: NSPredicate?) -> AnyPublisher { + print("Objects is supposed to be deleted but nothing has happened") + return Just(()).setFailureType(to: StorageException.self).eraseToAnyPublisher() + } + + func readAll(_ model: T.Type, predicate: NSPredicate?, sorted: Sorted?) -> AnyPublisher<[T], StorageException> where T: Storable { + print("Objects is supposed to be readed but nothing has happened") + return Just([]).setFailureType(to: StorageException.self).eraseToAnyPublisher() + } +} diff --git a/Sources/Providers/Storage/Base/Storable.swift b/Sources/Providers/Storage/Base/Storable.swift new file mode 100644 index 0000000..3ac95a6 --- /dev/null +++ b/Sources/Providers/Storage/Base/Storable.swift @@ -0,0 +1,12 @@ +// +// Storable.swift +// +// +// Created by Miguel Angel on 01-06-21. +// + +import Foundation + +public protocol Storable { + init() +} diff --git a/Sources/Providers/Storage/StorageAgent.swift b/Sources/Providers/Storage/StorageAgent.swift index a523954..db99770 100644 --- a/Sources/Providers/Storage/StorageAgent.swift +++ b/Sources/Providers/Storage/StorageAgent.swift @@ -6,23 +6,27 @@ // import Combine +import Foundation import AltairMDKCommon public protocol StorageAgent: AnyObject { - associatedtype Storable - associatedtype Predicate - func insert(_ object: Storable) -> AnyPublisher - func insertAll(_ objects: [Storable]) -> AnyPublisher - func update(_ object: Storable) -> AnyPublisher - func delete(_ object: Storable) -> AnyPublisher - func deleteAll(_ model: Storable.Type, predicate: Predicate?) -> AnyPublisher - func readAll(_ model: Storable.Type, predicate: Predicate?, sorted: Sorted?) -> AnyPublisher<[Storable], StorageException> + func insert(object: Storable) -> AnyPublisher + func insertAll(objects: [Storable]) -> AnyPublisher + func update(object: Storable) -> AnyPublisher + func delete(object: Storable) -> AnyPublisher + func deleteAll(_ model: Storable.Type, predicate: NSPredicate?) -> AnyPublisher + func readAll(_ model: T.Type, predicate: NSPredicate?, sorted: Sorted?) -> AnyPublisher<[T], StorageException> } public enum StorageException { case unknown(Error) case notInitialized + case readObjectFail + case insertObjectFail + case updateObjectFail + case deleteObjectFail case storePathNotFound + case objectNotSupported case dbModelFileNotFound case dbModelCreationFail } @@ -37,9 +41,14 @@ extension StorageException: Exception { switch self { case .unknown: return "mdk.stg.01" case .notInitialized: return "mdk.stg.02" - case .storePathNotFound: return "mdk.stg.03" - case .dbModelFileNotFound: return "mdk.stg.04" - case .dbModelCreationFail: return "mdk.stg.05" + case .readObjectFail: return "mdk.stg.03" + case .insertObjectFail: return "mdk.stg.04" + case .updateObjectFail: return "mdk.stg.05" + case .deleteObjectFail: return "mdk.stg.06" + case .storePathNotFound: return "mdk.stg.07" + case .objectNotSupported: return "mdk.stg.08" + case .dbModelFileNotFound: return "mdk.stg.09" + case .dbModelCreationFail: return "mdk.stg.10" } } @@ -48,9 +57,19 @@ extension StorageException: Exception { case .unknown(let error): return "Unknown Storage Error: \(error.localizedDescription)" case .notInitialized: - return "DB not initialized" + return "DB not initialized for previus error" + case .readObjectFail: + return "Read object failed!" + case .insertObjectFail: + return "Insert object failed!" + case .updateObjectFail: + return "Update object failed!" + case .deleteObjectFail: + return "Delete object failed!" case .storePathNotFound: return "Gettings document directory fail" + case .objectNotSupported: + return "Object downcast to the concrete storage type fail " case .dbModelFileNotFound: return "DB File not found" case .dbModelCreationFail: diff --git a/Sources/Providers/Storage/StorageProvider.swift b/Sources/Providers/Storage/StorageProvider.swift index 2016c25..8cb66c6 100644 --- a/Sources/Providers/Storage/StorageProvider.swift +++ b/Sources/Providers/Storage/StorageProvider.swift @@ -1,8 +1,36 @@ // -// File.swift +// StorageProvider.swift // // // Created by Miguel Angel on 25-05-21. // -import Foundation +public protocol StorageProviderProtocol { + var agent: StorageAgent { get } +} + +public enum StorageStrategy { + case noneStorage + case coreData +} + +public final class StorageProvider: StorageProviderProtocol { + public let agent: StorageAgent + + public init(strategy: StorageStrategy) { + do { + switch strategy { + case .noneStorage: + agent = NoneStorageAgent() + case .coreData: + agent = try CoreDataAgent(configuration: .basic(identifier: "agregar la wea")) + } + } catch { + print("StorageAgent-\(String(reflecting: strategy)) initialization failed!") + print("StorageAgent Error: \(error)") + print("Initialization fallback NoneStorageAgent") + agent = NoneStorageAgent() + } + } + +} From d2eefdbdc135e01ac5374c5ecbb5cf11cc733222 Mon Sep 17 00:00:00 2001 From: Miguel Angel Zapata Date: Wed, 2 Jun 2021 12:50:56 -0400 Subject: [PATCH 14/22] Update policy merge --- Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift b/Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift index e345fc5..1342e67 100644 --- a/Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift +++ b/Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift @@ -26,6 +26,7 @@ final class CoreDataAgent: StorageAgent { private func initDB(modelName: String, storeType: StoreType) throws { let coordinator = try CoreDataStoreCoordinator.persistentStoreCoordinator(modelName: modelName, storeType: storeType) self.managedContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType) + self.managedContext?.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy self.managedContext?.persistentStoreCoordinator = coordinator } From 3cec59539b33a466bd1c9fc1f6cfb8e4df2aadf0 Mon Sep 17 00:00:00 2001 From: Miguel Angel Zapata Date: Wed, 2 Jun 2021 16:43:55 -0400 Subject: [PATCH 15/22] Add method to create the model This to avoid dependency of CoreData context in the creation of the model --- .../Agents/CoreData/CoreDataAgent.swift | 27 +++++++++++++++++++ .../Storage/Agents/NoneStorageAgent.swift | 11 ++++++++ Sources/Providers/Storage/StorageAgent.swift | 5 ++-- 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift b/Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift index 1342e67..b966e39 100644 --- a/Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift +++ b/Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift @@ -30,6 +30,18 @@ final class CoreDataAgent: StorageAgent { self.managedContext?.persistentStoreCoordinator = coordinator } + // TODO: Try to create the object using the context init: "let object = CoreDataModel(context: context)" - @mzapatae at 02/06/21 + func create(_ model: T.Type) -> T? where T: Storable { + guard + let context = managedContext, + let entityDescription = NSEntityDescription.entity(forEntityName: .init(describing: model.self), in: context) + else { + print("Failed to create CoreData entity description \(String(describing: model.self))") + return nil + } + return NSManagedObject(entity: entityDescription, insertInto: context) as? T + } + func insert(object: Storable) -> AnyPublisher { guard let managedObject = object as? NSManagedObject else { return Fail(error: .objectNotSupported).eraseToAnyPublisher() } guard let context = managedContext else { return Fail(error: .notInitialized).eraseToAnyPublisher() } @@ -118,5 +130,20 @@ final class CoreDataAgent: StorageAgent { return Fail(outputType: [T].self, failure: .readObjectFail).eraseToAnyPublisher() } } + + func read2All(predicate: NSPredicate?, sorted: Sorted?) -> AnyPublisher<[T], StorageException> where T: Storable { + guard let type = T.self as? NSManagedObject.Type else { return Fail(error: .objectNotSupported).eraseToAnyPublisher() } + guard let context = managedContext else { return Fail(error: .notInitialized).eraseToAnyPublisher() } + do { + let fetchRequest = type.fetchRequest() as NSFetchRequest + fetchRequest.predicate = predicate + + let results = try context.fetch(fetchRequest) as? [T] ?? [] + return Just(results).setFailureType(to: StorageException.self).eraseToAnyPublisher() + } catch { + print("Read all objects error: \(error)") + return Fail(outputType: [T].self, failure: .readObjectFail).eraseToAnyPublisher() + } + } } diff --git a/Sources/Providers/Storage/Agents/NoneStorageAgent.swift b/Sources/Providers/Storage/Agents/NoneStorageAgent.swift index 0ea30eb..67e0306 100644 --- a/Sources/Providers/Storage/Agents/NoneStorageAgent.swift +++ b/Sources/Providers/Storage/Agents/NoneStorageAgent.swift @@ -10,6 +10,11 @@ import Foundation final class NoneStorageAgent: StorageAgent { + func create(_ model: T.Type) -> T? where T: Storable { + print("Object is supposed to be created but nothing has happened") + return T.init() + } + func insert(object: Storable) -> AnyPublisher { print("Object is supposed to be inserted but nothing has happened") return Just(()).setFailureType(to: StorageException.self).eraseToAnyPublisher() @@ -39,4 +44,10 @@ final class NoneStorageAgent: StorageAgent { print("Objects is supposed to be readed but nothing has happened") return Just([]).setFailureType(to: StorageException.self).eraseToAnyPublisher() } + + func read2All(predicate: NSPredicate?, sorted: Sorted?) -> AnyPublisher<[T], StorageException> where T : Storable { + print("Objects is supposed to be readed but nothing has happened") + return Just([]).setFailureType(to: StorageException.self).eraseToAnyPublisher() + } + } diff --git a/Sources/Providers/Storage/StorageAgent.swift b/Sources/Providers/Storage/StorageAgent.swift index db99770..6d08d60 100644 --- a/Sources/Providers/Storage/StorageAgent.swift +++ b/Sources/Providers/Storage/StorageAgent.swift @@ -10,12 +10,14 @@ import Foundation import AltairMDKCommon public protocol StorageAgent: AnyObject { + func create(_ model: T.Type) -> T? func insert(object: Storable) -> AnyPublisher func insertAll(objects: [Storable]) -> AnyPublisher func update(object: Storable) -> AnyPublisher func delete(object: Storable) -> AnyPublisher func deleteAll(_ model: Storable.Type, predicate: NSPredicate?) -> AnyPublisher func readAll(_ model: T.Type, predicate: NSPredicate?, sorted: Sorted?) -> AnyPublisher<[T], StorageException> + func read2All(predicate: NSPredicate?, sorted: Sorted?) -> AnyPublisher<[T], StorageException> } public enum StorageException { @@ -69,12 +71,11 @@ extension StorageException: Exception { case .storePathNotFound: return "Gettings document directory fail" case .objectNotSupported: - return "Object downcast to the concrete storage type fail " + return "Object type for the concrete storage is not supported" case .dbModelFileNotFound: return "DB File not found" case .dbModelCreationFail: return "Object model creation fail" - } } From 71c38f6d7ca2509c0cbff51ee9d8219d1cbb0478 Mon Sep 17 00:00:00 2001 From: Miguel Angel Zapata Date: Wed, 2 Jun 2021 21:07:45 -0400 Subject: [PATCH 16/22] Make method readAll more generic --- .../Agents/CoreData/CoreDataAgent.swift | 19 ++----------------- .../Storage/Agents/NoneStorageAgent.swift | 7 +------ Sources/Providers/Storage/StorageAgent.swift | 3 +-- 3 files changed, 4 insertions(+), 25 deletions(-) diff --git a/Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift b/Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift index b966e39..d4fbc8f 100644 --- a/Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift +++ b/Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift @@ -116,22 +116,7 @@ final class CoreDataAgent: StorageAgent { } } - func readAll(_ model: T.Type, predicate: NSPredicate?, sorted: Sorted?) -> AnyPublisher<[T], StorageException> where T: Storable { - guard let type = model as? NSManagedObject.Type else { return Fail(error: .objectNotSupported).eraseToAnyPublisher() } - guard let context = managedContext else { return Fail(error: .notInitialized).eraseToAnyPublisher() } - do { - let fetchRequest = type.fetchRequest() as NSFetchRequest - fetchRequest.predicate = predicate - - let results = try context.fetch(fetchRequest) as? [T] ?? [] - return Just(results).setFailureType(to: StorageException.self).eraseToAnyPublisher() - } catch { - print("Read all objects error: \(error)") - return Fail(outputType: [T].self, failure: .readObjectFail).eraseToAnyPublisher() - } - } - - func read2All(predicate: NSPredicate?, sorted: Sorted?) -> AnyPublisher<[T], StorageException> where T: Storable { + func readAll(predicate: NSPredicate? = .none, sorted: Sorted? = .none) -> AnyPublisher<[T], StorageException> where T: Storable { guard let type = T.self as? NSManagedObject.Type else { return Fail(error: .objectNotSupported).eraseToAnyPublisher() } guard let context = managedContext else { return Fail(error: .notInitialized).eraseToAnyPublisher() } do { @@ -145,5 +130,5 @@ final class CoreDataAgent: StorageAgent { return Fail(outputType: [T].self, failure: .readObjectFail).eraseToAnyPublisher() } } - + } diff --git a/Sources/Providers/Storage/Agents/NoneStorageAgent.swift b/Sources/Providers/Storage/Agents/NoneStorageAgent.swift index 67e0306..d5ad345 100644 --- a/Sources/Providers/Storage/Agents/NoneStorageAgent.swift +++ b/Sources/Providers/Storage/Agents/NoneStorageAgent.swift @@ -40,12 +40,7 @@ final class NoneStorageAgent: StorageAgent { return Just(()).setFailureType(to: StorageException.self).eraseToAnyPublisher() } - func readAll(_ model: T.Type, predicate: NSPredicate?, sorted: Sorted?) -> AnyPublisher<[T], StorageException> where T: Storable { - print("Objects is supposed to be readed but nothing has happened") - return Just([]).setFailureType(to: StorageException.self).eraseToAnyPublisher() - } - - func read2All(predicate: NSPredicate?, sorted: Sorted?) -> AnyPublisher<[T], StorageException> where T : Storable { + func readAll(predicate: NSPredicate?, sorted: Sorted?) -> AnyPublisher<[T], StorageException> where T: Storable { print("Objects is supposed to be readed but nothing has happened") return Just([]).setFailureType(to: StorageException.self).eraseToAnyPublisher() } diff --git a/Sources/Providers/Storage/StorageAgent.swift b/Sources/Providers/Storage/StorageAgent.swift index 6d08d60..e599264 100644 --- a/Sources/Providers/Storage/StorageAgent.swift +++ b/Sources/Providers/Storage/StorageAgent.swift @@ -16,8 +16,7 @@ public protocol StorageAgent: AnyObject { func update(object: Storable) -> AnyPublisher func delete(object: Storable) -> AnyPublisher func deleteAll(_ model: Storable.Type, predicate: NSPredicate?) -> AnyPublisher - func readAll(_ model: T.Type, predicate: NSPredicate?, sorted: Sorted?) -> AnyPublisher<[T], StorageException> - func read2All(predicate: NSPredicate?, sorted: Sorted?) -> AnyPublisher<[T], StorageException> + func readAll(predicate: NSPredicate?, sorted: Sorted?) -> AnyPublisher<[T], StorageException> } public enum StorageException { From 330a197a61eddc461a492f31a2ca777a444b1d45 Mon Sep 17 00:00:00 2001 From: Miguel Angel Zapata Date: Mon, 7 Jun 2021 17:10:53 -0400 Subject: [PATCH 17/22] Revert "Make method readAll more generic" This reverts commit 71c38f6d7ca2509c0cbff51ee9d8219d1cbb0478. --- .../Agents/CoreData/CoreDataAgent.swift | 19 +++++++++++++++++-- .../Storage/Agents/NoneStorageAgent.swift | 7 ++++++- Sources/Providers/Storage/StorageAgent.swift | 3 ++- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift b/Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift index d4fbc8f..b966e39 100644 --- a/Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift +++ b/Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift @@ -116,7 +116,22 @@ final class CoreDataAgent: StorageAgent { } } - func readAll(predicate: NSPredicate? = .none, sorted: Sorted? = .none) -> AnyPublisher<[T], StorageException> where T: Storable { + func readAll(_ model: T.Type, predicate: NSPredicate?, sorted: Sorted?) -> AnyPublisher<[T], StorageException> where T: Storable { + guard let type = model as? NSManagedObject.Type else { return Fail(error: .objectNotSupported).eraseToAnyPublisher() } + guard let context = managedContext else { return Fail(error: .notInitialized).eraseToAnyPublisher() } + do { + let fetchRequest = type.fetchRequest() as NSFetchRequest + fetchRequest.predicate = predicate + + let results = try context.fetch(fetchRequest) as? [T] ?? [] + return Just(results).setFailureType(to: StorageException.self).eraseToAnyPublisher() + } catch { + print("Read all objects error: \(error)") + return Fail(outputType: [T].self, failure: .readObjectFail).eraseToAnyPublisher() + } + } + + func read2All(predicate: NSPredicate?, sorted: Sorted?) -> AnyPublisher<[T], StorageException> where T: Storable { guard let type = T.self as? NSManagedObject.Type else { return Fail(error: .objectNotSupported).eraseToAnyPublisher() } guard let context = managedContext else { return Fail(error: .notInitialized).eraseToAnyPublisher() } do { @@ -130,5 +145,5 @@ final class CoreDataAgent: StorageAgent { return Fail(outputType: [T].self, failure: .readObjectFail).eraseToAnyPublisher() } } - + } diff --git a/Sources/Providers/Storage/Agents/NoneStorageAgent.swift b/Sources/Providers/Storage/Agents/NoneStorageAgent.swift index d5ad345..67e0306 100644 --- a/Sources/Providers/Storage/Agents/NoneStorageAgent.swift +++ b/Sources/Providers/Storage/Agents/NoneStorageAgent.swift @@ -40,7 +40,12 @@ final class NoneStorageAgent: StorageAgent { return Just(()).setFailureType(to: StorageException.self).eraseToAnyPublisher() } - func readAll(predicate: NSPredicate?, sorted: Sorted?) -> AnyPublisher<[T], StorageException> where T: Storable { + func readAll(_ model: T.Type, predicate: NSPredicate?, sorted: Sorted?) -> AnyPublisher<[T], StorageException> where T: Storable { + print("Objects is supposed to be readed but nothing has happened") + return Just([]).setFailureType(to: StorageException.self).eraseToAnyPublisher() + } + + func read2All(predicate: NSPredicate?, sorted: Sorted?) -> AnyPublisher<[T], StorageException> where T : Storable { print("Objects is supposed to be readed but nothing has happened") return Just([]).setFailureType(to: StorageException.self).eraseToAnyPublisher() } diff --git a/Sources/Providers/Storage/StorageAgent.swift b/Sources/Providers/Storage/StorageAgent.swift index e599264..6d08d60 100644 --- a/Sources/Providers/Storage/StorageAgent.swift +++ b/Sources/Providers/Storage/StorageAgent.swift @@ -16,7 +16,8 @@ public protocol StorageAgent: AnyObject { func update(object: Storable) -> AnyPublisher func delete(object: Storable) -> AnyPublisher func deleteAll(_ model: Storable.Type, predicate: NSPredicate?) -> AnyPublisher - func readAll(predicate: NSPredicate?, sorted: Sorted?) -> AnyPublisher<[T], StorageException> + func readAll(_ model: T.Type, predicate: NSPredicate?, sorted: Sorted?) -> AnyPublisher<[T], StorageException> + func read2All(predicate: NSPredicate?, sorted: Sorted?) -> AnyPublisher<[T], StorageException> } public enum StorageException { From 965f8a2d15427e43a4f4874dd88040d6a88e2b92 Mon Sep 17 00:00:00 2001 From: Miguel Angel Zapata Date: Thu, 10 Jun 2021 10:33:37 -0400 Subject: [PATCH 18/22] Fix object create NoneStorage --- Sources/Providers/Storage/Agents/NoneStorageAgent.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Providers/Storage/Agents/NoneStorageAgent.swift b/Sources/Providers/Storage/Agents/NoneStorageAgent.swift index 67e0306..31c0162 100644 --- a/Sources/Providers/Storage/Agents/NoneStorageAgent.swift +++ b/Sources/Providers/Storage/Agents/NoneStorageAgent.swift @@ -12,7 +12,7 @@ final class NoneStorageAgent: StorageAgent { func create(_ model: T.Type) -> T? where T: Storable { print("Object is supposed to be created but nothing has happened") - return T.init() + return nil } func insert(object: Storable) -> AnyPublisher { From 0b9967f40c98a52155919b4066386358466d87cf Mon Sep 17 00:00:00 2001 From: Miguel Angel Zapata Date: Thu, 10 Jun 2021 12:39:19 -0400 Subject: [PATCH 19/22] Change sideEffect definition --- .../Common/Extensions/Combine/Publisher+NoRetain.swift | 2 +- .../Common/Extensions/Combine/Publisher+Store.swift | 5 ++--- Sources/Common/Types/SideEffect.swift | 10 +++++----- Sources/Providers/Storage/StorageProvider.swift | 6 +++--- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/Sources/Common/Extensions/Combine/Publisher+NoRetain.swift b/Sources/Common/Extensions/Combine/Publisher+NoRetain.swift index 4f5f1e5..fcb5956 100644 --- a/Sources/Common/Extensions/Combine/Publisher+NoRetain.swift +++ b/Sources/Common/Extensions/Combine/Publisher+NoRetain.swift @@ -17,7 +17,7 @@ extension Publisher where Self.Failure == Never { /// .assignNoRetain(to: \.exception, on: self) /// .store(in: &bag) /// - func assignNoRetain(to keyPath: ReferenceWritableKeyPath< Root, + public func assignNoRetain(to keyPath: ReferenceWritableKeyPath< Root, Self.Output >, on object: Root) -> AnyCancellable where Root: AnyObject { sink { [weak object] (value) in object?[keyPath: keyPath] = value diff --git a/Sources/Common/Extensions/Combine/Publisher+Store.swift b/Sources/Common/Extensions/Combine/Publisher+Store.swift index 3319618..7863e37 100644 --- a/Sources/Common/Extensions/Combine/Publisher+Store.swift +++ b/Sources/Common/Extensions/Combine/Publisher+Store.swift @@ -9,14 +9,13 @@ extension Publishers { public static func store( initial: State, - input: PassthroughSubject, reduce: @escaping (State, Action) -> State, scheduler: Scheduler, - sideEffects: [SideEffect] + sideEffects: [SideEffect] ) -> AnyPublisher { let state = CurrentValueSubject(initial) - let actions = sideEffects.map { $0.run(input.eraseToAnyPublisher()) } + let actions = sideEffects.map { $0.run(state.eraseToAnyPublisher()) } return Deferred { Publishers.MergeMany(actions) diff --git a/Sources/Common/Types/SideEffect.swift b/Sources/Common/Types/SideEffect.swift index 2fd1a55..f22825a 100644 --- a/Sources/Common/Types/SideEffect.swift +++ b/Sources/Common/Types/SideEffect.swift @@ -4,14 +4,14 @@ import Combine -public struct SideEffect { - public let run: (AnyPublisher) -> AnyPublisher +public struct SideEffect { + public let run: (AnyPublisher) -> AnyPublisher } extension SideEffect { - public init(effects: @escaping (Action) -> Effect) where Effect.Output == Action, Effect.Failure == Never { - self.run = { action -> AnyPublisher in - action.map { effects($0) }.switchToLatest().eraseToAnyPublisher() + public init(effects: @escaping (State) -> Effect) where Effect.Output == Action, Effect.Failure == Never { + self.run = { state -> AnyPublisher in + state.map { effects($0) }.switchToLatest().eraseToAnyPublisher() } } } diff --git a/Sources/Providers/Storage/StorageProvider.swift b/Sources/Providers/Storage/StorageProvider.swift index 8cb66c6..f43de75 100644 --- a/Sources/Providers/Storage/StorageProvider.swift +++ b/Sources/Providers/Storage/StorageProvider.swift @@ -11,7 +11,7 @@ public protocol StorageProviderProtocol { public enum StorageStrategy { case noneStorage - case coreData + case coreData(name: String) } public final class StorageProvider: StorageProviderProtocol { @@ -22,8 +22,8 @@ public final class StorageProvider: StorageProviderProtocol { switch strategy { case .noneStorage: agent = NoneStorageAgent() - case .coreData: - agent = try CoreDataAgent(configuration: .basic(identifier: "agregar la wea")) + case .coreData(let name): + agent = try CoreDataAgent(configuration: .basic(identifier: name)) } } catch { print("StorageAgent-\(String(reflecting: strategy)) initialization failed!") From 638b8dcba69d1bea0deb3a55e59bcf3f8fce5abd Mon Sep 17 00:00:00 2001 From: Miguel Angel Zapata Date: Sat, 12 Jun 2021 00:08:06 -0400 Subject: [PATCH 20/22] Now the Provider admit the url of the DB file --- .../Storage/Agents/CoreData/CoreDataAgent.swift | 14 +++++++------- .../Agents/CoreData/CoreDataStoreCoordinator.swift | 12 ++++++------ .../Providers/Storage/Base/ConfigurationType.swift | 9 +-------- Sources/Providers/Storage/Base/Storable.swift | 2 +- Sources/Providers/Storage/StorageProvider.swift | 8 +++++--- 5 files changed, 20 insertions(+), 25 deletions(-) diff --git a/Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift b/Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift index b966e39..64b6088 100644 --- a/Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift +++ b/Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift @@ -16,15 +16,15 @@ final class CoreDataAgent: StorageAgent { required init(configuration: ConfigurationType) throws { switch configuration { - case .basic: - try initDB(modelName: configuration.identifier(), storeType: .sqLiteStoreType) - case .inMemory: - try initDB(modelName: configuration.identifier(), storeType: .inMemoryStoreType) + case .basic(dbFile: let dbFile): + try initDB(urlModel: dbFile, storeType: .sqLiteStoreType) + case .inMemory(identifier: _): + try initDB(urlModel: .none, storeType: .inMemoryStoreType) } } - private func initDB(modelName: String, storeType: StoreType) throws { - let coordinator = try CoreDataStoreCoordinator.persistentStoreCoordinator(modelName: modelName, storeType: storeType) + private func initDB(urlModel: URL?, storeType: StoreType) throws { + let coordinator = try CoreDataStoreCoordinator.persistentStoreCoordinator(urlModel: urlModel, storeType: storeType) self.managedContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType) self.managedContext?.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy self.managedContext?.persistentStoreCoordinator = coordinator @@ -34,7 +34,7 @@ final class CoreDataAgent: StorageAgent { func create(_ model: T.Type) -> T? where T: Storable { guard let context = managedContext, - let entityDescription = NSEntityDescription.entity(forEntityName: .init(describing: model.self), in: context) + let entityDescription = NSEntityDescription.entity(forEntityName: model.entityName, in: context) else { print("Failed to create CoreData entity description \(String(describing: model.self))") return nil diff --git a/Sources/Providers/Storage/Agents/CoreData/CoreDataStoreCoordinator.swift b/Sources/Providers/Storage/Agents/CoreData/CoreDataStoreCoordinator.swift index 6b2a052..118f26b 100644 --- a/Sources/Providers/Storage/Agents/CoreData/CoreDataStoreCoordinator.swift +++ b/Sources/Providers/Storage/Agents/CoreData/CoreDataStoreCoordinator.swift @@ -14,27 +14,27 @@ enum StoreType: String { class CoreDataStoreCoordinator { - static func persistentStoreCoordinator(modelName: String, storeType: StoreType) throws -> NSPersistentStoreCoordinator { - return try NSPersistentStoreCoordinator.coordinator(modelName: modelName, storeType: storeType) + static func persistentStoreCoordinator(urlModel: URL?, storeType: StoreType) throws -> NSPersistentStoreCoordinator { + return try NSPersistentStoreCoordinator.coordinator(urlModel: urlModel, storeType: storeType) } } private extension NSPersistentStoreCoordinator { - static func coordinator(modelName: String, storeType: StoreType) throws -> NSPersistentStoreCoordinator { - guard let modelURL = Bundle.main.url(forResource: modelName, withExtension: "momd") else { + static func coordinator(urlModel: URL?, storeType: StoreType) throws -> NSPersistentStoreCoordinator { + guard let dbModel = urlModel else { throw StorageException.dbModelFileNotFound } - guard let managedObjectModel = NSManagedObjectModel(contentsOf: modelURL) else { + guard let managedObjectModel = NSManagedObjectModel(contentsOf: dbModel) else { throw StorageException.dbModelCreationFail } let persistentContainer = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel) switch storeType { case .sqLiteStoreType: - try persistentContainer.configureSQLiteStore(name: modelName) + try persistentContainer.configureSQLiteStore(name: dbModel.deletingPathExtension().lastPathComponent) case .inMemoryStoreType: try persistentContainer.configureInMemoryStore() } diff --git a/Sources/Providers/Storage/Base/ConfigurationType.swift b/Sources/Providers/Storage/Base/ConfigurationType.swift index 3fe6196..7e2d793 100644 --- a/Sources/Providers/Storage/Base/ConfigurationType.swift +++ b/Sources/Providers/Storage/Base/ConfigurationType.swift @@ -8,13 +8,6 @@ import Foundation public enum ConfigurationType { - case basic(identifier: String) + case basic(dbFile: URL?) case inMemory(identifier: String = "") - - func identifier() -> String { - switch self { - case .basic(let identifier): return identifier - case .inMemory(let identifier): return identifier - } - } } diff --git a/Sources/Providers/Storage/Base/Storable.swift b/Sources/Providers/Storage/Base/Storable.swift index 3ac95a6..42b0bd2 100644 --- a/Sources/Providers/Storage/Base/Storable.swift +++ b/Sources/Providers/Storage/Base/Storable.swift @@ -8,5 +8,5 @@ import Foundation public protocol Storable { - init() + static var entityName: String { get } } diff --git a/Sources/Providers/Storage/StorageProvider.swift b/Sources/Providers/Storage/StorageProvider.swift index f43de75..c9012aa 100644 --- a/Sources/Providers/Storage/StorageProvider.swift +++ b/Sources/Providers/Storage/StorageProvider.swift @@ -5,13 +5,15 @@ // Created by Miguel Angel on 25-05-21. // +import Foundation + public protocol StorageProviderProtocol { var agent: StorageAgent { get } } public enum StorageStrategy { case noneStorage - case coreData(name: String) + case coreData(dbFile: URL?) } public final class StorageProvider: StorageProviderProtocol { @@ -22,8 +24,8 @@ public final class StorageProvider: StorageProviderProtocol { switch strategy { case .noneStorage: agent = NoneStorageAgent() - case .coreData(let name): - agent = try CoreDataAgent(configuration: .basic(identifier: name)) + case .coreData(let url): + agent = try CoreDataAgent(configuration: .basic(dbFile: url)) } } catch { print("StorageAgent-\(String(reflecting: strategy)) initialization failed!") From a9c0a5ae233384b78db7dbd2bf03919dbc8a7d5e Mon Sep 17 00:00:00 2001 From: Miguel Angel Zapata Date: Mon, 14 Jun 2021 12:29:51 -0400 Subject: [PATCH 21/22] Removing UseCase Protocol --- Sources/Common/Protocols/UseCase.swift | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 Sources/Common/Protocols/UseCase.swift diff --git a/Sources/Common/Protocols/UseCase.swift b/Sources/Common/Protocols/UseCase.swift deleted file mode 100644 index 3225892..0000000 --- a/Sources/Common/Protocols/UseCase.swift +++ /dev/null @@ -1,10 +0,0 @@ -// -// Created by Miguel Angel on 25-03-21. -// - -import Combine - -public protocol UseCase: AnyObject { - associatedtype T - func execute() -> AnyPublisher -} From 170fbcc181c5af570314db1ff3121efbf2dc1f6c Mon Sep 17 00:00:00 2001 From: Miguel Angel Zapata Date: Wed, 16 Jun 2021 16:16:54 -0400 Subject: [PATCH 22/22] Improve StorageProvider initialization - To represent the failure of the initialization of the storage - Also add a Read First method to simplify the return of one element - Remove the NoneStorage --- .../Agents/CoreData/CoreDataAgent.swift | 31 ++++++----- .../Storage/Agents/NoneStorageAgent.swift | 53 ------------------- Sources/Providers/Storage/Base/Sorted.swift | 11 ---- Sources/Providers/Storage/StorageAgent.swift | 4 +- .../Providers/Storage/StorageProvider.swift | 16 ++---- 5 files changed, 23 insertions(+), 92 deletions(-) delete mode 100644 Sources/Providers/Storage/Agents/NoneStorageAgent.swift delete mode 100644 Sources/Providers/Storage/Base/Sorted.swift diff --git a/Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift b/Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift index 64b6088..b0c719b 100644 --- a/Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift +++ b/Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift @@ -14,12 +14,17 @@ final class CoreDataAgent: StorageAgent { var managedContext: NSManagedObjectContext? - required init(configuration: ConfigurationType) throws { - switch configuration { - case .basic(dbFile: let dbFile): - try initDB(urlModel: dbFile, storeType: .sqLiteStoreType) - case .inMemory(identifier: _): - try initDB(urlModel: .none, storeType: .inMemoryStoreType) + required init(configuration: ConfigurationType) { + do { + switch configuration { + case .basic(dbFile: let dbFile): + try initDB(urlModel: dbFile, storeType: .sqLiteStoreType) + case .inMemory(identifier: _): + try initDB(urlModel: .none, storeType: .inMemoryStoreType) + } + } catch { + print("StorageAgent: CoreData initialization failed!") + print("Error: \(error)") } } @@ -116,23 +121,23 @@ final class CoreDataAgent: StorageAgent { } } - func readAll(_ model: T.Type, predicate: NSPredicate?, sorted: Sorted?) -> AnyPublisher<[T], StorageException> where T: Storable { + func readFirst(_ model: T.Type, predicate: NSPredicate?) -> AnyPublisher where T: Storable { guard let type = model as? NSManagedObject.Type else { return Fail(error: .objectNotSupported).eraseToAnyPublisher() } guard let context = managedContext else { return Fail(error: .notInitialized).eraseToAnyPublisher() } do { let fetchRequest = type.fetchRequest() as NSFetchRequest fetchRequest.predicate = predicate - let results = try context.fetch(fetchRequest) as? [T] ?? [] - return Just(results).setFailureType(to: StorageException.self).eraseToAnyPublisher() + let result = try context.fetch(fetchRequest).first as? T + return Just(result).setFailureType(to: StorageException.self).eraseToAnyPublisher() } catch { - print("Read all objects error: \(error)") - return Fail(outputType: [T].self, failure: .readObjectFail).eraseToAnyPublisher() + print("Read First object error: \(error)") + return Fail(outputType: T?.self, failure: .readObjectFail).eraseToAnyPublisher() } } - func read2All(predicate: NSPredicate?, sorted: Sorted?) -> AnyPublisher<[T], StorageException> where T: Storable { - guard let type = T.self as? NSManagedObject.Type else { return Fail(error: .objectNotSupported).eraseToAnyPublisher() } + func readAll(_ model: T.Type, predicate: NSPredicate?) -> AnyPublisher<[T], StorageException> where T: Storable { + guard let type = model as? NSManagedObject.Type else { return Fail(error: .objectNotSupported).eraseToAnyPublisher() } guard let context = managedContext else { return Fail(error: .notInitialized).eraseToAnyPublisher() } do { let fetchRequest = type.fetchRequest() as NSFetchRequest diff --git a/Sources/Providers/Storage/Agents/NoneStorageAgent.swift b/Sources/Providers/Storage/Agents/NoneStorageAgent.swift deleted file mode 100644 index 31c0162..0000000 --- a/Sources/Providers/Storage/Agents/NoneStorageAgent.swift +++ /dev/null @@ -1,53 +0,0 @@ -// -// NoneAgent.swift -// -// -// Created by Miguel Angel on 01-06-21. -// - -import Combine -import Foundation - -final class NoneStorageAgent: StorageAgent { - - func create(_ model: T.Type) -> T? where T: Storable { - print("Object is supposed to be created but nothing has happened") - return nil - } - - func insert(object: Storable) -> AnyPublisher { - print("Object is supposed to be inserted but nothing has happened") - return Just(()).setFailureType(to: StorageException.self).eraseToAnyPublisher() - } - - func insertAll(objects: [Storable]) -> AnyPublisher { - print("Objects is supposed to be inserted but nothing has happened") - return Just(()).setFailureType(to: StorageException.self).eraseToAnyPublisher() - } - - func update(object: Storable) -> AnyPublisher { - print("Object is supposed to be updated but nothing has happened") - return Just(()).setFailureType(to: StorageException.self).eraseToAnyPublisher() - } - - func delete(object: Storable) -> AnyPublisher { - print("Object is supposed to be deleted but nothing has happened") - return Just(()).setFailureType(to: StorageException.self).eraseToAnyPublisher() - } - - func deleteAll(_ model: Storable.Type, predicate: NSPredicate?) -> AnyPublisher { - print("Objects is supposed to be deleted but nothing has happened") - return Just(()).setFailureType(to: StorageException.self).eraseToAnyPublisher() - } - - func readAll(_ model: T.Type, predicate: NSPredicate?, sorted: Sorted?) -> AnyPublisher<[T], StorageException> where T: Storable { - print("Objects is supposed to be readed but nothing has happened") - return Just([]).setFailureType(to: StorageException.self).eraseToAnyPublisher() - } - - func read2All(predicate: NSPredicate?, sorted: Sorted?) -> AnyPublisher<[T], StorageException> where T : Storable { - print("Objects is supposed to be readed but nothing has happened") - return Just([]).setFailureType(to: StorageException.self).eraseToAnyPublisher() - } - -} diff --git a/Sources/Providers/Storage/Base/Sorted.swift b/Sources/Providers/Storage/Base/Sorted.swift deleted file mode 100644 index 53fbf5c..0000000 --- a/Sources/Providers/Storage/Base/Sorted.swift +++ /dev/null @@ -1,11 +0,0 @@ -// -// Sorted.swift -// -// -// Created by Miguel Angel on 25-05-21. -// - -public struct Sorted { - var key: String - var ascending: Bool = true -} diff --git a/Sources/Providers/Storage/StorageAgent.swift b/Sources/Providers/Storage/StorageAgent.swift index 6d08d60..764ad93 100644 --- a/Sources/Providers/Storage/StorageAgent.swift +++ b/Sources/Providers/Storage/StorageAgent.swift @@ -16,8 +16,8 @@ public protocol StorageAgent: AnyObject { func update(object: Storable) -> AnyPublisher func delete(object: Storable) -> AnyPublisher func deleteAll(_ model: Storable.Type, predicate: NSPredicate?) -> AnyPublisher - func readAll(_ model: T.Type, predicate: NSPredicate?, sorted: Sorted?) -> AnyPublisher<[T], StorageException> - func read2All(predicate: NSPredicate?, sorted: Sorted?) -> AnyPublisher<[T], StorageException> + func readFirst(_ model: T.Type, predicate: NSPredicate?) -> AnyPublisher + func readAll(_ model: T.Type, predicate: NSPredicate?) -> AnyPublisher<[T], StorageException> } public enum StorageException { diff --git a/Sources/Providers/Storage/StorageProvider.swift b/Sources/Providers/Storage/StorageProvider.swift index c9012aa..67b54d1 100644 --- a/Sources/Providers/Storage/StorageProvider.swift +++ b/Sources/Providers/Storage/StorageProvider.swift @@ -12,7 +12,6 @@ public protocol StorageProviderProtocol { } public enum StorageStrategy { - case noneStorage case coreData(dbFile: URL?) } @@ -20,18 +19,9 @@ public final class StorageProvider: StorageProviderProtocol { public let agent: StorageAgent public init(strategy: StorageStrategy) { - do { - switch strategy { - case .noneStorage: - agent = NoneStorageAgent() - case .coreData(let url): - agent = try CoreDataAgent(configuration: .basic(dbFile: url)) - } - } catch { - print("StorageAgent-\(String(reflecting: strategy)) initialization failed!") - print("StorageAgent Error: \(error)") - print("Initialization fallback NoneStorageAgent") - agent = NoneStorageAgent() + switch strategy { + case .coreData(let url): + agent = CoreDataAgent(configuration: .basic(dbFile: url)) } }