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/Extensions/Combine/Publisher+NoRetain.swift b/Sources/Common/Extensions/Combine/Publisher+NoRetain.swift new file mode 100644 index 0000000..fcb5956 --- /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) + /// + 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/Extensions/SwiftUI/View+Navigation.swift b/Sources/Common/Extensions/SwiftUI/View+Navigation.swift new file mode 100644 index 0000000..53a6e56 --- /dev/null +++ b/Sources/Common/Extensions/SwiftUI/View+Navigation.swift @@ -0,0 +1,84 @@ +// +// 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 + } + } + + /// ### 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 }, + 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: @escaping (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/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/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 } diff --git a/Sources/Common/Protocols/Store.swift b/Sources/Common/Protocols/Store.swift index 6d25630..eface3a 100644 --- a/Sources/Common/Protocols/Store.swift +++ b/Sources/Common/Protocols/Store.swift @@ -2,11 +2,23 @@ // Created by Miguel Angel on 25-03-21. // +import SwiftUI import Foundation public protocol Store: ObservableObject { associatedtype State associatedtype Action var state: State { get } - func trigger(action: Action) + 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/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 -} 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 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/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 + } + } +} 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 [] + } + } + +} 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) + } + +} 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() + } + +} diff --git a/Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift b/Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift new file mode 100644 index 0000000..b0c719b --- /dev/null +++ b/Sources/Providers/Storage/Agents/CoreData/CoreDataAgent.swift @@ -0,0 +1,154 @@ +// +// CoreDataAgent.swift +// +// +// Created by Miguel Angel on 01-06-21. +// + +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 { + + var managedContext: NSManagedObjectContext? + + 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)") + } + } + + 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 + } + + // 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: model.entityName, 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() } + 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 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 result = try context.fetch(fetchRequest).first as? T + return Just(result).setFailureType(to: StorageException.self).eraseToAnyPublisher() + } catch { + print("Read First object error: \(error)") + return Fail(outputType: T?.self, failure: .readObjectFail).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 + 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/CoreData/CoreDataStoreCoordinator.swift b/Sources/Providers/Storage/Agents/CoreData/CoreDataStoreCoordinator.swift new file mode 100644 index 0000000..118f26b --- /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(urlModel: URL?, storeType: StoreType) throws -> NSPersistentStoreCoordinator { + return try NSPersistentStoreCoordinator.coordinator(urlModel: urlModel, storeType: storeType) + } + +} + +private extension NSPersistentStoreCoordinator { + + static func coordinator(urlModel: URL?, storeType: StoreType) throws -> NSPersistentStoreCoordinator { + guard let dbModel = urlModel else { + throw StorageException.dbModelFileNotFound + } + + guard let managedObjectModel = NSManagedObjectModel(contentsOf: dbModel) else { + throw StorageException.dbModelCreationFail + } + + let persistentContainer = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel) + switch storeType { + case .sqLiteStoreType: + try persistentContainer.configureSQLiteStore(name: dbModel.deletingPathExtension().lastPathComponent) + 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 new file mode 100644 index 0000000..7e2d793 --- /dev/null +++ b/Sources/Providers/Storage/Base/ConfigurationType.swift @@ -0,0 +1,13 @@ +// +// ConfigurationType.swift +// +// +// Created by Miguel Angel on 25-05-21. +// + +import Foundation + +public enum ConfigurationType { + case basic(dbFile: URL?) + case inMemory(identifier: String = "") +} diff --git a/Sources/Providers/Storage/Base/Storable.swift b/Sources/Providers/Storage/Base/Storable.swift new file mode 100644 index 0000000..42b0bd2 --- /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 { + static var entityName: String { get } +} diff --git a/Sources/Providers/Storage/StorageAgent.swift b/Sources/Providers/Storage/StorageAgent.swift new file mode 100644 index 0000000..764ad93 --- /dev/null +++ b/Sources/Providers/Storage/StorageAgent.swift @@ -0,0 +1,82 @@ +// +// StorageAgent.swift +// +// +// Created by Miguel Angel on 25-05-21. +// + +import Combine +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 readFirst(_ model: T.Type, predicate: NSPredicate?) -> AnyPublisher + func readAll(_ model: T.Type, predicate: NSPredicate?) -> 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 +} + +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 .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" + } + } + + public var errorDescription: String? { + switch self { + case .unknown(let error): + return "Unknown Storage Error: \(error.localizedDescription)" + case .notInitialized: + 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 type for the concrete storage is not supported" + 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..67b54d1 --- /dev/null +++ b/Sources/Providers/Storage/StorageProvider.swift @@ -0,0 +1,28 @@ +// +// StorageProvider.swift +// +// +// Created by Miguel Angel on 25-05-21. +// + +import Foundation + +public protocol StorageProviderProtocol { + var agent: StorageAgent { get } +} + +public enum StorageStrategy { + case coreData(dbFile: URL?) +} + +public final class StorageProvider: StorageProviderProtocol { + public let agent: StorageAgent + + public init(strategy: StorageStrategy) { + switch strategy { + case .coreData(let url): + agent = CoreDataAgent(configuration: .basic(dbFile: url)) + } + } + +} 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), - ] -}