diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 25f6b01f..7a445e36 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,6 +23,10 @@ jobs: steps: - uses: actions/checkout@v3 + - name: Swiftlint + # IMPORTANT: using --lenient to avoid errors. Once all errors are fixed, it should be deleted and run a normal "swiftlint lint" + run: swiftlint lint --lenient + - name: Select Xcode run: sudo xcode-select -switch /Applications/Xcode_$XCODE_VERSION.app && /usr/bin/xcodebuild -version diff --git a/.swiftformat b/.swiftformat new file mode 100644 index 00000000..6db89887 --- /dev/null +++ b/.swiftformat @@ -0,0 +1,42 @@ +# file options + +--exclude Tests/XCTestManifests.swift,Snapshots,DerivedData + +# format options + +--allman false +--binarygrouping 4,8 +--commas always +--comments indent +--decimalgrouping 3,6 +--elseposition same-line +--guardelse same-line +--empty void +--exponentcase lowercase +--exponentgrouping disabled +--fractiongrouping disabled +--header ignore +--hexgrouping 4,8 +--hexliteralcase uppercase +--ifdef indent +--indent 4 +--indentcase false +--importgrouping testable-bottom +--linebreaks lf +--octalgrouping 4,8 +--operatorfunc spaced +--patternlet hoist +--ranges spaced +--self remove +--semicolons never +--stripunusedargs always +--trimwhitespace always +--wraparguments preserve +--wrapcollections preserve +--maxwidth 115 +--redundanttype explicit + +# rules + +--enable isEmpty +--disable wrapMultilineStatementBraces diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 00000000..b500a2d7 --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,70 @@ +disabled_rules: # rule identifiers to exclude from running + # - trailing_whitespace + - todo +opt_in_rules: # some rules are only opt-in + # Find all the available rules by running: + # swiftlint rules + - empty_count + - unavailable_function +excluded: # paths to ignore during linting. Takes precedence over `included`. + - Carthage + - DerivedData +analyzer_rules: # Rules run by `swiftlint analyze` (experimental) + - explicit_self + +# configurable rules can be customized from this configuration file +# binary rules can set their severity level +force_cast: warning # implicitly +force_try: + severity: warning # explicitly +# rules that have both warning and error levels, can set just the warning level +# implicitly +line_length: + warning: 160 + error: 160 + ignores_urls: true + ignores_function_declarations: true + ignores_comments: true + +# they can set both implicitly with an array +type_body_length: + - 300 # warning + - 400 # error +# or they can set both explicitly +file_length: + warning: 500 + error: 700 +# naming rules can set warnings/errors for min_length and max_length +# additionally they can set excluded names +type_name: + min_length: 4 # only warning + max_length: # warning and error + warning: 40 + error: 50 + excluded: + - iPhone + - T + - K + - kSec +identifier_name: + # additional_allowed_characters: + # - _ + allowed_symbols: "_" + min_length: # only min_length + warning: 0 + error: 1 # only error + max_length: + warning: 50 + error: 70 + excluded: # excluded via string array + - id + - URL + - SwiftUI +function_parameter_count: + warning: 7 + error : 9 +reporter: "xcode" # reporter type (xcode, json, csv, checkstyle, junit, html, emoji, sonarqube, markdown) +# warnings should be fixed immediately +warning_threshold: 10 +nesting: + type_level: 3 diff --git a/Example/Harmony/AppDelegate.swift b/Example/Harmony/AppDelegate.swift index 95de4d68..9032f2ee 100644 --- a/Example/Harmony/AppDelegate.swift +++ b/Example/Harmony/AppDelegate.swift @@ -6,9 +6,9 @@ // Copyright (c) 2017 Mobile Jazz. All rights reserved. // -import UIKit import Harmony import SwiftUI +import UIKit let applicationComponent: ApplicationComponent = ApplicationDefaultModule() @@ -16,25 +16,26 @@ enum ApplicationUI { case UIKit case SwiftUI } + // Modify this property to change the UI implementation! let applicationUI: ApplicationUI = .SwiftUI @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { - var window: UIWindow? - var observable : Observable! - - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + var observable: Observable! + + func application(_: UIApplication, + didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { switch applicationUI { case .UIKit: let splash = SplashViewController() let container = ContainerViewController(splash) - + window = UIWindow(frame: UIScreen.main.bounds) window?.rootViewController = container window?.makeKeyAndVisible() - + DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2)) { let storyboard = UIStoryboard(name: "Main", bundle: nil) let mainVC = storyboard.instantiateInitialViewController()! @@ -43,11 +44,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate { case .SwiftUI: let splash = SplashView() let container = ContainerViewController(UIHostingController(rootView: splash)) - + window = UIWindow(frame: UIScreen.main.bounds) window?.rootViewController = container window?.makeKeyAndVisible() - + DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2)) { let main = NavigationView { ItemListView(viewState: ItemListViewState()) @@ -55,29 +56,37 @@ class AppDelegate: UIResponder, UIApplicationDelegate { container.set(UIHostingController(rootView: main), animation: .crossDisolve) } } - + return true } - - func applicationWillResignActive(_ application: UIApplication) { - // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. - // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. + + func applicationWillResignActive(_: UIApplication) { + // Sent when the application is about to move from active to inactive state. This can occur for certain types + // of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the + // application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games + // should use this method to pause the game. } - - func applicationDidEnterBackground(_ application: UIApplication) { - // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. - // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + + func applicationDidEnterBackground(_: UIApplication) { + // Use this method to release shared resources, save user data, invalidate timers, and store enough + // application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of + // applicationWillTerminate: when the user quits. } - - func applicationWillEnterForeground(_ application: UIApplication) { - // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. + + func applicationWillEnterForeground(_: UIApplication) { + // Called as part of the transition from the background to the inactive state; here you can undo many of the + // changes made on entering the background. } - - func applicationDidBecomeActive(_ application: UIApplication) { - // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + + func applicationDidBecomeActive(_: UIApplication) { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application + // was previously in the background, optionally refresh the user interface. } - - func applicationWillTerminate(_ application: UIApplication) { - // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + + func applicationWillTerminate(_: UIApplication) { + // Called when the application is about to terminate. Save data if appropriate. See also + // applicationDidEnterBackground:. } } diff --git a/Example/Harmony/Core/ApplicationProvider.swift b/Example/Harmony/Core/ApplicationProvider.swift index c901cf5d..36f9f4f9 100644 --- a/Example/Harmony/Core/ApplicationProvider.swift +++ b/Example/Harmony/Core/ApplicationProvider.swift @@ -6,8 +6,8 @@ // Copyright © 2021 CocoaPods. All rights reserved. // -import Harmony import Alamofire +import Harmony // Enabling vastra service as an object validation extension VastraService: ObjectValidation {} @@ -17,17 +17,17 @@ protocol ApplicationComponent { } class ApplicationDefaultModule: ApplicationComponent { - private lazy var logger: Logger = DeviceConsoleLogger() private lazy var backgroundExecutor: Executor = DispatchQueueExecutor() - + private lazy var apiClient: Session = { // Alamofire Session Manager - let sessionManager = Session(interceptor: BaseURLRequestAdapter(URL(string:"https://demo3068405.mockable.io")!, - [UnauthorizedStatusCodeRequestRetrier()])) + let sessionManager = + Session(interceptor: BaseURLRequestAdapter(URL(string: "https://demo3068405.mockable.io")!, + [UnauthorizedStatusCodeRequestRetrier()])) return sessionManager }() - + private lazy var storage: AnyDataSource = { // Storage (FileSystem) let storage = FileSystemStorageDataSource(fileManager: FileManager.default, @@ -37,16 +37,16 @@ class ApplicationDefaultModule: ApplicationComponent { // Storage (UserDefaults) // let storage = DeviceStorageDataSource(UserDefaults.standard, prefix: "ItemEntity") - + // Storage (Keychain) // let storage = KeychainDataSource(KeychainService("com.mobilejazz.storage.item")) - + // Attaching a logger return AnyDataSource( DebugDataSource(storage, logger: self.logger) ) }() - + lazy var itemComponent: ItemComponent = ItemDefaultModule(executor: self.backgroundExecutor, apiClient: self.apiClient, storage: self.storage) diff --git a/Example/Harmony/Core/Features/Item/Data/ItemEntity.swift b/Example/Harmony/Core/Features/Item/Data/ItemEntity.swift index bbaa5927..4116b93e 100644 --- a/Example/Harmony/Core/Features/Item/Data/ItemEntity.swift +++ b/Example/Harmony/Core/Features/Item/Data/ItemEntity.swift @@ -7,13 +7,13 @@ import Foundation import Harmony -struct ItemEntity : VastraTimestampStrategyDataSource, Codable { +struct ItemEntity: VastraTimestampStrategyDataSource, Codable { var id: String? var name: String var price: Double var count: Int var imageURL: URL? - + init() { self.init(id: nil, name: "Blank", price: 0.0, count: 0, imageURL: nil) } @@ -25,13 +25,12 @@ struct ItemEntity : VastraTimestampStrategyDataSource, Codable { self.count = count self.imageURL = imageURL } - - // MARK: VastraTimestampStrategyDataSource - - var lastUpdate: Date? = nil - + + // MARK: VastraTimestampStrategyDataSource + + var lastUpdate: Date? + func expiryTimeInterval() -> Time { return .seconds(30) } } - diff --git a/Example/Harmony/Core/Features/Item/Data/ItemNetworkDataSource.swift b/Example/Harmony/Core/Features/Item/Data/ItemNetworkDataSource.swift index e42f4445..6effe4c4 100644 --- a/Example/Harmony/Core/Features/Item/Data/ItemNetworkDataSource.swift +++ b/Example/Harmony/Core/Features/Item/Data/ItemNetworkDataSource.swift @@ -5,29 +5,28 @@ import Foundation -import Harmony import Alamofire +import Harmony -extension ItemEntity { - fileprivate static var fromNetworkMap : [String : String] { - return ["image-url" : "imageURL"] +private extension ItemEntity { + static var fromNetworkMap: [String: String] { + return ["image-url": "imageURL"] } - - fileprivate static var toNetworkMap : [String : String] { - return ["imageURL" : "image-url"] + + static var toNetworkMap: [String: String] { + return ["imageURL": "image-url"] } } -class ItemNetworkDataSource : GetDataSource { - +class ItemNetworkDataSource: GetDataSource { typealias T = ItemEntity - - var sessionManager : Session - + + var sessionManager: Session + required init(_ sessionManager: Session) { self.sessionManager = sessionManager } - + func get(_ query: Query) -> Future { switch query.self { case let query as IdQuery: @@ -36,7 +35,7 @@ class ItemNetworkDataSource : GetDataSource { query.fatalError(.get, self) } } - + func getAll(_ query: Query) -> Future<[ItemEntity]> { switch query.self { case is AllItemsQuery, is AllObjectsQuery: @@ -53,17 +52,18 @@ private extension ItemNetworkDataSource { private func getById(_ id: String) -> Future { let url = "/items/\(id)" return sessionManager.request(url).toFuture().flatMap { data -> Future in - guard let json = data as? [String : AnyObject] else { + guard let json = data as? [String: AnyObject] else { throw CoreError.NotFound() } - let future = json.decodeAs(ItemEntity.self, keyDecodingStrategy: .map(ItemEntity.fromNetworkMap)) { item in - item.lastUpdate = Date() - } + let future = json + .decodeAs(ItemEntity.self, keyDecodingStrategy: .map(ItemEntity.fromNetworkMap)) { item in + item.lastUpdate = Date() + } return future } } - - private func getAllItems() -> Future<[ItemEntity]> { + + private func getAllItems() -> Future<[ItemEntity]> { let url = "/items" return sessionManager.request(url).toFuture().flatMap { data -> Future<[ItemEntity]> in guard let results = data as? [[String: AnyObject]] else { @@ -74,12 +74,12 @@ private extension ItemNetworkDataSource { }) } } - + private func searchItems(_ text: String) -> Future<[ItemEntity]> { let url = "/items" return sessionManager.request(url, - parameters: ["name" : text]) - .toFuture().flatMap { data -> Future<[ItemEntity]> in + parameters: ["name": text]) + .toFuture().flatMap { data -> Future<[ItemEntity]> in guard let json = data as? [String: AnyObject] else { return Future([]) // no json to parse } @@ -89,6 +89,6 @@ private extension ItemNetworkDataSource { return results.decodeAs(keyDecodingStrategy: .map(ItemEntity.fromNetworkMap), forEach: { item in item.lastUpdate = Date() }) - } + } } } diff --git a/Example/Harmony/Core/Features/Item/Data/ItemQuery.swift b/Example/Harmony/Core/Features/Item/Data/ItemQuery.swift index 2fd9fedf..3d9960e3 100644 --- a/Example/Harmony/Core/Features/Item/Data/ItemQuery.swift +++ b/Example/Harmony/Core/Features/Item/Data/ItemQuery.swift @@ -10,7 +10,7 @@ import Foundation import Harmony struct SearchItemsQuery: Query { - let text : String + let text: String init(_ text: String) { self.text = text } diff --git a/Example/Harmony/Core/Features/Item/Domain/Interactors/GetAllItemsInteractor.swift b/Example/Harmony/Core/Features/Item/Domain/Interactors/GetAllItemsInteractor.swift index 84e830c5..eef9e65a 100644 --- a/Example/Harmony/Core/Features/Item/Domain/Interactors/GetAllItemsInteractor.swift +++ b/Example/Harmony/Core/Features/Item/Domain/Interactors/GetAllItemsInteractor.swift @@ -11,7 +11,7 @@ import Harmony struct GetAllItemsInteractor { let executor: Executor let getItems: Interactor.GetAllByQuery - + func execute(_ operation: Harmony.Operation = DefaultOperation()) -> Future<[Item]> { return executor.submit { r in let items = try self.getItems.execute(AllItemsQuery(), operation, in: DirectExecutor()).result.get() diff --git a/Example/Harmony/Core/Features/Item/Domain/Mappers/ItemMapper.swift b/Example/Harmony/Core/Features/Item/Domain/Mappers/ItemMapper.swift index b44006d4..ff21fc54 100644 --- a/Example/Harmony/Core/Features/Item/Domain/Mappers/ItemMapper.swift +++ b/Example/Harmony/Core/Features/Item/Domain/Mappers/ItemMapper.swift @@ -9,7 +9,13 @@ import Harmony class ItemToItemEntityMapper: Mapper { override func map(_ from: Item) throws -> ItemEntity { - return ItemEntity(id: from.id, name: from.name, price: from.price, count: from.count, imageURL: from.imageURL) + return ItemEntity( + id: from.id, + name: from.name, + price: from.price, + count: from.count, + imageURL: from.imageURL + ) } } diff --git a/Example/Harmony/Core/Features/Item/Domain/Model/Item.swift b/Example/Harmony/Core/Features/Item/Domain/Model/Item.swift index 2f153fa4..ddc5cb9e 100644 --- a/Example/Harmony/Core/Features/Item/Domain/Model/Item.swift +++ b/Example/Harmony/Core/Features/Item/Domain/Model/Item.swift @@ -10,7 +10,7 @@ import UIKit import Harmony -struct Item : Codable { +struct Item: Codable { let id: String? let name: String let price: Double diff --git a/Example/Harmony/Core/Features/Item/Domain/Presenters/ItemListPresenter.swift b/Example/Harmony/Core/Features/Item/Domain/Presenters/ItemListPresenter.swift index 9e856fe7..514a518a 100644 --- a/Example/Harmony/Core/Features/Item/Domain/Presenters/ItemListPresenter.swift +++ b/Example/Harmony/Core/Features/Item/Domain/Presenters/ItemListPresenter.swift @@ -10,7 +10,7 @@ import Harmony protocol ItemListPresenterView: AnyObject { func onDisplayProgressHud(show: Bool) - + func onDisplayItems(_ items: [Item]) func onNavigateToItem(_ item: Item) func onDisplayFailedToFetchItems(_ error: Error) @@ -18,19 +18,19 @@ protocol ItemListPresenterView: AnyObject { protocol ItemListPresenter { func onEventLoadList() - func onActionSelected(item : Item) + func onActionSelected(item: Item) func onActionReloadList() } class ItemListDefaultPresenter: ItemListPresenter { private weak var view: ItemListPresenterView? private let getItems: GetAllItemsInteractor - + init(_ view: ItemListPresenterView, _ getItems: GetAllItemsInteractor) { self.view = view self.getItems = getItems } - + func onEventLoadList() { view?.onDisplayProgressHud(show: true) getItems.execute().then { items in @@ -41,14 +41,14 @@ class ItemListDefaultPresenter: ItemListPresenter { self.view?.onDisplayFailedToFetchItems(error) } } - + func onActionSelected(item: Item) { view?.onNavigateToItem(item) } - + func onActionReloadList() { view?.onDisplayProgressHud(show: true) - self.getItems.execute(MainSyncOperation()).then { items in + getItems.execute(MainSyncOperation()).then { items in self.view?.onDisplayProgressHud(show: false) self.view?.onDisplayItems(items) }.fail { error in diff --git a/Example/Harmony/Core/Features/Item/ItemProvider.swift b/Example/Harmony/Core/Features/Item/ItemProvider.swift index 74f6e87e..0faba55e 100644 --- a/Example/Harmony/Core/Features/Item/ItemProvider.swift +++ b/Example/Harmony/Core/Features/Item/ItemProvider.swift @@ -6,8 +6,8 @@ // Copyright © 2021 CocoaPods. All rights reserved. // -import Harmony import Alamofire +import Harmony protocol ItemComponent { func itemListPresenter(view: ItemListPresenterView) -> ItemListPresenter @@ -17,58 +17,60 @@ class ItemDefaultModule: ItemComponent { private let executor: Executor private let apiClient: Session private let storage: AnyDataSource - + init(executor: Executor, apiClient: Session, storage: AnyDataSource) { self.executor = executor self.apiClient = apiClient self.storage = storage } - + private lazy var networkDataSource: AnyDataSource = { let baseDataSource = ItemNetworkDataSource(self.apiClient) - + // To debug the UI upon random API behavior, adding this intermediate layer let itemNetworkDataSource = DebugDataSource(DataSourceAssembler(get: baseDataSource), delay: .sync(0.5), - error: .error(CoreError.Failed("Debug Fail"), probability: 0.01), + error: .error( + CoreError.Failed("Debug Fail"), + probability: 0.01 + ), logger: DeviceConsoleLogger()) - + // Adding retry behavior in case of error let networkDataSource = RetryDataSource(itemNetworkDataSource, retryCount: 1) { error in - return error._code == NSURLErrorTimedOut && error._domain == NSURLErrorDomain + error._code == NSURLErrorTimedOut && error._domain == NSURLErrorDomain } return AnyDataSource(networkDataSource) }() - + private lazy var storageDataSource: AnyDataSource = { - return AnyDataSource( + AnyDataSource( DataSourceMapper(dataSource: self.storage, toInMapper: EncodableToDataMapper(), toOutMapper: DataToDecodableMapper()) ) }() - + private lazy var repository: AnyRepository = { - let vastra = VastraService([VastraTimestampStrategy()]) let storageValidationDataSource = DataSourceValidator(dataSource: self.storageDataSource, validator: vastra) - + let cacheRepo = CacheRepository(main: self.networkDataSource, cache: storageValidationDataSource) return AnyRepository( RepositoryMapper(repository: cacheRepo, - toInMapper: EncodableToDecodableMapper(), - toOutMapper: EncodableToDecodableMapper()) + toInMapper: EncodableToDecodableMapper(), + toOutMapper: EncodableToDecodableMapper()) ) }() - + private func getAllItemsInteractor() -> GetAllItemsInteractor { - return GetAllItemsInteractor(executor: self.executor, - getItems: Interactor.GetAllByQuery(DirectExecutor(), self.repository)) + return GetAllItemsInteractor(executor: executor, + getItems: Interactor.GetAllByQuery(DirectExecutor(), repository)) } - + func itemListPresenter(view: ItemListPresenterView) -> ItemListPresenter { - return ItemListDefaultPresenter(view, self.getAllItemsInteractor()) + return ItemListDefaultPresenter(view, getAllItemsInteractor()) } } diff --git a/Example/Harmony/Core/Features/NetworkUtils/UnauthorizedStatusCodeRequestRetrier.swift b/Example/Harmony/Core/Features/NetworkUtils/UnauthorizedStatusCodeRequestRetrier.swift index 6d12b6b6..363560f4 100644 --- a/Example/Harmony/Core/Features/NetworkUtils/UnauthorizedStatusCodeRequestRetrier.swift +++ b/Example/Harmony/Core/Features/NetworkUtils/UnauthorizedStatusCodeRequestRetrier.swift @@ -11,11 +11,10 @@ import UIKit import Alamofire class UnauthorizedStatusCodeRequestRetrier: RequestRetrier { - func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) { + func retry(_ request: Request, for _: Session, dueTo _: Error, completion: @escaping (RetryResult) -> Void) { if request.response?.statusCode == 401 { // TODO: Logout user } completion(.doNotRetry) } } - diff --git a/Example/Harmony/Screens/SwiftUI/ItemDetail/ItemDetailView.swift b/Example/Harmony/Screens/SwiftUI/ItemDetail/ItemDetailView.swift index d47c652a..586ab828 100644 --- a/Example/Harmony/Screens/SwiftUI/ItemDetail/ItemDetailView.swift +++ b/Example/Harmony/Screens/SwiftUI/ItemDetail/ItemDetailView.swift @@ -6,12 +6,12 @@ // Copyright © 2017 Mobile Jazz. All rights reserved. // -import SwiftUI import Kingfisher +import SwiftUI struct ItemDetailView: View { var item: Item - + var body: some View { VStack { KFImage(item.imageURL) @@ -24,7 +24,7 @@ struct ItemDetailView: View { Spacer() Text("\(Int(item.price))€") .font(.system(size: 80, weight: .bold)) - .foregroundColor(Color(red: 0, green: 190.0/256.0, blue: 176.0/256.0)) + .foregroundColor(Color(red: 0, green: 190.0 / 256.0, blue: 176.0 / 256.0)) Spacer() }.navigationTitle(item.name) } @@ -33,7 +33,15 @@ struct ItemDetailView: View { struct ItemDetailView_Previews: PreviewProvider { static var previews: some View { NavigationView { - ItemDetailView(item: Item(id: "1", name: "Macbook Pro", price: 1234.56, count: 12, imageURL: URL(string: "(https://store.storeimages.cdn-apple.com/4668/as-images.apple.com/is/mbp-spacegray-select-202206_GEO_ES?wid=904&hei=840&fmt=jpeg&qlt=90&.v=1654014007395"))) + ItemDetailView(item: Item( + id: "1", + name: "Macbook Pro", + price: 1234.56, + count: 12, + imageURL: URL( + string: "(https://store.storeimages.cdn-apple.com/4668/as-images.apple.com/is/mbp-spacegray-select-202206_GEO_ES?wid=904&hei=840&fmt=jpeg&qlt=90&.v=1654014007395" + ) + )) } } } diff --git a/Example/Harmony/Screens/SwiftUI/ItemList/ItemListView.swift b/Example/Harmony/Screens/SwiftUI/ItemList/ItemListView.swift index 0104b1b7..5df242c3 100644 --- a/Example/Harmony/Screens/SwiftUI/ItemList/ItemListView.swift +++ b/Example/Harmony/Screens/SwiftUI/ItemList/ItemListView.swift @@ -6,13 +6,12 @@ // Copyright © 2022 CocoaPods. All rights reserved. // -import SwiftUI import Kingfisher +import SwiftUI struct ItemListView: View { - @StateObject var viewState: ItemListViewState - + var body: some View { if viewState.isLoading { ProgressView() @@ -52,7 +51,7 @@ struct ItemListView: View { Spacer() Text("\(Int(item.price))€") .font(.system(size: 23, weight: .bold)) - .foregroundColor(Color(red: 0, green: 190.0/256.0, blue: 176.0/256.0)) + .foregroundColor(Color(red: 0, green: 190.0 / 256.0, blue: 176.0 / 256.0)) } } } @@ -68,7 +67,6 @@ struct ItemListView: View { } label: { Image(systemName: "arrow.clockwise") } - } } } diff --git a/Example/Harmony/Screens/SwiftUI/ItemList/ItemListViewState.swift b/Example/Harmony/Screens/SwiftUI/ItemList/ItemListViewState.swift index 8dab9af8..f801f3ba 100644 --- a/Example/Harmony/Screens/SwiftUI/ItemList/ItemListViewState.swift +++ b/Example/Harmony/Screens/SwiftUI/ItemList/ItemListViewState.swift @@ -9,32 +9,31 @@ import Foundation class ItemListViewState: ObservableObject, ItemListPresenterView { - @Published var items: [Item] @Published var isLoading: Bool = true - @Published var error: Error? = nil - @Published var selectedItem: Item? = nil - + @Published var error: Error? + @Published var selectedItem: Item? + init(items: [Item] = []) { self.items = items presenter.onEventLoadList() } - + lazy var presenter = applicationComponent.itemComponent.itemListPresenter(view: self) - + func onDisplayProgressHud(show: Bool) { - self.isLoading = show + isLoading = show } - + func onDisplayItems(_ items: [Item]) { self.items = items - self.error = nil + error = nil } - + func onNavigateToItem(_ item: Item) { selectedItem = item } - + func onDisplayFailedToFetchItems(_ error: Error) { self.error = error } diff --git a/Example/Harmony/Screens/SwiftUI/Splash/SplashView.swift b/Example/Harmony/Screens/SwiftUI/Splash/SplashView.swift index f8f949da..df7cabec 100644 --- a/Example/Harmony/Screens/SwiftUI/Splash/SplashView.swift +++ b/Example/Harmony/Screens/SwiftUI/Splash/SplashView.swift @@ -11,7 +11,7 @@ import SwiftUI struct SplashView: View { var body: some View { ZStack { - Color(red: 0, green: 190.0/256.0, blue: 176.0/256.0) + Color(red: 0, green: 190.0 / 256.0, blue: 176.0 / 256.0) .ignoresSafeArea() VStack { Text("Splash") @@ -20,7 +20,6 @@ struct SplashView: View { ProgressView() .progressViewStyle(CircularProgressViewStyle(tint: Color.white)) } - } } } diff --git a/Example/Harmony/Screens/UIKit/ItemDetail/ItemDetailViewController.swift b/Example/Harmony/Screens/UIKit/ItemDetail/ItemDetailViewController.swift index 7e5ffa30..ad6520cc 100644 --- a/Example/Harmony/Screens/UIKit/ItemDetail/ItemDetailViewController.swift +++ b/Example/Harmony/Screens/UIKit/ItemDetail/ItemDetailViewController.swift @@ -9,26 +9,25 @@ import UIKit class ItemDetailViewController: UIViewController { - - @IBOutlet weak var imageView: UIImageView! - @IBOutlet weak var nameLabel: UILabel! - @IBOutlet weak var unitsLabel: UILabel! - @IBOutlet weak var priceLabel: UILabel! - + @IBOutlet var imageView: UIImageView! + @IBOutlet var nameLabel: UILabel! + @IBOutlet var unitsLabel: UILabel! + @IBOutlet var priceLabel: UILabel! + // Temp item to obtain it from previous screen. // This could be improved. - var item : Item? - + var item: Item? + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - + if let item = item { - self.title = item.name - + title = item.name + nameLabel.text = item.name unitsLabel.text = "\(item.count) units" priceLabel.text = "\(item.price)€" - + if let imageURL = item.imageURL { imageView.kf.setImage(with: imageURL) } else { diff --git a/Example/Harmony/Screens/UIKit/ItemList/ItemCell.swift b/Example/Harmony/Screens/UIKit/ItemList/ItemCell.swift index 673a0ec0..928438f7 100644 --- a/Example/Harmony/Screens/UIKit/ItemList/ItemCell.swift +++ b/Example/Harmony/Screens/UIKit/ItemList/ItemCell.swift @@ -6,16 +6,15 @@ // Copyright © 2017 Mobile Jazz. All rights reserved. // -import UIKit import Kingfisher +import UIKit class ItemCell: UITableViewCell { + @IBOutlet var itemImageView: UIImageView! + @IBOutlet var itemNameLabel: UILabel! + @IBOutlet var itemCountLabel: UILabel! + @IBOutlet var itemPriceLabel: UILabel! - @IBOutlet weak var itemImageView: UIImageView! - @IBOutlet weak var itemNameLabel: UILabel! - @IBOutlet weak var itemCountLabel: UILabel! - @IBOutlet weak var itemPriceLabel: UILabel! - override func awakeFromNib() { super.awakeFromNib() // Initialization code diff --git a/Example/Harmony/Screens/UIKit/ItemList/ItemListViewController.swift b/Example/Harmony/Screens/UIKit/ItemList/ItemListViewController.swift index 797970b5..4cb366d7 100644 --- a/Example/Harmony/Screens/UIKit/ItemList/ItemListViewController.swift +++ b/Example/Harmony/Screens/UIKit/ItemList/ItemListViewController.swift @@ -6,36 +6,35 @@ // Copyright © 2017 Mobile Jazz. All rights reserved. // -import UIKit import Harmony +import UIKit class ItemListViewController: UIViewController, ItemListPresenterView, UITableViewDataSource, UITableViewDelegate { - lazy var presenter = applicationComponent.itemComponent.itemListPresenter(view: self) - - @IBOutlet weak var tableView: UITableView! - @IBOutlet weak var activityIndicator: UIActivityIndicatorView! - - private var items : [Item] = [] - + + @IBOutlet var tableView: UITableView! + @IBOutlet var activityIndicator: UIActivityIndicatorView! + + private var items: [Item] = [] + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) presenter.onEventLoadList() } - + override func viewDidLoad() { super.viewDidLoad() - + tableView.rowHeight = UITableView.automaticDimension tableView.estimatedRowHeight = 68 } - - @IBAction func reloadButtonAction(_ sender: Any) { + + @IBAction func reloadButtonAction(_: Any) { presenter.onActionReloadList() } - + // MARK: ItemListPresenterView - + func onDisplayProgressHud(show: Bool) { if show { activityIndicator.startAnimating() @@ -45,64 +44,64 @@ class ItemListViewController: UIViewController, ItemListPresenterView, UITableVi tableView.isHidden = false } } - + func onDisplayItems(_ items: [Item]) { self.items = items tableView.reloadData() } - + func onNavigateToItem(_ item: Item) { performSegue(withIdentifier: "segue.item.detail", sender: item) } - + func onDisplayFailedToFetchItems(_ error: Error) { let alert = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "Dismiss", style: .cancel, handler: nil)) present(alert, animated: true, completion: nil) } - + // MARK: Segues - + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "segue.item.detail" { let detailPresenter = segue.destination as! ItemDetailViewController let item = sender as! Item - + detailPresenter.item = item } } - + // MARK: UITableViewDataSource - - func numberOfSections(in tableView: UITableView) -> Int { + + func numberOfSections(in _: UITableView) -> Int { return 1 } - - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + + func tableView(_: UITableView, numberOfRowsInSection _: Int) -> Int { return items.count } - + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "ItemCellIdentifier", for: indexPath) as! ItemCell - + let item = items[indexPath.row] - + cell.itemNameLabel.text = item.name cell.itemCountLabel.text = "\(item.count) units" cell.itemPriceLabel.text = "\(item.price)€" - + if let imageURL = item.imageURL { cell.itemImageView.kf.setImage(with: imageURL) } else { cell.itemImageView.image = nil } - + return cell } - + // MARK: UITableViewDelegate - - func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + + func tableView(_: UITableView, didSelectRowAt indexPath: IndexPath) { presenter.onActionSelected(item: items[indexPath.row]) } } diff --git a/Example/Harmony/Screens/UIKit/Splash/SplashViewController.swift b/Example/Harmony/Screens/UIKit/Splash/SplashViewController.swift index ffe31d3c..dfc963a5 100644 --- a/Example/Harmony/Screens/UIKit/Splash/SplashViewController.swift +++ b/Example/Harmony/Screens/UIKit/Splash/SplashViewController.swift @@ -8,6 +8,4 @@ import UIKit -class SplashViewController: UIViewController { - -} +class SplashViewController: UIViewController {} diff --git a/Example/Harmony/Utils/Alamofire+Future.swift b/Example/Harmony/Utils/Alamofire+Future.swift index 5dbc7d76..18f83b44 100644 --- a/Example/Harmony/Utils/Alamofire+Future.swift +++ b/Example/Harmony/Utils/Alamofire+Future.swift @@ -14,33 +14,37 @@ // limitations under the License. // -import Foundation import Alamofire +import Foundation import Harmony -public enum HarmonyAlamofireError : Error { +public enum HarmonyAlamofireError: Error { case jsonSerialization } public extension DataRequest { /// Inserts the JSON data response into a Future with a mapping window - func then(queue: DispatchQueue? = nil, - options: JSONSerialization.ReadingOptions = .allowFragments, - success: @escaping (Any) throws -> T, - failure: @escaping (Error, HTTPURLResponse?, Data?) -> Error = { (error, _, _) in error }) -> Future { + func then( + queue: DispatchQueue? = nil, + options: JSONSerialization.ReadingOptions = .allowFragments, + success: @escaping (Any) throws -> T, + failure: @escaping (Error, HTTPURLResponse?, Data?) -> Error = { error, _, _ in error } + ) -> Future { return Future { resolver in - self.validate().response(queue: queue ?? .main, responseSerializer: JSONResponseSerializer(options: options)) { response in - switch response.result { - case .failure(let error): - resolver.set(failure(error, response.response, response.data)) - case .success(let data): - do { - resolver.set(try success(data)) - } catch (let error) { - resolver.set(error) + self.validate() + .response(queue: queue ?? .main, + responseSerializer: JSONResponseSerializer(options: options)) { response in + switch response.result { + case let .failure(error): + resolver.set(failure(error, response.response, response.data)) + case let .success(data): + do { + resolver.set(try success(data)) + } catch { + resolver.set(error) + } } } - } } } diff --git a/Example/Harmony/Utils/BaseURLRequestAdapter.swift b/Example/Harmony/Utils/BaseURLRequestAdapter.swift index 3c428d27..c874f815 100644 --- a/Example/Harmony/Utils/BaseURLRequestAdapter.swift +++ b/Example/Harmony/Utils/BaseURLRequestAdapter.swift @@ -14,60 +14,74 @@ // limitations under the License. // -import Foundation import Alamofire +import Foundation public class BaseURLRequestAdapter: RequestInterceptor { - // Example of usage of a bearer token - public let baseURL : URL - private var retriers : [RequestRetrier] = [] - - public init(_ baseURL: URL, _ retriers : [RequestRetrier]) { + public let baseURL: URL + private var retriers: [RequestRetrier] = [] + + public init(_ baseURL: URL, _ retriers: [RequestRetrier]) { self.baseURL = baseURL self.retriers = retriers } - - public func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result) -> Void) { + + public func adapt( + _ urlRequest: URLRequest, + for _: Session, + completion: @escaping (Result) -> Void + ) { guard let incomingURL = urlRequest.url else { completion(.success(urlRequest)) return } - + if incomingURL.scheme != nil { completion(.success(urlRequest)) return } - + var request = urlRequest - + var components = URLComponents() components.scheme = baseURL.scheme components.host = baseURL.host components.path = "\(baseURL.path)\(incomingURL.path)" - + if let query = incomingURL.query { components.query = query } - + request.url = components.url - + completion(.success(request)) } - - public func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) { + + public func retry( + _ request: Request, + for session: Session, + dueTo error: Error, + completion: @escaping (RetryResult) -> Void + ) { should(0, session, retry: request, with: error, completion: completion) } - - private func should(_ index: Int, _ manager: Session, retry request: Request, with error: Error, completion: @escaping (RetryResult) -> Void) { + + private func should( + _ index: Int, + _ manager: Session, + retry request: Request, + with error: Error, + completion: @escaping (RetryResult) -> Void + ) { if index < retriers.count { let retrier = retriers[index] retrier.retry(request, for: manager, dueTo: error) { retryResult in switch retryResult { - case .retry, .retryWithDelay(_), .doNotRetry: + case .retry, .retryWithDelay, .doNotRetry: completion(retryResult) - case .doNotRetryWithError(_): - self.should(index+1, manager, retry: request, with: error, completion: completion) + case .doNotRetryWithError: + self.should(index + 1, manager, retry: request, with: error, completion: completion) } } } else { diff --git a/Example/Harmony/Utils/ContainerViewController.swift b/Example/Harmony/Utils/ContainerViewController.swift index 1c22749d..eab735b1 100644 --- a/Example/Harmony/Utils/ContainerViewController.swift +++ b/Example/Harmony/Utils/ContainerViewController.swift @@ -23,9 +23,8 @@ import UIKit /// Typically used as the window's rootViewController. /// public class ContainerViewController: UIViewController { - - private static let animationDuration : Double = 0.40 - private static let animationOptions = UIView.AnimationOptions(rawValue:(7<<16)) + private static let animationDuration: Double = 0.40 + private static let animationOptions = UIView.AnimationOptions(rawValue: 7 << 16) public enum Animation { case none @@ -35,7 +34,7 @@ public class ContainerViewController: UIViewController { } /// The contained view controller - public private(set) var viewController : UIViewController + public private(set) var viewController: UIViewController /// Main initializer /// @@ -46,7 +45,7 @@ public class ContainerViewController: UIViewController { } public required init?(coder aDecoder: NSCoder) { - self.viewController = UIViewController(nibName: nil, bundle: nil) + viewController = UIViewController(nibName: nil, bundle: nil) super.init(coder: aDecoder) } @@ -64,64 +63,64 @@ public class ContainerViewController: UIViewController { // Configuring the new view controller's view let subview = viewController.view! subview.autoresizingMask = [.flexibleWidth, .flexibleHeight] - subview.frame = self.view.bounds + subview.frame = view.bounds // Storing the new view controller self.viewController = viewController oldVC.willMove(toParent: nil) - self.addChild(newVC) + addChild(newVC) - switch (animation) { + switch animation { case .none: oldVC.view.removeFromSuperview() - self.view.insertSubview(newVC.view, at: 0) + view.insertSubview(newVC.view, at: 0) oldVC.removeFromParent() newVC.didMove(toParent: self) case .crossDisolve: - self.view.insertSubview(newVC.view, belowSubview: oldVC.view) + view.insertSubview(newVC.view, belowSubview: oldVC.view) UIView.animate(withDuration: ContainerViewController.animationDuration, delay: 0, options: ContainerViewController.animationOptions, animations: { - oldVC.view.alpha = 0.0 - }, - completion: { (success) in - oldVC.view.removeFromSuperview() - oldVC.removeFromParent() - newVC.didMove(toParent: self) - }) + oldVC.view.alpha = 0.0 + }, + completion: { _ in + oldVC.view.removeFromSuperview() + oldVC.removeFromParent() + newVC.didMove(toParent: self) + }) case .newModalBottom: newVC.view.alpha = 0.0 - newVC.view.frame = self.view.bounds.offsetBy(dx: 0, dy: UIScreen.main.bounds.size.height) - self.view.insertSubview(newVC.view, aboveSubview: oldVC.view) + newVC.view.frame = view.bounds.offsetBy(dx: 0, dy: UIScreen.main.bounds.size.height) + view.insertSubview(newVC.view, aboveSubview: oldVC.view) UIView.animate(withDuration: ContainerViewController.animationDuration, delay: 0, options: ContainerViewController.animationOptions, animations: { - newVC.view.alpha = 1.0 - newVC.view.frame = self.view.bounds - }, - completion: { (success) in - oldVC.view.removeFromSuperview() - oldVC.removeFromParent() - newVC.didMove(toParent: self) - }) + newVC.view.alpha = 1.0 + newVC.view.frame = self.view.bounds + }, + completion: { _ in + oldVC.view.removeFromSuperview() + oldVC.removeFromParent() + newVC.didMove(toParent: self) + }) case .oldModalBottom: - let frame = self.view.bounds.offsetBy(dx: 0, dy: UIScreen.main.bounds.size.height) - self.view.insertSubview(newVC.view, belowSubview: oldVC.view) + let frame = view.bounds.offsetBy(dx: 0, dy: UIScreen.main.bounds.size.height) + view.insertSubview(newVC.view, belowSubview: oldVC.view) UIView.animate(withDuration: ContainerViewController.animationDuration, delay: 0, options: ContainerViewController.animationOptions, animations: { - oldVC.view.alpha = 0.0 - oldVC.view.frame = frame - }, - completion: { (success) in - oldVC.view.removeFromSuperview() - oldVC.removeFromParent() - newVC.didMove(toParent: self) - }) + oldVC.view.alpha = 0.0 + oldVC.view.frame = frame + }, + completion: { _ in + oldVC.view.removeFromSuperview() + oldVC.removeFromParent() + newVC.didMove(toParent: self) + }) } } else { // View not loaded yet. Just storing the instance for later processing @@ -131,20 +130,20 @@ public class ContainerViewController: UIViewController { override public func viewDidLoad() { super.viewDidLoad() - if !self.children.contains(viewController) { + if !children.contains(viewController) { let subview = viewController.view! subview.autoresizingMask = [.flexibleWidth, .flexibleHeight] - subview.frame = self.view.bounds + subview.frame = view.bounds addChild(viewController) - self.view.addSubview(subview) + view.addSubview(subview) viewController.didMove(toParent: self) } } override public func viewWillLayoutSubviews() { super.viewWillLayoutSubviews() - self.view.subviews.first?.frame = self.view.bounds + view.subviews.first?.frame = view.bounds } override public func willMove(toParent parent: UIViewController?) { @@ -166,21 +165,15 @@ public class ContainerViewController: UIViewController { } override public var preferredStatusBarStyle: UIStatusBarStyle { - get { - return viewController.preferredStatusBarStyle - } + return viewController.preferredStatusBarStyle } override public var shouldAutorotate: Bool { - get { - return viewController.shouldAutorotate - } + return viewController.shouldAutorotate } override public var supportedInterfaceOrientations: UIInterfaceOrientationMask { - get { - return viewController.supportedInterfaceOrientations - } + return viewController.supportedInterfaceOrientations } } @@ -189,7 +182,7 @@ public extension UIViewController { /// /// - Returns: The first ContainerViewController found in the hierarchy. func containerViewController() -> ContainerViewController? { - var vc : UIViewController? = self + var vc: UIViewController? = self while vc != nil { if let viewController = vc { if viewController.isKind(of: ContainerViewController.self) { diff --git a/Example/Harmony/Utils/EmptyNavigationLink.swift b/Example/Harmony/Utils/EmptyNavigationLink.swift index b6d5ac3f..9969aee5 100644 --- a/Example/Harmony/Utils/EmptyNavigationLink.swift +++ b/Example/Harmony/Utils/EmptyNavigationLink.swift @@ -19,11 +19,11 @@ import SwiftUI public struct EmptyNavigationLink: View { private let lazyDestination: LazyView private let isActive: Binding - + public init( @ViewBuilder destination: @escaping (T) -> Destination, selection: Binding - ) { + ) { lazyDestination = LazyView(destination(selection.wrappedValue!)) isActive = .init( get: { selection.wrappedValue != nil }, @@ -34,7 +34,7 @@ public struct EmptyNavigationLink: View { } ) } - + public var body: some View { NavigationLink( destination: lazyDestination, diff --git a/Example/Harmony/Utils/LazyView.swift b/Example/Harmony/Utils/LazyView.swift index d07846b7..2f4b576d 100644 --- a/Example/Harmony/Utils/LazyView.swift +++ b/Example/Harmony/Utils/LazyView.swift @@ -21,6 +21,7 @@ public struct LazyView: View { public init(_ build: @autoclosure @escaping () -> Content) { self.build = build } + public var body: Content { build() } diff --git a/Example/Harmony/Utils/MultiRequestAdapter.swift b/Example/Harmony/Utils/MultiRequestAdapter.swift index fd98efcf..e894fcf8 100644 --- a/Example/Harmony/Utils/MultiRequestAdapter.swift +++ b/Example/Harmony/Utils/MultiRequestAdapter.swift @@ -14,18 +14,21 @@ // limitations under the License. // -import Foundation import Alamofire +import Foundation -public class MultiRequestAdapter : RequestAdapter { - - private var adapters : [RequestAdapter] = [] - - public init(_ adapters : [RequestAdapter]) { +public class MultiRequestAdapter: RequestAdapter { + private var adapters: [RequestAdapter] = [] + + public init(_ adapters: [RequestAdapter]) { self.adapters = adapters } - - public func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result) -> Void) { + + public func adapt( + _ urlRequest: URLRequest, + for session: Session, + completion: @escaping (Result) -> Void + ) { for adapter in adapters { adapter.adapt(urlRequest, for: session, completion: completion) } diff --git a/Example/Harmony/Utils/URLLoggerRequestAdapter.swift b/Example/Harmony/Utils/URLLoggerRequestAdapter.swift index e7343bcd..dac1cc7f 100644 --- a/Example/Harmony/Utils/URLLoggerRequestAdapter.swift +++ b/Example/Harmony/Utils/URLLoggerRequestAdapter.swift @@ -14,24 +14,27 @@ // limitations under the License. // -import Foundation import Alamofire +import Foundation import Harmony /// /// Logs CURLS of incoming requests /// public class URLLoggerRequestAdapter: RequestAdapter { - private let logger: Logger private let tag: String? - + public init(_ logger: Logger, tag: String? = nil) { self.logger = logger self.tag = tag } - - public func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result) -> Void) { + + public func adapt( + _ urlRequest: URLRequest, + for _: Session, + completion: @escaping (Result) -> Void + ) { let curl = urlRequest.curl(pretty: true) logger.info(tag: tag, "\(curl)") return completion(.success(urlRequest)) @@ -44,22 +47,21 @@ private extension URLRequest { let complement = pretty ? "\\\n" : "" let method = "-X \(httpMethod ?? "GET") \(complement)" let url = "\"" + (self.url?.absoluteString ?? "") + "\"" - + var header = "" - - if let httpHeaders = self.allHTTPHeaderFields, httpHeaders.keys.count > 0 { + + if let httpHeaders = allHTTPHeaderFields, !httpHeaders.keys.isEmpty { for (key, value) in httpHeaders { header += "-H \"\(key): \(value)\" \(complement)" } } - - if let bodyData = self.httpBody, let bodyString = String(data: bodyData, encoding: .utf8) { + + if let bodyData = httpBody, let bodyString = String(data: bodyData, encoding: .utf8) { data = "-d \"\(bodyString)\" \(complement)" } - + let command = "curl -i " + complement + method + header + data + url - + return command } } - diff --git a/Example/Tests/Future+DeallocTests.swift b/Example/Tests/Future+DeallocTests.swift index 612caca6..2e2b62e3 100644 --- a/Example/Tests/Future+DeallocTests.swift +++ b/Example/Tests/Future+DeallocTests.swift @@ -18,9 +18,8 @@ import XCTest @testable import Harmony class FutureDeallocTests: XCTestCase { - func testFuturePendingDealloc() { - weak var weakFuture: Future? = nil + weak var weakFuture: Future? autoreleasepool { XCTAssertNil(weakFuture) let future = Future() @@ -29,5 +28,4 @@ class FutureDeallocTests: XCTestCase { } XCTAssertNil(weakFuture) } - } diff --git a/Example/Tests/Future+InitTests.swift b/Example/Tests/Future+InitTests.swift index e0b1b1a3..c796ff38 100755 --- a/Example/Tests/Future+InitTests.swift +++ b/Example/Tests/Future+InitTests.swift @@ -18,163 +18,162 @@ import XCTest @testable import Harmony class FutureInitTests: XCTestCase { - func testFutureEmptyConstructor() { // Arrange & Act. let future = Future() - + // Assert. XCTAssertTrue(future.state == Future.State.blank) } - + func testFutureValueConstructor() { // Arrange & Act. let future = Future(42) - + // Assert. XCTAssertTrue(future.state == Future.State.waitingThen) XCTAssertNotNil(future._result) switch future._result! { - case .value(let value): + case let .value(value): XCTAssertEqual(value, 42) - case .error(_): + case .error: XCTAssert(false) } } - + func testFutureErrorConstructor() { // Arrange & Act. let future = Future(Test.Error.code42) - + // Assert. XCTAssertTrue(future.state == Future.State.waitingThen) XCTAssertNotNil(future._result) switch future._result! { - case .value(_): + case .value: XCTAssert(false) - case .error(let error): + case let .error(error): XCTAssertTrue(error == Test.Error.code42) } } - + func testFutureFutureValueConstructor() { // Arrange & Act. let f = Future(42) let future = Future(f) - + // Assert. XCTAssertTrue(future.state == Future.State.waitingThen) XCTAssertNotNil(future._result) switch future._result! { - case .value(let value): + case let .value(value): XCTAssertEqual(value, 42) - case .error(_): + case .error: XCTAssert(false) } } - + func testFutureFutureErrorConstructor() { // Arrange & Act. let f = Future(Test.Error.code42) let future = Future(f) - + // Assert. XCTAssertTrue(future.state == Future.State.waitingThen) XCTAssertNotNil(future._result) switch future._result! { - case .value(_): + case .value: XCTAssert(false) - case .error(let error): + case let .error(error): XCTAssertTrue(error == Test.Error.code42) } } - + func testFutureClosureValueConstructor() { - let future = Future() { future in + let future = Future { future in future.set(42) } - + // Assert. XCTAssertTrue(future.state == Future.State.waitingThen) XCTAssertNotNil(future._result) switch future._result! { - case .value(let value): + case let .value(value): XCTAssertEqual(value, 42) - case .error(_): + case .error: XCTAssert(false) } } - + func testFutureClosureErrorConstructor() { - let future = Future() { future in + let future = Future { future in future.set(Test.Error.code42) } - + // Assert. XCTAssertTrue(future.state == Future.State.waitingThen) XCTAssertNotNil(future._result) switch future._result! { - case .value(_): + case .value: XCTAssert(false) - case .error(let error): + case let .error(error): XCTAssertTrue(error == Test.Error.code42) } } - + func testFutureClosureErrorThrowConstructor() { - let future = Future() { future in + let future = Future { _ in throw Test.Error.code42 } - + // Assert. XCTAssertTrue(future.state == Future.State.waitingThen) XCTAssertNotNil(future._result) switch future._result! { - case .value(_): + case .value: XCTAssert(false) - case .error(let error): + case let .error(error): XCTAssertTrue(error == Test.Error.code42) } } - + func testFutureDirectClosureValueConstructor() { let future = Future(value: { 42 }) - + // Assert. XCTAssertTrue(future.state == Future.State.waitingThen) XCTAssertNotNil(future._result) switch future._result! { - case .value(let value): + case let .value(value): XCTAssertEqual(value, 42) - case .error(_): + case .error: XCTAssert(false) } } - + func testFutureDirectClosureErrorConstructor() { - let future = Future() { Test.Error.code42 } - + let future = Future { Test.Error.code42 } + // Assert. XCTAssertTrue(future.state == Future.State.waitingThen) XCTAssertNotNil(future._result) switch future._result! { - case .value(_): + case .value: XCTAssert(false) - case .error(let error): + case let .error(error): XCTAssertTrue(error == Test.Error.code42) } } - + func testFutureDirectClosureErrorThrowConstructor() { let future = Future(value: { throw Test.Error.code42 }) - + // Assert. XCTAssertTrue(future.state == Future.State.waitingThen) XCTAssertNotNil(future._result) switch future._result! { - case .value(_): + case .value: XCTAssert(false) - case .error(let error): + case let .error(error): XCTAssertTrue(error == Test.Error.code42) } } diff --git a/Example/Tests/Future+SetTests.swift b/Example/Tests/Future+SetTests.swift index 9eecf3d1..45797efd 100644 --- a/Example/Tests/Future+SetTests.swift +++ b/Example/Tests/Future+SetTests.swift @@ -18,7 +18,6 @@ import XCTest @testable import Harmony class FutureSetTests: XCTestCase { - func testFutureSetValue() { // Arrange let future = Future() @@ -30,143 +29,143 @@ class FutureSetTests: XCTestCase { XCTAssertTrue(future.state == Future.State.waitingThen) XCTAssertNotNil(future._result) switch future._result! { - case .value(let value): + case let .value(value): XCTAssertEqual(value, 42) - case .error(_): + case .error: XCTAssert(false) } } - + func testFutureSetValueTwice() { // Arrange. let future = Future() - + // Act. future.set(42) future.set(13) - + // Assert. XCTAssertTrue(future.state == Future.State.waitingThen) XCTAssertNotNil(future._result) switch future._result! { - case .value(let value): + case let .value(value): XCTAssertEqual(value, 42) - case .error(_): + case .error: XCTAssert(false) } } - + func testFutureSetError() { // Arrange. let future = Future() - + // Act. future.set(Test.Error.code42) - + // Assert. XCTAssertTrue(future.state == Future.State.waitingThen) XCTAssertNotNil(future._result) switch future._result! { - case .value(_): + case .value: XCTAssert(false) - case .error(let error): + case let .error(error): XCTAssertTrue(error == Test.Error.code42) } } - + func testFutureSetErrorTwice() { // Arrange. let future = Future() - + // Act. future.set(Test.Error.code42) future.set(Test.Error.code13) - + // Assert. XCTAssertTrue(future.state == Future.State.waitingThen) XCTAssertNotNil(future._result) switch future._result! { - case .value(_): + case .value: XCTAssert(false) - case .error(let error): + case let .error(error): XCTAssertTrue(error == Test.Error.code42) } } - + func testFutureSetValueThenError() { // Arrange. let future = Future() - + // Act. future.set(42) future.set(Test.Error.code13) - + // Assert. XCTAssertTrue(future.state == Future.State.waitingThen) XCTAssertNotNil(future._result) switch future._result! { - case .value(let value): + case let .value(value): XCTAssertEqual(value, 42) - case .error(_): + case .error: XCTAssert(false) } } - + func testFutureSetErrorThenValue() { // Arrange. let future = Future() - + // Act. future.set(Test.Error.code42) future.set(13) - + // Assert. XCTAssertTrue(future.state == Future.State.waitingThen) XCTAssertNotNil(future._result) switch future._result! { - case .value(_): + case .value: XCTAssert(false) - case .error(let error): + case let .error(error): XCTAssertTrue(error == Test.Error.code42) } } - + func testFutureSetFuture1() { // Arrange let future1 = Future() let future2 = Future() - + // Act future1.set(42) future2.set(future1) - + // Assert. XCTAssertTrue(future2.state == .waitingThen) XCTAssertNotNil(future2._result) switch future2._result! { - case .value(let value): + case let .value(value): XCTAssertEqual(value, 42) - case .error(_): + case .error: XCTAssert(false) } } - + func testFutureSetFuture2() { // Arrange let future1 = Future() let future2 = Future() - + // Act future2.set(future1) future1.set(42) - + // Assert. XCTAssertTrue(future2.state == .waitingThen) XCTAssertNotNil(future2._result) switch future2._result! { - case .value(let value): + case let .value(value): XCTAssertEqual(value, 42) - case .error(_): + case .error: XCTAssert(false) } } diff --git a/Example/Tests/Future+ThenTests.swift b/Example/Tests/Future+ThenTests.swift index 5745071d..b6a92d45 100644 --- a/Example/Tests/Future+ThenTests.swift +++ b/Example/Tests/Future+ThenTests.swift @@ -18,102 +18,100 @@ import XCTest @testable import Harmony class FutureThenTests: XCTestCase { - func testFutureFirstSetAfterThen() { // Arrange + Act let future = Future(42) - + // Assert - future.then() { value in + future.then { value in XCTAssertEqual(value, 42) - }.fail { _ in - XCTAssert(false) + }.fail { _ in + XCTAssert(false) } XCTAssertTrue(future.state == .sent) } - + func testFutureFirstThenAfterSet() { // Arrange let future = Future() - let expectation1 = self.expectation(description: "1 then") - + let expectation1 = expectation(description: "1 then") + // Assert XCTAssertTrue(future.state == .blank) - future.then() { value in + future.then { value in expectation1.fulfill() XCTAssertEqual(value, 42) - }.fail { _ in - XCTAssert(false) + }.fail { _ in + XCTAssert(false) } XCTAssertTrue(future.state == .waitingContent) - + // Act future.set(42) - + // Assert XCTAssertTrue(future.state == .sent) - - self.wait(for: [expectation1], timeout: 1) + + wait(for: [expectation1], timeout: 1) } - + func testFutureDoubleThen() { // Arrange + Act let future = Future(42) - - let expectation1 = self.expectation(description: "1 then") - let expectation2 = self.expectation(description: "2 then") - + + let expectation1 = expectation(description: "1 then") + let expectation2 = expectation(description: "2 then") + // Assert - future.then() { value in + future.then { value in expectation1.fulfill() XCTAssertEqual(value, 42) - }.then { value in - expectation2.fulfill() - XCTAssertEqual(value, 42) - }.fail { _ in - XCTAssert(false) + }.then { value in + expectation2.fulfill() + XCTAssertEqual(value, 42) + }.fail { _ in + XCTAssert(false) } XCTAssertTrue(future.state == .sent) - - self.wait(for: [expectation1, expectation2], timeout: 1) + + wait(for: [expectation1, expectation2], timeout: 1) } - + func testFutureLotsOfThen() { // Arrange + Act let future = Future(42) - - let expectation1 = self.expectation(description: "1 then") - let expectation2 = self.expectation(description: "2 then") - let expectation3 = self.expectation(description: "3 then") - let expectation4 = self.expectation(description: "4 then") - let expectation5 = self.expectation(description: "5 then") - let expectation6 = self.expectation(description: "6 then") - + + let expectation1 = expectation(description: "1 then") + let expectation2 = expectation(description: "2 then") + let expectation3 = expectation(description: "3 then") + let expectation4 = expectation(description: "4 then") + let expectation5 = expectation(description: "5 then") + let expectation6 = expectation(description: "6 then") + // Assert - future.then() { value in + future.then { value in expectation1.fulfill() XCTAssertEqual(value, 42) - }.then { value in - expectation2.fulfill() - XCTAssertEqual(value, 42) - }.then { value in - expectation3.fulfill() - XCTAssertEqual(value, 42) - }.then { value in - expectation4.fulfill() - XCTAssertEqual(value, 42) - }.then { value in - expectation5.fulfill() - XCTAssertEqual(value, 42) - }.then { value in - expectation6.fulfill() - XCTAssertEqual(value, 42) - }.fail { _ in - XCTAssert(false) + }.then { value in + expectation2.fulfill() + XCTAssertEqual(value, 42) + }.then { value in + expectation3.fulfill() + XCTAssertEqual(value, 42) + }.then { value in + expectation4.fulfill() + XCTAssertEqual(value, 42) + }.then { value in + expectation5.fulfill() + XCTAssertEqual(value, 42) + }.then { value in + expectation6.fulfill() + XCTAssertEqual(value, 42) + }.fail { _ in + XCTAssert(false) } XCTAssertTrue(future.state == .sent) - - self.wait(for: [expectation1, expectation2, expectation3, expectation4, expectation5, expectation6], timeout: 1) - } + wait(for: [expectation1, expectation2, expectation3, expectation4, expectation5, expectation6], timeout: 1) + } } diff --git a/Example/Tests/Performance/FuturePerformanceTests.swift b/Example/Tests/Performance/FuturePerformanceTests.swift index 982fd427..48e1c817 100644 --- a/Example/Tests/Performance/FuturePerformanceTests.swift +++ b/Example/Tests/Performance/FuturePerformanceTests.swift @@ -19,7 +19,6 @@ import XCTest @testable import Harmony class FuturePerformanceTests: XCTestCase { - /// Measures the average time needed to create a resolved `Future` and get into a `then` block /// chained to it. func testThenOnSerialQueue() { @@ -28,11 +27,11 @@ class FuturePerformanceTests: XCTestCase { expectation.expectedFulfillmentCount = Constants.iterationCount let queue = DispatchQueue(label: #function, qos: .userInitiated) let semaphore = DispatchSemaphore(value: 0) - + // Act. DispatchQueue.main.async { let time = dispatch_benchmark(Constants.iterationCount) { - Future(true).then(DispatchQueueExecutor(queue)) { value in + Future(true).then(DispatchQueueExecutor(queue)) { _ in semaphore.signal() expectation.fulfill() } @@ -40,11 +39,11 @@ class FuturePerformanceTests: XCTestCase { } print(average: time) } - + // Assert. waitForExpectations(timeout: 10) } - + /// Measures the average time needed to create a resolved `Future`, chain two `then` blocks on /// it and get into the last `then` block. func testDoubleThenOnSerialQueue() { @@ -53,25 +52,25 @@ class FuturePerformanceTests: XCTestCase { expectation.expectedFulfillmentCount = Constants.iterationCount let queue = DispatchQueue(label: #function, qos: .userInitiated) let semaphore = DispatchSemaphore(value: 0) - + // Act. DispatchQueue.main.async { let time = dispatch_benchmark(Constants.iterationCount) { - Future(true).then(DispatchQueueExecutor(queue)) { value in - - }.then(DispatchQueueExecutor(queue)) { value in - semaphore.signal() - expectation.fulfill() + Future(true).then(DispatchQueueExecutor(queue)) { _ in + + }.then(DispatchQueueExecutor(queue)) { _ in + semaphore.signal() + expectation.fulfill() } semaphore.wait() } print(average: time) } - + // Assert. waitForExpectations(timeout: 10) } - + /// Measures the average time needed to create a resolved `Future`, chain three `then` blocks on /// it and get into the last `then` block. func testTripleThenOnSerialQueue() { @@ -80,28 +79,28 @@ class FuturePerformanceTests: XCTestCase { expectation.expectedFulfillmentCount = Constants.iterationCount let queue = DispatchQueue(label: #function, qos: .userInitiated) let semaphore = DispatchSemaphore(value: 0) - + // Act. DispatchQueue.main.async { let time = dispatch_benchmark(Constants.iterationCount) { let future = Future(true) - future.then(DispatchQueueExecutor(queue)) { value in - - }.then(DispatchQueueExecutor(queue)) { value in - - }.then(DispatchQueueExecutor(queue)) { value in - semaphore.signal() - expectation.fulfill() + future.then(DispatchQueueExecutor(queue)) { _ in + + }.then(DispatchQueueExecutor(queue)) { _ in + + }.then(DispatchQueueExecutor(queue)) { _ in + semaphore.signal() + expectation.fulfill() } semaphore.wait() } print(average: time) } - + // Assert. waitForExpectations(timeout: 10) } - + /// Measures the total time needed to resolve a lot of pending `Future` with chained `then` /// blocks on them on a concurrent queue and wait for each of them to get into chained block. func testThenOnConcurrentQueue() { @@ -109,21 +108,21 @@ class FuturePerformanceTests: XCTestCase { let queue = DispatchQueue(label: #function, qos: .userInitiated, attributes: .concurrent) let group = DispatchGroup() var futures = [Future]() - for _ in 0..() - future.then(DispatchQueueExecutor(queue)) { value in + future.then(DispatchQueueExecutor(queue)) { _ in group.leave() } futures.append(future) } let startDate = Date() - + // Act. for future in futures { future.set(true) } - + // Assert. XCTAssert(group.wait(timeout: .now() + 1) == .success) let endDate = Date() diff --git a/Example/Tests/Performance/GCDPerformanceTests.swift b/Example/Tests/Performance/GCDPerformanceTests.swift index 2463d4b7..fb7911f6 100644 --- a/Example/Tests/Performance/GCDPerformanceTests.swift +++ b/Example/Tests/Performance/GCDPerformanceTests.swift @@ -18,7 +18,6 @@ import UIKit import XCTest class GCDPerformanceTests: XCTestCase { - /// Measures the average time needed to get into a dispatch_async block. func testDispatchAsyncOnSerialQueue() { // Arrange. @@ -26,7 +25,7 @@ class GCDPerformanceTests: XCTestCase { expectation.expectedFulfillmentCount = Constants.iterationCount let queue = DispatchQueue(label: #function, qos: .userInitiated) let semaphore = DispatchSemaphore(value: 0) - + // Act. DispatchQueue.main.async { let time = dispatch_benchmark(Constants.iterationCount) { @@ -38,11 +37,11 @@ class GCDPerformanceTests: XCTestCase { } print(average: time) } - + // Assert. waitForExpectations(timeout: 10) } - + /// Measures the average time needed to get into a doubly nested dispatch_async block. func testDoubleDispatchAsyncOnSerialQueue() { // Arrange. @@ -50,7 +49,7 @@ class GCDPerformanceTests: XCTestCase { expectation.expectedFulfillmentCount = Constants.iterationCount let queue = DispatchQueue(label: #function, qos: .userInitiated) let semaphore = DispatchSemaphore(value: 0) - + // Act. DispatchQueue.main.async { let time = dispatch_benchmark(Constants.iterationCount) { @@ -64,11 +63,11 @@ class GCDPerformanceTests: XCTestCase { } print(average: time) } - + // Assert. waitForExpectations(timeout: 10) } - + /// Measures the average time needed to get into a triply nested dispatch_async block. func testTripleDispatchAsyncOnSerialQueue() { // Arrange. @@ -76,7 +75,7 @@ class GCDPerformanceTests: XCTestCase { expectation.expectedFulfillmentCount = Constants.iterationCount let queue = DispatchQueue(label: #function, qos: .userInitiated) let semaphore = DispatchSemaphore(value: 0) - + // Act. DispatchQueue.main.async { let time = dispatch_benchmark(Constants.iterationCount) { @@ -92,11 +91,11 @@ class GCDPerformanceTests: XCTestCase { } print(average: time) } - + // Assert. waitForExpectations(timeout: 10) } - + /// Measures the total time needed to perform a lot of `DispatchQueue.async` blocks on /// a concurrent queue. func testDispatchAsyncOnConcurrentQueue() { @@ -104,21 +103,21 @@ class GCDPerformanceTests: XCTestCase { let queue = DispatchQueue(label: #function, qos: .userInitiated, attributes: .concurrent) let group = DispatchGroup() var blocks = [() -> Void]() - for _ in 0..(true).then(on: queue) { _ in - }.then(on: queue) { _ in - semaphore.signal() - expectation.fulfill() + }.then(on: queue) { _ in + semaphore.signal() + expectation.fulfill() } semaphore.wait() } print(average: time) } - + // Assert. waitForExpectations(timeout: 10) } - + /// Measures the average time needed to create a resolved `Promise`, chain three `then` blocks on /// it and get into the last `then` block. func testTripleThenOnSerialQueue() { @@ -80,25 +78,25 @@ class PromisesPerformanceTest: XCTestCase { expectation.expectedFulfillmentCount = Constants.iterationCount let queue = DispatchQueue(label: #function, qos: .userInitiated) let semaphore = DispatchSemaphore(value: 0) - + // Act. DispatchQueue.main.async { let time = dispatch_benchmark(Constants.iterationCount) { Promise(true).then(on: queue) { _ in - }.then(on: queue) { _ in - }.then(on: queue) { _ in - semaphore.signal() - expectation.fulfill() + }.then(on: queue) { _ in + }.then(on: queue) { _ in + semaphore.signal() + expectation.fulfill() } semaphore.wait() } print(average: time) } - + // Assert. waitForExpectations(timeout: 10) } - + /// Measures the total time needed to resolve a lot of pending `Promise` with chained `then` /// blocks on them on a concurrent queue and wait for each of them to get into chained block. func testThenOnConcurrentQueue() { @@ -106,7 +104,7 @@ class PromisesPerformanceTest: XCTestCase { let queue = DispatchQueue(label: #function, qos: .userInitiated, attributes: .concurrent) let group = DispatchGroup() var promises = [Promise]() - for _ in 0...pending() promise.then(on: queue) { _ in @@ -115,12 +113,12 @@ class PromisesPerformanceTest: XCTestCase { promises.append(promise) } let startDate = Date() - + // Act. for promise in promises { promise.fulfill(true) } - + // Assert. XCTAssert(group.wait(timeout: .now() + 1) == .success) let endDate = Date() diff --git a/Example/Tests/TestHelper.swift b/Example/Tests/TestHelper.swift index 6c3aa4e0..cc46ca38 100644 --- a/Example/Tests/TestHelper.swift +++ b/Example/Tests/TestHelper.swift @@ -19,7 +19,7 @@ import Foundation // MARK: - Constants struct Constants { - static let iterationCount = 10_000 + static let iterationCount = 10000 } // MARK: - Helpers @@ -33,13 +33,14 @@ func print(total time: TimeInterval) { } /// Namespace for test helpers. -public struct Test { +public enum Test { public enum Error: Int, CustomNSError { case code13 = 13 case code42 = 42 public static var errorDomain: String { return "com.mobilejazz.Harmony" } + public var errorCode: Int { return rawValue } public var errorUserInfo: [String: Any] { return [:] } } diff --git a/Package.swift b/Package.swift index ca345056..4ec7fc5d 100644 --- a/Package.swift +++ b/Package.swift @@ -7,7 +7,7 @@ let package = Package( name: "Harmony", platforms: [ .iOS(.v12), - .macOS(.v10_15) + .macOS(.v10_15), ], products: [ .library( @@ -17,7 +17,7 @@ let package = Package( .library( name: "HarmonyTesting", targets: ["HarmonyTesting"] - ) + ), ], targets: [ .target( @@ -31,6 +31,6 @@ let package = Package( .testTarget( name: "HarmonyTests", dependencies: ["Harmony"] - ) + ), ] ) diff --git a/Sources/Harmony/Common/Error/ClassError.swift b/Sources/Harmony/Common/Error/ClassError.swift index 778b89c4..537aa7b9 100644 --- a/Sources/Harmony/Common/Error/ClassError.swift +++ b/Sources/Harmony/Common/Error/ClassError.swift @@ -40,17 +40,16 @@ import Foundation /// } /// } /// -open class ClassError : Error, CustomStringConvertible { - +open class ClassError: Error, CustomStringConvertible { /// The error's domain - public let domain : String - + public let domain: String + /// The error's code - public let code : Int - + public let code: Int + /// The error's description - public let description : String - + public let description: String + /// Main initializer /// /// - Parameters: @@ -58,22 +57,22 @@ open class ClassError : Error, CustomStringConvertible { /// - code: The error's code /// - description: The error's description /// - userInfo: The error's user info (default is empty dictionary) - public init(domain: String = "default" , code: Int, description: String) { + public init(domain: String = "default", code: Int, description: String) { self.domain = domain self.code = code self.description = description } - + /// Localized error description public var localizedDescription: String { return description } - + /// Subclasses can override this method to return a custom user info for NSError transformation - open func userInfo() -> [String:Any] { - return [NSLocalizedDescriptionKey : description] + open func userInfo() -> [String: Any] { + return [NSLocalizedDescriptionKey: description] } - + /// Converts the CoreError into a NSError format /// - Parameter domain: The domain for the NSError. If nil (default), the domain used is self.domain /// - Returns: An NSError instnace @@ -82,15 +81,14 @@ open class ClassError : Error, CustomStringConvertible { } } -extension ClassError : LocalizedError { +extension ClassError: LocalizedError { /// A localized message describing what error occurred. public var errorDescription: String? { return description } } - -extension ClassError : Hashable { +extension ClassError: Hashable { /// Hash method public func hash(into hasher: inout Hasher) { hasher.combine(domain) @@ -98,14 +96,14 @@ extension ClassError : Hashable { } } -extension ClassError : Equatable { +extension ClassError: Equatable { /// Two ClassError are the same if code and domain are the same. - public static func ==(lhs: ClassError, rhs: ClassError) -> Bool { + public static func == (lhs: ClassError, rhs: ClassError) -> Bool { return lhs.code == rhs.code && lhs.domain == rhs.domain } } -extension ClassError : CustomDebugStringConvertible { +extension ClassError: CustomDebugStringConvertible { /// Debug description public var debugDescription: String { return "Error Code <\(code)>: \(description)" diff --git a/Sources/Harmony/Common/Error/CoreError.swift b/Sources/Harmony/Common/Error/CoreError.swift index 5ba74050..a04c1758 100644 --- a/Sources/Harmony/Common/Error/CoreError.swift +++ b/Sources/Harmony/Common/Error/CoreError.swift @@ -20,90 +20,88 @@ import Foundation /// CoreError is used as a namespace /// public struct CoreError { - - private init() { } - + private init() {} + // The domain for all CoreError errors. public static let domain = "com.mobilejazz.core.error" - + /// Unknown error - public class Unknown : ClassError { + public class Unknown: ClassError { public init(_ description: String = "Unknown Error") { super.init(domain: CoreError.domain, code: 0, description: description) } - - public override init(domain: String , code: Int, description: String) { + + override public init(domain: String, code: Int, description: String) { super.init(domain: domain, code: code, description: description) } } - + /// Not found error - public class NotFound : ClassError { + public class NotFound: ClassError { public init(_ description: String = "Not Found") { - super.init(domain:CoreError.domain, code: 1, description: description) + super.init(domain: CoreError.domain, code: 1, description: description) } } - + /// Illegal argument error - public class IllegalArgument : ClassError { + public class IllegalArgument: ClassError { public init(_ description: String = "Illegal Argument") { - super.init(domain:CoreError.domain, code: 2, description: description) + super.init(domain: CoreError.domain, code: 2, description: description) } } - + /// Illegal argument error - public class NotImplemented : ClassError { + public class NotImplemented: ClassError { public init(_ description: String = "Not Implemented") { - super.init(domain:CoreError.domain, code: 3, description: description) + super.init(domain: CoreError.domain, code: 3, description: description) } } - + /// Not valid error - public class NotValid : ClassError { + public class NotValid: ClassError { public init(_ description: String = "Object or action not valid") { - super.init(domain:CoreError.domain, code: 4, description: description) + super.init(domain: CoreError.domain, code: 4, description: description) } } - + /// Failed error - public class Failed : ClassError { + public class Failed: ClassError { public init(_ description: String = "Action failed") { - super.init(domain:CoreError.domain, code: 5, description: description) + super.init(domain: CoreError.domain, code: 5, description: description) } } - + public class QueryNotSupported: ClassError { public init(_ description: String = "Query not supported") { - super.init(domain:CoreError.domain, code: 6, description: description) + super.init(domain: CoreError.domain, code: 6, description: description) } } } -extension CoreError { - +public extension CoreError { /// A base implementation for NSError to ClassError conversion - public class NSError : Unknown { + class NSError: Unknown { /// The NSError - public let error : Foundation.NSError - + public let error: Foundation.NSError + /// Default initializer /// /// - Parameter error: The incoming NSError - public init(_ error : Foundation.NSError) { + public init(_ error: Foundation.NSError) { self.error = error super.init(domain: error.domain, code: error.code, description: error.localizedDescription) } - - public override func userInfo() -> [String : Any] { + + override public func userInfo() -> [String: Any] { return error.userInfo } } - + /// OSStatus fail error. Subtype of Failed error. - public class OSStatusFailure : Failed { + class OSStatusFailure: Failed { /// The OSStatus - public let status : OSStatus - + public let status: OSStatus + /// Default initializer /// /// - Parameters: @@ -113,12 +111,11 @@ extension CoreError { self.status = status super.init(description ?? "OSStatus failure with code \(status)") } - - public override func userInfo() -> [String : Any] { + + override public func userInfo() -> [String: Any] { var userInfo = super.userInfo() userInfo["OSStatus"] = status return userInfo } } } - diff --git a/Sources/Harmony/Common/Error/ErrorHelper.swift b/Sources/Harmony/Common/Error/ErrorHelper.swift index 340d597e..c91116c5 100644 --- a/Sources/Harmony/Common/Error/ErrorHelper.swift +++ b/Sources/Harmony/Common/Error/ErrorHelper.swift @@ -18,30 +18,30 @@ import Foundation public extension NSError { static func domain(_ string: String = "", base: String = "\(Bundle.main.bundleIdentifier!)") -> String { - if string.count > 0 { + if !string.isEmpty { return base + "." + string } return base } - + convenience init(_ message: String, - reason: String? = nil, - domain: String = NSError.domain(), - code: Int = 0, - userInfo: (inout [String : Any]) -> Void = { _ in }) { - var userInfoDict: [String : Any] = [NSLocalizedDescriptionKey : message] + reason: String? = nil, + domain: String = NSError.domain(), + code: Int = 0, + userInfo: (inout [String: Any]) -> Void = { _ in }) { + var userInfoDict: [String: Any] = [NSLocalizedDescriptionKey: message] if let reason = reason { userInfoDict[NSLocalizedFailureReasonErrorKey] = reason } userInfo(&userInfoDict) self.init(domain: domain, code: code, userInfo: userInfoDict) } - + convenience init(_ message: String, - reason: String? = nil, - subdomain: String, - code: Int = 0, - userInfo: (inout [String : Any]) -> Void = { _ in }) { - self.init(message, reason: reason, domain: NSError.domain(subdomain), code: code, userInfo: userInfo ) + reason: String? = nil, + subdomain: String, + code: Int = 0, + userInfo: (inout [String: Any]) -> Void = { _ in }) { + self.init(message, reason: reason, domain: NSError.domain(subdomain), code: code, userInfo: userInfo) } } diff --git a/Sources/Harmony/Common/Extensions/Data+Extensions.swift b/Sources/Harmony/Common/Extensions/Data+Extensions.swift index 1de5e597..4510b7ed 100644 --- a/Sources/Harmony/Common/Extensions/Data+Extensions.swift +++ b/Sources/Harmony/Common/Extensions/Data+Extensions.swift @@ -14,15 +14,14 @@ // limitations under the License. // -import Foundation import CommonCrypto +import Foundation -extension Data { - +public extension Data { /// Creates random data of the given length. /// /// - Parameter length: The length of the data. - public init?(randomOfLength length: UInt) { + init?(randomOfLength length: UInt) { var bytes = [UInt8](repeating: 0, count: Int(length)) let status = SecRandomCopyBytes(kSecRandomDefault, Int(length), &bytes) if status == errSecSuccess { @@ -31,22 +30,22 @@ extension Data { return nil } } - + /// HexEncoding options - public struct HexEncodingOptions: OptionSet { + struct HexEncodingOptions: OptionSet { public let rawValue: Int - public init(rawValue : Int) { + public init(rawValue: Int) { self.rawValue = rawValue } - + public static let upperCase = HexEncodingOptions(rawValue: 1 << 0) } - + /// Returns the hexadecimal representation of the data. /// /// - Parameter options: Use .upperCase if needed. /// - Returns: The hexadecimal string representation. - public func hexEncodedString(options: HexEncodingOptions = []) -> String { + func hexEncodedString(options: HexEncodingOptions = []) -> String { let hexDigits = Array((options.contains(.upperCase) ? "0123456789ABCDEF" : "0123456789abcdef").utf16) var chars: [unichar] = [] chars.reserveCapacity(2 * count) @@ -56,81 +55,81 @@ extension Data { } return String(utf16CodeUnits: chars, count: chars.count) } - - public init?(hexEncoded string: String) { + + init?(hexEncoded string: String) { // Get the UTF8 characters of this string let chars = Array(string.utf8) - + if chars.count % 2 != 0 { // Length of string must be even return nil } - + // Keep the bytes in an UInt8 array and later convert it to Data var bytes = [UInt8]() bytes.reserveCapacity(string.count / 2) - + // It is a lot faster to use a lookup map instead of strtoul let map: [UInt8] = [ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 01234567 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 89:;<=>? - 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, // @ABCDEFG - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // HIJKLMNO + 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00, // @ABCDEFG + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // HIJKLMNO ] - + // Grab two characters at a time, map them and turn it into a byte for i in stride(from: 0, to: string.count, by: 2) { let char1 = chars[i] - let char2 = chars[i+1] - + let char2 = chars[i + 1] + // ASCII validation - guard (48 /* 0 */<= char1 && char1 <= 57 /* 9 */) || - (65 /* a */<= char1 && char1 <= 70 /* f */) || - (97 /* A */ <= char1 && char1 <= 102 /* F */) else { - return nil + guard (48 /* 0 */ <= char1 && char1 <= 57 /* 9 */ ) || + (65 /* a */ <= char1 && char1 <= 70 /* f */ ) || + (97 /* A */ <= char1 && char1 <= 102 /* F */ ) else { + return nil } - - guard (48 /* 0 */<= char2 && char2 <= 57 /* 9 */) || - (65 /* a */<= char2 && char2 <= 70 /* f */) || - (97 /* A */ <= char2 && char2 <= 102 /* F */) else { - return nil + + guard (48 /* 0 */ <= char2 && char2 <= 57 /* 9 */ ) || + (65 /* a */ <= char2 && char2 <= 70 /* f */ ) || + (97 /* A */ <= char2 && char2 <= 102 /* F */ ) else { + return nil } - + let index1 = Int(char1 & 0x1F ^ 0x10) let index2 = Int(char2 & 0x1F ^ 0x10) bytes.append(map[index1] << 4 | map[index2]) } - + self.init(bytes) } - public func sha256() -> String { + func sha256() -> String { return digest().hexEncodedString() } - + private func digest() -> Data { let digestLength = Int(CC_SHA256_DIGEST_LENGTH) var hash = [UInt8](repeating: 0, count: digestLength) - CC_SHA256((self as NSData).bytes, UInt32(self.count), &hash) + CC_SHA256((self as NSData).bytes, UInt32(count), &hash) return NSData(bytes: hash, length: digestLength) as Data } - - private func hexStringFromData(input: NSData) -> String { + + private func hexStringFromData(input: NSData) -> String { var bytes = [UInt8](repeating: 0, count: input.length) input.getBytes(&bytes, length: input.length) - + var hexString = "" for byte in bytes { - hexString += String(format:"%02x", UInt8(byte)) + hexString += String(format: "%02x", UInt8(byte)) } - + return hexString } } public extension String { - func sha256() -> String{ - if let stringData = self.data(using: String.Encoding.utf8) { + func sha256() -> String { + if let stringData = data(using: String.Encoding.utf8) { return stringData.sha256() } return "" diff --git a/Sources/Harmony/Common/Extensions/JSONDecoding.swift b/Sources/Harmony/Common/Extensions/JSONDecoding.swift index 7ee7886d..831bfc30 100644 --- a/Sources/Harmony/Common/Extensions/JSONDecoding.swift +++ b/Sources/Harmony/Common/Extensions/JSONDecoding.swift @@ -17,48 +17,51 @@ import Foundation public extension Dictionary where Key == String, Value == AnyObject { - func decodeAs(_ type : T.Type, - keyDecodingStrategy : JSONDecoder.KeyDecodingStrategy = .useDefaultKeys) throws -> T where T : Decodable { + func decodeAs( + _ type: T.Type, + keyDecodingStrategy: JSONDecoder.KeyDecodingStrategy = .useDefaultKeys + ) throws -> T where T: Decodable { let data = try JSONSerialization.data(withJSONObject: self, options: .prettyPrinted) let decoder = JSONDecoder() decoder.keyDecodingStrategy = keyDecodingStrategy let object = try decoder.decode(type, from: data) return object } - - func decodeAs(_ type : T.Type, - keyDecodingStrategy : JSONDecoder.KeyDecodingStrategy = .useDefaultKeys, - completion: (inout T) -> Void = { _ in }) -> Future where T : Decodable { + + func decodeAs(_ type: T.Type, + keyDecodingStrategy: JSONDecoder.KeyDecodingStrategy = .useDefaultKeys, + completion: (inout T) -> Void = { _ in }) -> Future where T: Decodable { do { - var object : T = try decodeAs(type, keyDecodingStrategy: keyDecodingStrategy) + var object: T = try decodeAs(type, keyDecodingStrategy: keyDecodingStrategy) completion(&object) return Future(object) - } catch(let error) { + } catch { return Future(error) } } } -public extension Array where Element == [String : AnyObject] { - func decodeAs(keyDecodingStrategy : JSONDecoder.KeyDecodingStrategy = .useDefaultKeys) throws -> [T] where T : Decodable { +public extension Array where Element == [String: AnyObject] { + func decodeAs(keyDecodingStrategy: JSONDecoder.KeyDecodingStrategy = .useDefaultKeys) throws -> [T] + where T: Decodable { let data = try JSONSerialization.data(withJSONObject: self, options: .prettyPrinted) let decoder = JSONDecoder() decoder.keyDecodingStrategy = keyDecodingStrategy - let array = try decoder.decode(Array.self, from: data) + let array = try decoder.decode([T].self, from: data) return array } - - func decodeAs(keyDecodingStrategy : JSONDecoder.KeyDecodingStrategy = .useDefaultKeys, - forEach: (inout T) -> Void = { _ in }, - completion: (inout [T]) -> Void = { _ in }) -> Future<[T]> where T : Decodable { + + func decodeAs(keyDecodingStrategy: JSONDecoder.KeyDecodingStrategy = .useDefaultKeys, + forEach: (inout T) -> Void = { _ in }, + completion: (inout [T]) -> Void = { _ in }) -> Future<[T]> where T: Decodable { do { - var array : [T] = try decodeAs(keyDecodingStrategy: keyDecodingStrategy) + var array: [T] = try decodeAs(keyDecodingStrategy: keyDecodingStrategy) for index in array.indices { - forEach(&(array[index])) + forEach(&array[index]) } completion(&array) return Future(array) - } catch(let error) { + } catch { return Future(error) } } diff --git a/Sources/Harmony/Common/Extensions/Number+Extensions.swift b/Sources/Harmony/Common/Extensions/Number+Extensions.swift index db53526d..6012804a 100644 --- a/Sources/Harmony/Common/Extensions/Number+Extensions.swift +++ b/Sources/Harmony/Common/Extensions/Number+Extensions.swift @@ -20,7 +20,7 @@ public extension Int { /// Returns a random value. /// - Returns: A random value. static func random() -> Int { - return Int.random(in: Int.min...Int.max) + return Int.random(in: Int.min ... Int.max) } } @@ -28,7 +28,7 @@ public extension Int8 { /// Returns a random value. /// - Returns: A random value. static func random() -> Int8 { - return Int8.random(in: Int8.min...Int8.max) + return Int8.random(in: Int8.min ... Int8.max) } } @@ -36,7 +36,7 @@ public extension Int16 { /// Returns a random value. /// - Returns: A random value. static func random() -> Int16 { - return Int16.random(in: Int16.min...Int16.max) + return Int16.random(in: Int16.min ... Int16.max) } } @@ -44,7 +44,7 @@ public extension Int32 { /// Returns a random value. /// - Returns: A random value. static func random() -> Int32 { - return Int32.random(in: Int32.min...Int32.max) + return Int32.random(in: Int32.min ... Int32.max) } } @@ -52,7 +52,7 @@ public extension Int64 { /// Returns a random value. /// - Returns: A random value. static func random() -> Int64 { - return Int64.random(in: Int64.min...Int64.max) + return Int64.random(in: Int64.min ... Int64.max) } } @@ -60,7 +60,7 @@ public extension UInt { /// Returns a random value. /// - Returns: A random value. static func random() -> UInt { - return UInt.random(in: UInt.min...UInt.max) + return UInt.random(in: UInt.min ... UInt.max) } } @@ -68,7 +68,7 @@ public extension UInt8 { /// Returns a random value. /// - Returns: A random value. static func random() -> UInt8 { - return UInt8.random(in: UInt8.min...UInt8.max) + return UInt8.random(in: UInt8.min ... UInt8.max) } } @@ -76,7 +76,7 @@ public extension UInt16 { /// Returns a random value. /// - Returns: A random value. static func random() -> UInt16 { - return UInt16.random(in: UInt16.min...UInt16.max) + return UInt16.random(in: UInt16.min ... UInt16.max) } } @@ -84,7 +84,7 @@ public extension UInt32 { /// Returns a random value. /// - Returns: A random value. static func random() -> UInt32 { - return UInt32.random(in: UInt32.min...UInt32.max) + return UInt32.random(in: UInt32.min ... UInt32.max) } } @@ -92,7 +92,7 @@ public extension UInt64 { /// Returns a random value. /// - Returns: A random value. static func random() -> UInt64 { - return UInt64.random(in: UInt64.min...UInt64.max) + return UInt64.random(in: UInt64.min ... UInt64.max) } } @@ -100,7 +100,7 @@ public extension Float { /// Returns a random value. /// - Returns: A random value. static func random() -> Float { - return Float.random(in: Float.leastNormalMagnitude...Float.greatestFiniteMagnitude) + return Float.random(in: Float.leastNormalMagnitude ... Float.greatestFiniteMagnitude) } } @@ -108,6 +108,6 @@ public extension Double { /// Returns a random value. /// - Returns: A random value. static func random() -> Double { - return Double.random(in: Double.leastNormalMagnitude...Double.greatestFiniteMagnitude) + return Double.random(in: Double.leastNormalMagnitude ... Double.greatestFiniteMagnitude) } } diff --git a/Sources/Harmony/Common/Extensions/String+Extensions.swift b/Sources/Harmony/Common/Extensions/String+Extensions.swift index 329a9184..7cfade91 100644 --- a/Sources/Harmony/Common/Extensions/String+Extensions.swift +++ b/Sources/Harmony/Common/Extensions/String+Extensions.swift @@ -14,91 +14,151 @@ // limitations under the License. // -import Foundation import CommonCrypto +import Foundation -private let alphabet = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z", - "A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z", - "0","1","2","3","4","5","6","7","8","9"] +private let alphabet = [ + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", +] public extension String { - /// Creates a random string of the given length. /// /// - Parameter length: The length of the string. init(randomOfLength length: UInt) { var string = "" - for _ in 0.. [String] { return components(separatedBy: " ") } - + /// Returns the first word. /// /// - Returns: The first word. func firstWord() -> String { - if let first = self.words().first { + if let first = words().first { return first } return self } - + /// Returns the last word. /// /// - Returns: The last word. func lastWord() -> String { - if let last = self.words().last { + if let last = words().last { return last } return self } - + /// Removes the first word. /// /// - Returns: A string without the first word. func deleteFirstWord() -> String { let words = self.words() - - guard words.count > 0 else { + + guard !words.isEmpty else { return "" } - + let first = words.first! - guard first.count + 1 < self.count else { + guard first.count + 1 < count else { return "" } - + return replacingOccurrences(of: self, with: "", options: String.CompareOptions.anchored, - range: Range(NSMakeRange(0, first.count + 1), in: self)) + range: Range(NSRange(location: 0, length: first.count + 1), in: self)) } - + /// MD5 Hash /// /// - Returns: A MD5 Hashed string func md5() -> String { - guard let data = self.data(using: .utf8) else { + guard let data = data(using: .utf8) else { return self } var digest = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH)) #if swift(>=5.0) - _ = data.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) in - return CC_MD5(bytes.baseAddress, CC_LONG(data.count), &digest) - } + _ = data.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) in + CC_MD5(bytes.baseAddress, CC_LONG(data.count), &digest) + } #else - _ = data.withUnsafeBytes { bytes in - return CC_MD5(bytes, CC_LONG(data.count), &digest) - } + _ = data.withUnsafeBytes { bytes in + CC_MD5(bytes, CC_LONG(data.count), &digest) + } #endif return digest.map { String(format: "%02x", $0) }.joined() } diff --git a/Sources/Harmony/Common/JSON/CodingKeyStrategies.swift b/Sources/Harmony/Common/JSON/CodingKeyStrategies.swift index 07214335..060ccaf8 100644 --- a/Sources/Harmony/Common/JSON/CodingKeyStrategies.swift +++ b/Sources/Harmony/Common/JSON/CodingKeyStrategies.swift @@ -16,11 +16,10 @@ import Foundation -extension JSONEncoder.KeyEncodingStrategy { - - public static func map(_ keys: [String : String]) -> JSONEncoder.KeyEncodingStrategy { +public extension JSONEncoder.KeyEncodingStrategy { + static func map(_ keys: [String: String]) -> JSONEncoder.KeyEncodingStrategy { return .custom { codingKeys in - + let originalKey = codingKeys.last! guard let mappedKey = keys[originalKey.stringValue] else { return originalKey @@ -32,11 +31,10 @@ extension JSONEncoder.KeyEncodingStrategy { } } -extension JSONDecoder.KeyDecodingStrategy { - - public static func map(_ keys: [String : String]) -> JSONDecoder.KeyDecodingStrategy { +public extension JSONDecoder.KeyDecodingStrategy { + static func map(_ keys: [String: String]) -> JSONDecoder.KeyDecodingStrategy { return .custom { codingKeys in - + let originalKey = codingKeys.last! guard let mappedKey = keys[originalKey.stringValue] else { return originalKey diff --git a/Sources/Harmony/Common/JSON/MutableCodingKey.swift b/Sources/Harmony/Common/JSON/MutableCodingKey.swift index 30dc1943..19a11f6a 100644 --- a/Sources/Harmony/Common/JSON/MutableCodingKey.swift +++ b/Sources/Harmony/Common/JSON/MutableCodingKey.swift @@ -19,24 +19,23 @@ import Foundation /// /// Wrapper to allow us to edit a coding key. /// -public struct MutableCodingKey : CodingKey { - +public struct MutableCodingKey: CodingKey { public var stringValue: String public var intValue: Int? - + public init(_ base: CodingKey) { self.init(stringValue: base.stringValue, intValue: base.intValue) } - + public init(stringValue: String) { self.stringValue = stringValue } - + public init(intValue: Int) { - self.stringValue = "\(intValue)" + stringValue = "\(intValue)" self.intValue = intValue } - + public init(stringValue: String, intValue: Int?) { self.stringValue = stringValue self.intValue = intValue diff --git a/Sources/Harmony/Common/KeyValueObserver.swift b/Sources/Harmony/Common/KeyValueObserver.swift index 882da862..8be23200 100644 --- a/Sources/Harmony/Common/KeyValueObserver.swift +++ b/Sources/Harmony/Common/KeyValueObserver.swift @@ -19,25 +19,24 @@ import Foundation /// /// Swift object acting as a key value observer /// -public class KeyValueObserver : NSObject where V : Any { - - private var context : String - private let resolver : ObservableResolver - private var keyPath : String - private var target : NSObject - +public class KeyValueObserver: NSObject where V: Any { + private var context: String + private let resolver: ObservableResolver + private var keyPath: String + private var target: NSObject + /// Convenience init method to use a closure /// /// - Parameters: /// - target: The object to observe /// - keyPath: The keyPath to observe /// - closure: The callback closure - public convenience init(target: NSObject, keyPath: String, _ closure: @escaping (V) -> Void ) { + public convenience init(target: NSObject, keyPath: String, _ closure: @escaping (V) -> Void) { let observable = Observable() observable.then { closure($0) } self.init(target: target, keyPath: keyPath, resolver: ObservableResolver(observable)) } - + /// Main init method to use a reactive future resolver as callback /// /// - Parameters: @@ -48,16 +47,21 @@ public class KeyValueObserver : NSObject where V : Any { self.keyPath = keyPath self.target = target self.resolver = resolver - self.context = "com.mobilejazz.KeyValueObserver<\(String(describing: V.self))>.\(keyPath)" + context = "com.mobilejazz.KeyValueObserver<\(String(describing: V.self))>.\(keyPath)" super.init() self.target.addObserver(self, forKeyPath: keyPath, options: .new, context: &context) } - + deinit { self.target.removeObserver(self, forKeyPath: keyPath) } - - public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { + + override public func observeValue( + forKeyPath keyPath: String?, + of _: Any?, + change: [NSKeyValueChangeKey: Any]?, + context: UnsafeMutableRawPointer? + ) { if context == &self.context, let path = keyPath, path == self.keyPath { if let value = change?[NSKeyValueChangeKey.newKey] { resolver.set(value as! V) @@ -65,4 +69,3 @@ public class KeyValueObserver : NSObject where V : Any { } } } - diff --git a/Sources/Harmony/Common/LinkRecognizer.swift b/Sources/Harmony/Common/LinkRecognizer.swift index e1e90d07..c998182b 100644 --- a/Sources/Harmony/Common/LinkRecognizer.swift +++ b/Sources/Harmony/Common/LinkRecognizer.swift @@ -16,86 +16,90 @@ import Foundation -public protocol LinkRecognizerObserver : AnyObject { +public protocol LinkRecognizerObserver: AnyObject { func linkRecognizer(_ linkRecognizer: LinkRecognizer, didRecognizeURLForKey key: String, components: [String]) - func linkRecognizer(_ linkRecognizer: LinkRecognizer, didFailToRecognizeURL url: URL, result: LinkRecognizer.Result) + func linkRecognizer( + _ linkRecognizer: LinkRecognizer, + didFailToRecognizeURL url: URL, + result: LinkRecognizer.Result + ) } -public protocol LinkRecognizerDelegate : AnyObject { - func linkRecognizer(_ linkRecognizer: LinkRecognizer, willRecognizeURLForKey key: String, components: [String]) -> Bool +public protocol LinkRecognizerDelegate: AnyObject { + func linkRecognizer(_ linkRecognizer: LinkRecognizer, willRecognizeURLForKey key: String, + components: [String]) -> Bool } public class LinkRecognizer { - - public enum Result : CustomStringConvertible { + public enum Result: CustomStringConvertible { case valid([String]) case unknownScheme case unsupportedLink - + public var description: String { - get { - switch (self) { - case .valid(let components): - return "Valid with components: \(components)" - case .unknownScheme: - return "UnkonwnScheme" - case .unsupportedLink: - return "UnsupportedLink" - } + switch self { + case let .valid(components): + return "Valid with components: \(components)" + case .unknownScheme: + return "UnkonwnScheme" + case .unsupportedLink: + return "UnsupportedLink" } } } - - public struct Pattern { + + public enum Pattern { public static let numeric = "(\\d+)" public static let nonNumeric = "(\\D+)" public static let alphanumeric = "(\\w+)" public static let alphanumericAndDash = "([\\w,-]+)" } - public struct Options : OptionSet { + public struct Options: OptionSet { public var rawValue: Int public typealias RawValue = Int public init(rawValue: Int) { self.rawValue = rawValue } - public static let none = Options([]) - public static let anchoredStart = Options(rawValue: 1 << 0) - public static let anchoredEnd = Options(rawValue: 1 << 1) - public static let caseInsensitive = Options(rawValue: 1 << 2) + + public static let none = Options([]) + public static let anchoredStart = Options(rawValue: 1 << 0) + public static let anchoredEnd = Options(rawValue: 1 << 1) + public static let caseInsensitive = Options(rawValue: 1 << 2) } - - public weak var delegate : LinkRecognizerDelegate? - - private let scheme : String - private var patterns : [String : String] = [:] - private let options : Options + + public weak var delegate: LinkRecognizerDelegate? + + private let scheme: String + private var patterns: [String: String] = [:] + private let options: Options private let observers = NSHashTable.weakObjects() - + public init(scheme: String, options: Options = .none) { self.scheme = scheme self.options = options } - - public func registerPattern(_ pattern : String, forKey key: String) { + + public func registerPattern(_ pattern: String, forKey key: String) { patterns[key] = pattern } - + public func recognize(_ link: URL) -> Result { guard let scheme = link.scheme else { return Result.unknownScheme } - + if scheme != self.scheme { return Result.unknownScheme } - + guard let linkStr = (link as NSURL).resourceSpecifier else { return Result.unsupportedLink } - - let regexOptions = options.contains(.caseInsensitive) ? NSRegularExpression.Options.caseInsensitive : NSRegularExpression.Options(rawValue: 0) - + + let regexOptions = options.contains(.caseInsensitive) ? NSRegularExpression.Options + .caseInsensitive : NSRegularExpression.Options(rawValue: 0) + for (key, pattern) in patterns { var patternStr = pattern if options.contains(.caseInsensitive) { @@ -104,45 +108,50 @@ public class LinkRecognizer { if options.contains(.anchoredEnd) { patternStr = "\(patternStr)$" } - + do { let regex = try NSRegularExpression(pattern: patternStr, options: regexOptions) if let result = regex.firstMatch(in: linkStr, - options: NSRegularExpression.MatchingOptions(rawValue:0), - range: NSMakeRange(0, linkStr.count)) { - var captures : [String] = [] - for index in 0.. String { switch level { - case .trace: - return "TRACE" - case .debug: - return "DEBUG" - case .info: - return "INFO" - case .warning: - return "WARNING" - case .error: - return "ERROR" - case .fatal: - return "FATAL" + case .trace: + return "TRACE" + case .debug: + return "DEBUG" + case .info: + return "INFO" + case .warning: + return "WARNING" + case .error: + return "ERROR" + case .fatal: + return "FATAL" } } } diff --git a/Sources/Harmony/Common/Logger/Logger.swift b/Sources/Harmony/Common/Logger/Logger.swift index d312a99f..2fccb6ae 100644 --- a/Sources/Harmony/Common/Logger/Logger.swift +++ b/Sources/Harmony/Common/Logger/Logger.swift @@ -28,7 +28,6 @@ import Foundation /// Abstracts concrete implementations of a logger system. @objc(MJLogger) public protocol Logger { - /// Logs a String object using a given level /// /// - Parameters: @@ -36,7 +35,7 @@ import Foundation /// - tag: An additional label to help categorise logs. /// - message: The message to be logged. func log(level: LogLevel, tag: String?, message: String) - + /// Logs a key-value pair /// /// - Parameters: @@ -46,75 +45,75 @@ import Foundation } // MARK: - Default implementations + public extension Logger { - /// Logs a String message using an trace level. /// /// - Parameters: /// - tag: An additional label to help categorise logs. /// - message: String to be logged func trace(tag: String? = nil, _ message: String) { - self.log(level: .trace, tag: tag, message: message) + log(level: .trace, tag: tag, message: message) } - + /// Logs a String message using an debug level. /// /// - Parameters: /// - tag: An additional label to help categorise logs. /// - message: String to be logged func debug(tag: String? = nil, _ message: String) { - self.log(level: .debug, tag: tag, message: message) + log(level: .debug, tag: tag, message: message) } - + /// Logs a String message using an info level. /// /// - Parameters: /// - tag: An additional label to help categorise logs. /// - message: String to be logged func info(tag: String? = nil, _ message: String) { - self.log(level: .info, tag: tag, message: message) + log(level: .info, tag: tag, message: message) } - + /// Logs a String message using a warning level. /// /// - Parameters: /// - tag: An additional label to help categorise logs. /// - message: String to be logged func warning(tag: String? = nil, _ message: String) { - self.log(level: .warning, tag: tag, message: message) + log(level: .warning, tag: tag, message: message) } - + /// Logs a String message using an error level. /// /// - Parameters: /// - tag: An additional label to help categorise logs. /// - message: String to be logged func error(tag: String? = nil, _ message: String) { - self.log(level: .error, tag: tag, message: message) + log(level: .error, tag: tag, message: message) } - + /// Logs a String message using a fatal level. /// /// - Parameters: /// - tag: An additional label to help categorise logs. /// - message: String to be logged func fatal(tag: String? = nil, _ message: String) { - self.log(level: .fatal, tag: tag, message: message) + log(level: .fatal, tag: tag, message: message) } - + /// Logs a key-value pair /// /// - Parameters: /// - key: They key /// - value: The value func log(key: String, value: Any?) { - self.info("\(key)=\(value ?? "-")") + info("\(key)=\(value ?? "-")") } } /// Logger that does nothing. -public class VoidLogger : Logger { +public class VoidLogger: Logger { public init() {} - public func log(level: LogLevel, tag: String?, message: String) {} - public func log(key: String, value: Any?) {} + public func log(level _: LogLevel, tag _: String?, message _: String) {} + public func log(key _: String, value _: Any?) {} } diff --git a/Sources/Harmony/Common/ScopeLock/ScopeLock.swift b/Sources/Harmony/Common/ScopeLock/ScopeLock.swift index 8deb27de..83c3b74e 100644 --- a/Sources/Harmony/Common/ScopeLock/ScopeLock.swift +++ b/Sources/Harmony/Common/ScopeLock/ScopeLock.swift @@ -16,12 +16,12 @@ import Foundation -fileprivate protocol LockProvider { +private protocol LockProvider { associatedtype K func lockWithScope(_ scope: K) -> NSLock } -fileprivate class MapTableLockProvider: LockProvider where T: AnyObject { +private class MapTableLockProvider: LockProvider where T: AnyObject { typealias K = T let lock = NSLock() var map = NSMapTable.weakToStrongObjects() @@ -38,10 +38,10 @@ fileprivate class MapTableLockProvider: LockProvider where T: AnyObject { } } -fileprivate class DictionaryLockProvider: LockProvider where T : Hashable { +private class DictionaryLockProvider: LockProvider where T: Hashable { typealias K = T let lock = NSLock() - var map : [T : NSLock] = [:] + var map: [T: NSLock] = [:] func lockWithScope(_ scope: T) -> NSLock { lock.lock() defer { lock.unlock() } @@ -55,10 +55,10 @@ fileprivate class DictionaryLockProvider: LockProvider where T : Hashable { } } -fileprivate let objectLockProvider = MapTableLockProvider() -fileprivate let stringLockProvider = DictionaryLockProvider() -fileprivate let integerLockProvider = DictionaryLockProvider() -fileprivate let typeLockProvider = DictionaryLockProvider() +private let objectLockProvider = MapTableLockProvider() +private let stringLockProvider = DictionaryLockProvider() +private let integerLockProvider = DictionaryLockProvider() +private let typeLockProvider = DictionaryLockProvider() /// Easy lock sync interface matching the usability of objc's @syncrhonized(var) { } /// @@ -73,7 +73,6 @@ fileprivate let typeLockProvider = DictionaryLockProvider() /// } /// } public struct ScopeLock { - /// The scope /// /// - none: No scope defined. The ScopeLock will be instance specific. @@ -92,28 +91,28 @@ public struct ScopeLock { } /// The lock - private let lock : NSLock - + private let lock: NSLock + /// Main init public init(_ scope: Scope) { switch scope { case .none: lock = NSLock() - case .lock(let inLock): + case let .lock(inLock): lock = inLock case .global: lock = typeLockProvider.lockWithScope(String(describing: T.self)) - case .object(let object): + case let .object(object): lock = objectLockProvider.lockWithScope(object) - case .string(let string): + case let .string(string): lock = stringLockProvider.lockWithScope(string) - case .int(let integer): + case let .int(integer): lock = integerLockProvider.lockWithScope(integer) } } - + /// Init from another ScopeLock - public init(_ scopeLock : ScopeLock) { + public init(_ scopeLock: ScopeLock) { lock = scopeLock.lock } @@ -121,52 +120,52 @@ public struct ScopeLock { public init() { self.init(Void.self) } - + /// Convenience init to use objects as scope - public init(_ object: T) where T:AnyObject { + public init(_ object: T) where T: AnyObject { self.init(Scope.object(object)) } - - public init(_ type: T.Type) { + + public init(_: T.Type) { self.init(Scope.global) } - + /// Convenience init to use integers as scope public init(_ value: Int) { self.init(Scope.int(value)) } - + /// Convenience init to use strings as scope public init(_ string: String) { self.init(Scope.string(string)) } - + /// Convenience init to use a given lock public init(_ lock: NSLock) { self.init(Scope.lock(lock)) } - + /// The syncing method public func sync(_ closure: () -> T) -> T { lock.lock() defer { lock.unlock() } return closure() } - + /// The syncing method public func sync(_ closure: () -> T?) -> T? { lock.lock() defer { lock.unlock() } return closure() } - + /// The syncing method public func sync(_ closure: () -> Void) { lock.lock() closure() lock.unlock() } - + /// The async lock method. The nested closure must be called upon unlocking. public func async(_ closure: (@escaping () -> Void) -> Void) { lock.lock() diff --git a/Sources/Harmony/Common/ScopeLock/ScopeLockFuture.swift b/Sources/Harmony/Common/ScopeLock/ScopeLockFuture.swift index 19bd46b3..61fa45b5 100644 --- a/Sources/Harmony/Common/ScopeLock/ScopeLockFuture.swift +++ b/Sources/Harmony/Common/ScopeLock/ScopeLockFuture.swift @@ -28,7 +28,7 @@ public extension ScopeLock { } } } - + /// Syncing method using an async closure /// Note the sync will kept active until the future sets its value or error. /// diff --git a/Sources/Harmony/Data/DataSource/AnyDataSource.swift b/Sources/Harmony/Data/DataSource/AnyDataSource.swift index 62859e81..af1cbeb8 100644 --- a/Sources/Harmony/Data/DataSource/AnyDataSource.swift +++ b/Sources/Harmony/Data/DataSource/AnyDataSource.swift @@ -19,37 +19,37 @@ import Foundation /// /// Type eraser class for DataSource, following Apple's Swift Standard Library approach. /// -public final class AnyDataSource : GetDataSource, PutDataSource, DeleteDataSource { +public final class AnyDataSource: GetDataSource, PutDataSource, DeleteDataSource { private let box: DataSourceBoxBase - + /// Default initializer. /// /// - Parameters: /// - dataSource: The dataSource to abstract - public init(_ dataSource: D) where D:GetDataSource, D:PutDataSource, D:DeleteDataSource, D.T == T { + public init(_ dataSource: D) where D: GetDataSource, D: PutDataSource, D: DeleteDataSource, D.T == T { box = DataSourceBox(dataSource) } - + public func get(_ query: Query) -> Future { return box.get(query) } - + public func getAll(_ query: Query) -> Future<[T]> { return box.getAll(query) } - + public func put(_ value: T?, in query: Query) -> Future { return box.put(value, in: query) } - + public func putAll(_ array: [T], in query: Query) -> Future<[T]> { return box.putAll(array, in: query) } - + public func delete(_ query: Query) -> Future { return box.delete(query) } - + public func deleteAll(_ query: Query) -> Future { return box.deleteAll(query) } @@ -57,66 +57,67 @@ public final class AnyDataSource : GetDataSource, PutDataSource, DeleteDataS /// /// This is an abstract class. Do not use it. -/// DataSource base class defining a generic type T (which is unrelated to the associated type of the DataSource protocol) +/// DataSource base class defining a generic type T (which is unrelated to the associated type of the DataSource +// protocol) /// -internal class DataSourceBoxBase : GetDataSource, PutDataSource, DeleteDataSource { - - func get(_ query: Query) -> Future { +internal class DataSourceBoxBase: GetDataSource, PutDataSource, DeleteDataSource { + func get(_: Query) -> Future { fatalError("This method is abstract.") } - - func getAll(_ query: Query) -> Future<[T]> { + + func getAll(_: Query) -> Future<[T]> { fatalError("This method is abstract.") } - - func put(_ value: T?, in query: Query) -> Future { + + func put(_: T?, in _: Query) -> Future { fatalError("This method is abstract.") } - - func putAll(_ array: [T], in query: Query) -> Future<[T]> { + + func putAll(_: [T], in _: Query) -> Future<[T]> { fatalError("This method is abstract.") } - - func delete(_ query: Query) -> Future { + + func delete(_: Query) -> Future { fatalError("This method is abstract.") } - - func deleteAll(_ query: Query) -> Future { + + func deleteAll(_: Query) -> Future { fatalError("This method is abstract.") } } /// -/// A data source box, which has as generic type a DataSource and links the DataSourceBoxBase type T as the Base.T type. +/// A data source box, which has as generic type a DataSource and links the DataSourceBoxBase type T as the Base.T +// type. /// -internal class DataSourceBox : DataSourceBoxBase where Base:GetDataSource, Base:PutDataSource, Base:DeleteDataSource { - +internal class DataSourceBox: DataSourceBoxBase where Base: GetDataSource, Base: PutDataSource, + Base: DeleteDataSource { private let base: Base - + init(_ base: Base) { self.base = base } - + override func get(_ query: Query) -> Future { return base.get(query) } - + override func getAll(_ query: Query) -> Future<[T]> { return base.getAll(query) } - + override func put(_ value: T?, in query: Query) -> Future { return base.put(value, in: query) } - + override func putAll(_ array: [T], in query: Query) -> Future<[T]> { return base.putAll(array, in: query) } - + override func delete(_ query: Query) -> Future { return base.delete(query) } - + override func deleteAll(_ query: Query) -> Future { return base.deleteAll(query) } diff --git a/Sources/Harmony/Data/DataSource/AnyGetDataSource.swift b/Sources/Harmony/Data/DataSource/AnyGetDataSource.swift index 57276651..286986b4 100644 --- a/Sources/Harmony/Data/DataSource/AnyGetDataSource.swift +++ b/Sources/Harmony/Data/DataSource/AnyGetDataSource.swift @@ -19,9 +19,9 @@ import Foundation /// /// Type eraser class for DataSource, following Apple's Swift Standard Library approach. /// -public final class AnyGetDataSource : GetDataSource { +public final class AnyGetDataSource: GetDataSource { private let box: GetDataSourceBoxBase - + /// Default initializer. /// /// - Parameters: @@ -29,21 +29,21 @@ public final class AnyGetDataSource : GetDataSource { public init(_ dataSource: D) where D.T == T { box = GetDataSourceBox(dataSource) } - + public func get(_ query: Query) -> Future { return box.get(query) } - + public func getAll(_ query: Query) -> Future<[T]> { return box.getAll(query) } } -extension GetDataSource { +public extension GetDataSource { /// Returns an AnyGetDataSource abstraction of the current data source /// /// - Returns: An AnyGetDataSource abstraction - public func asAnyGetDataSource() -> AnyGetDataSource { + func asAnyGetDataSource() -> AnyGetDataSource { if let dataSource = self as? AnyGetDataSource { return dataSource } @@ -53,34 +53,34 @@ extension GetDataSource { /// /// This is an abstract class. Do not use it. -/// GetDataSource base class defining a generic type T (which is unrelated to the associated type of the GetDataSource protocol) +/// GetDataSource base class defining a generic type T (which is unrelated to the associated type of the +// GetDataSource protocol) /// -internal class GetDataSourceBoxBase : GetDataSource { - - func get(_ query: Query) -> Future { +internal class GetDataSourceBoxBase: GetDataSource { + func get(_: Query) -> Future { fatalError("This method is abstract.") } - - func getAll(_ query: Query) -> Future<[T]> { + + func getAll(_: Query) -> Future<[T]> { fatalError("This method is abstract.") } } /// -/// A data source box, which has as generic type a GetDataSource and links the GetDataSourceBoxBase type T as the Base.T type. +/// A data source box, which has as generic type a GetDataSource and links the GetDataSourceBoxBase type T as the +// Base.T type. /// -internal class GetDataSourceBox : GetDataSourceBoxBase { - +internal class GetDataSourceBox: GetDataSourceBoxBase { private let base: Base - + init(_ base: Base) { self.base = base } - + override func get(_ query: Query) -> Future { return base.get(query) } - + override func getAll(_ query: Query) -> Future<[T]> { return base.getAll(query) } diff --git a/Sources/Harmony/Data/DataSource/AnyPutDataSource.swift b/Sources/Harmony/Data/DataSource/AnyPutDataSource.swift index 090f2622..b6ab3b48 100644 --- a/Sources/Harmony/Data/DataSource/AnyPutDataSource.swift +++ b/Sources/Harmony/Data/DataSource/AnyPutDataSource.swift @@ -19,9 +19,9 @@ import Foundation /// /// Type eraser class for DataSource, following Apple's Swift Standard Library approach. /// -public final class AnyPutDataSource : PutDataSource { +public final class AnyPutDataSource: PutDataSource { private let box: PutDataSourceBoxBase - + /// Default initializer. /// /// - Parameters: @@ -29,21 +29,21 @@ public final class AnyPutDataSource : PutDataSource { public init(_ dataSource: D) where D.T == T { box = PutDataSourceBox(dataSource) } - + public func put(_ value: T?, in query: Query) -> Future { return box.put(value, in: query) } - + public func putAll(_ array: [T], in query: Query) -> Future<[T]> { return box.putAll(array, in: query) } } -extension PutDataSource { +public extension PutDataSource { /// Returns an AnyPutDataSource abstraction of the current data source /// /// - Returns: An AnyPutDataSource abstraction - public func asAnyPutDataSource() -> AnyPutDataSource { + func asAnyPutDataSource() -> AnyPutDataSource { if let dataSource = self as? AnyPutDataSource { return dataSource } @@ -53,34 +53,34 @@ extension PutDataSource { /// /// This is an abstract class. Do not use it. -/// PutDataSource base class defining a generic type T (which is unrelated to the associated type of the PutDataSource protocol) +/// PutDataSource base class defining a generic type T (which is unrelated to the associated type of the +// PutDataSource protocol) /// -internal class PutDataSourceBoxBase : PutDataSource { - - func put(_ value: T?, in query: Query) -> Future { +internal class PutDataSourceBoxBase: PutDataSource { + func put(_: T?, in _: Query) -> Future { fatalError("This method is abstract.") } - - func putAll(_ array: [T], in query: Query) -> Future<[T]> { + + func putAll(_: [T], in _: Query) -> Future<[T]> { fatalError("This method is abstract.") } } /// -/// A data source box, which has as generic type a PutDataSource and links the PutDataSourceBoxBase type T as the Base.T type. +/// A data source box, which has as generic type a PutDataSource and links the PutDataSourceBoxBase type T as the +// Base.T type. /// -internal class PutDataSourceBox : PutDataSourceBoxBase { - +internal class PutDataSourceBox: PutDataSourceBoxBase { private let base: Base - + init(_ base: Base) { self.base = base } - + override func put(_ value: T?, in query: Query) -> Future { return base.put(value, in: query) } - + override func putAll(_ array: [T], in query: Query) -> Future<[T]> { return base.putAll(array, in: query) } diff --git a/Sources/Harmony/Data/DataSource/DataSource.swift b/Sources/Harmony/Data/DataSource/DataSource.swift index c43c26ef..64fb238e 100644 --- a/Sources/Harmony/Data/DataSource/DataSource.swift +++ b/Sources/Harmony/Data/DataSource/DataSource.swift @@ -16,21 +16,20 @@ import Foundation -public protocol DataSource { } +public protocol DataSource {} /// /// Interface for a Get data source. /// -public protocol GetDataSource : DataSource { - +public protocol GetDataSource: DataSource { associatedtype T - + /// Get a single method /// /// - Parameter query: An instance conforming to Query that encapsules the get query information /// - Returns: A Future containing the fetched object or an error .notFound if not found func get(_ query: Query) -> Future - + /// Main get method /// /// - Parameter query: An instance conforming to Query that encapsules the get query information @@ -38,12 +37,12 @@ public protocol GetDataSource : DataSource { func getAll(_ query: Query) -> Future<[T]> } -extension GetDataSource { - public func get(_ id: K) -> Future where K:Hashable { +public extension GetDataSource { + func get(_ id: K) -> Future where K: Hashable { return get(IdQuery(id)) } - - public func getAll(_ id: K) -> Future<[T]> where K:Hashable { + + func getAll(_ id: K) -> Future<[T]> where K: Hashable { return getAll(IdQuery(id)) } } @@ -51,33 +50,34 @@ extension GetDataSource { /// /// Interface for a Put data source. /// -public protocol PutDataSource : DataSource { - +public protocol PutDataSource: DataSource { associatedtype T - + /// Put by query method /// /// - Parameter query: An instance conforming to Query that encapsules the get query information - /// - Returns: A future of T type. Some data sources might add some extra fields after the put operation, e.g. id or timestamp fields. + /// - Returns: A future of T type. Some data sources might add some extra fields after the put operation, e.g. + // id or timestamp fields. @discardableResult func put(_ value: T?, in query: Query) -> Future - + /// Put by query method /// /// - Parameter query: An instance conforming to Query that encapsules the get query information - /// - Returns: A future of T type. Some data sources might add some extra fields after the put operation, e.g. id or timestamp fields. + /// - Returns: A future of T type. Some data sources might add some extra fields after the put operation, e.g. + // id or timestamp fields. @discardableResult func putAll(_ array: [T], in query: Query) -> Future<[T]> } -extension PutDataSource { +public extension PutDataSource { @discardableResult - public func put(_ value: T?, forId id: K) -> Future where K:Hashable { + func put(_ value: T?, forId id: K) -> Future where K: Hashable { return put(value, in: IdQuery(id)) } - + @discardableResult - public func putAll(_ array: [T], forId id: K) -> Future<[T]> where K:Hashable { + func putAll(_ array: [T], forId id: K) -> Future<[T]> where K: Hashable { return putAll(array, in: IdQuery(id)) } } @@ -85,44 +85,45 @@ extension PutDataSource { /// /// Interface for a Delete data source. /// -public protocol DeleteDataSource : DataSource { +public protocol DeleteDataSource: DataSource { /// Delete by query method /// /// - Parameter query: An instance conforming to Query that encapusles the delete query information /// - Returns: A future of Void type. @discardableResult func delete(_ query: Query) -> Future - + /// Delete by query method /// /// - Parameter query: An instance conforming to Query that encapusles the delete query information /// - Returns: A future of Void type. - @available(*, deprecated, message: "Use delete with AllObjectsQuery to remove all entries or with any other Query to remove one or more entries") + @available(*, deprecated, + message: "Use delete with AllObjectsQuery to remove all entries or with any other Query to remove one or more entries") @discardableResult func deleteAll(_ query: Query) -> Future } -extension DeleteDataSource { +public extension DeleteDataSource { @discardableResult - public func delete(_ id: K) -> Future where K:Hashable { + func delete(_ id: K) -> Future where K: Hashable { return delete(IdQuery(id)) } - + @available(*, deprecated, message: "Use delete instead") @discardableResult - public func deleteAll(_ id: K) -> Future where K:Hashable { + func deleteAll(_ id: K) -> Future where K: Hashable { return deleteAll(IdQuery(id)) } } -public enum DataSourceCRUD : CustomStringConvertible { +public enum DataSourceCRUD: CustomStringConvertible { case get case getAll case put case putAll case delete case deleteAll - + public var description: String { switch self { case .get: return "get" @@ -135,8 +136,11 @@ public enum DataSourceCRUD : CustomStringConvertible { } } -extension Query { - public func fatalError(_ method: DataSourceCRUD, _ origin: D) -> Never where D : DataSource { - Swift.fatalError("Undefined query \(String(describing: self)) for method \(method) on \(String(describing: type(of: origin)))") +public extension Query { + func fatalError(_ method: DataSourceCRUD, _ origin: D) -> Never where D: DataSource { + Swift + .fatalError( + "Undefined query \(String(describing: self)) for method \(method) on \(String(describing: type(of: origin)))" + ) } } diff --git a/Sources/Harmony/Data/DataSource/DataSourceAssembler.swift b/Sources/Harmony/Data/DataSource/DataSourceAssembler.swift index e130f9f5..89a7fb37 100644 --- a/Sources/Harmony/Data/DataSource/DataSourceAssembler.swift +++ b/Sources/Harmony/Data/DataSource/DataSourceAssembler.swift @@ -19,12 +19,17 @@ import Foundation /// /// Assambles a CRUD data source into a single data source object /// -public final class DataSourceAssembler : GetDataSource, PutDataSource, DeleteDataSource where Get.T == T, Put.T == T { - - private let getDataSource : Get - private let putDataSource : Put - private let deleteDataSource : Delete - +public final class DataSourceAssembler< + Get: GetDataSource, + Put: PutDataSource, + Delete: DeleteDataSource, + T +>: GetDataSource, PutDataSource, DeleteDataSource + where Get.T == T, Put.T == T { + private let getDataSource: Get + private let putDataSource: Put + private let deleteDataSource: Delete + /// Main initializer /// /// - Parameters: @@ -36,98 +41,98 @@ public final class DataSourceAssembler Future { return getDataSource.get(query) } - + public func getAll(_ query: Query) -> Future<[T]> { return getDataSource.getAll(query) } - + @discardableResult public func put(_ value: T?, in query: Query) -> Future { return putDataSource.put(value, in: query) } - + @discardableResult public func putAll(_ array: [T], in query: Query) -> Future<[T]> { return putDataSource.putAll(array, in: query) } - + @discardableResult public func delete(_ query: Query) -> Future { return deleteDataSource.delete(query) } - + @discardableResult public func deleteAll(_ query: Query) -> Future { return deleteDataSource.deleteAll(query) } } -extension DataSourceAssembler where Get == Put, Get == Delete { +public extension DataSourceAssembler where Get == Put, Get == Delete { /// Initializer for a single DataSource /// /// - Parameter dataSource: The data source - public convenience init(_ dataSource: Get) { + convenience init(_ dataSource: Get) { self.init(get: dataSource, put: dataSource, delete: dataSource) } } -extension DataSourceAssembler where Put == VoidPutDataSource, Delete == VoidDeleteDataSource { +public extension DataSourceAssembler where Put == VoidPutDataSource, Delete == VoidDeleteDataSource { /// Initializer for a single DataSource /// /// - Parameter getDataSource: The data source - public convenience init(get getDataSource: Get) { + convenience init(get getDataSource: Get) { self.init(get: getDataSource, put: VoidPutDataSource(), delete: VoidDeleteDataSource()) } } -extension DataSourceAssembler where Get == VoidGetDataSource, Delete == VoidDeleteDataSource { +public extension DataSourceAssembler where Get == VoidGetDataSource, Delete == VoidDeleteDataSource { /// Initializer for a single DataSource /// /// - Parameter putDataSource: The data source - public convenience init(put putDataSource: Put) { + convenience init(put putDataSource: Put) { self.init(get: VoidGetDataSource(), put: putDataSource, delete: VoidDeleteDataSource()) } } -extension DataSourceAssembler where Get == VoidGetDataSource, Put == VoidPutDataSource { +public extension DataSourceAssembler where Get == VoidGetDataSource, Put == VoidPutDataSource { /// Initializer for a single DataSource /// /// - Parameter deleteDataSource: The data source - public convenience init(delete deleteDataSource: Delete) { + convenience init(delete deleteDataSource: Delete) { self.init(get: VoidGetDataSource(), put: VoidPutDataSource(), delete: deleteDataSource) } } -extension DataSourceAssembler where Get == VoidGetDataSource { +public extension DataSourceAssembler where Get == VoidGetDataSource { /// Initializer for a single DataSource /// /// - Parameter putDataSource: The data source /// - Parameter deleteDataSource: The data source - public convenience init(put putDataSource: Put, delete deleteDataSource: Delete) { + convenience init(put putDataSource: Put, delete deleteDataSource: Delete) { self.init(get: VoidGetDataSource(), put: putDataSource, delete: deleteDataSource) } } -extension DataSourceAssembler where Put == VoidPutDataSource { +public extension DataSourceAssembler where Put == VoidPutDataSource { /// Initializer for a single DataSource /// /// - Parameter getDataSource: The data source /// - Parameter deleteDataSource: The data source - public convenience init(get getDataSource: Get, delete deleteDataSource: Delete) { + convenience init(get getDataSource: Get, delete deleteDataSource: Delete) { self.init(get: getDataSource, put: VoidPutDataSource(), delete: deleteDataSource) } } -extension DataSourceAssembler where Delete == VoidDeleteDataSource { +public extension DataSourceAssembler where Delete == VoidDeleteDataSource { /// Initializer for a single DataSource /// /// - Parameter getDataSource: The data source /// - Parameter putDataSource: The data source - public convenience init(get getDataSource: Get, put putDataSource: Put) { + convenience init(get getDataSource: Get, put putDataSource: Put) { self.init(get: getDataSource, put: putDataSource, delete: VoidDeleteDataSource()) } } diff --git a/Sources/Harmony/Data/DataSource/DataSourceValidator.swift b/Sources/Harmony/Data/DataSource/DataSourceValidator.swift index 56b693f9..1e797bda 100644 --- a/Sources/Harmony/Data/DataSource/DataSourceValidator.swift +++ b/Sources/Harmony/Data/DataSource/DataSourceValidator.swift @@ -21,11 +21,11 @@ import Foundation /// Note that validation only occur in the get and getAll methods. /// If not valid, the returned future is resolved with a ValidationError.notValid error /// -public class DataSourceValidator : GetDataSource, PutDataSource, DeleteDataSource where D:GetDataSource, D:PutDataSource, D:DeleteDataSource, D.T == T { - - private let dataSource : D +public class DataSourceValidator: GetDataSource, PutDataSource, DeleteDataSource + where D: GetDataSource, D: PutDataSource, D: DeleteDataSource, D.T == T { + private let dataSource: D private let validator: ObjectValidation - + /// Default initializer /// /// - Parameters: @@ -35,7 +35,7 @@ public class DataSourceValidator : GetDataSource, PutDataSource, DeleteDat self.dataSource = dataSource self.validator = validator } - + public func get(_ query: Query) -> Future { return dataSource.get(query).filter { value in if !self.validator.isObjectValid(value) { @@ -43,7 +43,7 @@ public class DataSourceValidator : GetDataSource, PutDataSource, DeleteDat } } } - + public func getAll(_ query: Query) -> Future<[T]> { return dataSource.getAll(query).filter { values in if !self.validator.isArrayValid(values) { @@ -51,22 +51,22 @@ public class DataSourceValidator : GetDataSource, PutDataSource, DeleteDat } } } - + @discardableResult public func put(_ value: T?, in query: Query) -> Future { return dataSource.put(value, in: query) } - + @discardableResult public func putAll(_ array: [T], in query: Query) -> Future<[T]> { return dataSource.putAll(array, in: query) } - + @discardableResult public func delete(_ query: Query) -> Future { return dataSource.delete(query) } - + @discardableResult public func deleteAll(_ query: Query) -> Future { return dataSource.deleteAll(query) diff --git a/Sources/Harmony/Data/DataSource/DebugDataSource.swift b/Sources/Harmony/Data/DataSource/DebugDataSource.swift index 325711fc..60592f67 100644 --- a/Sources/Harmony/Data/DataSource/DebugDataSource.swift +++ b/Sources/Harmony/Data/DataSource/DebugDataSource.swift @@ -14,7 +14,6 @@ // limitations under the License. // - import Foundation public enum DebugDataSourceDelay { @@ -26,13 +25,13 @@ public enum DebugDataSourceDelay { public enum DebugDataSourceError { case none case error(Error, probability: Double) - + func fail() throws { switch self { case .none: break - case .error(let error, let probability): - if Double.random(in: 0.0...1.0) <= probability { + case let .error(error, probability): + if Double.random(in: 0.0 ... 1.0) <= probability { throw error } } @@ -40,40 +39,39 @@ public enum DebugDataSourceError { } private class DebugDataSourceToken { - - let id : String = { + let id: String = { let base = String(randomOfLength: 8) let seed = DispatchTime.now() return String("\(base)::\(seed.uptimeNanoseconds)".md5().suffix(8)).uppercased() }() - - private var startTime : DispatchTime? - private var endTime : DispatchTime? - + + private var startTime: DispatchTime? + private var endTime: DispatchTime? + func start() { startTime = DispatchTime.now() } - + func end() { endTime = DispatchTime.now() } - + func time() -> TimeInterval? { guard let start = startTime, let end = endTime else { return nil } - let seconds = TimeInterval((end.uptimeNanoseconds - start.uptimeNanoseconds)) / 1_000_000_000 + let seconds = TimeInterval(end.uptimeNanoseconds - start.uptimeNanoseconds) / 1_000_000_000 return seconds } } -public class DebugDataSource : GetDataSource, PutDataSource, DeleteDataSource where D: GetDataSource, D:PutDataSource, D:DeleteDataSource, D.T == T { - - private let dataSource : D - private let delay : DebugDataSourceDelay - private let error : DebugDataSourceError - private let logger : Logger - +public class DebugDataSource: GetDataSource, PutDataSource, DeleteDataSource where D: GetDataSource, + D: PutDataSource, D: DeleteDataSource, D.T == T { + private let dataSource: D + private let delay: DebugDataSourceDelay + private let error: DebugDataSourceError + private let logger: Logger + public init(_ dataSource: D, delay: DebugDataSourceDelay = .none, error: DebugDataSourceError = .none, @@ -83,72 +81,76 @@ public class DebugDataSource : GetDataSource, PutDataSource, DeleteDataSou self.error = error self.logger = logger } - - private func postprocess(_ future: Future, _ method: DataSourceCRUD, _ token: DebugDataSourceToken) -> Future { + + private func postprocess(_ future: Future, _ method: DataSourceCRUD, + _ token: DebugDataSourceToken) -> Future { return addError(addDelay(future.onCompletion { token.end() })).then { result in self.log(method, token, "Completed with result: \(String(describing: result))") - }.fail{ error in - self.log(method, token, "Failed with error: \(error)") + }.fail { error in + self.log(method, token, "Failed with error: \(error)") } } - + private func addDelay(_ future: Future) -> Future { switch delay { case .none: return future - case .sync(let delay): + case let .sync(delay): return future.withBlockingDelay(delay) - case .async(let delay, let queue): + case let .async(delay, queue): return future.withDelay(delay, queue: queue) } } - + private func addError(_ future: Future) -> Future { return future.filter { _ in try self.error.fail() } } - + private func log(_ method: DataSourceCRUD, _ token: DebugDataSourceToken, _ message: String) { if let time = token.time() { - logger.info(tag: String(describing: type(of: dataSource)), "[\(method).\(token.id) in <\(time)>s]: \(message)") + logger.info( + tag: String(describing: type(of: dataSource)), + "[\(method).\(token.id) in <\(time)>s]: \(message)" + ) } else { logger.info(tag: String(describing: type(of: dataSource)), "[\(method).\(token.id)]: \(message)") } } - + public func get(_ query: Query) -> Future { let token = DebugDataSourceToken() log(.get, token, String(describing: query)) token.start() return postprocess(dataSource.get(query), .get, token) } - + public func getAll(_ query: Query) -> Future<[T]> { let token = DebugDataSourceToken() log(.getAll, token, String(describing: query)) token.start() return postprocess(dataSource.getAll(query), .getAll, token) } - + public func put(_ value: T?, in query: Query) -> Future { let token = DebugDataSourceToken() log(.put, token, "\(String(describing: query)) - \(String(describing: value))") return postprocess(dataSource.put(value, in: query), .put, token) } - + public func putAll(_ array: [T], in query: Query) -> Future<[T]> { let token = DebugDataSourceToken() log(.putAll, token, "\(String(describing: query)) - \(String(describing: array))") token.start() return postprocess(dataSource.putAll(array, in: query), .putAll, token) } - + public func delete(_ query: Query) -> Future { let token = DebugDataSourceToken() log(.delete, token, String(describing: query)) token.start() return postprocess(dataSource.delete(query), .delete, token) } - + public func deleteAll(_ query: Query) -> Future { let token = DebugDataSourceToken() log(.deleteAll, token, String(describing: query)) diff --git a/Sources/Harmony/Data/DataSource/DeviceStorageDataSource.swift b/Sources/Harmony/Data/DataSource/DeviceStorageDataSource.swift index 01da3c02..17d0b0b2 100644 --- a/Sources/Harmony/Data/DataSource/DeviceStorageDataSource.swift +++ b/Sources/Harmony/Data/DataSource/DeviceStorageDataSource.swift @@ -20,10 +20,10 @@ public enum DeviceStorageType { case regular case prefix(String) case rootKey(String) - + fileprivate func key(_ key: String) -> String { switch self { - case .prefix(let prefix): + case let .prefix(prefix): switch prefix.count { case 0: return key @@ -36,52 +36,50 @@ public enum DeviceStorageType { } } -public class DeviceStorageDataSource : GetDataSource, PutDataSource, DeleteDataSource { - - private let userDefaults : UserDefaults - private let storageType : DeviceStorageType - +public class DeviceStorageDataSource: GetDataSource, PutDataSource, DeleteDataSource { + private let userDefaults: UserDefaults + private let storageType: DeviceStorageType + public init(_ userDefaults: UserDefaults = UserDefaults.standard, storageType: DeviceStorageType = .regular) { self.userDefaults = userDefaults self.storageType = storageType } - + public init(_ userDefaults: UserDefaults = UserDefaults.standard, prefix: String) { self.userDefaults = userDefaults - self.storageType = .prefix(prefix) + storageType = .prefix(prefix) } - + private func getRootKey() -> String? { switch storageType { - case .rootKey(let rootKey): - return "\(rootKey)<\(String(describing:T.self))>" + case let .rootKey(rootKey): + return "\(rootKey)<\(String(describing: T.self))>" default: return nil } } - + public func get(_ query: Query) -> Future { switch query { case let query as KeyQuery: let key = storageType.key(query.key) - guard let value : T = { - + guard let value: T = { if let rootKey = getRootKey() { return userDefaults.dictionary(forKey: rootKey)?[key] as? T } - + return userDefaults.object(forKey: key) as? T - - }() else { - return Future(CoreError.NotFound()) + + }() else { + return Future(CoreError.NotFound()) } - + return Future(value) default: query.fatalError(.get, self) } } - + public func getAll(_ query: Query) -> Future<[T]> { switch query { case let query as IdsQuery: @@ -90,16 +88,16 @@ public class DeviceStorageDataSource : GetDataSource, PutDataSource, DeleteD switch storageType { case .regular: query.fatalError(.getAll, self) - case .rootKey(_): + case .rootKey: let rootKey = getRootKey()! // Will always be present in this case - + guard let dict = userDefaults.dictionary(forKey: rootKey) else { return Future(CoreError.NotFound()) } // Dict can be composed of components of type T or [T]. // Let's add it all together in an array. var array = [T]() - dict.forEach { (key, value) in + dict.forEach { _, value in if let value = value as? T { array.append(value) } else if let values = value as? [T] { @@ -109,12 +107,12 @@ public class DeviceStorageDataSource : GetDataSource, PutDataSource, DeleteD } } return Future(array) - case .prefix(let prefix): + case let .prefix(prefix): var array = [T]() - userDefaults.dictionaryRepresentation().forEach { (key, value) in + userDefaults.dictionaryRepresentation().forEach { key, value in // Let's search for keys with the given prefix guard key.hasPrefix(prefix) else { return } - + // value now can be composed of type T or [T] or any other type. // Let's add it all together in an array if base type is T. if let value = value as? T { @@ -144,7 +142,7 @@ public class DeviceStorageDataSource : GetDataSource, PutDataSource, DeleteD query.fatalError(.getAll, self) } } - + @discardableResult public func put(_ value: T?, in query: Query) -> Future { switch query { @@ -154,7 +152,7 @@ public class DeviceStorageDataSource : GetDataSource, PutDataSource, DeleteD } let key = storageType.key(query.key) if let rootKey = getRootKey() { - var root = userDefaults.dictionary(forKey: rootKey) ?? [String : Any]() + var root = userDefaults.dictionary(forKey: rootKey) ?? [String: Any]() root[key] = value userDefaults.set(root, forKey: rootKey) } else { @@ -166,7 +164,7 @@ public class DeviceStorageDataSource : GetDataSource, PutDataSource, DeleteD query.fatalError(.put, self) } } - + @discardableResult public func putAll(_ array: [T], in query: Query) -> Future<[T]> { switch query { @@ -178,7 +176,7 @@ public class DeviceStorageDataSource : GetDataSource, PutDataSource, DeleteD case let query as KeyQuery: let key = storageType.key(query.key) if let rootKey = getRootKey() { - var root = userDefaults.dictionary(forKey: rootKey) ?? [String : Any]() + var root = userDefaults.dictionary(forKey: rootKey) ?? [String: Any]() root[key] = array userDefaults.set(root, forKey: rootKey) } else { @@ -190,26 +188,26 @@ public class DeviceStorageDataSource : GetDataSource, PutDataSource, DeleteD query.fatalError(.putAll, self) } } - + @discardableResult public func delete(_ query: Query) -> Future { switch query { case let query as IdsQuery: - return Future.batch(query.ids.map { delete(IdQuery($0)) }).map { _ in Void() } + return Future.batch(query.ids.map { delete(IdQuery($0)) }).map { _ in () } case is AllObjectsQuery: switch storageType { case .regular: query.fatalError(.deleteAll, self) - case .rootKey(_): + case .rootKey: let rootKey = getRootKey()! // Will always be present in this case userDefaults.removeObject(forKey: rootKey) userDefaults.synchronize() - return Future(Void()) - case .prefix(let prefix): - userDefaults.dictionaryRepresentation().forEach { (key, value) in + return Future(()) + case let .prefix(prefix): + userDefaults.dictionaryRepresentation().forEach { key, value in // Let's search for keys with the given prefix guard key.hasPrefix(prefix) else { return } - + // value now can be composed of type T or [T] or any other type. // Let's delete the object if its type match if let _ = value as? T { @@ -220,27 +218,26 @@ public class DeviceStorageDataSource : GetDataSource, PutDataSource, DeleteD // Ignore the value as its type doesn't match } } - return Future(Void()) + return Future(()) } case let query as KeyQuery: let key = storageType.key(query.key) if let rootKey = getRootKey() { - var root = userDefaults.dictionary(forKey: rootKey) ?? [String : Any]() + var root = userDefaults.dictionary(forKey: rootKey) ?? [String: Any]() root.removeValue(forKey: key) userDefaults.set(root, forKey: rootKey) } else { userDefaults.removeObject(forKey: key) } userDefaults.synchronize() - return Future(Void()) + return Future(()) default: query.fatalError(.delete, self) } } - + @discardableResult public func deleteAll(_ query: Query) -> Future { delete(query) } } - diff --git a/Sources/Harmony/Data/DataSource/FileSystemStorageDataSource.swift b/Sources/Harmony/Data/DataSource/FileSystemStorageDataSource.swift index 261c54e3..86261ca5 100644 --- a/Sources/Harmony/Data/DataSource/FileSystemStorageDataSource.swift +++ b/Sources/Harmony/Data/DataSource/FileSystemStorageDataSource.swift @@ -24,18 +24,18 @@ import Foundation /// - `put(data, "data_file.dat").fail { error in [...] }` /// - `delete("my_file.dat").fail { error in [...] }` /// -public class FileSystemStorageDataSource : GetDataSource, PutDataSource, DeleteDataSource { +public class FileSystemStorageDataSource: GetDataSource, PutDataSource, DeleteDataSource { public typealias T = Data - + public enum FileNameEncoding { case none case sha256 case md5 case custom((String) -> String) } - - private let fileManager : FileManager - public let directory : URL + + private let fileManager: FileManager + public let directory: URL public let fileNameEncoding: FileNameEncoding private let writingOptions: Data.WritingOptions @@ -44,26 +44,42 @@ public class FileSystemStorageDataSource : GetDataSource, PutDataSource, DeleteD /// - Parameters: /// - fileManager: The FileManager /// - directory: The directory where to store data - public init(fileManager: FileManager, directory: URL, writingOptions: Data.WritingOptions = [], fileNameEncoding: FileNameEncoding = .sha256) { + public init( + fileManager: FileManager, + directory: URL, + writingOptions: Data.WritingOptions = [], + fileNameEncoding: FileNameEncoding = .sha256 + ) { self.fileManager = fileManager self.directory = directory self.writingOptions = writingOptions self.fileNameEncoding = fileNameEncoding } - + /// Convenience initializer. Returns nil if the document directory is not reachable. /// /// - Parameters: - /// - relativePath: The relative path (example: "MyFolder/MySubfolder"), that will be appended on the documents directory - public convenience init?(fileManager: FileManager, relativePath: String, writingOptions: Data.WritingOptions = [], fileNameEncoding: FileNameEncoding = .sha256) { + /// - relativePath: The relative path (example: "MyFolder/MySubfolder"), that will be appended on the + // documents directory + public convenience init?( + fileManager: FileManager, + relativePath: String, + writingOptions: Data.WritingOptions = [], + fileNameEncoding: FileNameEncoding = .sha256 + ) { guard let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return nil } let url = documentsURL.appendingPathComponent(relativePath) - print(url); - self.init(fileManager: fileManager, directory: url, writingOptions: writingOptions, fileNameEncoding: fileNameEncoding) + print(url) + self.init( + fileManager: fileManager, + directory: url, + writingOptions: writingOptions, + fileNameEncoding: fileNameEncoding + ) } - + private func fileName(_ key: String) -> String { switch fileNameEncoding { case .none: @@ -72,15 +88,15 @@ public class FileSystemStorageDataSource : GetDataSource, PutDataSource, DeleteD return key.sha256() case .md5: return key.md5() - case .custom(let encoder): + case let .custom(encoder): return encoder(key) } } - + private func fileURL(_ key: String) -> URL { return directory.appendingPathComponent(fileName(key)) } - + public func get(_ query: Query) -> Future { switch query { case let query as KeyQuery: @@ -93,11 +109,11 @@ public class FileSystemStorageDataSource : GetDataSource, PutDataSource, DeleteD query.fatalError(.get, self) } } - + public func getAll(_ query: Query) -> Future<[Data]> { switch query { case let query as IdsQuery: - let futures : [Future] = query.ids.map { id in + let futures: [Future] = query.ids.map { id in let path = fileURL(id).path guard let data = fileManager.contents(atPath: path) else { return Future(CoreError.NotFound("Data not found at path: \(path)")) @@ -112,22 +128,24 @@ public class FileSystemStorageDataSource : GetDataSource, PutDataSource, DeleteD .contentsOfDirectory(at: directory, includingPropertiesForKeys: [.isDirectoryKey]) .filter { url in // Filter out folders - do { return !(try url.resourceValues(forKeys: [.isDirectoryKey])).isDirectory! } - catch { return false } + do { return !(try url.resourceValues(forKeys: [.isDirectoryKey])).isDirectory! } catch { + return false + } }.forEach { url in - guard let data = fileManager.contents(atPath: url.path) else { - throw CoreError.NotFound("Data not found at path: \(url.path)") + guard let data = fileManager.contents(atPath: url.path) else { + throw CoreError.NotFound("Data not found at path: \(url.path)") + } + // Attempting to unarchive in case it was an array + // if let datas = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: + // [NSArray.self, NSData.self], from: data) as? [Data] { + if let datas = NSKeyedUnarchiver.unarchiveObject(with: data) as? [Data] { + // it was an array! + array.append(contentsOf: datas) + } else { + // Not an array! + array.append(data) + } } - // Attempting to unarchive in case it was an array -// if let datas = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSArray.self, NSData.self], from: data) as? [Data] { - if let datas = NSKeyedUnarchiver.unarchiveObject(with: data) as? [Data] { - // it was an array! - array.append(contentsOf: datas) - } else { - // Not an array! - array.append(data) - } - } r.set(array) } case let query as KeyQuery: @@ -135,7 +153,8 @@ public class FileSystemStorageDataSource : GetDataSource, PutDataSource, DeleteD guard let data = fileManager.contents(atPath: path) else { return Future(CoreError.NotFound("Data not found at path: \(path)")) } -// guard let array = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSArray.self, NSData.self], from: data) as? [Data] else { + // guard let array = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSArray.self, + // NSData.self], from: data) as? [Data] else { guard let array = NSKeyedUnarchiver.unarchiveObject(with: data) as? [Data] else { return Future(CoreError.NotFound("Data not found at path: \(path)")) } @@ -144,7 +163,7 @@ public class FileSystemStorageDataSource : GetDataSource, PutDataSource, DeleteD query.fatalError(.getAll, self) } } - + public func put(_ value: Data?, in query: Query) -> Future { guard let data = value else { return Future(CoreError.IllegalArgument("value cannot be nil")) @@ -156,13 +175,17 @@ public class FileSystemStorageDataSource : GetDataSource, PutDataSource, DeleteD let fileURL = self.fileURL(keyQuery.key) let folderURL = fileURL.deletingLastPathComponent() if fileManager.fileExists(atPath: folderURL.path) == false { - try fileManager.createDirectory(atPath: folderURL.path, withIntermediateDirectories: true, attributes: nil) + try fileManager.createDirectory( + atPath: folderURL.path, + withIntermediateDirectories: true, + attributes: nil + ) } try data.write(to: fileURL, options: writingOptions) r.set(data) } } - + public func putAll(_ array: [Data], in query: Query) -> Future<[Data]> { switch query { case let query as IdsQuery: @@ -170,11 +193,15 @@ public class FileSystemStorageDataSource : GetDataSource, PutDataSource, DeleteD query.fatalError(.putAll, self) } return Future { r in - try query.ids.enumerated().forEach { (offset, id) in + try query.ids.enumerated().forEach { offset, id in let fileURL = self.fileURL(id) let folderURL = fileURL.deletingLastPathComponent() if fileManager.fileExists(atPath: folderURL.path) == false { - try fileManager.createDirectory(atPath: folderURL.path, withIntermediateDirectories: true, attributes: nil) + try fileManager.createDirectory( + atPath: folderURL.path, + withIntermediateDirectories: true, + attributes: nil + ) } try array[offset].write(to: fileURL, options: writingOptions) } @@ -185,29 +212,32 @@ public class FileSystemStorageDataSource : GetDataSource, PutDataSource, DeleteD let fileURL = self.fileURL(query.key) let folderURL = fileURL.deletingLastPathComponent() if fileManager.fileExists(atPath: folderURL.path) == false { - try fileManager.createDirectory(atPath: folderURL.path, withIntermediateDirectories: true, attributes: nil) + try fileManager.createDirectory( + atPath: folderURL.path, + withIntermediateDirectories: true, + attributes: nil + ) } -// let data = try NSKeyedArchiver.archivedData(withRootObject: array, requiringSecureCoding: false) + // let data = try NSKeyedArchiver.archivedData(withRootObject: array, + // requiringSecureCoding: false) let data = NSKeyedArchiver.archivedData(withRootObject: array) try data.write(to: fileURL, options: writingOptions) r.set(array) } default: query.fatalError(.getAll, self) - } - } public func delete(_ query: Query) -> Future { switch query { case let query as IdsQuery: - let futures : [Future] = query.ids.map { id in - return Future { + let futures: [Future] = query.ids.map { id in + Future { try? fileManager.removeItem(at: fileURL(id)) } } - return Future.batch(futures).map { _ in Void() } + return Future.batch(futures).map { _ in () } case is AllObjectsQuery: return Future { // Deleting everything! @@ -221,7 +251,7 @@ public class FileSystemStorageDataSource : GetDataSource, PutDataSource, DeleteD query.fatalError(.delete, self) } } - + public func deleteAll(_ query: Query) -> Future { delete(query) } diff --git a/Sources/Harmony/Data/DataSource/InMemoryDataSource.swift b/Sources/Harmony/Data/DataSource/InMemoryDataSource.swift index feed10ca..3997e3b0 100644 --- a/Sources/Harmony/Data/DataSource/InMemoryDataSource.swift +++ b/Sources/Harmony/Data/DataSource/InMemoryDataSource.swift @@ -16,13 +16,12 @@ import Foundation -public class InMemoryDataSource : GetDataSource, PutDataSource, DeleteDataSource { - - private var objects : [String : T] = [:] - private var arrays : [String : [T]] = [:] - +public class InMemoryDataSource: GetDataSource, PutDataSource, DeleteDataSource { + private var objects: [String: T] = [:] + private var arrays: [String: [T]] = [:] + public init() {} - + public func get(_ query: Query) -> Future { switch query { case let query as KeyQuery: @@ -34,7 +33,7 @@ public class InMemoryDataSource : GetDataSource, PutDataSource, DeleteDataSou return Future(CoreError.QueryNotSupported()) } } - + public func getAll(_ query: Query) -> Future<[T]> { switch query { case is AllObjectsQuery: @@ -54,7 +53,7 @@ public class InMemoryDataSource : GetDataSource, PutDataSource, DeleteDataSou return Future(CoreError.QueryNotSupported()) } } - + @discardableResult public func put(_ value: T? = nil, in query: Query) -> Future { switch query { @@ -69,7 +68,7 @@ public class InMemoryDataSource : GetDataSource, PutDataSource, DeleteDataSou return Future(CoreError.QueryNotSupported()) } } - + @discardableResult public func putAll(_ array: [T], in query: Query) -> Future<[T]> { switch query { @@ -77,7 +76,7 @@ public class InMemoryDataSource : GetDataSource, PutDataSource, DeleteDataSou guard array.count == query.ids.count else { return Future(CoreError.IllegalArgument("Array lenght must be equal to query.ids length")) } - array.enumerated().forEach { (offset, element) in + array.enumerated().forEach { offset, element in arrays.removeValue(forKey: query.ids[offset]) objects[query.ids[offset]] = element } @@ -90,32 +89,32 @@ public class InMemoryDataSource : GetDataSource, PutDataSource, DeleteDataSou return Future(CoreError.QueryNotSupported()) } } - + @discardableResult public func delete(_ query: Query) -> Future { switch query { case is AllObjectsQuery: objects.removeAll() arrays.removeAll() - return Future(Void()) + return Future(()) case let query as IdsQuery: query.ids.forEach { key in clearAll(key: key) } - return Future(Void()) + return Future(()) case let query as KeyQuery: clearAll(key: query.key) - return Future(Void()) + return Future(()) default: return Future(CoreError.QueryNotSupported()) } } - + @discardableResult public func deleteAll(_ query: Query) -> Future { delete(query) } - + private func clearAll(key: String) { objects.removeValue(forKey: key) arrays.removeValue(forKey: key) diff --git a/Sources/Harmony/Data/DataSource/RetryDataSource.swift b/Sources/Harmony/Data/DataSource/RetryDataSource.swift index df3efa68..4961a0af 100644 --- a/Sources/Harmony/Data/DataSource/RetryDataSource.swift +++ b/Sources/Harmony/Data/DataSource/RetryDataSource.swift @@ -19,49 +19,50 @@ import Foundation /// /// A retry data source adds retry capabilities over an existing data source. /// -public class RetryDataSource : GetDataSource, PutDataSource, DeleteDataSource where D:GetDataSource, D:PutDataSource, D:DeleteDataSource, D.T == T { - - private let dataSource : D - private let retryCount : Int - private let retryIf : (Error) -> Bool - +public class RetryDataSource: GetDataSource, PutDataSource, DeleteDataSource where D: GetDataSource, + D: PutDataSource, D: DeleteDataSource, D.T == T { + private let dataSource: D + private let retryCount: Int + private let retryIf: (Error) -> Bool + /// Default initializer. /// /// - Parameters: /// - dataSource: The data source to retry /// - retryCount: The number of retries. Default value is 1. - /// - retryIf: A closure to evaluate each retry error. Return true to allow a retry, false otherwise. Default closure returns true. + /// - retryIf: A closure to evaluate each retry error. Return true to allow a retry, false otherwise. Default + // closure returns true. public init(_ dataSource: D, retryCount: Int = 1, - retryIf : @escaping (Error) -> Bool = { _ in true }) { + retryIf: @escaping (Error) -> Bool = { _ in true }) { self.dataSource = dataSource self.retryCount = retryCount self.retryIf = retryIf } - + public func get(_ query: Query) -> Future { return get(query, retryCount) } - + public func getAll(_ query: Query) -> Future<[T]> { return getAll(query, retryCount) } - + @discardableResult public func put(_ value: T?, in query: Query) -> Future { return put(value, in: query, retryCount) } - + @discardableResult public func putAll(_ array: [T], in query: Query) -> Future<[T]> { return putAll(array, in: query, retryCount) } - + @discardableResult public func delete(_ query: Query) -> Future { return delete(query, retryCount) } - + @discardableResult public func deleteAll(_ query: Query) -> Future { return deleteAll(query, retryCount) @@ -69,61 +70,60 @@ public class RetryDataSource : GetDataSource, PutDataSource, DeleteDataSou } extension RetryDataSource { - private func retry(_ it: Int, _ error: Error, _ closure: () -> Future) -> Future { // Must retry if: // - it is greater than zero // - The retryIf closure returns true - if it > 0 && retryIf(error) { + if it > 0, retryIf(error) { return closure() } return Future(error) } - + private func get(_ query: Query, _ it: Int) -> Future { return dataSource.get(query).recover { error in - return self.retry(it, error) { - return self.get(query, it - 1) + self.retry(it, error) { + self.get(query, it - 1) } } } - + private func getAll(_ query: Query, _ it: Int) -> Future<[T]> { return dataSource.getAll(query).recover { error in - return self.retry(it, error) { - return self.getAll(query, it - 1) + self.retry(it, error) { + self.getAll(query, it - 1) } } } - + private func put(_ value: T?, in query: Query, _ it: Int) -> Future { return dataSource.put(value, in: query).recover { error in - return self.retry(it, error) { - return self.put(value, in: query, it - 1) + self.retry(it, error) { + self.put(value, in: query, it - 1) } } } - + private func putAll(_ array: [T], in query: Query, _ it: Int) -> Future<[T]> { return dataSource.putAll(array, in: query).recover { error in - return self.retry(it, error) { - return self.putAll(array, in: query, it - 1) + self.retry(it, error) { + self.putAll(array, in: query, it - 1) } } } - + private func delete(_ query: Query, _ it: Int) -> Future { return dataSource.delete(query).recover { error in - return self.retry(it, error) { - return self.delete(query, it - 1) + self.retry(it, error) { + self.delete(query, it - 1) } } } - + private func deleteAll(_ query: Query, _ it: Int) -> Future { return dataSource.deleteAll(query).recover { error in - return self.retry(it, error) { - return self.deleteAll(query, it - 1) + self.retry(it, error) { + self.deleteAll(query, it - 1) } } } diff --git a/Sources/Harmony/Data/DataSource/TimedCacheDataSource.swift b/Sources/Harmony/Data/DataSource/TimedCacheDataSource.swift index 9e794424..aca60cd6 100644 --- a/Sources/Harmony/Data/DataSource/TimedCacheDataSource.swift +++ b/Sources/Harmony/Data/DataSource/TimedCacheDataSource.swift @@ -16,38 +16,39 @@ import Foundation -fileprivate func print(_ object: String) { +private func print(_ object: String) { #if DEBUG - Swift.print(object) + Swift.print(object) #endif } /// -/// The TimedCacheDataSource is KeyQuery-based data source, wrapper of another data source and acting as a fast cache. +/// The TimedCacheDataSource is KeyQuery-based data source, wrapper of another data source and acting as a fast +// cache. /// It uses the date time of the access to cache the values, acting as a TLRU cache. /// -public class TimedCacheDataSource : GetDataSource, PutDataSource, DeleteDataSource where D : GetDataSource, D : PutDataSource, D : DeleteDataSource, D.T == T { - - private let dataSource : D +public class TimedCacheDataSource: GetDataSource, PutDataSource, DeleteDataSource + where D: GetDataSource, D: PutDataSource, D: DeleteDataSource, D.T == T { + private let dataSource: D private let expiryInterval: TimeInterval - - private var objects : [String : (object: T, date: Date)] = [:] - private var arrays : [String : (array:[T], date: Date)] = [:] - + + private var objects: [String: (object: T, date: Date)] = [:] + private var arrays: [String: (array: [T], date: Date)] = [:] + /// Default initializer /// /// - Parameters: /// - dataSource: The DataSource to cache. /// - expiryInterval: The expiry interval used in the cache. Default value is 300 seconds (5 minutes). - public init(_ dataSource : D, expiryInterval: TimeInterval = 300) { + public init(_ dataSource: D, expiryInterval: TimeInterval = 300) { self.dataSource = dataSource self.expiryInterval = expiryInterval } - - private func isValid(_ date : Date) -> Bool { + + private func isValid(_ date: Date) -> Bool { return date.timeIntervalSinceNow <= expiryInterval } - + public func get(_ query: Query) -> Future { switch query { case let query as KeyQuery: @@ -56,15 +57,17 @@ public class TimedCacheDataSource : GetDataSource, PutDataSource, DeleteDat } return dataSource.get(query).then { object in self.objects[query.key] = (object, Date()) - }.fail { error in - self.objects[query.key] = nil + }.fail { _ in + self.objects[query.key] = nil } default: - print("TimedCacheDataSource can't cache the result of the \(type(of: dataSource)).get call because \(type(of: query)) doesn't conform to KeyQuery.") + print( + "TimedCacheDataSource can't cache the result of the \(type(of: dataSource)).get call because \(type(of: query)) doesn't conform to KeyQuery." + ) return dataSource.get(query) } } - + public func getAll(_ query: Query) -> Future<[T]> { switch query { case let query as KeyQuery: @@ -73,31 +76,34 @@ public class TimedCacheDataSource : GetDataSource, PutDataSource, DeleteDat } return dataSource.getAll(query).then { array in self.arrays[query.key] = (array, Date()) - }.fail { error in - self.arrays[query.key] = nil + }.fail { _ in + self.arrays[query.key] = nil } default: - print("TimedCacheDataSource can't cache the result of the \(type(of: dataSource)).getAll call because \(type(of: query)) doesn't conform to KeyQuery.") + print("TimedCacheDataSource can't cache the result of the \(type(of: dataSource)).getAll call " + + "because \(type(of: query)) doesn't conform to KeyQuery.") return dataSource.getAll(query) } } - + @discardableResult public func put(_ value: T? = nil, in query: Query) -> Future { switch query { case let query as KeyQuery: return dataSource.put(value, in: query).then { object in - self.arrays[query.key] = nil + self.arrays[query.key] = nil self.objects[query.key] = (object, Date()) - }.fail { error in - self.objects[query.key] = nil + }.fail { _ in + self.objects[query.key] = nil } default: - print("TimedCacheDataSource can't cache the result of the \(type(of: dataSource)).put call because \(type(of: query)) doesn't conform to KeyQuery.") + print( + "TimedCacheDataSource can't cache the result of the \(type(of: dataSource)).put call because \(type(of: query)) doesn't conform to KeyQuery." + ) return dataSource.put(value, in: query) } } - + @discardableResult public func putAll(_ array: [T], in query: Query) -> Future<[T]> { switch query { @@ -105,15 +111,16 @@ public class TimedCacheDataSource : GetDataSource, PutDataSource, DeleteDat return dataSource.putAll(array, in: query).then { array in self.objects[query.key] = nil self.arrays[query.key] = (array, Date()) - }.fail { error in - self.arrays[query.key] = nil + }.fail { _ in + self.arrays[query.key] = nil } default: - print("TimedCacheDataSource can't cache the result of the \(type(of: dataSource)).putAll call because \(type(of: query)) doesn't conform to KeyQuery.") + print("TimedCacheDataSource can't cache the result of the \(type(of: dataSource)).putAll " + + "call because \(type(of: query)) doesn't conform to KeyQuery.") return dataSource.putAll(array, in: query) } } - + @discardableResult public func delete(_ query: Query) -> Future { switch query { @@ -123,14 +130,14 @@ public class TimedCacheDataSource : GetDataSource, PutDataSource, DeleteDat self.arrays[query.key] = nil } default: - print("TimedCacheDataSource can't cache the result of the \(type(of: dataSource)).delete call because \(type(of: query)) doesn't conform to KeyQuery.") + print("TimedCacheDataSource can't cache the result of the \(type(of: dataSource)).delete call " + + "because \(type(of: query)) doesn't conform to KeyQuery.") return dataSource.delete(query) } } - + @discardableResult public func deleteAll(_ query: Query) -> Future { delete(query) } } - diff --git a/Sources/Harmony/Data/DataSource/VoidDataSource.swift b/Sources/Harmony/Data/DataSource/VoidDataSource.swift index 910d0898..ed571ef3 100644 --- a/Sources/Harmony/Data/DataSource/VoidDataSource.swift +++ b/Sources/Harmony/Data/DataSource/VoidDataSource.swift @@ -19,39 +19,39 @@ import Foundation /// /// Void get data source implementation /// -public class VoidGetDataSource : GetDataSource { - public init() { } - public func get(_ query: Query) -> Future { return Future(CoreError.NotImplemented()) } - public func getAll(_ query: Query) -> Future<[T]> { return Future(CoreError.NotImplemented()) } +public class VoidGetDataSource: GetDataSource { + public init() {} + public func get(_: Query) -> Future { return Future(CoreError.NotImplemented()) } + public func getAll(_: Query) -> Future<[T]> { return Future(CoreError.NotImplemented()) } } /// /// Void put data source implementation /// -public class VoidPutDataSource : PutDataSource { - public init() { } - public func put(_ value: T?, in query: Query) -> Future { return Future(CoreError.NotImplemented()) } - public func putAll(_ array: [T], in query: Query) -> Future<[T]> { return Future(CoreError.NotImplemented()) } +public class VoidPutDataSource: PutDataSource { + public init() {} + public func put(_: T?, in _: Query) -> Future { return Future(CoreError.NotImplemented()) } + public func putAll(_: [T], in _: Query) -> Future<[T]> { return Future(CoreError.NotImplemented()) } } /// /// Void delete data source implementation /// -public class VoidDeleteDataSource : DeleteDataSource { - public init() { } - public func delete(_ query: Query) -> Future { return Future(CoreError.NotImplemented()) } - public func deleteAll(_ query: Query) -> Future { return Future(CoreError.NotImplemented()) } +public class VoidDeleteDataSource: DeleteDataSource { + public init() {} + public func delete(_: Query) -> Future { return Future(CoreError.NotImplemented()) } + public func deleteAll(_: Query) -> Future { return Future(CoreError.NotImplemented()) } } /// /// Void data source implementation /// -public class VoidDataSource : GetDataSource, PutDataSource, DeleteDataSource { - public init() { } - public func get(_ query: Query) -> Future { return Future(CoreError.NotImplemented()) } - public func getAll(_ query: Query) -> Future<[T]> { return Future(CoreError.NotImplemented()) } - public func put(_ value: T?, in query: Query) -> Future { return Future(CoreError.NotImplemented()) } - public func putAll(_ array: [T], in query: Query) -> Future<[T]> { return Future(CoreError.NotImplemented()) } - public func delete(_ query: Query) -> Future { return Future(CoreError.NotImplemented()) } - public func deleteAll(_ query: Query) -> Future { return Future(CoreError.NotImplemented()) } +public class VoidDataSource: GetDataSource, PutDataSource, DeleteDataSource { + public init() {} + public func get(_: Query) -> Future { return Future(CoreError.NotImplemented()) } + public func getAll(_: Query) -> Future<[T]> { return Future(CoreError.NotImplemented()) } + public func put(_: T?, in _: Query) -> Future { return Future(CoreError.NotImplemented()) } + public func putAll(_: [T], in _: Query) -> Future<[T]> { return Future(CoreError.NotImplemented()) } + public func delete(_: Query) -> Future { return Future(CoreError.NotImplemented()) } + public func deleteAll(_: Query) -> Future { return Future(CoreError.NotImplemented()) } } diff --git a/Sources/Harmony/Data/Mapper/DataSourceMapper.swift b/Sources/Harmony/Data/Mapper/DataSourceMapper.swift index 10367b20..b1562e18 100644 --- a/Sources/Harmony/Data/Mapper/DataSourceMapper.swift +++ b/Sources/Harmony/Data/Mapper/DataSourceMapper.swift @@ -17,54 +17,54 @@ import Foundation /// -/// This data source uses mappers to map objects and redirects them to the contained data source, acting as a simple "translator". +/// This data source uses mappers to map objects and redirects them to the contained data source, acting as a simple +// "translator". /// -public class GetDataSourceMapper : GetDataSource where D.T == In { - +public class GetDataSourceMapper: GetDataSource where D.T == In { public typealias T = Out - - private let dataSource : D - private let toOutMapper: Mapper - + + private let dataSource: D + private let toOutMapper: Mapper + /// Default initializer /// /// - Parameters: /// - dataSource: The contained dataSource /// - toOutMapper: In to Out mapper public init(dataSource: D, - toOutMapper: Mapper) { + toOutMapper: Mapper) { self.dataSource = dataSource self.toOutMapper = toOutMapper } - + public func get(_ query: Query) -> Future { return dataSource.get(query).map { value in - return try self.toOutMapper.map(value) + try self.toOutMapper.map(value) } } - + public func getAll(_ query: Query) -> Future<[Out]> { return dataSource.getAll(query).map { try self.toOutMapper.map($0) } } } extension GetDataSource { - func withMapping(_ toOutMapper: Mapper) -> GetDataSourceMapper { + func withMapping(_ toOutMapper: Mapper) -> GetDataSourceMapper { return GetDataSourceMapper(dataSource: self, toOutMapper: toOutMapper) } } /// -/// This data source uses mappers to map objects and redirects them to the contained data source, acting as a simple "translator". +/// This data source uses mappers to map objects and redirects them to the contained data source, acting as a simple +// "translator". /// -public class PutDataSourceMapper : PutDataSource where D.T == In { - +public class PutDataSourceMapper: PutDataSource where D.T == In { public typealias T = Out - - private let dataSource : D - private let toInMapper: Mapper - private let toOutMapper: Mapper - + + private let dataSource: D + private let toInMapper: Mapper + private let toOutMapper: Mapper + /// Default initializer /// /// - Parameters: @@ -72,47 +72,51 @@ public class PutDataSourceMapper : PutDataSource where /// - toInMapper: Out to In mapper /// - toOutMapper: In to Out mapper public init(dataSource: D, - toInMapper: Mapper, - toOutMapper: Mapper) { + toInMapper: Mapper, + toOutMapper: Mapper) { self.dataSource = dataSource self.toInMapper = toInMapper self.toOutMapper = toOutMapper } - + @discardableResult public func put(_ value: Out?, in query: Query) -> Future { return Future(future: { - var mapped : In? = nil + var mapped: In? if let value = value { mapped = try toInMapper.map(value) } return dataSource.put(mapped, in: query).map { try self.toOutMapper.map($0) } }) } - + @discardableResult public func putAll(_ array: [Out], in query: Query) -> Future<[Out]> { - return Future { dataSource.putAll(try toInMapper.map(array), in: query).map { try self.toOutMapper.map($0) } } + return Future { + dataSource.putAll(try toInMapper.map(array), in: query).map { try self.toOutMapper.map($0) } + } } } extension PutDataSource { - func withMapping(in toInMapper: Mapper, out toOutMapper: Mapper) -> PutDataSourceMapper { + func withMapping(in toInMapper: Mapper, + out toOutMapper: Mapper) -> PutDataSourceMapper { return PutDataSourceMapper(dataSource: self, toInMapper: toInMapper, toOutMapper: toOutMapper) } } /// -/// This data source uses mappers to map objects and redirects them to the contained data source, acting as a simple "translator". +/// This data source uses mappers to map objects and redirects them to the contained data source, acting as a simple +// "translator". /// -public class DataSourceMapper : GetDataSource, PutDataSource, DeleteDataSource where D:GetDataSource, D:PutDataSource, D:DeleteDataSource, D.T == In { - +public class DataSourceMapper: GetDataSource, PutDataSource, DeleteDataSource + where D: GetDataSource, D: PutDataSource, D: DeleteDataSource, D.T == In { public typealias T = Out - private let dataSource : D - private let toInMapper: Mapper - private let toOutMapper: Mapper - + private let dataSource: D + private let toInMapper: Mapper + private let toOutMapper: Mapper + /// Default initializer /// /// - Parameters: @@ -120,44 +124,46 @@ public class DataSourceMapper : GetDataSource, PutDataSource, DeleteD /// - toInMapper: Out to In mapper /// - toOutMapper: In to Out mapper public init(dataSource: D, - toInMapper: Mapper, - toOutMapper: Mapper) { + toInMapper: Mapper, + toOutMapper: Mapper) { self.dataSource = dataSource self.toInMapper = toInMapper self.toOutMapper = toOutMapper } - + public func get(_ query: Query) -> Future { return dataSource.get(query).map { value in - return try self.toOutMapper.map(value) + try self.toOutMapper.map(value) } } - + public func getAll(_ query: Query) -> Future<[Out]> { return dataSource.getAll(query).map { try self.toOutMapper.map($0) } } - + @discardableResult public func put(_ value: Out?, in query: Query) -> Future { return Future(future: { - var mapped : In? = nil + var mapped: In? if let value = value { mapped = try toInMapper.map(value) } return dataSource.put(mapped, in: query).map { try self.toOutMapper.map($0) } }) } - + @discardableResult public func putAll(_ array: [Out], in query: Query) -> Future<[Out]> { - return Future { dataSource.putAll(try toInMapper.map(array), in: query).map { try self.toOutMapper.map($0) } } + return Future { + dataSource.putAll(try toInMapper.map(array), in: query).map { try self.toOutMapper.map($0) } + } } - + @discardableResult public func delete(_ query: Query) -> Future { return dataSource.delete(query) } - + @discardableResult public func deleteAll(_ query: Query) -> Future { return dataSource.deleteAll(query) diff --git a/Sources/Harmony/Data/Mapper/Mapper+Codable.swift b/Sources/Harmony/Data/Mapper/Mapper+Codable.swift index ee30de9f..2c019e48 100644 --- a/Sources/Harmony/Data/Mapper/Mapper+Codable.swift +++ b/Sources/Harmony/Data/Mapper/Mapper+Codable.swift @@ -16,14 +16,14 @@ import Foundation -public class EncodableToDataMapper : Mapper where T: Encodable { - private let mapping : [String : String] - - public init(_ mapping : [String : String] = [:]) { +public class EncodableToDataMapper: Mapper where T: Encodable { + private let mapping: [String: String] + + public init(_ mapping: [String: String] = [:]) { self.mapping = mapping } - - public override func map(_ from: T) -> Data { + + override public func map(_ from: T) -> Data { let encoder = JSONEncoder() encoder.keyEncodingStrategy = .map(mapping) let data = try! encoder.encode(from) @@ -31,14 +31,14 @@ public class EncodableToDataMapper : Mapper where T: Encodable { } } -public class DataToDecodableMapper : Mapper where T: Decodable { - private let mapping : [String : String] - - public init(_ mapping : [String : String] = [:]) { +public class DataToDecodableMapper: Mapper where T: Decodable { + private let mapping: [String: String] + + public init(_ mapping: [String: String] = [:]) { self.mapping = mapping } - - public override func map(_ from: Data) -> T { + + override public func map(_ from: Data) -> T { let decoder = JSONDecoder() decoder.keyDecodingStrategy = .map(mapping) let value = try! decoder.decode(T.self, from: from) @@ -46,20 +46,19 @@ public class DataToDecodableMapper : Mapper where T: Decodable { } } -public class EncodableToDecodableMapper : Mapper where D: Decodable, E: Encodable { - - private let mapping : [String : String] - - public init(_ mapping : [String : String] = [:]) { +public class EncodableToDecodableMapper: Mapper where D: Decodable, E: Encodable { + private let mapping: [String: String] + + public init(_ mapping: [String: String] = [:]) { self.mapping = mapping } - - public override func map(_ from: E) -> D { + + override public func map(_ from: E) -> D { let encoder = JSONEncoder() // Encoding to a format that is readable by the decoder encoder.keyEncodingStrategy = .map(mapping) let data = try! encoder.encode(from) - + let decoder = JSONDecoder() // No need to customize the keyDecodingStrategy let value = try! decoder.decode(D.self, from: data) diff --git a/Sources/Harmony/Data/Mapper/Mapper+NSCoding.swift b/Sources/Harmony/Data/Mapper/Mapper+NSCoding.swift index 7a0bb2f5..4d796951 100644 --- a/Sources/Harmony/Data/Mapper/Mapper+NSCoding.swift +++ b/Sources/Harmony/Data/Mapper/Mapper+NSCoding.swift @@ -16,16 +16,16 @@ import Foundation -public class NSCodingToDataMapper : Mapper { - public override func map(_ from: T) -> Data { -// return try! NSKeyedArchiver.archivedData(withRootObject: from, requiringSecureCoding: false) +public class NSCodingToDataMapper: Mapper { + override public func map(_ from: T) -> Data { + // return try! NSKeyedArchiver.archivedData(withRootObject: from, requiringSecureCoding: false) return NSKeyedArchiver.archivedData(withRootObject: from) } } -public class DataToNSCodingMapper : Mapper { // where T:NSObject { - public override func map(_ from: Data) -> T { -// return try! NSKeyedUnarchiver.unarchivedObject(ofClass: T.self, from: from)! +public class DataToNSCodingMapper: Mapper { // where T:NSObject { + override public func map(_ from: Data) -> T { + // return try! NSKeyedUnarchiver.unarchivedObject(ofClass: T.self, from: from)! return NSKeyedUnarchiver.unarchiveObject(with: from) as! T } } diff --git a/Sources/Harmony/Data/Mapper/Mapper.swift b/Sources/Harmony/Data/Mapper/Mapper.swift index 3a785f8c..76831005 100644 --- a/Sources/Harmony/Data/Mapper/Mapper.swift +++ b/Sources/Harmony/Data/Mapper/Mapper.swift @@ -19,48 +19,47 @@ import Foundation /// /// Abstract class to map an object type to another object type /// -open class Mapper { - +open class Mapper { /// Default initializer - public init() { } - + public init() {} + /// Mapping method /// /// - Parameter from: The original object /// - Returns: The new mapped object /// - Throws: Mapping error. Typically of type CoreError.Failed - open func map(_ from: From) throws -> To { + open func map(_: From) throws -> To { fatalError("Undefined mapper. Class Mapper must be subclassed.") } } -extension Mapper { +public extension Mapper { /// Mapping method for arrays /// /// - Parameter array: An array of objects /// - Returns: An array of mapped objects /// - Throws: Mapping error. Typically of type CoreError.Failed - public func map(_ array: [From]) throws -> [To] { + func map(_ array: [From]) throws -> [To] { return try array.map { try map($0) } } - + // Mapping method for dictionaries /// /// - Parameter dictionary: A dictionary of key-value, where value is typed as "From" /// - Returns: A dictionary of mapped values /// - Throws: Mapping error. Typically of type CoreError.Failed - public func map(_ dictionary: [K:From]) throws -> [K:To] { + func map(_ dictionary: [K: From]) throws -> [K: To] { return try dictionary.mapValues { try map($0) } } } -extension Mapper where From: Hashable, To : Hashable { +public extension Mapper where From: Hashable, To: Hashable { /// Mapping method for sets /// /// - Parameter set: A set to be mapped /// - Returns: A mapped set /// - Throws: Mapping error. Typically of type CoreError.Failed - public func map(_ set: Set) throws -> Set { + func map(_ set: Set) throws -> Set { return Set(try set.map { try map($0) }) } } @@ -68,8 +67,8 @@ extension Mapper where From: Hashable, To : Hashable { /// /// VoidMapper throws a CoreError.NotImplemented /// -public class VoidMapper : Mapper { - public override func map(_ from: From) throws -> To { +public class VoidMapper: Mapper { + override public func map(_: From) throws -> To { throw CoreError.NotImplemented() } } @@ -77,17 +76,17 @@ public class VoidMapper : Mapper { /// /// A mock mapper returns the mock object when mapping any object. /// -public class MockMapper : Mapper { - private let mock : To - +public class MockMapper: Mapper { + private let mock: To + /// Default initializer /// /// - Parameter mock: The mock object to return when mapping. public init(_ mock: To) { self.mock = mock } - - public override func map(_ from: From) throws -> To { + + override public func map(_: From) throws -> To { return mock } } @@ -95,8 +94,8 @@ public class MockMapper : Mapper { /// /// IdentityMapper returns the same value /// -public class IdentityMapper : Mapper { - public override func map(_ from: T) throws -> T { +public class IdentityMapper: Mapper { + override public func map(_ from: T) throws -> T { return from } } @@ -104,10 +103,13 @@ public class IdentityMapper : Mapper { /// /// CastMapper tries to casts the input value to the mapped type. Otherwise, throws an CoreError.Failed error. /// -public class CastMapper : Mapper { - public override func map(_ from: From) throws -> To { +public class CastMapper: Mapper { + override public func map(_ from: From) throws -> To { guard let to = from as? To else { - throw CoreError.Failed("CastMapper failed to map an object of type \(type(of: from)) to \(String(describing: To.self))") + throw CoreError + .Failed( + "CastMapper failed to map an object of type \(type(of: from)) to \(String(describing: To.self))" + ) } return to } @@ -116,19 +118,18 @@ public class CastMapper : Mapper { /// /// Mapper defined by a closure /// -public class ClosureMapper : Mapper { - - private let closure : (From) throws -> To - +public class ClosureMapper: Mapper { + private let closure: (From) throws -> To + /// Default initializer /// /// - Parameter closure: The map closure - public init (_ closure : @escaping (From) throws -> To) { + public init(_ closure: @escaping (From) throws -> To) { self.closure = closure super.init() } - - public override func map(_ from: From) throws -> To { + + override public func map(_ from: From) throws -> To { return try closure(from) } } @@ -136,22 +137,21 @@ public class ClosureMapper : Mapper { /// /// Composes a two mappers A->B & B->C to create a mapper A->C. /// -public class ComposedMapper : Mapper { - - private let mapperAB : Mapper - private let mapperBC : Mapper - +public class ComposedMapper: Mapper { + private let mapperAB: Mapper + private let mapperBC: Mapper + /// Default initializer /// /// - Parameters: /// - mapperAB: Mapper A->B /// - mapperBC: Mapper B->C - public init(_ mapperAB: Mapper , _ mapperBC: Mapper) { + public init(_ mapperAB: Mapper, _ mapperBC: Mapper) { self.mapperAB = mapperAB self.mapperBC = mapperBC } - - public override func map(_ from: A) throws -> C { + + override public func map(_ from: A) throws -> C { return try mapperBC.map(try mapperAB.map(from)) } } @@ -161,8 +161,8 @@ public class ComposedMapper : Mapper { /// /// Converts Data into a base64 encoded string /// -public class DataToBase64StringMapper : Mapper { - public override func map(_ from: Data) throws -> String { +public class DataToBase64StringMapper: Mapper { + override public func map(_ from: Data) throws -> String { return from.base64EncodedString() } } @@ -171,8 +171,8 @@ public class DataToBase64StringMapper : Mapper { /// Converts base64 encoded string to data. /// Throws CoreError.Failed if string is invalid. /// -public class Base64StringToDataMapper : Mapper { - public override func map(_ from: String) throws -> Data { +public class Base64StringToDataMapper: Mapper { + override public func map(_ from: String) throws -> Data { guard let data = Data(base64Encoded: from) else { throw CoreError.Failed("Failed to map String into Data.") } @@ -183,8 +183,8 @@ public class Base64StringToDataMapper : Mapper { /// /// Converts Data into a hexadecimal encoded string /// -public class DataToHexStringMapper : Mapper { - public override func map(_ from: Data) throws -> String { +public class DataToHexStringMapper: Mapper { + override public func map(_ from: Data) throws -> String { return from.hexEncodedString() } } @@ -193,8 +193,8 @@ public class DataToHexStringMapper : Mapper { /// Converts hexadecimal encoded string to data. /// Throws CoreError.Failed if string is invalid. /// -public class HexStringToDataMapper : Mapper { - public override func map(_ from: String) throws -> Data { +public class HexStringToDataMapper: Mapper { + override public func map(_ from: String) throws -> Data { guard let data = Data(hexEncoded: from) else { throw CoreError.Failed("Failed to map String into Data.") } diff --git a/Sources/Harmony/Data/Operation/Operation.swift b/Sources/Harmony/Data/Operation/Operation.swift index 2e004b0f..6724c05f 100644 --- a/Sources/Harmony/Data/Operation/Operation.swift +++ b/Sources/Harmony/Data/Operation/Operation.swift @@ -17,13 +17,14 @@ import Foundation /// -/// An operation defines an abstraction on how data must be fetched (how and to which data source a query must be forwarded). +/// An operation defines an abstraction on how data must be fetched (how and to which data source a query must be +// forwarded). /// -public protocol Operation { } +public protocol Operation {} /// /// The default operation. All repository implementations must accept this operation. /// -public class DefaultOperation : Operation { - public init() { } +public class DefaultOperation: Operation { + public init() {} } diff --git a/Sources/Harmony/Data/Query/Query.swift b/Sources/Harmony/Data/Query/Query.swift index d8b728ce..bac85792 100644 --- a/Sources/Harmony/Data/Query/Query.swift +++ b/Sources/Harmony/Data/Query/Query.swift @@ -19,84 +19,83 @@ import Foundation /// /// Default query interface /// -public protocol Query { } +public protocol Query {} /// Protocol to use a query as a key for a key value interface -public protocol KeyQuery : Query { +public protocol KeyQuery: Query { /// The key associated to the query - var key : String { get } + var key: String { get } } /// Void query -public class VoidQuery : Query { - public init() { } +public class VoidQuery: Query { + public init() {} } /// A query by an id -open class IdQuery : Query, KeyQuery where T:Hashable { - public let id : T +open class IdQuery: Query, KeyQuery where T: Hashable { + public let id: T public init(_ id: T) { self.id = id } - public var key : String { - get { - switch T.self { - case is String.Type: - return id as! String - case is Int.Type: - return "\(id as! Int)" - default: - return "\(id.hashValue)" - } + + public var key: String { + switch T.self { + case is String.Type: + return id as! String + case is Int.Type: + return "\(id as! Int)" + default: + return "\(id.hashValue)" } } } /// A query by an array of Ids -open class IdsQuery : Query where T:Hashable { - public let ids : [T] +open class IdsQuery: Query where T: Hashable { + public let ids: [T] public init(_ ids: [T]) { self.ids = ids } } /// All objects query -public class AllObjectsQuery : Query, KeyQuery { - public init() { } +public class AllObjectsQuery: Query, KeyQuery { + public init() {} public var key: String { return "allObjects" } } /// Single object query -public class ObjectQuery : Query { - public let value : T - public init(_ value : T) { +public class ObjectQuery: Query { + public let value: T + public init(_ value: T) { self.value = value } } // ObjectQuery is KeyQuery when T is hashable -extension ObjectQuery : KeyQuery where T:Hashable { +extension ObjectQuery: KeyQuery where T: Hashable { public var key: String { return "\(value.hashValue)" } } /// Array based query -public class ObjectsQuery : Query { - public let values : [T] - public init(_ values : [T]) { +public class ObjectsQuery: Query { + public let values: [T] + public init(_ values: [T]) { self.values = values } } /// Abstract pagination query -open class PaginationQuery : Query { } +open class PaginationQuery: Query {} /// Pagination by offset and limit -open class PaginationOffsetLimitQuery : PaginationQuery { - public let offset : Int - public let limit : Int - public init(_ offset : Int, _ limit : Int) { +open class PaginationOffsetLimitQuery: PaginationQuery { + public let offset: Int + public let limit: Int + public init(_ offset: Int, _ limit: Int) { self.offset = offset self.limit = limit } diff --git a/Sources/Harmony/Data/Repository/AnyGetRepository.swift b/Sources/Harmony/Data/Repository/AnyGetRepository.swift index 09447ebf..46fea76f 100644 --- a/Sources/Harmony/Data/Repository/AnyGetRepository.swift +++ b/Sources/Harmony/Data/Repository/AnyGetRepository.swift @@ -19,10 +19,9 @@ import Foundation /// /// A type eraser for the GetRepository type, following Apple's Swift Standard Library approach. /// -public final class AnyGetRepository : GetRepository { - +public final class AnyGetRepository: GetRepository { private let box: GetRepositoryBoxBase - + /// Default initializer. /// /// - Parameters: @@ -30,21 +29,21 @@ public final class AnyGetRepository : GetRepository { public init(_ repository: R) where R.T == T { box = GetRepositoryBox(repository) } - + public func get(_ query: Query, operation: Operation) -> Future { return box.get(query, operation: operation) } - + public func getAll(_ query: Query, operation: Operation) -> Future<[T]> { return box.getAll(query, operation: operation) } } -extension GetRepository { +public extension GetRepository { /// Returns an AnyGetRepository abstraction of the current repository /// /// - Returns: An AnyGetRepository abstraction - public func asAnyGetRepository() -> AnyGetRepository { + func asAnyGetRepository() -> AnyGetRepository { if let repo = self as? AnyGetRepository { return repo } @@ -54,34 +53,34 @@ extension GetRepository { /// /// This is an abstract class. Do not use it. -/// GetRepository base class defining a generic type T (which is unrelated to the associated type of the GetRepository protocol) +/// GetRepository base class defining a generic type T (which is unrelated to the associated type of the +// GetRepository protocol) /// -internal class GetRepositoryBoxBase : GetRepository { - - func get(_ query: Query, operation: Operation) -> Future { +internal class GetRepositoryBoxBase: GetRepository { + func get(_: Query, operation _: Operation) -> Future { fatalError("This method is abstract.") } - - func getAll(_ query: Query, operation: Operation) -> Future<[T]> { + + func getAll(_: Query, operation _: Operation) -> Future<[T]> { fatalError("This method is abstract.") } } /// -/// A repository box, which has as generic type a GetRepository and links the GetRepositoryBoxBase type T as the Base.T type. +/// A repository box, which has as generic type a GetRepository and links the GetRepositoryBoxBase type T as the +// Base.T type. /// -internal class GetRepositoryBox : GetRepositoryBoxBase { - +internal class GetRepositoryBox: GetRepositoryBoxBase { private let base: Base - + init(_ base: Base) { self.base = base } - + override func get(_ query: Query, operation: Operation) -> Future { return base.get(query, operation: operation) } - + override func getAll(_ query: Query, operation: Operation) -> Future<[T]> { return base.getAll(query, operation: operation) } diff --git a/Sources/Harmony/Data/Repository/AnyPutRepository.swift b/Sources/Harmony/Data/Repository/AnyPutRepository.swift index eaa9ac60..f8f31143 100644 --- a/Sources/Harmony/Data/Repository/AnyPutRepository.swift +++ b/Sources/Harmony/Data/Repository/AnyPutRepository.swift @@ -19,10 +19,9 @@ import Foundation /// /// A type eraser for the PutRepository type, following Apple's Swift Standard Library approach. /// -public final class AnyPutRepository : PutRepository { - +public final class AnyPutRepository: PutRepository { private let box: PutRepositoryBoxBase - + /// Default initializer. /// /// - Parameters: @@ -30,21 +29,21 @@ public final class AnyPutRepository : PutRepository { public init(_ repository: R) where R.T == T { box = PutRepositoryBox(repository) } - + public func put(_ value: T?, in query: Query, operation: Operation) -> Future { return box.put(value, in: query, operation: operation) } - + public func putAll(_ array: [T], in query: Query, operation: Operation) -> Future<[T]> { return box.putAll(array, in: query, operation: operation) } } -extension PutRepository { +public extension PutRepository { /// Returns an AnyPutRepository abstraction of the current repository /// /// - Returns: An AnyPutRepository abstraction - public func asAnyPutRepository() -> AnyPutRepository { + func asAnyPutRepository() -> AnyPutRepository { if let repo = self as? AnyPutRepository { return repo } @@ -54,26 +53,26 @@ extension PutRepository { /// /// This is an abstract class. Do not use it. -/// PutRepository base class defining a generic type T (which is unrelated to the associated type of the PutRepository protocol) +/// PutRepository base class defining a generic type T (which is unrelated to the associated type of the +// PutRepository protocol) /// -internal class PutRepositoryBoxBase : PutRepository { - - func put(_ value: T?, in query: Query, operation: Operation) -> Future { +internal class PutRepositoryBoxBase: PutRepository { + func put(_: T?, in _: Query, operation _: Operation) -> Future { fatalError("This method is abstract.") } - - func putAll(_ array: [T], in query: Query, operation: Operation) -> Future<[T]> { + + func putAll(_: [T], in _: Query, operation _: Operation) -> Future<[T]> { fatalError("This method is abstract.") } } /// -/// A repository box, which has as generic type a PutRepository and links the PutRepositoryBoxBase type T as the Base.T type. +/// A repository box, which has as generic type a PutRepository and links the PutRepositoryBoxBase type T as the +// Base.T type. /// -internal class PutRepositoryBox : PutRepositoryBoxBase { - +internal class PutRepositoryBox: PutRepositoryBoxBase { private let base: Base - + init(_ base: Base) { self.base = base } @@ -81,7 +80,7 @@ internal class PutRepositoryBox : PutRepositoryBoxBase Future { return base.put(value, in: query, operation: operation) } - + override func putAll(_ array: [T], in query: Query, operation: Operation) -> Future<[T]> { return base.putAll(array, in: query, operation: operation) } diff --git a/Sources/Harmony/Data/Repository/AnyRepository.swift b/Sources/Harmony/Data/Repository/AnyRepository.swift index 9854a3d8..328b34ef 100644 --- a/Sources/Harmony/Data/Repository/AnyRepository.swift +++ b/Sources/Harmony/Data/Repository/AnyRepository.swift @@ -19,38 +19,37 @@ import Foundation /// /// A type eraser for the Repository type, following Apple's Swift Standard Library approach. /// -public final class AnyRepository : GetRepository, PutRepository, DeleteRepository { - +public final class AnyRepository: GetRepository, PutRepository, DeleteRepository { private let box: RepositoryBoxBase - + /// Default initializer. /// /// - Parameters: /// - repository: The repository to abstract - public init(_ repository: R) where R:GetRepository, R:PutRepository, R:DeleteRepository, R.T == T { + public init(_ repository: R) where R: GetRepository, R: PutRepository, R: DeleteRepository, R.T == T { box = RepositoryBox(repository) } - + public func get(_ query: Query, operation: Operation) -> Future { return box.get(query, operation: operation) } - + public func getAll(_ query: Query, operation: Operation) -> Future<[T]> { return box.getAll(query, operation: operation) } - + public func put(_ value: T?, in query: Query, operation: Operation) -> Future { return box.put(value, in: query, operation: operation) } - + public func putAll(_ array: [T], in query: Query, operation: Operation) -> Future<[T]> { return box.putAll(array, in: query, operation: operation) } - + public func delete(_ query: Query, operation: Operation) -> Future { return box.delete(query, operation: operation) } - + public func deleteAll(_ query: Query, operation: Operation) -> Future { return box.deleteAll(query, operation: operation) } @@ -58,66 +57,67 @@ public final class AnyRepository : GetRepository, PutRepository, DeleteRepos /// /// This is an abstract class. Do not use it. -/// Repository base class defining a generic type T (which is unrelated to the associated type of the Repository protocol) +/// Repository base class defining a generic type T (which is unrelated to the associated type of the Repository +// protocol) /// -internal class RepositoryBoxBase : GetRepository, PutRepository, DeleteRepository { - - func get(_ query: Query, operation: Operation) -> Future { +internal class RepositoryBoxBase: GetRepository, PutRepository, DeleteRepository { + func get(_: Query, operation _: Operation) -> Future { fatalError("This method is abstract.") } - - func getAll(_ query: Query, operation: Operation) -> Future<[T]> { + + func getAll(_: Query, operation _: Operation) -> Future<[T]> { fatalError("This method is abstract.") } - - func put(_ value: T?, in query: Query, operation: Operation) -> Future { + + func put(_: T?, in _: Query, operation _: Operation) -> Future { fatalError("This method is abstract.") } - - func putAll(_ array: [T], in query: Query, operation: Operation) -> Future<[T]> { + + func putAll(_: [T], in _: Query, operation _: Operation) -> Future<[T]> { fatalError("This method is abstract.") } - - func delete(_ query: Query, operation: Operation) -> Future { + + func delete(_: Query, operation _: Operation) -> Future { fatalError("This method is abstract.") } - - func deleteAll(_ query: Query, operation: Operation) -> Future { + + func deleteAll(_: Query, operation _: Operation) -> Future { fatalError("This method is abstract.") } } /// -/// A repository box, which has as generic type a Repository and links the RepositoryBoxBase type T as the Base.T type. +/// A repository box, which has as generic type a Repository and links the RepositoryBoxBase type T as the Base.T +// type. /// -internal class RepositoryBox : RepositoryBoxBase where Base:GetRepository, Base:PutRepository, Base:DeleteRepository { - +internal class RepositoryBox: RepositoryBoxBase where Base: GetRepository, Base: PutRepository, + Base: DeleteRepository { private let base: Base - + init(_ base: Base) { self.base = base } - + override func get(_ query: Query, operation: Operation) -> Future { return base.get(query, operation: operation) } - + override func getAll(_ query: Query, operation: Operation) -> Future<[T]> { return base.getAll(query, operation: operation) } - + override func put(_ value: T?, in query: Query, operation: Operation) -> Future { return base.put(value, in: query, operation: operation) } - + override func putAll(_ array: [T], in query: Query, operation: Operation) -> Future<[T]> { return base.putAll(array, in: query, operation: operation) } - + override func delete(_ query: Query, operation: Operation) -> Future { return base.delete(query, operation: operation) } - + override func deleteAll(_ query: Query, operation: Operation) -> Future { return base.deleteAll(query, operation: operation) } diff --git a/Sources/Harmony/Data/Repository/CacheRepository.swift b/Sources/Harmony/Data/Repository/CacheRepository.swift index dab34541..92955f84 100644 --- a/Sources/Harmony/Data/Repository/CacheRepository.swift +++ b/Sources/Harmony/Data/Repository/CacheRepository.swift @@ -17,15 +17,16 @@ import Foundation /// MainOperation: Data processing will only use the "main data source". -public class MainOperation : Operation { public init () { } } +public class MainOperation: Operation { public init() {} } -/// MainSyncOperation: Data processing will use the "main data source" and then sync result with the "cache data source". -public class MainSyncOperation : Operation { public init () { } } +/// MainSyncOperation: Data processing will use the "main data source" and then sync result with the "cache data +// source". +public class MainSyncOperation: Operation { public init() {} } /// CacheOperation: Data processing will only use the "cache data source". -public class CacheOperation : Operation { +public class CacheOperation: Operation { public let ignoreValidation: Bool - + /// Main initializer /// /// - Parameter ignoreValidation: A flag indicating if validation must be ignored. @@ -34,20 +35,20 @@ public class CacheOperation : Operation { } } - /// CacheSyncOperation: Data processing will use the "cache data source" and sync with the "main data source". /// /// If fallback returns true, then in case of network error, the repository will return the cached /// data independently of it's validity. -public class CacheSyncOperation : Operation { - public let fallback : (Error) -> Bool - +public class CacheSyncOperation: Operation { + public let fallback: (Error) -> Bool + /// Main initializer /// /// - Parameter fallback: The fallback closure containg the error. Default value returns false. public init(fallback: @escaping (Error) -> Bool = { _ in false }) { self.fallback = fallback } + /// Convenience initializer /// /// - Parameter fallback: The fallback behavior. @@ -59,17 +60,21 @@ public class CacheSyncOperation : Operation { /// /// Repository containing two data sources: a fast-access data source and a slow-access data source. /// -/// Using the `MainOperation`, `MainSyncOperation`, `CacheOperation` and `CacheSyncOperation`, the end user can access the fast or slow data source. -/// A typical example of usage is using this repository to alternate between a network-based data source (main data source) and a local cache storage data source (cache data source). +/// Using the `MainOperation`, `MainSyncOperation`, `CacheOperation` and `CacheSyncOperation`, the end user can +// access the fast or slow data source. +/// A typical example of usage is using this repository to alternate between a network-based data source (main data +// source) and a local cache storage data source (cache data source). /// -/// Note that by using the `DefaultOperation`, the CacheRepository will act as a regular cache: behaving as a `CacheSyncOperation` on GET methods and behaving as a `MainSyncOperation` on PUT and DELETE methods. +/// Note that by using the `DefaultOperation`, the CacheRepository will act as a regular cache: behaving as a +// `CacheSyncOperation` on GET methods and behaving as a `MainSyncOperation` on PUT and DELETE methods. /// -public class CacheRepository : GetRepository, PutRepository, DeleteRepository where M:GetDataSource, M:PutDataSource, M:DeleteDataSource, C:GetDataSource, C:PutDataSource, C:DeleteDataSource, M.T == T, C.T == T { - - private let main : M - private let cache : C - private let validator : ObjectValidation - +public class CacheRepository: GetRepository, PutRepository, DeleteRepository + where M: GetDataSource, M: PutDataSource, M: DeleteDataSource, C: GetDataSource, C: PutDataSource, + C: DeleteDataSource, M.T == T, C.T == T { + private let main: M + private let cache: C + private let validator: ObjectValidation + /// Main initializer /// /// - Parameters: @@ -81,7 +86,7 @@ public class CacheRepository : GetRepository, PutRepository, DeleteReposi self.cache = cache self.validator = validator } - + public func get(_ query: Query, operation: Operation) -> Future { switch operation { case is DefaultOperation: @@ -100,7 +105,7 @@ public class CacheRepository : GetRepository, PutRepository, DeleteReposi } case is MainSyncOperation: return main.get(query).flatMap { entity in - return self.cache.put(entity, in: query) + self.cache.put(entity, in: query) } case let op as CacheSyncOperation: var cachedValue: T! @@ -126,16 +131,16 @@ public class CacheRepository : GetRepository, PutRepository, DeleteReposi } else { return Future(error) } - } + } default: return Future(error) } - } + } default: operation.fatalError(.get, self) } } - + public func getAll(_ query: Query, operation: Operation) -> Future<[T]> { switch operation { case is DefaultOperation: @@ -154,7 +159,7 @@ public class CacheRepository : GetRepository, PutRepository, DeleteReposi } case is MainSyncOperation: return main.getAll(query).flatMap { entities in - return self.cache.putAll(entities, in: query) + self.cache.putAll(entities, in: query) } case let op as CacheSyncOperation: var cachedValues: [T]! @@ -180,16 +185,16 @@ public class CacheRepository : GetRepository, PutRepository, DeleteReposi } else { return Future(error) } - } + } default: return Future(error) } - } + } default: operation.fatalError(.getAll, self) } } - + @discardableResult public func put(_ value: T?, in query: Query, operation: Operation) -> Future { switch operation { @@ -201,17 +206,17 @@ public class CacheRepository : GetRepository, PutRepository, DeleteReposi return cache.put(value, in: query) case is MainSyncOperation: return main.put(value, in: query).flatMap { value in - return self.cache.put(value, in: query) + self.cache.put(value, in: query) } case is CacheSyncOperation: return cache.put(value, in: query).flatMap { value in - return self.main.put(value, in: query) + self.main.put(value, in: query) } default: operation.fatalError(.put, self) } } - + @discardableResult public func putAll(_ array: [T], in query: Query, operation: Operation) -> Future<[T]> { switch operation { @@ -223,17 +228,17 @@ public class CacheRepository : GetRepository, PutRepository, DeleteReposi return cache.putAll(array, in: query) case is MainSyncOperation: return main.putAll(array, in: query).flatMap { array in - return self.cache.putAll(array, in: query) + self.cache.putAll(array, in: query) } case is CacheSyncOperation: return cache.putAll(array, in: query).flatMap { array in - return self.main.putAll(array, in: query) + self.main.putAll(array, in: query) } default: operation.fatalError(.putAll, self) } } - + @discardableResult public func delete(_ query: Query, operation: Operation) -> Future { switch operation { @@ -255,7 +260,7 @@ public class CacheRepository : GetRepository, PutRepository, DeleteReposi operation.fatalError(.delete, self) } } - + @discardableResult public func deleteAll(_ query: Query, operation: Operation) -> Future { switch operation { diff --git a/Sources/Harmony/Data/Repository/Repository.swift b/Sources/Harmony/Data/Repository/Repository.swift index 99b38541..00a15091 100644 --- a/Sources/Harmony/Data/Repository/Repository.swift +++ b/Sources/Harmony/Data/Repository/Repository.swift @@ -16,18 +16,17 @@ import Foundation -public protocol Repository { } +public protocol Repository {} -public protocol GetRepository : Repository { - +public protocol GetRepository: Repository { associatedtype T - + /// Get a single method /// /// - Parameter query: An instance conforming to Query that encapsules the get query information /// - Returns: A Future of an optional repository's type func get(_ query: Query, operation: Operation) -> Future - + /// Main get method /// /// - Parameter query: An instance conforming to Query that encapsules the get query information @@ -35,86 +34,87 @@ public protocol GetRepository : Repository { func getAll(_ query: Query, operation: Operation) -> Future<[T]> } -extension GetRepository { - public func get(_ id: K, operation: Operation) -> Future where K:Hashable { +public extension GetRepository { + func get(_ id: K, operation: Operation) -> Future where K: Hashable { return get(IdQuery(id), operation: operation) } - - public func getAll(_ id: K, operation: Operation) -> Future<[T]> where K:Hashable { + + func getAll(_ id: K, operation: Operation) -> Future<[T]> where K: Hashable { return getAll(IdQuery(id), operation: operation) } } -public protocol PutRepository : Repository { - +public protocol PutRepository: Repository { associatedtype T - + /// Put by query method /// /// - Parameter query: An instance conforming to Query that encapsules the get query information - /// - Returns: A future of T type. Some data sources might add some extra fields after the put operation, e.g. id or timestamp fields. + /// - Returns: A future of T type. Some data sources might add some extra fields after the put operation, e.g. + // id or timestamp fields. @discardableResult func put(_ value: T?, in query: Query, operation: Operation) -> Future - + /// Put by query method /// /// - Parameter query: An instance conforming to Query that encapsules the get query information - /// - Returns: A future of T type. Some data sources might add some extra fields after the put operation, e.g. id or timestamp fields. + /// - Returns: A future of T type. Some data sources might add some extra fields after the put operation, e.g. + // id or timestamp fields. @discardableResult func putAll(_ array: [T], in query: Query, operation: Operation) -> Future<[T]> } -extension PutRepository { +public extension PutRepository { @discardableResult - public func put(_ value: T?, forId id: K, operation: Operation) -> Future where K:Hashable { + func put(_ value: T?, forId id: K, operation: Operation) -> Future where K: Hashable { return put(value, in: IdQuery(id), operation: operation) } - + @discardableResult - public func putAll(_ array: [T], forId id: K, operation: Operation) -> Future<[T]> where K:Hashable { + func putAll(_ array: [T], forId id: K, operation: Operation) -> Future<[T]> where K: Hashable { return putAll(array, in: IdQuery(id), operation: operation) } } -public protocol DeleteRepository : Repository { - +public protocol DeleteRepository: Repository { /// Delete by query method /// /// - Parameter query: An instance conforming to Query that encapusles the delete query information /// - Returns: A future of Void type. @discardableResult func delete(_ query: Query, operation: Operation) -> Future - + /// Delete by query method /// /// - Parameter query: An instance conforming to Query that encapusles the delete query information /// - Returns: A future of Void type. - @available(*, deprecated, message: "Use delete with AllObjectsQuery to remove all entries or with any other Query to remove one or more entries") + @available(*, deprecated, + message: "Use delete with AllObjectsQuery to remove all entries or with any other Query to remove one or more entries") @discardableResult func deleteAll(_ query: Query, operation: Operation) -> Future } -extension DeleteRepository { +public extension DeleteRepository { @discardableResult - public func delete(_ id: K, operation: Operation) -> Future where K:Hashable { + func delete(_ id: K, operation: Operation) -> Future where K: Hashable { return delete(IdQuery(id), operation: operation) } - + @available(*, deprecated, message: "Use delete instead") @discardableResult - public func deleteAll(_ id: K, operation: Operation) -> Future where K:Hashable { + func deleteAll(_ id: K, operation: Operation) -> Future where K: Hashable { return deleteAll(IdQuery(id), operation: operation) } } -public enum RepositoryCRUD : CustomStringConvertible { +public enum RepositoryCRUD: CustomStringConvertible { case get case getAll case put case putAll case delete case deleteAll - + public var description: String { switch self { case .get: return "get" @@ -127,8 +127,11 @@ public enum RepositoryCRUD : CustomStringConvertible { } } -extension Operation { - public func fatalError(_ method: RepositoryCRUD, _ origin: R) -> Never where R : Repository { - Swift.fatalError("Undefined operation \(String(describing: self)) for method \(method) on \(String(describing: type(of: origin)))") +public extension Operation { + func fatalError(_ method: RepositoryCRUD, _ origin: R) -> Never where R: Repository { + Swift + .fatalError( + "Undefined operation \(String(describing: self)) for method \(method) on \(String(describing: type(of: origin)))" + ) } } diff --git a/Sources/Harmony/Data/Repository/RepositoryAssembler.swift b/Sources/Harmony/Data/Repository/RepositoryAssembler.swift index b34f57ed..bc5f156b 100644 --- a/Sources/Harmony/Data/Repository/RepositoryAssembler.swift +++ b/Sources/Harmony/Data/Repository/RepositoryAssembler.swift @@ -19,12 +19,17 @@ import Foundation /// /// Assambles a CRUD repository into a single repository object /// -public final class RepositoryAssembler : GetRepository, PutRepository, DeleteRepository where Get.T == T, Put.T == T { - - private let getRepository : Get - private let putRepository : Put - private let deleteRepository : Delete - +public final class RepositoryAssembler< + Get: GetRepository, + Put: PutRepository, + Delete: DeleteRepository, + T +>: GetRepository, PutRepository, DeleteRepository + where Get.T == T, Put.T == T { + private let getRepository: Get + private let putRepository: Put + private let deleteRepository: Delete + /// Main initializer /// /// - Parameters: @@ -36,98 +41,98 @@ public final class RepositoryAssembler Future { return getRepository.get(query, operation: operation) } - + public func getAll(_ query: Query, operation: Operation) -> Future<[T]> { return getRepository.getAll(query, operation: operation) } - + @discardableResult public func put(_ value: T?, in query: Query, operation: Operation) -> Future { return putRepository.put(value, in: query, operation: operation) } - + @discardableResult public func putAll(_ array: [T], in query: Query, operation: Operation) -> Future<[T]> { return putRepository.putAll(array, in: query, operation: operation) } - + @discardableResult public func delete(_ query: Query, operation: Operation) -> Future { return deleteRepository.delete(query, operation: operation) } - + @discardableResult public func deleteAll(_ query: Query, operation: Operation) -> Future { return deleteRepository.deleteAll(query, operation: operation) } } -extension RepositoryAssembler where Get == Put, Get == Delete { +public extension RepositoryAssembler where Get == Put, Get == Delete { /// Initializer for a single Repository /// /// - Parameter repository: The repository - public convenience init(_ repository: Get) { + convenience init(_ repository: Get) { self.init(get: repository, put: repository, delete: repository) } } -extension RepositoryAssembler where Put == VoidPutRepository, Delete == VoidDeleteRepository { +public extension RepositoryAssembler where Put == VoidPutRepository, Delete == VoidDeleteRepository { /// Initializer for a single Repository /// /// - Parameter getRepository: The repository - public convenience init(get getRepository: Get) { + convenience init(get getRepository: Get) { self.init(get: getRepository, put: VoidPutRepository(), delete: VoidDeleteRepository()) } } -extension RepositoryAssembler where Get == VoidGetRepository, Delete == VoidDeleteRepository { +public extension RepositoryAssembler where Get == VoidGetRepository, Delete == VoidDeleteRepository { /// Initializer for a single Repository /// /// - Parameter putRepository: The repository - public convenience init(put putRepository: Put) { + convenience init(put putRepository: Put) { self.init(get: VoidGetRepository(), put: putRepository, delete: VoidDeleteRepository()) } } -extension RepositoryAssembler where Get == VoidGetRepository, Put == VoidPutRepository { +public extension RepositoryAssembler where Get == VoidGetRepository, Put == VoidPutRepository { /// Initializer for a single Repository /// /// - Parameter deleteRepository: The repository - public convenience init(delete deleteRepository: Delete) { + convenience init(delete deleteRepository: Delete) { self.init(get: VoidGetRepository(), put: VoidPutRepository(), delete: deleteRepository) } } -extension RepositoryAssembler where Get == VoidGetRepository { +public extension RepositoryAssembler where Get == VoidGetRepository { /// Initializer for a single Repository /// /// - Parameter putRepository: The repository /// - Parameter deleteRepository: The repository - public convenience init(put putRepository: Put, delete deleteRepository: Delete) { + convenience init(put putRepository: Put, delete deleteRepository: Delete) { self.init(get: VoidGetRepository(), put: putRepository, delete: deleteRepository) } } -extension RepositoryAssembler where Put == VoidPutRepository { +public extension RepositoryAssembler where Put == VoidPutRepository { /// Initializer for a single Repository /// /// - Parameter getRepository: The repository /// - Parameter deleteRepository: The repository - public convenience init(get getRepository: Get, delete deleteRepository: Delete) { + convenience init(get getRepository: Get, delete deleteRepository: Delete) { self.init(get: getRepository, put: VoidPutRepository(), delete: deleteRepository) } } -extension RepositoryAssembler where Delete == VoidDeleteRepository { +public extension RepositoryAssembler where Delete == VoidDeleteRepository { /// Initializer for a single Repository /// /// - Parameter getRepository: The repository /// - Parameter putRepository: The repository - public convenience init(get getRepository: Get, put putRepository: Put) { + convenience init(get getRepository: Get, put putRepository: Put) { self.init(get: getRepository, put: putRepository, delete: VoidDeleteRepository()) } } diff --git a/Sources/Harmony/Data/Repository/RepositoryMapper.swift b/Sources/Harmony/Data/Repository/RepositoryMapper.swift index 69fcf624..0860c7ba 100644 --- a/Sources/Harmony/Data/Repository/RepositoryMapper.swift +++ b/Sources/Harmony/Data/Repository/RepositoryMapper.swift @@ -17,54 +17,54 @@ import Foundation /// -/// This repository uses mappers to map objects and redirects them to the contained repository, acting as a simple "translator". +/// This repository uses mappers to map objects and redirects them to the contained repository, acting as a simple +// "translator". /// -public class GetRepositoryMapper : GetRepository where R.T == In { - +public class GetRepositoryMapper: GetRepository where R.T == In { public typealias T = Out - - private let repository : R - private let toOutMapper: Mapper - + + private let repository: R + private let toOutMapper: Mapper + /// Default initializer /// /// - Parameters: /// - repository: The contained repository /// - toOutMapper: In to Out mapper public init(repository: R, - toOutMapper: Mapper) { + toOutMapper: Mapper) { self.repository = repository self.toOutMapper = toOutMapper } - + public func get(_ query: Query, operation: Operation) -> Future { return repository.get(query, operation: operation).map { value in - return try self.toOutMapper.map(value) + try self.toOutMapper.map(value) } } - + public func getAll(_ query: Query, operation: Operation) -> Future<[Out]> { return repository.getAll(query, operation: operation).map { try self.toOutMapper.map($0) } } } extension GetRepository { - func withMapping(_ toOutMapper: Mapper) -> GetRepositoryMapper { + func withMapping(_ toOutMapper: Mapper) -> GetRepositoryMapper { return GetRepositoryMapper(repository: self, toOutMapper: toOutMapper) } } /// -/// This repository uses mappers to map objects and redirects them to the contained repository, acting as a simple "translator". +/// This repository uses mappers to map objects and redirects them to the contained repository, acting as a simple +// "translator". /// -public class PutRepositoryMapper : PutRepository where R.T == In { - +public class PutRepositoryMapper: PutRepository where R.T == In { public typealias T = Out - - private let repository : R - private let toInMapper: Mapper - private let toOutMapper: Mapper - + + private let repository: R + private let toInMapper: Mapper + private let toOutMapper: Mapper + /// Default initializer /// /// - Parameters: @@ -72,49 +72,52 @@ public class PutRepositoryMapper : PutRepository where /// - toInMapper: Out to In mapper /// - toOutMapper: In to Out mapper public init(repository: R, - toInMapper: Mapper, - toOutMapper: Mapper) { + toInMapper: Mapper, + toOutMapper: Mapper) { self.repository = repository self.toInMapper = toInMapper self.toOutMapper = toOutMapper } - + @discardableResult public func put(_ value: Out?, in query: Query, operation: Operation) -> Future { return Future(future: { - var mapped : In? = nil + var mapped: In? if let value = value { mapped = try toInMapper.map(value) } return repository.put(mapped, in: query, operation: operation).map { try self.toOutMapper.map($0) } }) } - + @discardableResult public func putAll(_ array: [Out], in query: Query, operation: Operation) -> Future<[Out]> { return Future { - return repository.putAll(try toInMapper.map(array), in: query, operation: operation).map { try self.toOutMapper.map($0) } + return repository.putAll(try toInMapper.map(array), in: query, operation: operation) + .map { try self.toOutMapper.map($0) } } } } extension PutRepository { - func withMapping(in toInMapper: Mapper, out toOutMapper: Mapper) -> PutRepositoryMapper { + func withMapping(in toInMapper: Mapper, + out toOutMapper: Mapper) -> PutRepositoryMapper { return PutRepositoryMapper(repository: self, toInMapper: toInMapper, toOutMapper: toOutMapper) } } /// -/// This repository uses mappers to map objects and redirects them to the contained repository, acting as a simple "translator". +/// This repository uses mappers to map objects and redirects them to the contained repository, acting as a simple +// "translator". /// -public class RepositoryMapper : GetRepository, PutRepository, DeleteRepository where R:GetRepository, R:PutRepository, R:DeleteRepository, R.T == In { - +public class RepositoryMapper: GetRepository, PutRepository, DeleteRepository + where R: GetRepository, R: PutRepository, R: DeleteRepository, R.T == In { public typealias T = Out - - private let repository : R - private let toInMapper: Mapper - private let toOutMapper: Mapper - + + private let repository: R + private let toInMapper: Mapper + private let toOutMapper: Mapper + /// Default initializer /// /// - Parameters: @@ -122,49 +125,49 @@ public class RepositoryMapper : GetRepository, PutRepository, DeleteR /// - toInMapper: Out to In mapper /// - toOutMapper: In to Out mapper public init(repository: R, - toInMapper: Mapper, - toOutMapper: Mapper) { + toInMapper: Mapper, + toOutMapper: Mapper) { self.repository = repository self.toInMapper = toInMapper self.toOutMapper = toOutMapper } - + public func get(_ query: Query, operation: Operation) -> Future { return repository.get(query, operation: operation).map { value in - return try self.toOutMapper.map(value) + try self.toOutMapper.map(value) } } - + public func getAll(_ query: Query, operation: Operation) -> Future<[Out]> { - return repository.getAll(query, operation: operation).map { try self.toOutMapper.map($0) } + return repository.getAll(query, operation: operation).map { try self.toOutMapper.map($0) } } - + @discardableResult public func put(_ value: Out?, in query: Query, operation: Operation) -> Future { return Future(future: { - var mapped : In? = nil + var mapped: In? if let value = value { mapped = try toInMapper.map(value) } return repository.put(mapped, in: query, operation: operation).map { try self.toOutMapper.map($0) } }) } - + @discardableResult public func putAll(_ array: [Out], in query: Query, operation: Operation) -> Future<[Out]> { return Future { - return repository.putAll(try toInMapper.map(array), in: query, operation: operation).map { try self.toOutMapper.map($0) } + return repository.putAll(try toInMapper.map(array), in: query, operation: operation) + .map { try self.toOutMapper.map($0) } } } - + @discardableResult public func delete(_ query: Query, operation: Operation) -> Future { return repository.delete(query, operation: operation) } - + @discardableResult public func deleteAll(_ query: Query, operation: Operation) -> Future { return repository.deleteAll(query, operation: operation) } } - diff --git a/Sources/Harmony/Data/Repository/RetryRepository.swift b/Sources/Harmony/Data/Repository/RetryRepository.swift index eed9de1f..fb1f5309 100644 --- a/Sources/Harmony/Data/Repository/RetryRepository.swift +++ b/Sources/Harmony/Data/Repository/RetryRepository.swift @@ -18,15 +18,16 @@ import Foundation private struct RetryRule { /// The amount of retries. If zero, the operation won't retry - public let count : Int + public let count: Int /// A closure defining the retry strategy. - public let retryIf : (Error) -> Bool - + public let retryIf: (Error) -> Bool + /// Validates if the current operation is enabled to retry /// /// - Parameter error: The incoming error /// - Returns: True if can retry, false otherwise. public func canRetry(_ error: Error) -> Bool { + // swiftlint:disable empty_count return count > 0 && retryIf(error) } @@ -34,37 +35,37 @@ private struct RetryRule { /// /// - Returns: The next retry rule public func next() -> RetryRule { - return RetryRule(count: count-1, retryIf: retryIf) + return RetryRule(count: count - 1, retryIf: retryIf) } } /// /// The retry operation /// -public class RetryOperation : Operation { - +public class RetryOperation: Operation { /// The retry rule - private let retryRule : RetryRule - + private let retryRule: RetryRule + /// The operation forwarded to the repository - public let operation : Operation - + public let operation: Operation + /// Main initializer /// /// - Parameters_ /// - operation: The operation that will be forwarded to the nested repository /// - count: The retry counter. Default value is 1. - /// - retryIf: A closure to evaluate each retry error. Return true to allow a retry, false otherwise. Default closure returns true. - public init(_ operation: Operation , count: Int = 1, retryIf: @escaping (Error) -> Bool = { _ in true }) { + /// - retryIf: A closure to evaluate each retry error. Return true to allow a retry, false otherwise. Default + // closure returns true. + public init(_ operation: Operation, count: Int = 1, retryIf: @escaping (Error) -> Bool = { _ in true }) { self.operation = operation - self.retryRule = RetryRule(count: count, retryIf: retryIf) + retryRule = RetryRule(count: count, retryIf: retryIf) } - - fileprivate init(_ operation: Operation, _ retryRule : RetryRule) { + + fileprivate init(_ operation: Operation, _ retryRule: RetryRule) { self.operation = operation self.retryRule = retryRule } - + /// Validates if the current operation is enabled to retry /// /// - Parameter error: The incoming error @@ -72,7 +73,7 @@ public class RetryOperation : Operation { public func canRetry(_ error: Error) -> Bool { return retryRule.canRetry(error) } - + /// Creates a new retry operation with the counter decremented by one. /// /// - Returns: A new retry operation @@ -85,23 +86,23 @@ public class RetryOperation : Operation { /// Repository adding a retry logic over an existing repository when an error happens. /// Incoming operations of a different type as RetryOperation will be forwarded to the contained repository. /// -public class RetryRepository : GetRepository, PutRepository, DeleteRepository where R:GetRepository, R:PutRepository, R:DeleteRepository, T == R.T { - +public class RetryRepository: GetRepository, PutRepository, DeleteRepository where R: GetRepository, + R: PutRepository, R: DeleteRepository, T == R.T { /// The nested repository - private let repository : R - + private let repository: R + /// The default retry rule - private let retryRule : RetryRule - + private let retryRule: RetryRule + /// Default initializer /// /// - Parameters: /// - repository: The contained repository public init(_ repository: R, retryCount: Int = 1, retryIf: @escaping (Error) -> Bool = { _ in true }) { self.repository = repository - self.retryRule = RetryRule(count: retryCount, retryIf: retryIf) + retryRule = RetryRule(count: retryCount, retryIf: retryIf) } - + public func get(_ query: Query, operation: Operation) -> Future { switch operation { case let retryOp as RetryOperation: @@ -116,7 +117,7 @@ public class RetryRepository : GetRepository, PutRepository, DeleteReposit return repository.get(query, operation: RetryOperation(operation, retryRule)) } } - + public func getAll(_ query: Query, operation: Operation) -> Future<[T]> { switch operation { case let retryOp as RetryOperation: @@ -131,7 +132,7 @@ public class RetryRepository : GetRepository, PutRepository, DeleteReposit return repository.getAll(query, operation: RetryOperation(operation, retryRule)) } } - + @discardableResult public func put(_ value: T?, in query: Query, operation: Operation) -> Future { switch operation { @@ -147,7 +148,7 @@ public class RetryRepository : GetRepository, PutRepository, DeleteReposit return repository.put(value, in: query, operation: RetryOperation(operation, retryRule)) } } - + @discardableResult public func putAll(_ array: [T], in query: Query, operation: Operation) -> Future<[T]> { switch operation { @@ -163,7 +164,7 @@ public class RetryRepository : GetRepository, PutRepository, DeleteReposit return repository.putAll(array, in: query, operation: RetryOperation(operation, retryRule)) } } - + @discardableResult public func delete(_ query: Query, operation: Operation) -> Future { switch operation { @@ -179,7 +180,7 @@ public class RetryRepository : GetRepository, PutRepository, DeleteReposit return repository.delete(query, operation: RetryOperation(operation, retryRule)) } } - + @discardableResult public func deleteAll(_ query: Query, operation: Operation) -> Future { switch operation { diff --git a/Sources/Harmony/Data/Repository/SingleDataSourceRepository.swift b/Sources/Harmony/Data/Repository/SingleDataSourceRepository.swift index 4249513c..8d2ed3f9 100644 --- a/Sources/Harmony/Data/Repository/SingleDataSourceRepository.swift +++ b/Sources/Harmony/Data/Repository/SingleDataSourceRepository.swift @@ -21,10 +21,9 @@ import Foundation /// All repository methods are directly forwarded to a single data source. /// Operation parameter is not used in any case. /// -public class SingleGetDataSourceRepository : GetRepository where D.T == T { - - private let dataSource : D - +public class SingleGetDataSourceRepository: GetRepository where D.T == T { + private let dataSource: D + /// Default initializer /// /// - Parameters: @@ -32,21 +31,21 @@ public class SingleGetDataSourceRepository : GetRepository w public init(_ dataSource: D) { self.dataSource = dataSource } - - public func get(_ query: Query, operation: Operation = DefaultOperation()) -> Future { + + public func get(_ query: Query, operation _: Operation = DefaultOperation()) -> Future { return dataSource.get(query) } - - public func getAll(_ query: Query, operation: Operation = DefaultOperation()) -> Future<[T]> { + + public func getAll(_ query: Query, operation _: Operation = DefaultOperation()) -> Future<[T]> { return dataSource.getAll(query) } } -extension GetDataSource { +public extension GetDataSource { /// Creates a single data source repository from a data source /// /// - Returns: A SingleGetDataSourceRepository repository - public func toGetRepository() -> SingleGetDataSourceRepository { + func toGetRepository() -> SingleGetDataSourceRepository { return SingleGetDataSourceRepository(self) } } @@ -56,10 +55,9 @@ extension GetDataSource { /// All repository methods are directly forwarded to a single data source. /// Operation parameter is not used in any case. /// -public class SinglePutDataSourceRepository : PutRepository where D.T == T { - - private let dataSource : D - +public class SinglePutDataSourceRepository: PutRepository where D.T == T { + private let dataSource: D + /// Default initializer /// /// - Parameters: @@ -67,23 +65,23 @@ public class SinglePutDataSourceRepository : PutRepository w public init(_ dataSource: D) { self.dataSource = dataSource } - + @discardableResult - public func put(_ value: T?, in query: Query, operation: Operation = DefaultOperation()) -> Future { + public func put(_ value: T?, in query: Query, operation _: Operation = DefaultOperation()) -> Future { return dataSource.put(value, in: query) } - + @discardableResult - public func putAll(_ array: [T], in query: Query, operation: Operation = DefaultOperation()) -> Future<[T]> { + public func putAll(_ array: [T], in query: Query, operation _: Operation = DefaultOperation()) -> Future<[T]> { return dataSource.putAll(array, in: query) } } -extension PutDataSource { +public extension PutDataSource { /// Creates a single data source repository from a data source /// /// - Returns: A SinglePutDataSourceRepository repository - public func toPutRepository() -> SinglePutDataSourceRepository { + func toPutRepository() -> SinglePutDataSourceRepository { return SinglePutDataSourceRepository(self) } } @@ -93,10 +91,9 @@ extension PutDataSource { /// All repository methods are directly forwarded to a single data source. /// Operation parameter is not used in any case. /// -public class SingleDeleteDataSourceRepository : DeleteRepository { - - private let dataSource : D - +public class SingleDeleteDataSourceRepository: DeleteRepository { + private let dataSource: D + /// Default initializer /// /// - Parameters: @@ -104,23 +101,23 @@ public class SingleDeleteDataSourceRepository : DeleteRepos public init(_ dataSource: D) { self.dataSource = dataSource } - + @discardableResult - public func delete(_ query: Query, operation: Operation = DefaultOperation()) -> Future { + public func delete(_ query: Query, operation _: Operation = DefaultOperation()) -> Future { return dataSource.delete(query) } - + @discardableResult - public func deleteAll(_ query: Query, operation: Operation = DefaultOperation()) -> Future { + public func deleteAll(_ query: Query, operation _: Operation = DefaultOperation()) -> Future { return dataSource.deleteAll(query) } } -extension DeleteDataSource { +public extension DeleteDataSource { /// Creates a single data source repository from a data source /// /// - Returns: A SingleDeleteDataSourceRepository repository - public func toDeleteRepository() -> SingleDeleteDataSourceRepository { + func toDeleteRepository() -> SingleDeleteDataSourceRepository { return SingleDeleteDataSourceRepository(self) } } @@ -130,10 +127,10 @@ extension DeleteDataSource { /// All repository methods are directly forwarded to a single data source. /// Operation parameter is not used in any case. /// -public class SingleDataSourceRepository : GetRepository, PutRepository, DeleteRepository where D:GetDataSource, D:PutDataSource, D:DeleteDataSource, D.T == T { - - private let dataSource : D - +public class SingleDataSourceRepository: GetRepository, PutRepository, DeleteRepository + where D: GetDataSource, D: PutDataSource, D: DeleteDataSource, D.T == T { + private let dataSource: D + /// Main initializer /// /// - Parameters: @@ -141,32 +138,32 @@ public class SingleDataSourceRepository : GetRepository, PutRepository, Del public init(_ dataSource: D) { self.dataSource = dataSource } - - public func get(_ query: Query, operation: Operation = DefaultOperation()) -> Future { + + public func get(_ query: Query, operation _: Operation = DefaultOperation()) -> Future { return dataSource.get(query) } - - public func getAll(_ query: Query, operation: Operation = DefaultOperation()) -> Future<[T]> { + + public func getAll(_ query: Query, operation _: Operation = DefaultOperation()) -> Future<[T]> { return dataSource.getAll(query) } - + @discardableResult - public func put(_ value: T?, in query: Query, operation: Operation = DefaultOperation()) -> Future { + public func put(_ value: T?, in query: Query, operation _: Operation = DefaultOperation()) -> Future { return dataSource.put(value, in: query) } - + @discardableResult - public func putAll(_ array: [T], in query: Query, operation: Operation = DefaultOperation()) -> Future<[T]> { + public func putAll(_ array: [T], in query: Query, operation _: Operation = DefaultOperation()) -> Future<[T]> { return dataSource.putAll(array, in: query) } - + @discardableResult - public func delete(_ query: Query, operation: Operation = DefaultOperation()) -> Future { + public func delete(_ query: Query, operation _: Operation = DefaultOperation()) -> Future { return dataSource.delete(query) } - + @discardableResult - public func deleteAll(_ query: Query, operation: Operation = DefaultOperation()) -> Future { + public func deleteAll(_ query: Query, operation _: Operation = DefaultOperation()) -> Future { return dataSource.deleteAll(query) } } diff --git a/Sources/Harmony/Data/Repository/VoidRepository.swift b/Sources/Harmony/Data/Repository/VoidRepository.swift index e4415b7b..7c55bcb7 100644 --- a/Sources/Harmony/Data/Repository/VoidRepository.swift +++ b/Sources/Harmony/Data/Repository/VoidRepository.swift @@ -19,39 +19,49 @@ import Foundation /// /// Void get repository implementation /// -public class VoidGetRepository : GetRepository { - public init() { } - public func get(_ query: Query, operation: Operation) -> Future { return Future(CoreError.NotImplemented()) } - public func getAll(_ query: Query, operation: Operation) -> Future<[T]> { return Future(CoreError.NotImplemented()) } +public class VoidGetRepository: GetRepository { + public init() {} + public func get(_: Query, operation _: Operation) -> Future { return Future(CoreError.NotImplemented()) } + public func getAll(_: Query, + operation _: Operation) -> Future<[T]> { return Future(CoreError.NotImplemented()) } } /// /// Void put repository implementation /// -public class VoidPutRepository : PutRepository { - public init() { } - public func put(_ value: T?, in query: Query, operation: Operation) -> Future { return Future(CoreError.NotImplemented()) } - public func putAll(_ array: [T], in query: Query, operation: Operation) -> Future<[T]> { return Future(CoreError.NotImplemented()) } +public class VoidPutRepository: PutRepository { + public init() {} + public func put(_: T?, in _: Query, + operation _: Operation) -> Future { return Future(CoreError.NotImplemented()) } + public func putAll(_: [T], in _: Query, + operation _: Operation) -> Future<[T]> { return Future(CoreError.NotImplemented()) } } /// /// Void delete repository implementation /// -public class VoidDeleteRepository : DeleteRepository { - public init() { } - public func delete(_ query: Query, operation: Operation) -> Future { return Future(CoreError.NotImplemented()) } - public func deleteAll(_ query: Query, operation: Operation) -> Future { return Future(CoreError.NotImplemented()) } +public class VoidDeleteRepository: DeleteRepository { + public init() {} + public func delete(_: Query, + operation _: Operation) -> Future { return Future(CoreError.NotImplemented()) } + public func deleteAll(_: Query, + operation _: Operation) -> Future { return Future(CoreError.NotImplemented()) } } /// /// Void repository implementation /// -public class VoidRepository : GetRepository, PutRepository, DeleteRepository { - public init() { } - public func get(_ query: Query, operation: Operation) -> Future { return Future(CoreError.NotImplemented()) } - public func getAll(_ query: Query, operation: Operation) -> Future<[T]> { return Future(CoreError.NotImplemented()) } - public func put(_ value: T?, in query: Query, operation: Operation) -> Future { return Future(CoreError.NotImplemented()) } - public func putAll(_ array: [T], in query: Query, operation: Operation) -> Future<[T]> { return Future(CoreError.NotImplemented()) } - public func delete(_ query: Query, operation: Operation) -> Future { return Future(CoreError.NotImplemented()) } - public func deleteAll(_ query: Query, operation: Operation) -> Future { return Future(CoreError.NotImplemented()) } +public class VoidRepository: GetRepository, PutRepository, DeleteRepository { + public init() {} + public func get(_: Query, operation _: Operation) -> Future { return Future(CoreError.NotImplemented()) } + public func getAll(_: Query, + operation _: Operation) -> Future<[T]> { return Future(CoreError.NotImplemented()) } + public func put(_: T?, in _: Query, + operation _: Operation) -> Future { return Future(CoreError.NotImplemented()) } + public func putAll(_: [T], in _: Query, + operation _: Operation) -> Future<[T]> { return Future(CoreError.NotImplemented()) } + public func delete(_: Query, + operation _: Operation) -> Future { return Future(CoreError.NotImplemented()) } + public func deleteAll(_: Query, + operation _: Operation) -> Future { return Future(CoreError.NotImplemented()) } } diff --git a/Sources/Harmony/Data/Validator/ObjectValidation.swift b/Sources/Harmony/Data/Validator/ObjectValidation.swift index 485ae92d..ca30a494 100644 --- a/Sources/Harmony/Data/Validator/ObjectValidation.swift +++ b/Sources/Harmony/Data/Validator/ObjectValidation.swift @@ -20,13 +20,12 @@ import Foundation public protocol ObjectValidation { /// Validates an object func isObjectValid(_ object: T) -> Bool - + /// Validates an array of objects func isArrayValid(_ objects: [T]) -> Bool } public extension ObjectValidation { - /// Validator method for arrays /// /// The validation process iterates over the array and is considered valid if all objects are valid. @@ -52,9 +51,9 @@ public extension ObjectValidation { /// /// Default implementation returns always true (all objects are valid) /// -public class DefaultObjectValidation : ObjectValidation { +public class DefaultObjectValidation: ObjectValidation { public init() {} - public func isObjectValid(_ object: T) -> Bool { + public func isObjectValid(_: T) -> Bool { return true } } diff --git a/Sources/Harmony/Data/Validator/VastraInvalidationStrategy.swift b/Sources/Harmony/Data/Validator/VastraInvalidationStrategy.swift index 9ea994bb..4ccfff72 100644 --- a/Sources/Harmony/Data/Validator/VastraInvalidationStrategy.swift +++ b/Sources/Harmony/Data/Validator/VastraInvalidationStrategy.swift @@ -17,7 +17,7 @@ import Foundation /// Object invalidation strategy data source. -public protocol VastraInvalidationStrategyDataSource { +public protocol VastraInvalidationStrategyDataSource { func isObjectInvalid() -> Bool } @@ -27,7 +27,7 @@ public protocol VastraInvalidationStrategyDataSource { /// /// - If the object is invalid, returns .Invalid /// - Otherwise returns .Unknown -public class VastraInvalidationStrategy : VastraStrategy { +public class VastraInvalidationStrategy: VastraStrategy { public init() {} public func isObjectValid(_ object: T) -> VastraStrategyResult { diff --git a/Sources/Harmony/Data/Validator/VastraReachabilityStrategy.swift b/Sources/Harmony/Data/Validator/VastraReachabilityStrategy.swift index 3a920a1e..1163a4f6 100644 --- a/Sources/Harmony/Data/Validator/VastraReachabilityStrategy.swift +++ b/Sources/Harmony/Data/Validator/VastraReachabilityStrategy.swift @@ -26,16 +26,16 @@ public protocol VastraReachability { /// - If internet is NOT available: the strategy considers all objects valid (.Valid) /// - If internet is available: the strategy doesn't decide if the object is valid (.Unknown) public class VastraReachabilityStrategy: VastraStrategy { - private let reachability : VastraReachability - + private let reachability: VastraReachability + /// Default initializer /// /// - Parameter reachability: An reachability object - public init(_ reachability : VastraReachability) { + public init(_ reachability: VastraReachability) { self.reachability = reachability } - - public func isObjectValid(_ object: T) -> VastraStrategyResult { + + public func isObjectValid(_: T) -> VastraStrategyResult { if reachability.isReachable() { return .unknown } else { diff --git a/Sources/Harmony/Data/Validator/VastraService.swift b/Sources/Harmony/Data/Validator/VastraService.swift index 0b22cca6..506e39ca 100644 --- a/Sources/Harmony/Data/Validator/VastraService.swift +++ b/Sources/Harmony/Data/Validator/VastraService.swift @@ -21,21 +21,22 @@ import Foundation /// /// The first strategy that validates or invalidates the object will define the validity of the object. /// -/// If any strategy succeeds to validate or invalidate the object, the validation will fail and the object will be considered as invalid. +/// If any strategy succeeds to validate or invalidate the object, the validation will fail and the object will be +// considered as invalid. public class VastraService { - /// Array of validation strategies. - public let strategies : [VastraStrategy] - + public let strategies: [VastraStrategy] + /// Main initializer /// /// - Parameter strategies: Array of validation strategies - public init(_ strategies : [VastraStrategy]) { + public init(_ strategies: [VastraStrategy]) { self.strategies = strategies } + /// If none of the strategies can resolve the state, this is the object validation state returned public var defaultValidationState = false - + /// Main validator method. /// /// The validation process iterates over the strategies array in order. diff --git a/Sources/Harmony/Data/Validator/VastraStrategy.swift b/Sources/Harmony/Data/Validator/VastraStrategy.swift index 56dd2306..d8d90f11 100644 --- a/Sources/Harmony/Data/Validator/VastraStrategy.swift +++ b/Sources/Harmony/Data/Validator/VastraStrategy.swift @@ -31,7 +31,6 @@ public enum VastraStrategyResult { /// Superclass strategy. Sublcasses will define custom strategies. /// public protocol VastraStrategy { - /// Strategy validation method. /// /// - Parameter object: The object to test diff --git a/Sources/Harmony/Data/Validator/VastraTimestampStrategy.swift b/Sources/Harmony/Data/Validator/VastraTimestampStrategy.swift index 60568c4a..6d24342c 100644 --- a/Sources/Harmony/Data/Validator/VastraTimestampStrategy.swift +++ b/Sources/Harmony/Data/Validator/VastraTimestampStrategy.swift @@ -23,7 +23,7 @@ public enum Time { case days(Int) case weeks(Int) case never - + fileprivate func toSeconds() -> TimeInterval? { switch self { case let .seconds(value): @@ -35,7 +35,7 @@ public enum Time { case let .days(value): return Double(value * 86400) case let .weeks(value): - return Double(value * 604800) + return Double(value * 604_800) case .never: return nil } @@ -44,31 +44,32 @@ public enum Time { /// Objects that will be validated using the VastraTimestampStrategy must implement this protocol. public protocol VastraTimestampStrategyDataSource { - var lastUpdate : Date? {get} + var lastUpdate: Date? { get } func expiryTimeInterval() -> Time } /// The strategy will measure the elapsed timeinterval from the `lastUpdate` to the current time. /// /// - If the elapsed timeinterval is smaller than expiryTimeInterval: The object will be considered valid (.Valid) -/// - If the elapsed timeinterval is greater than or equal to expiryTimeInterval: The object will be considered invalid (.Invalid) +/// - If the elapsed timeinterval is greater than or equal to expiryTimeInterval: The object will be considered +// invalid (.Invalid) /// - If there is no a lastUpdate date, the strategy won't decide object validity (.Unknown) public class VastraTimestampStrategy: VastraStrategy { - public init() { } - + public init() {} + public func isObjectValid(_ object: T) -> VastraStrategyResult { let lastUpdate = (object as! VastraTimestampStrategyDataSource).lastUpdate if lastUpdate == nil { return .unknown } - + let expiryTime = (object as! VastraTimestampStrategyDataSource).expiryTimeInterval() let diff = Date().timeIntervalSince(lastUpdate!) - - guard let seconds = expiryTime.toSeconds() else { + + guard let seconds = expiryTime.toSeconds() else { return .invalid } - + if diff < seconds { return .valid } else { diff --git a/Sources/Harmony/Domain/Interactor/Interactor.swift b/Sources/Harmony/Domain/Interactor/Interactor.swift index f939412c..f76a6abe 100644 --- a/Sources/Harmony/Domain/Interactor/Interactor.swift +++ b/Sources/Harmony/Domain/Interactor/Interactor.swift @@ -17,5 +17,4 @@ import Foundation // Namespace definition -public struct Interactor { } - +public struct Interactor {} diff --git a/Sources/Harmony/Domain/Interactor/InteractorDelete.swift b/Sources/Harmony/Domain/Interactor/InteractorDelete.swift index 9417a6d5..90c236d7 100644 --- a/Sources/Harmony/Domain/Interactor/InteractorDelete.swift +++ b/Sources/Harmony/Domain/Interactor/InteractorDelete.swift @@ -21,111 +21,115 @@ extension Interactor { /// Generic delete object by query interactor /// open class DeleteByQuery { - - private let executor : Executor + private let executor: Executor private let repository: DeleteRepository - + public required init(_ executor: Executor, _ repository: DeleteRepository) { self.executor = executor self.repository = repository } - + @discardableResult - open func execute(_ query: Query, _ operation: Operation = DefaultOperation(), in executor: Executor? = nil) -> Future { + open func execute(_ query: Query, _ operation: Operation = DefaultOperation(), + in executor: Executor? = nil) -> Future { let executor = executor ?? self.executor return executor.submit { resolver in resolver.set(self.repository.delete(query, operation: operation)) } } - + @discardableResult - open func execute(_ id: K, _ operation: Operation = DefaultOperation(), in executor: Executor? = nil) -> Future where K:Hashable { + open func execute(_ id: K, _ operation: Operation = DefaultOperation(), + in executor: Executor? = nil) -> Future where K: Hashable { let executor = executor ?? self.executor return executor.submit { resolver in resolver.set(self.repository.delete(id, operation: operation)) } } } - + /// /// Generic delete object interactor /// open class Delete { - - private let query : Query - private let executor : Executor + private let query: Query + private let executor: Executor private let repository: DeleteRepository - + public required init(_ executor: Executor, _ repository: DeleteRepository, _ query: Query) { self.query = query self.executor = executor self.repository = repository } - - public convenience init(_ executor: Executor, _ repository: DeleteRepository, _ id: K) where K:Hashable { + + public convenience init(_ executor: Executor, _ repository: DeleteRepository, _ id: K) + where K: Hashable { self.init(executor, repository, IdQuery(id)) } - + @discardableResult - open func execute(_ operation: Operation = DefaultOperation(), in executor: Executor? = nil) -> Future { + open func execute(_ operation: Operation = DefaultOperation(), + in executor: Executor? = nil) -> Future { let executor = executor ?? self.executor return executor.submit { resolver in resolver.set(self.repository.delete(self.query, operation: operation)) } } } - + /// /// Generic delete objects interactor /// open class DeleteAllByQuery { - - private let executor : Executor + private let executor: Executor private let repository: DeleteRepository - + public required init(_ executor: Executor, _ repository: DeleteRepository) { self.executor = executor self.repository = repository } - + @discardableResult - open func execute(_ query: Query, _ operation: Operation = DefaultOperation(), in executor: Executor? = nil) -> Future { + open func execute(_ query: Query, _ operation: Operation = DefaultOperation(), + in executor: Executor? = nil) -> Future { let executor = executor ?? self.executor return executor.submit { resolver in resolver.set(self.repository.deleteAll(query, operation: operation)) } } - + @discardableResult - open func execute(_ id: K, _ operation: Operation = DefaultOperation(), in executor: Executor? = nil) -> Future where K:Hashable { + open func execute(_ id: K, _ operation: Operation = DefaultOperation(), + in executor: Executor? = nil) -> Future where K: Hashable { let executor = executor ?? self.executor return executor.submit { resolver in resolver.set(self.repository.deleteAll(id, operation: operation)) } } } - + /// /// Generic delete objects interactor /// open class DeleteAll { - - private let query : Query - private let executor : Executor + private let query: Query + private let executor: Executor private let repository: DeleteRepository - + public required init(_ executor: Executor, _ repository: DeleteRepository, _ query: Query) { self.query = query self.executor = executor self.repository = repository } - - public convenience init(_ executor: Executor, _ repository: DeleteRepository, _ id: K) where K:Hashable { + + public convenience init(_ executor: Executor, _ repository: DeleteRepository, _ id: K) + where K: Hashable { self.init(executor, repository, IdQuery(id)) } - + @discardableResult - open func execute(_ operation: Operation = DefaultOperation(), in executor: Executor? = nil) -> Future { + open func execute(_ operation: Operation = DefaultOperation(), + in executor: Executor? = nil) -> Future { let executor = executor ?? self.executor return executor.submit { resolver in resolver.set(self.repository.deleteAll(self.query, operation: operation)) @@ -134,28 +138,28 @@ extension Interactor { } } -extension DeleteRepository { - public func toDeleteByQueryInteractor(_ executor: Executor) -> Interactor.DeleteByQuery { +public extension DeleteRepository { + func toDeleteByQueryInteractor(_ executor: Executor) -> Interactor.DeleteByQuery { return Interactor.DeleteByQuery(executor, self) } - - public func toDeleteInteractor(_ executor: Executor, _ query : Query) -> Interactor.Delete { + + func toDeleteInteractor(_ executor: Executor, _ query: Query) -> Interactor.Delete { return Interactor.Delete(executor, self, query) } - - public func toDeleteInteractor(_ executor: Executor, _ id : K) -> Interactor.Delete where K:Hashable { + + func toDeleteInteractor(_ executor: Executor, _ id: K) -> Interactor.Delete where K: Hashable { return Interactor.Delete(executor, self, id) } - - public func toDeleteAllByQueryInteractor(_ executor: Executor) -> Interactor.DeleteAllByQuery { + + func toDeleteAllByQueryInteractor(_ executor: Executor) -> Interactor.DeleteAllByQuery { return Interactor.DeleteAllByQuery(executor, self) } - - public func toDeleteAllInteractor(_ executor: Executor, _ query : Query) -> Interactor.DeleteAll { + + func toDeleteAllInteractor(_ executor: Executor, _ query: Query) -> Interactor.DeleteAll { return Interactor.DeleteAll(executor, self, query) } - - public func toDeleteAllInteractor(_ executor: Executor, _ id : K) -> Interactor.DeleteAll where K:Hashable { + + func toDeleteAllInteractor(_ executor: Executor, _ id: K) -> Interactor.DeleteAll where K: Hashable { return Interactor.DeleteAll(executor, self, id) } } diff --git a/Sources/Harmony/Domain/Interactor/InteractorGet.swift b/Sources/Harmony/Domain/Interactor/InteractorGet.swift index ed3adae5..fe31a71a 100644 --- a/Sources/Harmony/Domain/Interactor/InteractorGet.swift +++ b/Sources/Harmony/Domain/Interactor/InteractorGet.swift @@ -17,53 +17,60 @@ import Foundation extension Interactor { + // swiftlint:disable type_name + /// /// Generic get object interactor /// open class GetByQuery { - - private let executor : Executor + private let executor: Executor private let repository: AnyGetRepository - - public required init(_ executor: Executor, _ repository: R) where R:GetRepository, R.T == T { + + public required init(_ executor: Executor, _ repository: R) where R: GetRepository, R.T == T { self.executor = executor self.repository = repository.asAnyGetRepository() } - - open func execute(_ query: Query = VoidQuery(), _ operation: Operation = DefaultOperation(), in executor: Executor? = nil) -> Future { + + open func execute( + _ query: Query = VoidQuery(), + _ operation: Operation = DefaultOperation(), + in executor: Executor? = nil + ) -> Future { let executor = executor ?? self.executor return executor.submit { resolver in resolver.set(self.repository.get(query, operation: operation)) } } - - open func execute(_ id: K, _ operation: Operation = DefaultOperation(), in executor: Executor? = nil) -> Future where K:Hashable { + + open func execute(_ id: K, _ operation: Operation = DefaultOperation(), + in executor: Executor? = nil) -> Future where K: Hashable { let executor = executor ?? self.executor return executor.submit { resolver in resolver.set(self.repository.get(id, operation: operation)) } } } - + /// /// Generic get object interactor with a prefilled query /// open class Get { - - private let query : Query - private let executor : Executor + private let query: Query + private let executor: Executor private let repository: AnyGetRepository - - public required init(_ executor: Executor, _ repository: R, _ query: Query) where R : GetRepository, R.T == T { + + public required init(_ executor: Executor, _ repository: R, _ query: Query) where R: GetRepository, + R.T == T { self.query = query self.executor = executor self.repository = repository.asAnyGetRepository() } - - public convenience init(_ executor: Executor, _ repository: R, _ id: K) where K:Hashable, R:GetRepository, R.T == T { + + public convenience init(_ executor: Executor, _ repository: R, _ id: K) where K: Hashable, + R: GetRepository, R.T == T { self.init(executor, repository, IdQuery(id)) } - + open func execute(_ operation: Operation = DefaultOperation(), in executor: Executor? = nil) -> Future { let executor = executor ?? self.executor return executor.submit { resolver in @@ -71,55 +78,61 @@ extension Interactor { } } } - + /// /// Generic get objects interactor /// open class GetAllByQuery { - - private let executor : Executor + private let executor: Executor private let repository: AnyGetRepository - - public required init(_ executor: Executor, _ repository: R) where R : GetRepository, R.T == T { + + public required init(_ executor: Executor, _ repository: R) where R: GetRepository, R.T == T { self.executor = executor self.repository = repository.asAnyGetRepository() } - - open func execute(_ query: Query = AllObjectsQuery(), _ operation: Operation = DefaultOperation(), in executor: Executor? = nil) -> Future<[T]> { + + open func execute( + _ query: Query = AllObjectsQuery(), + _ operation: Operation = DefaultOperation(), + in executor: Executor? = nil + ) -> Future<[T]> { let executor = executor ?? self.executor return executor.submit { resolver in resolver.set(self.repository.getAll(query, operation: operation)) } } - - open func execute(_ id: K, _ operation: Operation = DefaultOperation(), in executor: Executor? = nil) -> Future<[T]> where K:Hashable { + + open func execute(_ id: K, _ operation: Operation = DefaultOperation(), + in executor: Executor? = nil) -> Future<[T]> where K: Hashable { let executor = executor ?? self.executor return executor.submit { resolver in resolver.set(self.repository.getAll(id, operation: operation)) } } } - + /// /// Generic get all objects interactor /// open class GetAll { - - private let query : Query - private let executor : Executor + private let query: Query + private let executor: Executor private let repository: AnyGetRepository - - public required init(_ executor: Executor, _ repository: R, _ query: Query) where R : GetRepository, R.T == T { + + public required init(_ executor: Executor, _ repository: R, _ query: Query) where R: GetRepository, + R.T == T { self.query = query self.executor = executor self.repository = repository.asAnyGetRepository() } - - public convenience init(_ executor: Executor, _ repository: R, _ id: K) where K:Hashable, R:GetRepository, R.T == T { + + public convenience init(_ executor: Executor, _ repository: R, _ id: K) where K: Hashable, + R: GetRepository, R.T == T { self.init(executor, repository, IdQuery(id)) } - - open func execute(_ operation: Operation = DefaultOperation(), in executor: Executor? = nil) -> Future<[T]> { + + open func execute(_ operation: Operation = DefaultOperation(), + in executor: Executor? = nil) -> Future<[T]> { let executor = executor ?? self.executor return executor.submit { resolver in resolver.set(self.repository.getAll(self.query, operation: operation)) @@ -128,28 +141,28 @@ extension Interactor { } } -extension GetRepository { - public func toGetByQueryInteractor(_ executor: Executor) -> Interactor.GetByQuery { +public extension GetRepository { + func toGetByQueryInteractor(_ executor: Executor) -> Interactor.GetByQuery { return Interactor.GetByQuery(executor, self) } - - public func toGetInteractor(_ executor: Executor, _ query : Query) -> Interactor.Get { + + func toGetInteractor(_ executor: Executor, _ query: Query) -> Interactor.Get { return Interactor.Get(executor, self, query) } - - public func toGetInteractor(_ executor: Executor, _ id : K) -> Interactor.Get where K:Hashable { + + func toGetInteractor(_ executor: Executor, _ id: K) -> Interactor.Get where K: Hashable { return Interactor.Get(executor, self, id) } - - public func toGetAllByQueryInteractor(_ executor: Executor) -> Interactor.GetAllByQuery { + + func toGetAllByQueryInteractor(_ executor: Executor) -> Interactor.GetAllByQuery { return Interactor.GetAllByQuery(executor, self) } - - public func toGetAllInteractor(_ executor: Executor, _ query : Query) -> Interactor.GetAll { + + func toGetAllInteractor(_ executor: Executor, _ query: Query) -> Interactor.GetAll { return Interactor.GetAll(executor, self, query) } - - public func toGetAllInteractor(_ executor: Executor, _ id : K) -> Interactor.GetAll where K:Hashable { + + func toGetAllInteractor(_ executor: Executor, _ id: K) -> Interactor.GetAll where K: Hashable { return Interactor.GetAll(executor, self, id) } } diff --git a/Sources/Harmony/Domain/Interactor/InteractorPut.swift b/Sources/Harmony/Domain/Interactor/InteractorPut.swift index 7c3f64bb..6cf7f78e 100644 --- a/Sources/Harmony/Domain/Interactor/InteractorPut.swift +++ b/Sources/Harmony/Domain/Interactor/InteractorPut.swift @@ -17,115 +17,137 @@ import Foundation extension Interactor { + // swiftlint:disable type_name + /// /// Generic put object by query interactor /// open class PutByQuery { - - private let executor : Executor + private let executor: Executor private let repository: AnyPutRepository - - public required init(_ executor: Executor, _ repository: R) where R:PutRepository, R.T == T { + + public required init(_ executor: Executor, _ repository: R) where R: PutRepository, R.T == T { self.executor = executor self.repository = repository.asAnyPutRepository() } - + @discardableResult - open func execute(_ value: T? = nil, query: Query = VoidQuery(), _ operation: Operation = DefaultOperation(), in executor: Executor? = nil) -> Future { + open func execute( + _ value: T? = nil, + query: Query = VoidQuery(), + _ operation: Operation = DefaultOperation(), + in executor: Executor? = nil + ) -> Future { let executor = executor ?? self.executor return executor.submit { resolver in resolver.set(self.repository.put(value, in: query, operation: operation)) } } - + @discardableResult - open func execute(_ value: T?, forId id: K, _ operation: Operation = DefaultOperation(), in executor: Executor? = nil) -> Future where K:Hashable { + open func execute(_ value: T?, forId id: K, _ operation: Operation = DefaultOperation(), + in executor: Executor? = nil) -> Future where K: Hashable { let executor = executor ?? self.executor return executor.submit { resolver in resolver.set(self.repository.put(value, forId: id, operation: operation)) } } } - + /// /// Generic put object interactor /// open class Put { - - private let query : Query - private let executor : Executor + private let query: Query + private let executor: Executor private let repository: AnyPutRepository - - public required init(_ executor: Executor, _ repository: R, _ query: Query) where R:PutRepository, R.T == T { + + public required init(_ executor: Executor, _ repository: R, _ query: Query) where R: PutRepository, + R.T == T { self.query = query self.executor = executor self.repository = repository.asAnyPutRepository() } - - public convenience init(_ executor: Executor, _ repository: R, _ id: K) where K:Hashable, R:PutRepository, R.T == T { + + public convenience init(_ executor: Executor, _ repository: R, _ id: K) where K: Hashable, + R: PutRepository, R.T == T { self.init(executor, repository, IdQuery(id)) } - + @discardableResult - open func execute(_ value: T? = nil, _ operation: Operation = DefaultOperation(), in executor: Executor? = nil) -> Future { + open func execute( + _ value: T? = nil, + _ operation: Operation = DefaultOperation(), + in executor: Executor? = nil + ) -> Future { let executor = executor ?? self.executor return executor.submit { resolver in resolver.set(self.repository.put(value, in: self.query, operation: operation)) } } } - + /// /// Generic put objects by query interactor /// open class PutAllByQuery { - - private let executor : Executor + private let executor: Executor private let repository: AnyPutRepository - - public required init(_ executor: Executor, _ repository: R) where R : PutRepository, R.T == T { + + public required init(_ executor: Executor, _ repository: R) where R: PutRepository, R.T == T { self.executor = executor self.repository = repository.asAnyPutRepository() } - + @discardableResult - open func execute(_ array: [T] = [], query: Query = VoidQuery(), _ operation: Operation = DefaultOperation(), in executor: Executor? = nil) -> Future<[T]> { + open func execute( + _ array: [T] = [], + query: Query = VoidQuery(), + _ operation: Operation = DefaultOperation(), + in executor: Executor? = nil + ) -> Future<[T]> { let executor = executor ?? self.executor return executor.submit { resolver in resolver.set(self.repository.putAll(array, in: query, operation: operation)) } } - + @discardableResult - open func execute(_ array: [T] = [], forId id: K, _ operation: Operation = DefaultOperation(), in executor: Executor? = nil) -> Future<[T]> where K:Hashable { + open func execute(_ array: [T] = [], forId id: K, _ operation: Operation = DefaultOperation(), + in executor: Executor? = nil) -> Future<[T]> where K: Hashable { let executor = executor ?? self.executor return executor.submit { resolver in resolver.set(self.repository.putAll(array, forId: id, operation: operation)) } } } - + /// /// Generic put objects interactor /// open class PutAll { - - private let query : Query - private let executor : Executor + private let query: Query + private let executor: Executor private let repository: AnyPutRepository - - public required init(_ executor: Executor, _ repository: R, _ query: Query) where R:PutRepository, R.T == T { + + public required init(_ executor: Executor, _ repository: R, _ query: Query) where R: PutRepository, + R.T == T { self.query = query self.executor = executor self.repository = repository.asAnyPutRepository() } - - public convenience init(_ executor: Executor, _ repository: R, _ id: K) where K:Hashable, R:PutRepository, R.T == T { + + public convenience init(_ executor: Executor, _ repository: R, _ id: K) where K: Hashable, + R: PutRepository, R.T == T { self.init(executor, repository, IdQuery(id)) } - + @discardableResult - open func execute(_ array: [T] = [], _ operation: Operation = DefaultOperation(), in executor: Executor? = nil) -> Future<[T]> { + open func execute( + _ array: [T] = [], + _ operation: Operation = DefaultOperation(), + in executor: Executor? = nil + ) -> Future<[T]> { let executor = executor ?? self.executor return executor.submit { resolver in resolver.set(self.repository.putAll(array, in: self.query, operation: operation)) @@ -134,28 +156,28 @@ extension Interactor { } } -extension PutRepository { - public func toPutByQueryInteractor(_ executor: Executor) -> Interactor.PutByQuery { +public extension PutRepository { + func toPutByQueryInteractor(_ executor: Executor) -> Interactor.PutByQuery { return Interactor.PutByQuery(executor, self) } - - public func toPutInteractor(_ executor: Executor, _ query : Query) -> Interactor.Put { + + func toPutInteractor(_ executor: Executor, _ query: Query) -> Interactor.Put { return Interactor.Put(executor, self, query) } - - public func toPutInteractor(_ executor: Executor, _ id : K) -> Interactor.Put where K:Hashable { + + func toPutInteractor(_ executor: Executor, _ id: K) -> Interactor.Put where K: Hashable { return Interactor.Put(executor, self, id) } - - public func toPutAllByQueryInteractor(_ executor: Executor) -> Interactor.PutAllByQuery { + + func toPutAllByQueryInteractor(_ executor: Executor) -> Interactor.PutAllByQuery { return Interactor.PutAllByQuery(executor, self) } - - public func toPutAllInteractor(_ executor: Executor, _ query : Query) -> Interactor.PutAll { + + func toPutAllInteractor(_ executor: Executor, _ query: Query) -> Interactor.PutAll { return Interactor.PutAll(executor, self, query) } - - public func toPutAllInteractor(_ executor: Executor, _ id : K) -> Interactor.PutAll where K:Hashable { + + func toPutAllInteractor(_ executor: Executor, _ id: K) -> Interactor.PutAll where K: Hashable { return Interactor.PutAll(executor, self, id) } } diff --git a/Sources/Harmony/Future/Executor/Collections+Executor.swift b/Sources/Harmony/Future/Executor/Collections+Executor.swift index 3f949289..beb23565 100644 --- a/Sources/Harmony/Future/Executor/Collections+Executor.swift +++ b/Sources/Harmony/Future/Executor/Collections+Executor.swift @@ -16,8 +16,7 @@ import Foundation -extension Array { - +public extension Array { /// Executor-based map method /// /// - Parameters: @@ -25,9 +24,9 @@ extension Array { /// - transform: The mapping closure /// - Returns: An array of mapped elements /// - Throws: The first mapping thrown error - public func map(_ executor : Executor, _ transform : @escaping (Element) throws -> T) rethrows -> [T] { - let futures : [Future] = self.map { element in - let future : Future = executor.submit { resolver in + func map(_ executor: Executor, _ transform: @escaping (Element) throws -> T) rethrows -> [T] { + let futures: [Future] = map { element in + let future: Future = executor.submit { resolver in resolver.set(try transform(element)) } return future @@ -36,8 +35,7 @@ extension Array { } } -extension Set { - +public extension Set { /// Executor-based map method /// /// - Parameters: @@ -45,9 +43,9 @@ extension Set { /// - transform: The mapping closure /// - Returns: A set of mapped elements /// - Throws: The first mapping thrown error - public func map(_ executor : Executor, _ transform : @escaping (Element) throws -> T) rethrows -> [T] { - let futures : [Future] = self.map { element in - let future : Future = executor.submit { resolver in + func map(_ executor: Executor, _ transform: @escaping (Element) throws -> T) rethrows -> [T] { + let futures: [Future] = map { element in + let future: Future = executor.submit { resolver in resolver.set(try transform(element)) } return future @@ -56,8 +54,7 @@ extension Set { } } -extension Dictionary { - +public extension Dictionary { /// Executor-based map method /// /// - Parameters: @@ -65,22 +62,23 @@ extension Dictionary { /// - transform: The mapping closure /// - Returns: A dictionary with mapped keys /// - Throws: The first mapping thrown error - public func mapKeys(_ executor : Executor, _ transform : @escaping (Key) throws -> T) rethrows -> [T : Value] { - let futures : [Future<(T,Value)>] = self.map { (key, value) in - let future : Future<(T,Value)> = executor.submit { resolver in + func mapKeys(_ executor: Executor, + _ transform: @escaping (Key) throws -> T) rethrows -> [T: Value] { + let futures: [Future<(T, Value)>] = map { key, value in + let future: Future<(T, Value)> = executor.submit { resolver in resolver.set((try transform(key), value)) } return future } - return try! Future.batch(futures).map { elements -> [T : Value] in - var dict = [T:Value]() + return try! Future.batch(futures).map { elements -> [T: Value] in + var dict = [T: Value]() elements.forEach { element in dict[element.0] = element.1 } return dict - }.result.get() + }.result.get() } - + /// Executor-based map method /// /// - Parameters: @@ -88,12 +86,12 @@ extension Dictionary { /// - transform: The mapping closure /// - Returns: A dictionary with mapped values /// - Throws: The first mapping thrown error - public func mapValues(_ executor : Executor, _ transform : @escaping (Value) throws -> T) rethrows -> [Key : T] { + func mapValues(_ executor: Executor, _ transform: @escaping (Value) throws -> T) rethrows -> [Key: T] { return try map(executor) { element -> T in - return try transform(element.value) + try transform(element.value) } } - + /// Executor-based map method /// /// - Parameters: @@ -101,15 +99,16 @@ extension Dictionary { /// - transform: The mapping closure /// - Returns: A dictionary with mapped values /// - Throws: The first mapping thrown error - public func map(_ executor : Executor, _ transform : @escaping ((key: Key, value: Value)) throws -> T) rethrows -> [Key : T] { - let futures : [Future<(Key,T)>] = self.map { (key, value) in - let future : Future<(Key,T)> = executor.submit { resolver in + func map(_ executor: Executor, + _ transform: @escaping ((key: Key, value: Value)) throws -> T) rethrows -> [Key: T] { + let futures: [Future<(Key, T)>] = map { key, value in + let future: Future<(Key, T)> = executor.submit { resolver in resolver.set((key, try transform((key, value)))) } return future } - return try! Future.batch(futures).map { elements -> [Key : T] in - var dict = [Key:T]() + return try! Future.batch(futures).map { elements -> [Key: T] in + var dict = [Key: T]() elements.forEach { element in dict[element.0] = element.1 } diff --git a/Sources/Harmony/Future/Executor/DirectExecutor.swift b/Sources/Harmony/Future/Executor/DirectExecutor.swift index 0d484aaa..9e2784e3 100644 --- a/Sources/Harmony/Future/Executor/DirectExecutor.swift +++ b/Sources/Harmony/Future/Executor/DirectExecutor.swift @@ -19,12 +19,12 @@ import Foundation /// /// A direct executor executes the closure on the current queue/thread synchronously. /// -public class DirectExecutor : Executor { - public private(set) var executing : Bool = false - public let name : String? = "com.mobilejazz.executor.direct" - - public init() { } - +public class DirectExecutor: Executor { + public private(set) var executing: Bool = false + public let name: String? = "com.mobilejazz.executor.direct" + + public init() {} + public func submit(_ closure: @escaping (@escaping () -> Void) -> Void) { executing = true let sempahore = DispatchSemaphore(value: 0) @@ -35,16 +35,15 @@ public class DirectExecutor : Executor { } public final class DelayedMainQueueExecutor: MainDirectExecutor, DelayedExecutor { - private let overrideDelay: Bool - + public init(overrideDelay: Bool = false) { self.overrideDelay = overrideDelay } - + public func submit(after: DispatchTime, _ closure: @escaping (@escaping () -> Void) -> Void) { - if self.overrideDelay { - self.submit(closure) + if overrideDelay { + submit(closure) } else { DispatchQueue.main.asyncAfter(deadline: after) { self.executing = true @@ -55,10 +54,13 @@ public final class DelayedMainQueueExecutor: MainDirectExecutor, DelayedExecutor } } } - - @discardableResult public func submit(after: DispatchTime, _ closure: @escaping (FutureResolver) throws -> Void) -> Future { + + @discardableResult public func submit( + after: DispatchTime, + _ closure: @escaping (FutureResolver) throws -> Void + ) -> Future { let future = Future() - self.submit(after: after) { end in + submit(after: after) { end in future.onSet { end() } @@ -66,19 +68,20 @@ public final class DelayedMainQueueExecutor: MainDirectExecutor, DelayedExecutor do { let resolver = FutureResolver(future) try closure(resolver) - } catch (let error) { + } catch { future.set(error) } } return future.toFuture() // Creating a new future to avoid a duplicate call to onSet to the same future } - + /// Submits a closure for its execution. /// /// - Parameter closure: The closure to be executed. An error can be thrown. /// - Returns: A future wrapping the error, if thrown. - @discardableResult public func submit(after: DispatchTime, _ closure: @escaping () throws -> Void) -> Future { - return self.submit(after: after) { resolver in + @discardableResult public func submit(after: DispatchTime, + _ closure: @escaping () throws -> Void) -> Future { + return submit(after: after) { resolver in try closure() resolver.set() } @@ -87,22 +90,22 @@ public final class DelayedMainQueueExecutor: MainDirectExecutor, DelayedExecutor // /// Executes on the main queue asynchronously. -/// However, if the submit is called in the main thread, the submitted closure is directly called as in a DirectExecutor. +/// However, if the submit is called in the main thread, the submitted closure is directly called as in a +// DirectExecutor. /// -public class MainDirectExecutor : Executor { - +public class MainDirectExecutor: Executor { public let name: String? = "com.mobilejazz.executor.main-direct" public var executing: Bool = false - - public init() { } - + + public init() {} + public func submit(_ closure: @escaping (@escaping () -> Void) -> Void) { if Thread.isMainThread { - self.executing = true + executing = true let sempahore = DispatchSemaphore(value: 0) closure { sempahore.signal() } sempahore.wait() - self.executing = false + executing = false } else { DispatchQueue.main.async { self.executing = true diff --git a/Sources/Harmony/Future/Executor/DispatchQueueExecutor.swift b/Sources/Harmony/Future/Executor/DispatchQueueExecutor.swift index 170dfae5..e2eaa0b4 100644 --- a/Sources/Harmony/Future/Executor/DispatchQueueExecutor.swift +++ b/Sources/Harmony/Future/Executor/DispatchQueueExecutor.swift @@ -21,14 +21,13 @@ private let executingKey = DispatchSpecificKey() /// /// DispatchQueue Executor extension. /// -extension DispatchQueue : Executor { - +extension DispatchQueue: Executor { public var name: String? { return label } - + public var executing: Bool { return getSpecific(key: executingKey) ?? false } - + public func submit(_ closure: @escaping (@escaping () -> Void) -> Void) { async { self.setSpecific(key: executingKey, value: true) @@ -43,8 +42,7 @@ extension DispatchQueue : Executor { /// /// GCD-based executor /// -public class DispatchQueueExecutor : Executor { - +public class DispatchQueueExecutor: Executor { /// The queue type /// /// - serialQueue: Serial queue @@ -53,21 +51,22 @@ public class DispatchQueueExecutor : Executor { case serial case concurrent } - + /// The dispatch queue - public let queue : DispatchQueue - + public let queue: DispatchQueue + /// Main initializer /// /// - Parameter queue: The dispatch queue public init(_ queue: DispatchQueue) { self.queue = queue } - + /// Convenience initializer /// /// - Parameter type: Queue type - public convenience init(_ type: QueueType = .serial, name: String = OperationQueueExecutor.nextExecutorName()) { + public convenience init(_ type: QueueType = .serial, + name: String = OperationQueueExecutor.nextExecutorName()) { switch type { case .serial: let queue = DispatchQueue(label: name) @@ -77,10 +76,10 @@ public class DispatchQueueExecutor : Executor { self.init(queue) } } - + // MARK: - Executor - - public var executing : Bool { return queue.executing } + + public var executing: Bool { return queue.executing } public var name: String? { return queue.name } public func submit(_ closure: @escaping (@escaping () -> Void) -> Void) { queue.submit(closure) } } diff --git a/Sources/Harmony/Future/Executor/Executor.swift b/Sources/Harmony/Future/Executor/Executor.swift index d31ee126..261b4763 100644 --- a/Sources/Harmony/Future/Executor/Executor.swift +++ b/Sources/Harmony/Future/Executor/Executor.swift @@ -21,14 +21,15 @@ import Foundation /// public protocol Executor { /// The executor name - var name : String? { get } + var name: String? { get } /// var indicating if the executor is executing - var executing : Bool { get } - + var executing: Bool { get } + /// Submits a closure for its execution /// - /// - Parameter closure: The code to be executed. The closure must call its subclosure after completing (either sync or async) + /// - Parameter closure: The code to be executed. The closure must call its subclosure after completing (either + // sync or async) /// - Returns: Nothing (Void) func submit(_ closure: @escaping (@escaping () -> Void) -> Void) } @@ -37,31 +38,32 @@ public protocol Executor { /// Refined type of Executor that delays execution of a closure /// public protocol DelayedExecutor: Executor { - /// Submits a closure for execution later in time /// - Parameters: /// - after: amount of time elapsed before the closure gets executed - /// - closure: The code to be executed. The closure must call its subclosure after completing (either sync or async) + /// - closure: The code to be executed. The closure must call its subclosure after completing (either sync or + // async) /// - Returns: Nothing (Void) func submit(after: DispatchTime, _ closure: @escaping (@escaping () -> Void) -> Void) /// Submits a closure for execution later in time /// - Parameters: /// - after: amount of time elapsed before the closure gets executed - /// - closure: The code to be executed. The closure must call its subclosure after completing (either sync or async) + /// - closure: The code to be executed. The closure must call its subclosure after completing (either sync or + // async) /// - Returns: Nothing (Void) @discardableResult - func submit(after: DispatchTime, _ closure: @escaping () throws -> Void) -> Future + func submit(after: DispatchTime, _ closure: @escaping () throws -> Void) -> Future } -fileprivate let lock = NSLock() -fileprivate var counter : Int = 0 +private let lock = NSLock() +private var counter: Int = 0 -extension Executor { +public extension Executor { /// Creates a unique executor name /// /// - Returns: An executor name - static public func nextExecutorName() -> String { + static func nextExecutorName() -> String { lock.lock() counter += 1 defer { lock.unlock() } diff --git a/Sources/Harmony/Future/Executor/ExecutorFactory.swift b/Sources/Harmony/Future/Executor/ExecutorFactory.swift index 266c03dd..c0d3ff59 100644 --- a/Sources/Harmony/Future/Executor/ExecutorFactory.swift +++ b/Sources/Harmony/Future/Executor/ExecutorFactory.swift @@ -16,28 +16,27 @@ import Foundation public class ExecutorFactory { - public enum Scope { case none case string(String) case integer(Int) case type(String) } - - private let builder : (String) -> Executor - private var stringScope : [String : Executor] = [:] - private var integerScope : [Int : Executor] = [:] - private var typeScope : [String : Executor] = [:] - + + private let builder: (String) -> Executor + private var stringScope: [String: Executor] = [:] + private var integerScope: [Int: Executor] = [:] + private var typeScope: [String: Executor] = [:] + public init(_ builder: @escaping (String) -> Executor) { self.builder = builder } - + private func get(_ scope: Scope, named name: String) -> Executor { switch scope { case .none: return builder(name) - case .string(let value): + case let .string(value): if let executor = stringScope[value] { return executor } else { @@ -45,7 +44,7 @@ public class ExecutorFactory { stringScope[value] = executor return executor } - case .type(let value): + case let .type(value): if let executor = typeScope[value] { return executor } else { @@ -53,7 +52,7 @@ public class ExecutorFactory { typeScope[value] = executor return executor } - case .integer(let value): + case let .integer(value): if let executor = integerScope[value] { return executor } else { @@ -63,19 +62,19 @@ public class ExecutorFactory { } } } - + public func get(named name: String = "com.mobilejazz.executor.default") -> Executor { return get(.none, named: name) } - + public func get(_ string: String) -> Executor { return get(.string(string), named: "executor." + string) } - + public func get(_ value: Int) -> Executor { return get(.integer(value), named: "executor.\(value)") } - + public func get(_ type: T.Type) -> Executor { let string = String(describing: type) return get(.type(string), named: string) diff --git a/Sources/Harmony/Future/Executor/OperationQueueExecutor.swift b/Sources/Harmony/Future/Executor/OperationQueueExecutor.swift index 2ea4b655..3e5e4a04 100644 --- a/Sources/Harmony/Future/Executor/OperationQueueExecutor.swift +++ b/Sources/Harmony/Future/Executor/OperationQueueExecutor.swift @@ -19,10 +19,9 @@ import Foundation /// /// OperationQueue Executor extension. /// -extension OperationQueue : Executor { - +extension OperationQueue: Executor { public var executing: Bool { return operationCount > 0 } - + public func submit(_ closure: @escaping (@escaping () -> Void) -> Void) { addOperation { let sempahore = DispatchSemaphore(value: 0) @@ -36,19 +35,18 @@ extension OperationQueue : Executor { /// OperationQueue based executor /// public class OperationQueueExecutor: Executor { - /// The operation queue - public let operationQueue : OperationQueue - + public let operationQueue: OperationQueue + /// The queue type /// /// - serialQueue: Serial queue /// - concurrentQueue: Concurrent queue public enum QueueType { case serial - case concurrent(count:Int) + case concurrent(count: Int) } - + /// Main initializer /// /// - Parameter operationQueue: The operation queue @@ -58,19 +56,20 @@ public class OperationQueueExecutor: Executor { } self.operationQueue = operationQueue } - + /// Convenience initalizer /// /// - Parameters: /// - type: The type of queue /// - name: The name of the queue - public convenience init (_ type : QueueType = .serial, name: String = OperationQueueExecutor.nextExecutorName()) { + public convenience init(_ type: QueueType = .serial, + name: String = OperationQueueExecutor.nextExecutorName()) { let operationQueue = OperationQueue() operationQueue.name = name switch type { case .serial: operationQueue.maxConcurrentOperationCount = 1 - case .concurrent(let count): + case let .concurrent(count): operationQueue.maxConcurrentOperationCount = count } self.init(operationQueue) diff --git a/Sources/Harmony/Future/Future/Executor+Future.swift b/Sources/Harmony/Future/Future/Executor+Future.swift index 9e230bf2..0bb3783d 100644 --- a/Sources/Harmony/Future/Future/Executor+Future.swift +++ b/Sources/Harmony/Future/Future/Executor+Future.swift @@ -16,16 +16,15 @@ import Foundation -extension Executor { - +public extension Executor { /// Submits a closure for its execution with a future to be filled. /// Note that the future returned is not the same as the future privded in the closure. /// /// - Parameter closure: The closure to be executed. The future must be filled. /// - Returns: A future wrapping the result. - @discardableResult public func submit(_ closure: @escaping (FutureResolver) throws -> Void) -> Future { + @discardableResult func submit(_ closure: @escaping (FutureResolver) throws -> Void) -> Future { let future = Future() - self.submit { end in + submit { end in future.onSet { end() } @@ -33,19 +32,19 @@ extension Executor { do { let resolver = FutureResolver(future) try closure(resolver) - } catch (let error) { + } catch { future.set(error) } } return future.toFuture() // Creating a new future to avoid a duplicate call to onSet to the same future } - + /// Submits a closure for its execution. /// /// - Parameter closure: The closure to be executed. An error can be thrown. /// - Returns: A future wrapping the error, if thrown. - @discardableResult public func submit(_ closure: @escaping () throws -> Void) -> Future { - return self.submit { resolver in + @discardableResult func submit(_ closure: @escaping () throws -> Void) -> Future { + return submit { resolver in try closure() resolver.set() } diff --git a/Sources/Harmony/Future/Future/Future+Batch.swift b/Sources/Harmony/Future/Future/Future+Batch.swift index 47bc6d70..fdfd30cf 100644 --- a/Sources/Harmony/Future/Future/Future+Batch.swift +++ b/Sources/Harmony/Future/Future/Future+Batch.swift @@ -16,36 +16,37 @@ import Foundation -extension Future { - +public extension Future { + // swiftlint:disable large_tuple + /// Creates a new future from a sequence of futures. /// /// - Parameter futures: A sequence of futures. /// - Returns: The future batch. - public static func batch(_ futures : Future ...) -> Future<[T]> { + static func batch(_ futures: Future ...) -> Future<[T]> { return Future.batch(futures) } - + /// Creates a new future from an array of futures. /// /// - Parameter futures: An array of futures. /// - Returns: The future batch. - public static func batch(_ futures : [Future]) -> Future<[T]> { - if futures.count == 0 { + static func batch(_ futures: [Future]) -> Future<[T]> { + if futures.isEmpty { return Future<[T]>([]) } - + let lock = NSLock() let future = Future<[T]>() - var dict : [Int:T] = [:] + var dict: [Int: T] = [:] for (idx, futureT) in futures.enumerated() { futureT.resolve(success: { value in lock.lock() dict[idx] = value if future.state != .sent { if dict.count == futures.count { - var array : [T] = [] - for idx in 0..(_ futureK: Future) -> Future<(T,K)> { + func zip(_ futureK: Future) -> Future<(T, K)> { return flatMap { valueT in - return futureK.map { valueK in - return (valueT, valueK) + futureK.map { valueK in + (valueT, valueK) } } } - + /// Creates a new future that holds the tupple of results - public func zip(_ futureK: Future, _ futureL: Future) -> Future<(T,K,L)> { + func zip(_ futureK: Future, _ futureL: Future) -> Future<(T, K, L)> { return zip(futureK).flatMap { valueTK in - return futureL.map { valueL in - return (valueTK.0, valueTK.1, valueL) + futureL.map { valueL in + (valueTK.0, valueTK.1, valueL) } } } - + /// Creates a new future that holds the tupple of results - public func zip(_ futureK: Future, _ futureL: Future, _ futureM: Future) -> Future<(T,K,L,M)> { + func zip(_ futureK: Future, _ futureL: Future, _ futureM: Future) -> Future<(T, K, L, M)> { return zip(futureK, futureL).flatMap { valueTKL in - return futureM.map { valueM in - return (valueTKL.0, valueTKL.1, valueTKL.2, valueM) + futureM.map { valueM in + (valueTKL.0, valueTKL.1, valueTKL.2, valueM) } } } - + /// Unzips a 2-tuple future into two futures - public func unzip() -> (Future,Future) where T == (K,L) { + func unzip() -> (Future, Future) where T == (K, L) { let futureK = Future() let futureL = Future() - resolve(success: {tuple in + resolve(success: { tuple in futureK.set(tuple.0) futureL.set(tuple.1) }, failure: { error in @@ -101,13 +102,13 @@ extension Future { }) return (futureK, futureL) } - + /// Unzips a 3-tuple future into three futures - public func unzip() -> (Future,Future,Future) where T == (K,L,M) { + func unzip() -> (Future, Future, Future) where T == (K, L, M) { let futureK = Future() let futureL = Future() let futureM = Future() - resolve(success: {tuple in + resolve(success: { tuple in futureK.set(tuple.0) futureL.set(tuple.1) futureM.set(tuple.2) @@ -118,14 +119,14 @@ extension Future { }) return (futureK, futureL, futureM) } - + /// Unzips a 4-tuple future into four futures - public func unzip() -> (Future,Future,Future,Future) where T == (K,L,M,N) { + func unzip() -> (Future, Future, Future, Future) where T == (K, L, M, N) { let futureK = Future() let futureL = Future() let futureM = Future() let futureN = Future() - resolve(success: {tuple in + resolve(success: { tuple in futureK.set(tuple.0) futureL.set(tuple.1) futureM.set(tuple.2) @@ -138,33 +139,36 @@ extension Future { }) return (futureK, futureL, futureM, futureN) } - + /// Collapses a 2-tuple future into a single value future - public func collapse(_ executor: Executor = DirectExecutor(), _ closure: @escaping (K,L) -> Z) -> Future where T == (K,L) { - return Future() { resolver in - resolve(success: {tuple in + func collapse(_ executor: Executor = DirectExecutor(), _ closure: @escaping (K, L) -> Z) -> Future + where T == (K, L) { + return Future { resolver in + resolve(success: { tuple in executor.submit { resolver.set(closure(tuple.0, tuple.1)) } }, failure: { error in executor.submit { resolver.set(error) } }) } } - + /// Collapses a 3-tuple future into a single value future - public func collapse(_ executor: Executor = DirectExecutor(), _ closure: @escaping (K,L,M) -> Z) -> Future where T == (K,L,M) { - return Future() { resolver in - resolve(success: {tuple in + func collapse(_ executor: Executor = DirectExecutor(), + _ closure: @escaping (K, L, M) -> Z) -> Future where T == (K, L, M) { + return Future { resolver in + resolve(success: { tuple in executor.submit { resolver.set(closure(tuple.0, tuple.1, tuple.2)) } }, failure: { error in executor.submit { resolver.set(error) } }) } } - + /// Collapses a 4-tuple future into a single value future - public func collapse(_ executor: Executor = DirectExecutor(), _ closure: @escaping (K,L,M,N) -> Z) -> Future where T == (K,L,M,N) { - return Future() { resolver in - resolve(success: {tuple in + func collapse(_ executor: Executor = DirectExecutor(), + _ closure: @escaping (K, L, M, N) -> Z) -> Future where T == (K, L, M, N) { + return Future { resolver in + resolve(success: { tuple in executor.submit { resolver.set(closure(tuple.0, tuple.1, tuple.2, tuple.3)) } }, failure: { error in executor.submit { resolver.set(error) } diff --git a/Sources/Harmony/Future/Future/Future+Extensions.swift b/Sources/Harmony/Future/Future/Future+Extensions.swift index 4a506d71..68ce6121 100644 --- a/Sources/Harmony/Future/Future/Future+Extensions.swift +++ b/Sources/Harmony/Future/Future/Future+Extensions.swift @@ -14,8 +14,8 @@ // limitations under the License. // -import Foundation import CoreGraphics +import Foundation // ------------ Basic Foundation Types ------------ // @@ -28,7 +28,7 @@ public extension Array { } public extension Dictionary { - func toFuture() -> Future<[Key:Value]> { + func toFuture() -> Future<[Key: Value]> { return Future(self) } } diff --git a/Sources/Harmony/Future/Future/Future+Functional.swift b/Sources/Harmony/Future/Future/Future+Functional.swift index 8a347262..eb8d67e3 100644 --- a/Sources/Harmony/Future/Future/Future+Functional.swift +++ b/Sources/Harmony/Future/Future/Future+Functional.swift @@ -17,7 +17,6 @@ import Foundation public extension Future { - /// Maps the value and return a new future with the value mapped /// /// - Parameters: @@ -28,15 +27,14 @@ public extension Future { return Future { resolver in resolve(success: { value in executor.submit { - do { resolver.set(try transform(value)) } - catch (let error) { resolver.set(error) } + do { resolver.set(try transform(value)) } catch { resolver.set(error) } } }, failure: { error in executor.submit { resolver.set(error) } }) } } - + /// Maps the error and return a new future with the error mapped /// /// - Parameters: @@ -45,63 +43,63 @@ public extension Future { /// - Returns: A error-mapped chained future func mapError(_ executor: Executor = DirectExecutor(), _ transform: @escaping (Error) -> Error) -> Future { return Future { resolver in - resolve(success: {value in + resolve(success: { value in executor.submit { resolver.set(value) } }, failure: { error in executor.submit { resolver.set(transform(error)) } }) } } - + /// Intercepts the value if success and returns a new future of a mapped type to be chained /// /// - Parameters: /// - executor: An optional executor to execute the closure. /// - closure: The flatmap closure /// - Returns: A chained future - func flatMap(_ executor: Executor = DirectExecutor(), _ closure: @escaping (T) throws -> Future) -> Future { + func flatMap(_ executor: Executor = DirectExecutor(), + _ closure: @escaping (T) throws -> Future) -> Future { return Future { resolver in - resolve(success: {value in + resolve(success: { value in executor.submit { - do { resolver.set(try closure(value)) } - catch (let error) { resolver.set(error) } + do { resolver.set(try closure(value)) } catch { resolver.set(error) } } }, failure: { error in executor.submit { resolver.set(error) } }) } } - + /// Replaces the current future value with the new future. /// Note: the future is chained only if the current is resolved without error. /// /// - Parameter future: The chained future /// - Returns: The incoming future chained to the current one func chain(_ future: Future) -> Future { - return flatMap { value in - return future + return flatMap { _ in + future } } - + /// Intercepts the error (if available) and returns a new future of type T /// /// - Parameters: /// - executor: An optional executor to execute the closure. /// - closure: The recover closure /// - Returns: A chained future - func recover(_ executor: Executor = DirectExecutor(), _ closure: @escaping (Error) throws -> Future) -> Future { + func recover(_ executor: Executor = DirectExecutor(), + _ closure: @escaping (Error) throws -> Future) -> Future { return Future { resolver in - resolve(success: {value in + resolve(success: { value in executor.submit { resolver.set(value) } }, failure: { error in executor.submit { - do { resolver.set(try closure(error)) } - catch (let error) { resolver.set(error) } + do { resolver.set(try closure(error)) } catch { resolver.set(error) } } }) } } - + /// Intercepts the error (if available) and returns a new future of type T /// /// - Parameters: @@ -109,9 +107,10 @@ public extension Future { /// - executor: An optional executor to execute the closure. /// - closure: The recover closure /// - Returns: A chained future - func recover(if errorType: E.Type, _ executor: Executor = DirectExecutor(), _ closure: @escaping (E) throws -> Future) -> Future { + func recover(if _: E.Type, _ executor: Executor = DirectExecutor(), + _ closure: @escaping (E) throws -> Future) -> Future { return Future { resolver in - resolve(success: {value in + resolve(success: { value in executor.submit { resolver.set(value) } }, failure: { error in executor.submit { @@ -119,7 +118,7 @@ public extension Future { case let error as E: do { resolver.set(try closure(error)) - } catch let error { + } catch { resolver.set(error) } default: @@ -129,7 +128,7 @@ public extension Future { }) } } - + /// Notifies completion of the future in both success or failure state. /// /// - Parameters: @@ -139,7 +138,7 @@ public extension Future { @discardableResult func onCompletion(_ executor: Executor = DirectExecutor(), _ closure: @escaping () -> Void) -> Future { return Future { resolver in - resolve(success: {value in + resolve(success: { value in executor.submit { closure() resolver.set(value) @@ -152,7 +151,7 @@ public extension Future { }) } } - + /// Filters the value and allows to exchange it by a thrown error /// /// - Parameters: @@ -161,12 +160,12 @@ public extension Future { /// - Returns: A chained future func filter(_ executor: Executor = DirectExecutor(), _ closure: @escaping (T) throws -> Void) -> Future { return Future { resolver in - resolve(success: {value in + resolve(success: { value in executor.submit { do { try closure(value) resolver.set(value) - } catch (let error) { + } catch { resolver.set(error) } } diff --git a/Sources/Harmony/Future/Future/Future+Operators.swift b/Sources/Harmony/Future/Future/Future+Operators.swift index 6587e03c..dab411b6 100644 --- a/Sources/Harmony/Future/Future/Future+Operators.swift +++ b/Sources/Harmony/Future/Future/Future+Operators.swift @@ -17,16 +17,17 @@ import Foundation /// Operator + overriding -public func +(left: Future, right: Future) -> Future<(T,K)> { +public func + (left: Future, right: Future) -> Future<(T, K)> { return left.zip(right) } precedencegroup MapPrecedance { associativity: left } -infix operator <^> : MapPrecedance + +infix operator <^>: MapPrecedance /// Map operator -public func <^>(future: Future, map: @escaping (T) -> K) -> Future { +public func <^> (future: Future, map: @escaping (T) -> K) -> Future { return future.map { value in map(value) } } diff --git a/Sources/Harmony/Future/Future/Future+Optionals.swift b/Sources/Harmony/Future/Future/Future+Optionals.swift index 372a351f..3c9c62de 100644 --- a/Sources/Harmony/Future/Future/Future+Optionals.swift +++ b/Sources/Harmony/Future/Future/Future+Optionals.swift @@ -16,12 +16,10 @@ import Foundation - /// Future can't unrwap because value is nil error -public class NilValueError : Error { public init() {} } +public class NilValueError: Error { public init() {} } public extension Future { - /// Unwrapes a future of an optional type, returning a future of a non-optional type /// /// - Returns: A chained future. @@ -33,19 +31,20 @@ public extension Future { return Future(value) } } - + /// Converts the non-optional typed future to an optional typed future /// /// - Returns: An optional typed future func optional() -> Future { - return self.map { $0 as T? } + return map { $0 as T? } } - + /// Calls the closure when the future is resolved with a nil value. /// /// - Parameter closure: The closure that will return a non-nil value. /// - Returns: A future with a non-optional type. - func fill(_ executor: Executor = DirectExecutor(), _ closure: @escaping () -> K) -> Future where T == K? { + func fill(_ executor: Executor = DirectExecutor(), _ closure: @escaping () -> K) -> Future + where T == K? { return flatMap(executor) { value in guard let value = value else { return Future(closure()) @@ -53,12 +52,13 @@ public extension Future { return Future(value) } } - + /// Calls the closure when the future is resolved with a nil value. /// /// - Parameter closure: The closure that will return a non-optional future. /// - Returns: A future with a non-optional type. - func flatFill(_ executor: Executor = DirectExecutor(), _ closure: @escaping () -> Future) -> Future where T == K? { + func flatFill(_ executor: Executor = DirectExecutor(), _ closure: @escaping () -> Future) -> Future + where T == K? { return flatMap(executor) { value in guard let value = value else { return Future(closure()) @@ -66,11 +66,12 @@ public extension Future { return Future(value) } } - + /// Performs a map of an optional future when the value is defined. /// /// - Returns: A chained future. - func unwrappedMap(_ executor: Executor = DirectExecutor(), _ closure: @escaping (K) -> P) -> Future where T == K? { + func unwrappedMap(_ executor: Executor = DirectExecutor(), _ closure: @escaping (K) -> P) -> Future + where T == K? { return flatMap(executor) { value in guard let value = value else { return Future(nil) diff --git a/Sources/Harmony/Future/Future/Future+Time.swift b/Sources/Harmony/Future/Future/Future+Time.swift index 0007f182..76a4cd37 100644 --- a/Sources/Harmony/Future/Future/Future+Time.swift +++ b/Sources/Harmony/Future/Future/Future+Time.swift @@ -17,7 +17,6 @@ import Foundation public extension Future { - /// Adds a delay to the then call. /// /// - Parameters: @@ -28,10 +27,10 @@ public extension Future { if interval == 0.0 { return self } - - return Future() { resolver in + + return Future { resolver in queue.asyncAfter(deadline: .now() + interval) { - self.resolve(success: {value in + self.resolve(success: { value in resolver.set(value) }, failure: { error in resolver.set(error) @@ -39,7 +38,7 @@ public extension Future { } } } - + /// Adds a sync delay (blocks current thread) after the future is resolved. /// /// - Parameters: @@ -49,17 +48,17 @@ public extension Future { if interval == 0.0 { return self } - - return Future() { resolver in + + return Future { resolver in Thread.sleep(forTimeInterval: interval) - self.resolve(success: {value in + self.resolve(success: { value in resolver.set(value) }, failure: { error in resolver.set(error) }) } } - + /// Calls the then block after the given deadline /// /// - Parameters: @@ -67,9 +66,9 @@ public extension Future { /// - queue: The queue to schedule the delay (by default the Main Queue). /// - Returns: A chained future. func after(_ deadline: DispatchTime, queue: DispatchQueue) -> Future { - return Future() { resolver in + return Future { resolver in queue.asyncAfter(deadline: deadline) { - self.resolve(success: {value in + self.resolve(success: { value in resolver.set(value) }, failure: { error in resolver.set(error) @@ -77,7 +76,7 @@ public extension Future { } } } - + /// Ensures the future is called after the given date. /// If the date is earlier than now, nothing happens. /// diff --git a/Sources/Harmony/Future/Future/Future.swift b/Sources/Harmony/Future/Future/Future.swift index 10618f1e..2b9c9b4f 100644 --- a/Sources/Harmony/Future/Future/Future.swift +++ b/Sources/Harmony/Future/Future/Future.swift @@ -16,68 +16,67 @@ import Foundation -fileprivate struct FutureError : Error { - let description : String +private struct FutureError: Error { + let description: String init(_ description: String) { self.description = description } } -extension FutureError { +private extension FutureError { /// Future content has already been set - fileprivate static let thenAlreadySet = FutureError("Then already set: cannot set a new then closure once is already set.") - + static let thenAlreadySet = FutureError("Then already set: cannot set a new then closure once is already set.") + /// Future content has already been set - fileprivate static let alreadySent = FutureError("Future is already sent.") - + static let alreadySent = FutureError("Future is already sent.") + /// Future content has already been set - fileprivate static let missingLambda = FutureError("Future cannot be sent as the then clousre hasn't been defined") + static let missingLambda = FutureError("Future cannot be sent as the then clousre hasn't been defined") } /// /// A FutureResolver resolves a Future. /// public struct FutureResolver { - - private var future : Future - + private var future: Future + /// Main initializer /// /// - Parameter future: The future to resolve public init(_ future: Future) { self.future = future } - + /// Sets the future value public func set(_ value: T) { future.set(value) } - + /// Sets the future error public func set(_ error: Error) { future.set(error) } - + /// Sets the future with another future public func set(_ future: Future) { self.future.set(future) } - - /// Sets the future with a value if not error. Either the value or the error must be provided, otherwise a crash will happen. + + /// Sets the future with a value if not error. Either the value or the error must be provided, otherwise a crash + // will happen. /// Note: error is prioritary, and if not error the value will be used. public func set(value: T?, error: Error?) { future.set(value: value, error: error) } } -extension FutureResolver where T==Void { +public extension FutureResolver where T == Void { /// Sets the future with a void value - public func set() { + func set() { future.set() } } - /// /// Future class. Wrapper of a future value of generic type T or an error. /// @@ -88,9 +87,9 @@ public class Future { case waitingThen case waitingContent case sent - + var localizedDescription: String { - switch (self) { + switch self { case .blank: return "Blank: empty future" case .waitingThen: @@ -102,10 +101,10 @@ public class Future { } } } - + /// The future state public private(set) var state: State = .blank - + /// The future's result /// /// - value: a value was provided @@ -113,120 +112,124 @@ public class Future { public indirect enum Result { case value(T) case error(Error) - + /// Returns the value or throws an error if exists @discardableResult public func get() throws -> T { switch self { - case .value(let v): + case let .value(v): return v - case .error(let e): + case let .error(e): throw e } } - + // Returns the value or if error, returns nil and sets the error @discardableResult public func get(error: inout Error?) -> T? { switch self { - case .value(let v): + case let .value(v): return v - case .error(let e): + case let .error(e): error = e return nil } } } - + /// The future result. Using _ prefix as the "result" method returns synchronously the result. - internal var _result : Result? = nil - + internal var _result: Result? + // Private variables private var onContentSet: ((inout T?, inout Error?) -> Void)? private var semaphore: DispatchSemaphore? private let lock = NSLock() private var success: ((_ value: T) -> Void)? private var failure: ((_ error: Error) -> Void)? - + /// Default initializer - public init() { } - + public init() {} + /// Value initializer public init(_ value: T) { set(value) } - + /// Error initializer public init(_ error: Error) { set(error) } - + /// Future initializer public init(_ future: Future) { set(future) } - + /// Future initializer public init(_ observable: Observable, atEvent index: Int = 0) { set(observable, atEvent: index) } - + /// Future initializer - public init(_ observable: Observable, ignoreErrors: Bool = false, atEventPassingTest closure: @escaping (T) -> Bool) { + public init( + _ observable: Observable, + ignoreErrors: Bool = false, + atEventPassingTest closure: @escaping (T) -> Bool + ) { set(observable, ignoreErrors: ignoreErrors, atEventPassingTest: closure) } - + /// Future initializer public init(_ closure: (FutureResolver) throws -> Void) { do { let resolver = FutureResolver(self) try closure(resolver) - } catch (let error) { + } catch { set(error) } } - + /// Future initializer public convenience init(future closure: () throws -> Future) { do { let future = try closure() self.init(future) - } catch (let error) { + } catch { self.init(error) } } - + /// Future initializer public convenience init(value closure: () throws -> T) { do { let value = try closure() self.init(value) - } catch (let error) { + } catch { self.init(error) } } - + /// Future initializer public convenience init(_ closure: () -> Error) { let error = closure() self.init(error) } - + /// Creates a new future from self public func toFuture() -> Future { return Future(self) } - + /// Sets the future value public func set(_ value: T) { set(value: value, error: nil) } - + /// Sets the future error public func set(_ error: Error) { set(value: nil, error: error) } - + /// Sets the future with another future public func set(_ future: Future) { future.resolve(success: { value in @@ -235,14 +238,15 @@ public class Future { self.set(error) }) } - + /// Sets the future with a given observable at the given event index /// /// - Parameters: /// - observable: The incoming observable - /// - eventIdx: The event index to set the future. Use 0 to indicate the following event (or the eixsting one if the observer is already resolved). Default is 0. + /// - eventIdx: The event index to set the future. Use 0 to indicate the following event (or the eixsting one + // if the observer is already resolved). Default is 0. public func set(_ observable: Observable, atEvent eventIdx: Int = 0) { - var obs : Observable? = observable.hub.subscribe() + var obs: Observable? = observable.hub.subscribe() var idx = eventIdx obs!.resolve(success: { value in if idx == 0 { @@ -258,15 +262,19 @@ public class Future { idx -= 1 }) } - + /// Sets the future with the given observable when the passing test results true. /// /// - Parameters: /// - observable: The incoming observable /// - closure: The test closure /// - ignoreErrors: if true, all errors will be ignored (and the future is not resolved). Default is false. - public func set(_ observable: Observable, ignoreErrors: Bool = false, atEventPassingTest closure: @escaping (T) -> Bool) { - var obs : Observable? = observable.hub.subscribe() + public func set( + _ observable: Observable, + ignoreErrors: Bool = false, + atEventPassingTest closure: @escaping (T) -> Bool + ) { + var obs: Observable? = observable.hub.subscribe() obs!.resolve(success: { value in if closure(value) { self.set(value) @@ -279,29 +287,30 @@ public class Future { } }) } - - /// Sets the future with a value if not error. Either the value or the error must be provided, otherwise a crash will happen. + + /// Sets the future with a value if not error. Either the value or the error must be provided, otherwise a crash + // will happen. /// Note: error is prioritary, and if not error the value will be used. public func set(value: T?, error: Error?) { - if _result != nil || state == .sent { + if _result != nil || state == .sent { // Do nothing return } - - var value : T? = value - var error : Error? = error + + var value: T? = value + var error: Error? = error if let onContentSet = onContentSet { onContentSet(&value, &error) self.onContentSet = nil } - + lock { if let error = error { _result = .error(error) } else { _result = .value(value!) } - + if success != nil || failure != nil { // Resolve the then closure send() @@ -314,7 +323,7 @@ public class Future { } } } - + /// Clears the stored value and the referenced then closures. /// Mainly, resets the state of the future to blank. /// @@ -329,18 +338,19 @@ public class Future { } return self } - + /// Closure called right after content is set, without waiting the then closure. - /// Note that multiple calls to this method are discouraged, resulting with only one onContentSet closure being called. + /// Note that multiple calls to this method are discouraged, resulting with only one onContentSet closure being + // called. /// Note too that if the future has already been sent, this closure is not called. /// /// - Parameter closure: The code to be executed public func onSet(_ closure: @escaping () -> Void) { - onContentSet = { (_,_) in + onContentSet = { _, _ in closure() } } - + /// Closure called right after content is set, without waiting the then closure. /// Multiple calls to this method are discouraged, resulting with only one onContentSet closure being called. /// Note too that if the future has already been sent, this closure is not called. @@ -349,15 +359,14 @@ public class Future { public func onSet(_ closure: @escaping (inout T?, inout Error?) -> Void) { onContentSet = closure } - + /// Then closure: delivers the value or the error internal func resolve(success: @escaping (T) -> Void = { _ in }, failure: @escaping (Error) -> Void = { _ in }) { - if self.success != nil || self.failure != nil { fatalError(FutureError.thenAlreadySet.description) } - + lock { self.success = success self.failure = failure @@ -369,27 +378,25 @@ public class Future { } } } - + /// Deliver the result syncrhonously. This method might block the calling thread. /// Note that the result can only be delivered once - public var result : Result { - get { - switch state { - case .waitingThen: - state = .sent - return _result! - case .blank: - semaphore = DispatchSemaphore(value: 0) - semaphore!.wait() - return self.result - case .waitingContent: - fatalError(FutureError.thenAlreadySet.description) - case .sent: - fatalError(FutureError.alreadySent.description) - } + public var result: Result { + switch state { + case .waitingThen: + state = .sent + return _result! + case .blank: + semaphore = DispatchSemaphore(value: 0) + semaphore!.wait() + return self.result + case .waitingContent: + fatalError(FutureError.thenAlreadySet.description) + case .sent: + fatalError(FutureError.alreadySent.description) } } - + /// Main then method to obtain the promised value. /// /// - Parameters: @@ -399,7 +406,7 @@ public class Future { @discardableResult public func then(_ executor: Executor = MainDirectExecutor(), _ success: @escaping (T) -> Void) -> Future { return Future { resolver in - resolve(success: {value in + resolve(success: { value in executor.submit { success(value) resolver.set(value) @@ -411,7 +418,7 @@ public class Future { }) } } - + /// Main failure method to obtain the promised error. /// /// - Parameters: @@ -419,9 +426,10 @@ public class Future { /// - failure: The fail closure. /// - Returns: A chained future @discardableResult - public func fail(_ executor: Executor = MainDirectExecutor(), _ failure: @escaping (Error) -> Void) -> Future { + public func fail(_ executor: Executor = MainDirectExecutor(), + _ failure: @escaping (Error) -> Void) -> Future { return Future { resolver in - resolve(success: {value in + resolve(success: { value in executor.submit { resolver.set(value) } @@ -433,7 +441,7 @@ public class Future { }) } } - + /// Completes the future (if not completed yet) public func complete() { lock { @@ -444,27 +452,27 @@ public class Future { } } } - + private func send() { switch _result! { - case .error(let error): + case let .error(error): guard let failure = failure else { print(FutureError.missingLambda.description) return } failure(error) - case .value(let value): + case let .value(value): guard let success = success else { print(FutureError.missingLambda.description) return } success(value) } - - self.success = nil - self.failure = nil + + success = nil + failure = nil } - + // Private lock method private func lock(_ closure: () -> Void) { lock.lock() @@ -474,16 +482,16 @@ public class Future { } /// To String extension -extension Future : CustomStringConvertible, CustomDebugStringConvertible { +extension Future: CustomStringConvertible, CustomDebugStringConvertible { public var description: String { switch state { case .blank: return "Empty future. Waiting for value, error and then closure." case .waitingThen: switch _result! { - case .error(let error): + case let .error(error): return "Future waiting for then closure with error set to: \(error)" - case .value(let value): + case let .value(value): return "Future waiting for then closure with value set to: \(value)" } case .waitingContent: @@ -491,9 +499,9 @@ extension Future : CustomStringConvertible, CustomDebugStringConvertible { case .sent: if let result = _result { switch result { - case .error(let error): + case let .error(error): return "Future sent with error: \(error)" - case .value(let value): + case let .value(value): return "Future sent with value: \(value)" } } else { @@ -501,14 +509,14 @@ extension Future : CustomStringConvertible, CustomDebugStringConvertible { } } } + public var debugDescription: String { return description } } -extension Future where T==Void { - public func set() { - set(Void()) +public extension Future where T == Void { + func set() { + set(()) } } - diff --git a/Sources/Harmony/Future/Observable/Observable+Batch.swift b/Sources/Harmony/Future/Observable/Observable+Batch.swift index ce7e7ca3..68be0f18 100644 --- a/Sources/Harmony/Future/Observable/Observable+Batch.swift +++ b/Sources/Harmony/Future/Observable/Observable+Batch.swift @@ -17,36 +17,39 @@ import Foundation public extension Observable { - + // swiftlint:disable large_tuple + /// Creates a new observable from a sequence of observables. - /// Note that the batch will be delivered once all observables have resolved at least once. After that, the batch will resolve on all new resolves. Each single error will be delivered independently. + /// Note that the batch will be delivered once all observables have resolved at least once. After that, the + // batch will resolve on all new resolves. Each single error will be delivered independently. /// /// - Parameter futures: A sequence of observables. /// - Returns: The observable batch. - static func batch(_ futures : Observable ...) -> Observable<[T]> { + static func batch(_ futures: Observable ...) -> Observable<[T]> { return Observable.batch(futures) } - + /// Creates a new observable from an array of observables. - /// Note that the batch will be delivered once all observables have resolved at least once. After that, the batch will resolve on all new resolves. Each single error will be delivered independently. + /// Note that the batch will be delivered once all observables have resolved at least once. After that, the + // batch will resolve on all new resolves. Each single error will be delivered independently. /// /// - Parameter futures: An array of observables. /// - Returns: The observable batch. - static func batch(_ futures : [Observable]) -> Observable<[T]> { - if futures.count == 0 { + static func batch(_ futures: [Observable]) -> Observable<[T]> { + if futures.isEmpty { return Observable<[T]>([]) } - + let lock = NSLock() let future = Observable<[T]>() - var dict : [Int:T] = [:] + var dict: [Int: T] = [:] for (idx, futureT) in futures.enumerated() { futureT.resolve(success: { value in lock.lock() dict[idx] = value if dict.count == futures.count { - var array : [T] = [] - for idx in 0..(_ observableK: Observable) -> Observable<(T,K)> { + func zip(_ observableK: Observable) -> Observable<(T, K)> { return flatMap { valueT in - return observableK.map { valueK in - return (valueT, valueK) + observableK.map { valueK in + (valueT, valueK) } } } - + /// Creates a new observable that holds the tupple of results - func zip(_ observableK: Observable, _ observableL: Observable) -> Observable<(T,K,L)> { + func zip(_ observableK: Observable, _ observableL: Observable) -> Observable<(T, K, L)> { return zip(observableK).flatMap { valueTK in - return observableL.map { valueL in - return (valueTK.0, valueTK.1, valueL) + observableL.map { valueL in + (valueTK.0, valueTK.1, valueL) } } } - + /// Creates a new observable that holds the tupple of results - func zip(_ observableK: Observable, _ observableL: Observable, _ observableM: Observable) -> Observable<(T,K,L,M)> { + func zip(_ observableK: Observable, _ observableL: Observable, + _ observableM: Observable) -> Observable<(T, K, L, M)> { return zip(observableK, observableL).flatMap { valueTKL in - return observableM.map { valueM in - return (valueTKL.0, valueTKL.1, valueTKL.2, valueM) + observableM.map { valueM in + (valueTKL.0, valueTKL.1, valueTKL.2, valueM) } } } - + /// Unzips a 2-tuple observable into two observables - func unzip() -> (Observable,Observable) where T == (K,L) { + func unzip() -> (Observable, Observable) where T == (K, L) { let observableK = Observable() let observableL = Observable() - resolve(success: {tuple in + resolve(success: { tuple in observableK.set(tuple.0) observableL.set(tuple.1) }, failure: { error in @@ -101,13 +105,13 @@ public extension Observable { }) return (observableK, observableL) } - + /// Unzips a 3-tuple observable into three observables - func unzip() -> (Observable,Observable,Observable) where T == (K,L,M) { + func unzip() -> (Observable, Observable, Observable) where T == (K, L, M) { let observableK = Observable() let observableL = Observable() let observableM = Observable() - resolve(success: {tuple in + resolve(success: { tuple in observableK.set(tuple.0) observableL.set(tuple.1) observableM.set(tuple.2) @@ -118,14 +122,15 @@ public extension Observable { }) return (observableK, observableL, observableM) } - + /// Unzips a 4-tuple observable into four observables - func unzip() -> (Observable,Observable,Observable,Observable) where T == (K,L,M,N) { + func unzip() -> (Observable, Observable, Observable, Observable) + where T == (K, L, M, N) { let observableK = Observable() let observableL = Observable() let observableM = Observable() let observableN = Observable() - resolve(success: {tuple in + resolve(success: { tuple in observableK.set(tuple.0) observableL.set(tuple.1) observableM.set(tuple.2) @@ -138,33 +143,36 @@ public extension Observable { }) return (observableK, observableL, observableM, observableN) } - + /// Collapses a 2-tuple observable into a single value observable - func collapse(_ executor: Executor = DirectExecutor(), _ closure: @escaping (K,L) -> Z) -> Observable where T == (K,L) { - return Observable() { observable in - resolve(success: {tuple in + func collapse(_ executor: Executor = DirectExecutor(), + _ closure: @escaping (K, L) -> Z) -> Observable where T == (K, L) { + return Observable { observable in + resolve(success: { tuple in executor.submit { observable.set(closure(tuple.0, tuple.1)) } }, failure: { error in executor.submit { observable.set(error) } }) } } - + /// Collapses a 3-tuple observable into a single value observable - func collapse(_ executor: Executor = DirectExecutor(), _ closure: @escaping (K,L,M) -> Z) -> Observable where T == (K,L,M) { - return Observable() { observable in - resolve(success: {tuple in + func collapse(_ executor: Executor = DirectExecutor(), + _ closure: @escaping (K, L, M) -> Z) -> Observable where T == (K, L, M) { + return Observable { observable in + resolve(success: { tuple in executor.submit { observable.set(closure(tuple.0, tuple.1, tuple.2)) } }, failure: { error in executor.submit { observable.set(error) } }) } } - + /// Collapses a 4-tuple observable into a single value observable - func collapse(_ executor: Executor = DirectExecutor(), _ closure: @escaping (K,L,M,N) -> Z) -> Observable where T == (K,L,M,N) { - return Observable() { observable in - resolve(success: {tuple in + func collapse(_ executor: Executor = DirectExecutor(), + _ closure: @escaping (K, L, M, N) -> Z) -> Observable where T == (K, L, M, N) { + return Observable { observable in + resolve(success: { tuple in executor.submit { observable.set(closure(tuple.0, tuple.1, tuple.2, tuple.3)) } }, failure: { error in executor.submit { observable.set(error) } diff --git a/Sources/Harmony/Future/Observable/Observable+Extensions.swift b/Sources/Harmony/Future/Observable/Observable+Extensions.swift index 4d929d2a..0452285b 100644 --- a/Sources/Harmony/Future/Observable/Observable+Extensions.swift +++ b/Sources/Harmony/Future/Observable/Observable+Extensions.swift @@ -14,8 +14,8 @@ // limitations under the License. // -import Foundation import CoreGraphics +import Foundation // ------------ Basic Foundation Types ------------ // @@ -28,7 +28,7 @@ public extension Array { } public extension Dictionary { - func toObservable() -> Observable<[Key:Value]> { + func toObservable() -> Observable<[Key: Value]> { return Observable(self) } } diff --git a/Sources/Harmony/Future/Observable/Observable+Functional.swift b/Sources/Harmony/Future/Observable/Observable+Functional.swift index 47b88958..47711257 100644 --- a/Sources/Harmony/Future/Observable/Observable+Functional.swift +++ b/Sources/Harmony/Future/Observable/Observable+Functional.swift @@ -17,7 +17,6 @@ import Foundation public extension Observable { - /// Maps the value and return a new observable with the value mapped /// /// - Parameters: @@ -28,22 +27,22 @@ public extension Observable { return Observable(parent: self) { resolver in resolve(success: { value in executor.submit { - do { resolver.set(try transform(value)) } - catch (let error) { resolver.set(error) } + do { resolver.set(try transform(value)) } catch { resolver.set(error) } } }, failure: { error in executor.submit { resolver.set(error) } }) } } - + /// Maps the error and return a new observable with the error mapped /// /// - Parameters: /// - executor: An optional executor to execute the closure. /// - transform: The map closure /// - Returns: A error-mapped chained future - func mapError(_ executor: Executor = DirectExecutor(), _ transform: @escaping (Error) -> Error) -> Observable { + func mapError(_ executor: Executor = DirectExecutor(), + _ transform: @escaping (Error) -> Error) -> Observable { return Observable(parent: self) { resolver in resolve(success: { value in executor.submit { resolver.set(value) } @@ -52,56 +51,56 @@ public extension Observable { }) } } - + /// Intercepts the value if success and returns a new observable of a mapped type to be chained /// /// - Parameters: /// - executor: An optional executor to execute the closure. /// - closure: The flatmap closure /// - Returns: A chained observable - func flatMap(_ executor: Executor = DirectExecutor(), _ closure: @escaping (T) throws -> Observable) -> Observable { + func flatMap(_ executor: Executor = DirectExecutor(), + _ closure: @escaping (T) throws -> Observable) -> Observable { return Observable(parent: self) { resolver in resolve(success: { value in executor.submit { - do { resolver.set(try closure(value)) } - catch (let error) { resolver.set(error) } + do { resolver.set(try closure(value)) } catch { resolver.set(error) } } }, failure: { error in executor.submit { resolver.set(error) } }) } } - + /// Replaces the current observable value with the new future. /// Note: the observable is chained only if the current is resolved without error. /// /// - Parameter observable: The chained observable /// - Returns: The incoming observable chained to the current one func chain(_ observable: Observable) -> Observable { - return flatMap { value in - return observable + return flatMap { _ in + observable } } - + /// Intercepts the error (if available) and returns a new observable of type T /// /// - Parameters: /// - executor: An optional executor to execute the closure. /// - closure: The recover closure /// - Returns: A chained observable - func recover(_ executor: Executor = DirectExecutor(), _ closure: @escaping (Error) throws -> Observable) -> Observable { + func recover(_ executor: Executor = DirectExecutor(), + _ closure: @escaping (Error) throws -> Observable) -> Observable { return Observable(parent: self) { resolver in resolve(success: { value in executor.submit { resolver.set(value) } }, failure: { error in executor.submit { - do { resolver.set(try closure(error)) } - catch (let error) { resolver.set(error) } + do { resolver.set(try closure(error)) } catch { resolver.set(error) } } }) } } - + /// Intercepts the error (if available) and returns a new observable of type T /// /// - Parameters: @@ -109,9 +108,10 @@ public extension Observable { /// - executor: An optional executor to execute the closure. /// - closure: The recover closure /// - Returns: A chained observable - func recover(if errorType: E.Type, _ executor: Executor = DirectExecutor(), _ closure: @escaping (E) throws -> Observable) -> Observable { + func recover(if _: E.Type, _ executor: Executor = DirectExecutor(), + _ closure: @escaping (E) throws -> Observable) -> Observable { return Observable(parent: self) { resolver in - resolve(success: {value in + resolve(success: { value in executor.submit { resolver.set(value) } }, failure: { error in executor.submit { @@ -119,7 +119,7 @@ public extension Observable { case let error as E: do { resolver.set(try closure(error)) - } catch let error { + } catch { resolver.set(error) } default: @@ -129,7 +129,7 @@ public extension Observable { }) } } - + /// Notifies completion of the observable in both success or failure state. /// /// - Parameters: @@ -152,21 +152,22 @@ public extension Observable { }) } } - + /// Filters the value and allows to exchange it by a thrown error /// /// - Parameters: /// - executor: An optional executor to execute the closure. /// - closure: The filter closure. Throw an error to replace it's value for an error. /// - Returns: A chained observable - func filter(_ executor: Executor = DirectExecutor(), _ closure: @escaping (T) throws -> Void) -> Observable { + func filter(_ executor: Executor = DirectExecutor(), + _ closure: @escaping (T) throws -> Void) -> Observable { return Observable(parent: self) { resolver in resolve(success: { value in executor.submit { do { try closure(value) resolver.set(value) - } catch (let error) { + } catch { resolver.set(error) } } diff --git a/Sources/Harmony/Future/Observable/Observable+Optionals.swift b/Sources/Harmony/Future/Observable/Observable+Optionals.swift index 283cb392..8842e567 100644 --- a/Sources/Harmony/Future/Observable/Observable+Optionals.swift +++ b/Sources/Harmony/Future/Observable/Observable+Optionals.swift @@ -17,7 +17,6 @@ import Foundation public extension Observable { - /// Unwrapes a observable of an optional type, returning a observable of a non-optional type /// /// - Returns: A chained observable. @@ -29,19 +28,20 @@ public extension Observable { return Observable(value, parent: self) } } - + /// Converts the non-optional typed observable to an optional typed observable /// /// - Returns: An optional typed observable func optional() -> Observable { - return self.map { $0 as T? } + return map { $0 as T? } } - + /// Calls the closure when the observable is resolved with a nil value. /// /// - Parameter closure: The closure that will return a non-nil value. /// - Returns: A observable with a non-optional type. - func fill(_ executor: Executor = DirectExecutor(), _ closure: @escaping () -> K) -> Observable where T == K? { + func fill(_ executor: Executor = DirectExecutor(), _ closure: @escaping () -> K) -> Observable + where T == K? { return flatMap(executor) { value in guard let value = value else { return Observable(closure()) @@ -49,12 +49,13 @@ public extension Observable { return Observable(value) } } - + /// Calls the closure when the observable is resolved with a nil value. /// /// - Parameter closure: The closure that will return a non-optional observable. /// - Returns: A observable with a non-optional type. - func flatFill(_ executor: Executor = DirectExecutor(), _ closure: @escaping () -> Observable) -> Observable where T == K? { + func flatFill(_ executor: Executor = DirectExecutor(), + _ closure: @escaping () -> Observable) -> Observable where T == K? { return flatMap(executor) { value in guard let value = value else { return Observable(closure()) @@ -62,11 +63,12 @@ public extension Observable { return Observable(value) } } - + /// Performs a map of an optional observable when the value is defined. /// /// - Returns: A chained observable. - func unwrappedMap(_ executor: Executor = DirectExecutor(), _ closure: @escaping (K) -> P) -> Observable where T == K? { + func unwrappedMap(_ executor: Executor = DirectExecutor(), + _ closure: @escaping (K) -> P) -> Observable where T == K? { return flatMap(executor) { value in guard let value = value else { return Observable(nil) diff --git a/Sources/Harmony/Future/Observable/Observable+Time.swift b/Sources/Harmony/Future/Observable/Observable+Time.swift index f1458ffb..698a3e04 100644 --- a/Sources/Harmony/Future/Observable/Observable+Time.swift +++ b/Sources/Harmony/Future/Observable/Observable+Time.swift @@ -17,7 +17,6 @@ import Foundation public extension Observable { - /// Adds a delay to the then call. /// /// - Parameters: @@ -27,7 +26,7 @@ public extension Observable { func withDelay(_ interval: TimeInterval, queue: DispatchQueue) -> Observable { return Observable(parent: self) { resolver in queue.asyncAfter(deadline: .now() + interval) { [weak self] in - self?.resolve(success: {value in + self?.resolve(success: { value in resolver.set(value) }, failure: { error in resolver.set(error) @@ -35,7 +34,7 @@ public extension Observable { } } } - + /// Adds a sync delay (blocks current thread) after the future is resolved. /// /// - Parameters: @@ -45,7 +44,7 @@ public extension Observable { if interval == 0.0 { return self } - + return Observable(parent: self) { resolver in Thread.sleep(forTimeInterval: interval) resolve(success: { value in @@ -55,7 +54,7 @@ public extension Observable { }) } } - + /// Calls the then block after the given deadline /// /// - Parameters: @@ -65,7 +64,7 @@ public extension Observable { func after(_ deadline: DispatchTime, queue: DispatchQueue) -> Observable { return Observable(parent: self) { resolver in queue.asyncAfter(deadline: deadline) { [weak self] in - self?.resolve(success: {value in + self?.resolve(success: { value in resolver.set(value) }, failure: { error in resolver.set(error) @@ -73,7 +72,7 @@ public extension Observable { } } } - + /// Ensures the observable is called after the given date. /// If the date is earlier than now, nothing happens. /// diff --git a/Sources/Harmony/Future/Observable/Observable.swift b/Sources/Harmony/Future/Observable/Observable.swift index 86cb32e1..b717a5be 100644 --- a/Sources/Harmony/Future/Observable/Observable.swift +++ b/Sources/Harmony/Future/Observable/Observable.swift @@ -16,76 +16,78 @@ import Foundation -fileprivate struct ObservableError : Error { - let description : String +private struct ObservableError: Error { + let description: String init(_ description: String) { self.description = description } } -extension ObservableError { +private extension ObservableError { /// Observable content has already been set - fileprivate static let thenAlreadySet = ObservableError("Then already set: cannot set a new then closure once is already set.") - + static let thenAlreadySet = + ObservableError("Then already set: cannot set a new then closure once is already set.") + /// Observable content has already been set - fileprivate static let missingLambda = ObservableError("Observable cannot be sent as the then clousre hasn't been defined") + static let missingLambda = ObservableError("Observable cannot be sent as the then clousre hasn't been defined") } /// /// A ObservableResolver resolves a Observable. /// public struct ObservableResolver { - - private weak var observable : Observable? - + private weak var observable: Observable? + /// Main initializer /// /// - Parameter observable: The observable to resolve public init(_ observable: Observable) { self.observable = observable } - + /// Sets the observable value public func set(_ value: T) { observable?.set(value) } - + /// Sets the observable error public func set(_ error: Error) { observable?.set(error) } - + /// Sets the observable with another observable public func set(_ observable: Observable) { self.observable?.set(observable) } - + /// Sets the observable with a future public func set(_ future: Future) { observable?.set(future) } - - /// Sets the observable with a value if not error. Either the value or the error must be provided, otherwise a crash will happen. + + /// Sets the observable with a value if not error. Either the value or the error must be provided, otherwise a + // crash will happen. /// Note: error is prioritary, and if not error the value will be used. public func set(value: T?, error: Error?) { observable?.set(value: value, error: error) } - - public func onDeinit(_ closure : @escaping () -> Void) { + + public func onDeinit(_ closure: @escaping () -> Void) { observable?.onDeinit(closure) } } -extension ObservableResolver where T==Void { +public extension ObservableResolver where T == Void { /// Sets the observable with a void value - public func set() { + func set() { observable?.set() } } /// /// Observable class. Wrapper of an observable value of generic type T or an error. -/// Observable chains must be retained from the end, as an observable child retains its parent, but the parent does not retains it child (this differs from the Future). +/// Observable chains must be retained from the end, as an observable child retains its parent, but the parent does +// not retains it child (this differs from the Future). /// /// Therefore, this is a good usage of an observable: /// class MyViewController { @@ -103,15 +105,14 @@ extension ObservableResolver where T==Void { /// } /// public class Observable { - /// Observable states public enum State { case blank case waitingThen case waitingContent - + var localizedDescription: String { - switch (self) { + switch self { case .blank: return "Blank: empty observable" case .waitingThen: @@ -121,13 +122,13 @@ public class Observable { } } } - + /// The observable state public private(set) var state: State = .blank - + // A storng reference to the parent observable, when chaining observables - public private(set) var parent : Any? - + public private(set) var parent: Any? + /// The observable's result /// /// - value: a value was provided @@ -135,34 +136,34 @@ public class Observable { public indirect enum Result { case value(T) case error(Error) - + /// Returns the value or throws an error if exists @discardableResult public func get() throws -> T { switch self { - case .value(let v): + case let .value(v): return v - case .error(let e): + case let .error(e): throw e } } - + // Returns the value or if error, returns nil and sets the error @discardableResult public func get(error: inout Error?) -> T? { switch self { - case .value(let v): + case let .value(v): return v - case .error(let e): + case let .error(e): error = e return nil } } } - + /// The observable result. Using _ prefix as the "result" method returns synchronously the result. - internal var _result : Result? = nil - + internal var _result: Result? + // Private variables private var onContentSet: ((inout T?, inout Error?) -> Void)? private var onDeinit: (() -> Void)? @@ -170,86 +171,86 @@ public class Observable { private let lock = NSLock() private var success: ((_ value: T) -> Void)? private var failure: ((_ error: Error) -> Void)? - + /// Returns a hub associated to the current observable public private(set) lazy var hub = Hub(self) - + deinit { onDeinit?() } - + /// Default initializer public init(parent: Any? = nil) { self.parent = parent } - + /// Value initializer public init(_ value: T, parent: Any? = nil) { self.parent = parent set(value) } - + /// Error initializer public init(_ error: Error, parent: Any? = nil) { self.parent = parent set(error) } - + /// Observable initializer public init(_ observable: Observable) { set(observable) } - + /// Observable initializer public init(_ future: Future) { set(future) } - + /// Observable initializer public init(parent: Any? = nil, _ closure: (ObservableResolver) throws -> Void) { self.parent = parent do { let resolver = ObservableResolver(self) try closure(resolver) - } catch (let error) { + } catch { set(error) } } - + /// Observable initializer public convenience init(_ closure: () -> Error) { let error = closure() self.init(error) } - + /// Creates a new observable from self public func toObservable() -> Observable { return Observable(self) } - + /// Sets the observable value public func set(_ value: T) { set(value: value, error: nil) } - + /// Sets the observable error public func set(_ error: Error) { set(value: nil, error: error) } - + /// Sets the observable with another observable public func set(_ observable: Observable) { // Current observable retains the incoming observable - self.parent = observable - + parent = observable + // Incoming observable DOES NOT retain the current observable observable.resolve(success: { [weak self] value in self?.set(value) - }, failure: { [weak self] error in - self?.set(error) + }, failure: { [weak self] error in + self?.set(error) }) } - + public func set(_ future: Future) { // Current observable does not retain the incoming future // Incoming future will retain the current observable @@ -259,23 +260,24 @@ public class Observable { self.set(error) }) } - - /// Sets the observable with a value if not error. Either the value or the error must be provided, otherwise a crash will happen. + + /// Sets the observable with a value if not error. Either the value or the error must be provided, otherwise a + // crash will happen. /// Note: error is prioritary, and if not error the value will be used. public func set(value: T?, error: Error?) { - var value : T? = value - var error : Error? = error + var value: T? = value + var error: Error? = error if let onContentSet = onContentSet { onContentSet(&value, &error) } - + lock { if let error = error { _result = .error(error) } else { _result = .value(value!) } - + if success != nil || failure != nil { // Resolve the then closure send() @@ -287,7 +289,7 @@ public class Observable { } } } - + /// Clears the stored value and the referenced then closures. /// Mainly, resets the state of the observable to blank. /// @@ -300,27 +302,26 @@ public class Observable { } return self } - + /// Closure called right after content is set, without waiting the then closure. - /// Note that multiple calls to this method are discouraged, resulting with only one onContentSet closure being called. + /// Note that multiple calls to this method are discouraged, resulting with only one onContentSet closure being + // called. /// Note too that if the observable has already been sent, this closure is not called. /// /// - Parameter closure: The code to be executed public func onSet(_ closure: @escaping () -> Void) { - onContentSet = { (_,_) in + onContentSet = { _, _ in closure() } } - - - + /// Closure called on deinit /// /// - Parameter closure: The closure to be executed on deinit. public func onDeinit(_ closure: @escaping () -> Void) { onDeinit = closure } - + /// Closure called right after content is set, without waiting the then closure. /// Multiple calls to this method are discouraged, resulting with only one onContentSet closure being called. /// Note too that if the observable has already been sent, this closure is not called. @@ -329,7 +330,7 @@ public class Observable { public func onSet(_ closure: @escaping (inout T?, inout Error?) -> Void) { onContentSet = closure } - + /// Then closure: delivers the value or the error internal func resolve(success: @escaping (T) -> Void = { _ in }, failure: @escaping (Error) -> Void = { _ in }) { @@ -343,24 +344,22 @@ public class Observable { } } } - + /// Deliver the result syncrhonously. This method might block the calling thread. /// Note that the result can only be delivered once if the observable is not reactive. - public var result : Result { - get { - switch state { - case .waitingThen: - return _result! - case .blank: - semaphore = DispatchSemaphore(value: 0) - semaphore!.wait() - return self.result - case .waitingContent: - fatalError(ObservableError.thenAlreadySet.description) - } + public var result: Result { + switch state { + case .waitingThen: + return _result! + case .blank: + semaphore = DispatchSemaphore(value: 0) + semaphore!.wait() + return self.result + case .waitingContent: + fatalError(ObservableError.thenAlreadySet.description) } } - + /// Main then method to obtain the promised value. /// /// - Parameters: @@ -368,9 +367,10 @@ public class Observable { /// - success: The then closure. /// - Returns: A chained observable @discardableResult - public func then(_ executor : Executor = MainDirectExecutor(), _ success: @escaping (T) -> Void) -> Observable { + public func then(_ executor: Executor = MainDirectExecutor(), + _ success: @escaping (T) -> Void) -> Observable { return Observable(parent: self) { resolver in - resolve(success: {value in + resolve(success: { value in executor.submit { success(value) resolver.set(value) @@ -382,7 +382,7 @@ public class Observable { }) } } - + /// Main failure method to obtain the promised error. /// /// - Parameters: @@ -390,9 +390,10 @@ public class Observable { /// - failure: The fail closure. /// - Returns: A chained observable @discardableResult - public func fail(_ executor : Executor = MainDirectExecutor(), _ failure: @escaping (Error) -> Void) -> Observable { + public func fail(_ executor: Executor = MainDirectExecutor(), + _ failure: @escaping (Error) -> Void) -> Observable { return Observable(parent: self) { resolver in - resolve(success: {value in + resolve(success: { value in executor.submit { resolver.set(value) } @@ -404,16 +405,16 @@ public class Observable { }) } } - + private func send() { switch _result! { - case .error(let error): + case let .error(error): guard let failure = failure else { print(ObservableError.missingLambda.description) return } failure(error) - case .value(let value): + case let .value(value): guard let success = success else { print(ObservableError.missingLambda.description) return @@ -421,6 +422,7 @@ public class Observable { success(value) } } + // Private lock method private func lock(_ closure: () -> Void) { lock.lock() @@ -429,33 +431,31 @@ public class Observable { } } - /// To String extension -extension Observable : CustomStringConvertible, CustomDebugStringConvertible { +extension Observable: CustomStringConvertible, CustomDebugStringConvertible { public var description: String { switch state { case .blank: return "Empty observable. Waiting for value, error and then closure." case .waitingThen: switch _result! { - case .error(let error): + case let .error(error): return "Observable waiting for then closure and error set to: \(error)" - case .value(let value): + case let .value(value): return "Observable waiting for then closure and value set to: \(value)" } case .waitingContent: return "Observable then closure set. Waiting for value or error." } } - + public var debugDescription: String { return description } } -extension Observable where T==Void { - public func set() { - set(Void()) +public extension Observable where T == Void { + func set() { + set(()) } } - diff --git a/Sources/Harmony/Future/Observable/ObservableHub.swift b/Sources/Harmony/Future/Observable/ObservableHub.swift index 7a2a2118..25b4bbe3 100644 --- a/Sources/Harmony/Future/Observable/ObservableHub.swift +++ b/Sources/Harmony/Future/Observable/ObservableHub.swift @@ -16,26 +16,26 @@ import Foundation -extension Observable { - +public extension Observable { + // swiftlint:disable type_name + /// /// A hub acts as a cloner of a given observable. /// It can create subscribed observables, making them trigger when the main one triggers. /// - public class Hub { - - private weak var observable : Observable? + class Hub { + private weak var observable: Observable? private let lock = NSLock() - private var subscribers : NSHashTable> = NSHashTable.weakObjects() - + private var subscribers: NSHashTable> = NSHashTable.weakObjects() + /// Default initializer. /// Note that this class will open the then closure of the observable passed on this method. /// /// - Parameter observable: The observable to be used - public init (_ observable: Observable) { + public init(_ observable: Observable) { self.observable = observable - - observable.resolve(success: {value in + + observable.resolve(success: { value in self.lock.lock() self.subscribers.allObjects.forEach { $0.set(value) } self.lock.unlock() @@ -45,27 +45,27 @@ extension Observable { self.lock.unlock() }) } - + /// Creates a new observable. public func subscribe() -> Observable { let subscriber = Observable(parent: observable) - + lock.lock() subscribers.add(subscriber) lock.unlock() - + // Sets the current value/error if exists if let result = observable?._result { switch result { - case .value(let value): + case let .value(value): subscriber.set(value) - case .error(let error): + case let .error(error): subscriber.set(error) } } return subscriber } - + /// /// Cleans all observables /// diff --git a/Sources/Harmony/Security/KeychainDataSource.swift b/Sources/Harmony/Security/KeychainDataSource.swift index d57c5514..199e7134 100644 --- a/Sources/Harmony/Security/KeychainDataSource.swift +++ b/Sources/Harmony/Security/KeychainDataSource.swift @@ -19,18 +19,17 @@ import Foundation // // Provides a keychain service for a key value interface. // -public class KeychainDataSource : GetDataSource, PutDataSource, DeleteDataSource where T : Codable { - - private let keychain : KeychainService - - public init(_ keychain : KeychainService) { +public class KeychainDataSource: GetDataSource, PutDataSource, DeleteDataSource where T: Codable { + private let keychain: KeychainService + + public init(_ keychain: KeychainService) { self.keychain = keychain } - + public func get(_ query: Query) -> Future { switch query { case let query as KeyQuery: - guard let value : T = keychain.get(query.key) else { + guard let value: T = keychain.get(query.key) else { return Future(CoreError.NotFound()) } return Future(value) @@ -38,22 +37,23 @@ public class KeychainDataSource : GetDataSource, PutDataSource, DeleteDataSou query.fatalError(.get, self) } } - + public func getAll(_ query: Query) -> Future<[T]> { switch query { case let query as KeyQuery: - guard let nsarray : NSArray = keychain.get(query.key) else { + guard let nsarray: NSArray = keychain.get(query.key) else { return Future(CoreError.NotFound()) } guard let array = nsarray as? [T] else { - return Future(CoreError.Failed("NSArray to Array<\(String(describing:T.self))> cast failed for key \(query.key)")) + return Future(CoreError + .Failed("NSArray to Array<\(String(describing: T.self))> cast failed for key \(query.key)")) } return Future(array) default: query.fatalError(.getAll, self) } } - + @discardableResult public func put(_ value: T?, in query: Query) -> Future { switch query { @@ -64,14 +64,16 @@ public class KeychainDataSource : GetDataSource, PutDataSource, DeleteDataSou switch keychain.set(value, forKey: query.key) { case .success: return Future(value) - case .failed(let status): - return Future(CoreError.OSStatusFailure(status, "Keychain failed to set value for key \(query.key) (OSStatus \(status))")) + case let .failed(status): + return Future(CoreError + .OSStatusFailure(status, + "Keychain failed to set value for key \(query.key) (OSStatus \(status))")) } default: query.fatalError(.put, self) } } - + @discardableResult public func putAll(_ array: [T], in query: Query) -> Future<[T]> { switch query { @@ -81,39 +83,46 @@ public class KeychainDataSource : GetDataSource, PutDataSource, DeleteDataSou switch try keychain.set(nsarray, forKey: query.key) { case .success: r.set(array) - case .failed(let status): - throw CoreError.OSStatusFailure(status, "Keychain failed to set value for key \(query.key) (OSStatus \(status))") + case let .failed(status): + throw CoreError.OSStatusFailure( + status, + "Keychain failed to set value for key \(query.key) (OSStatus \(status))" + ) } } default: query.fatalError(.putAll, self) } } - + @discardableResult public func delete(_ query: Query) -> Future { switch query { case let query as KeyQuery: switch keychain.delete(query.key) { case .success: - return Future(Void()) - case .failed(let status): - return Future(CoreError.OSStatusFailure(status, "Keychain failed to delete value for key \(query.key) (OSStatus \(status))")) + return Future(()) + case let .failed(status): + return Future(CoreError + .OSStatusFailure(status, + "Keychain failed to delete value for key \(query.key) (OSStatus \(status))")) } default: query.fatalError(.delete, self) } } - + @discardableResult public func deleteAll(_ query: Query) -> Future { switch query { case let query as KeyQuery: switch keychain.delete(query.key) { case .success: - return Future(Void()) - case .failed(let status): - return Future(CoreError.OSStatusFailure(status, "Keychain failed to delete value for key \(query.key) (OSStatus \(status))")) + return Future(()) + case let .failed(status): + return Future(CoreError + .OSStatusFailure(status, + "Keychain failed to delete value for key \(query.key) (OSStatus \(status))")) } default: query.fatalError(.deleteAll, self) diff --git a/Sources/Harmony/Security/KeychainGetInteractor.swift b/Sources/Harmony/Security/KeychainGetInteractor.swift index 16f1888d..ef7dca39 100644 --- a/Sources/Harmony/Security/KeychainGetInteractor.swift +++ b/Sources/Harmony/Security/KeychainGetInteractor.swift @@ -23,37 +23,37 @@ private let defaultExecutor = DispatchQueueExecutor() /// @available(*, deprecated, message: "Use the KeychainDataSource and Repository pattern instead.") public class KeychainGetInteractor { - - private let executor : Executor - private let keychain : KeychainService - private let key : String - + private let executor: Executor + private let keychain: KeychainService + private let key: String + /// Default initializer /// /// - Parameters: /// - executor: The executor to run the interactor /// - keychain: The keychain instance /// - key: The key to access the user defaults - public init(_ executor: Executor, _ keychain: KeychainService, _ key : String) { + public init(_ executor: Executor, _ keychain: KeychainService, _ key: String) { self.executor = executor self.keychain = keychain self.key = key } - + /// Convenience initializer. - /// This initializer uses a shared executor on all KeychainGetInteractor instances and the default service of Keychain(). + /// This initializer uses a shared executor on all KeychainGetInteractor instances and the default service of + // Keychain(). /// /// - Parameter key: The key to access the user defaults - public convenience init(_ key : String) { + public convenience init(_ key: String) { self.init(defaultExecutor, KeychainService(), key) } - + /// Main execution method /// /// - Returns: A future for the result value or error - public func execute() -> Future where T:Decodable { + public func execute() -> Future where T: Decodable { return executor.submit { future in - if let result : T = self.keychain.get(self.key) { + if let result: T = self.keychain.get(self.key) { future.set(result) } else { future.set(CoreError.NotFound("Value not found for key \(self.key)")) diff --git a/Sources/Harmony/Security/KeychainService.swift b/Sources/Harmony/Security/KeychainService.swift index 6c175e1c..d35725c0 100644 --- a/Sources/Harmony/Security/KeychainService.swift +++ b/Sources/Harmony/Security/KeychainService.swift @@ -1,4 +1,3 @@ - // Copyright 2017 Mobile Jazz SL // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,24 +20,22 @@ import Security /// A user-friendly interface to store Data inside the keychain. /// public class KeychainService { - /// Arguments for the keychain queries - private struct kSec { + private enum kSec { static let classGenericPassword = NSString(format: kSecClassGenericPassword) - static let `class` = NSString(format: kSecClass) - static let attrService = NSString(format: kSecAttrService) - static let attrAccount = NSString(format: kSecAttrAccount) - static let returnAttributes = NSString(format: kSecReturnAttributes) - static let valueData = NSString(format: kSecValueData) - static let matchLimit = NSString(format: kSecMatchLimit) - static let matchLimitOne = NSString(format: kSecMatchLimitOne) - static let returnData = NSString(format: kSecReturnData) + static let `class` = NSString(format: kSecClass) + static let attrService = NSString(format: kSecAttrService) + static let attrAccount = NSString(format: kSecAttrAccount) + static let returnAttributes = NSString(format: kSecReturnAttributes) + static let valueData = NSString(format: kSecValueData) + static let matchLimit = NSString(format: kSecMatchLimit) + static let matchLimitOne = NSString(format: kSecMatchLimitOne) + static let returnData = NSString(format: kSecReturnData) } - /// The Keychain's service name. - public let service : String - + public let service: String + /// Main initializer /// The initializer needs a service name. Each service will identify an independent keychain memory zone. /// @@ -46,34 +43,36 @@ public class KeychainService { public init(_ service: String = "com.default.service") { self.service = service } - + /// A keychain operation result. /// /// - success: A success result /// - failed: A failre result, including the status code. - public enum Result : Error { + public enum Result: Error { case success case failed(OSStatus) } - + /// Fetches the Data stored in the keychain for the given key. /// /// - Parameter key: The key. /// - Returns: The stored Data or nil. public func get(_ key: String) -> Data? { - let query = NSDictionary(objects:[kSec.classGenericPassword, service, key, kCFBooleanTrue!, kSec.matchLimitOne], - forKeys: [kSec.class, kSec.attrService, kSec.attrAccount, kSec.returnData, kSec.matchLimit]) - - var dataRef : CFTypeRef? + let query = NSDictionary( + objects: [kSec.classGenericPassword, service, key, kCFBooleanTrue!, kSec.matchLimitOne], + forKeys: [kSec.class, kSec.attrService, kSec.attrAccount, kSec.returnData, kSec.matchLimit] + ) + + var dataRef: CFTypeRef? let status = SecItemCopyMatching(query as CFDictionary, &dataRef) - + if status == errSecSuccess { let data = dataRef as? Data return data } return nil } - + /// Sets the given Data for the given key inside the keychain. /// /// - Parameters: @@ -82,14 +81,19 @@ public class KeychainService { /// - Returns: The operation result. @discardableResult public func set(_ data: Data, forKey key: String) -> Result { - let query = NSMutableDictionary(objects:[kSec.classGenericPassword, service, key, kCFBooleanTrue!], - forKeys:[kSec.class, kSec.attrService, kSec.attrAccount, kSec.returnAttributes]) + let query = NSMutableDictionary(objects: [kSec.classGenericPassword, service, key, kCFBooleanTrue!], + forKeys: [ + kSec.class, + kSec.attrService, + kSec.attrAccount, + kSec.returnAttributes, + ]) // Delete first and old entry let deleteStatus = SecItemDelete(query as CFDictionary) if deleteStatus != errSecSuccess { // Nothing to do } - + // Setting the data query.setObject(data, forKey: kSec.valueData) let status = SecItemAdd(query as CFDictionary, nil) @@ -99,15 +103,20 @@ public class KeychainService { return .success } } - + /// Deletes the Data associated to the given key. /// /// - Parameter key: The key. /// - Returns: The operation result. @discardableResult public func delete(_ key: String) -> Result { - let query = NSMutableDictionary(objects:[kSec.classGenericPassword, service, key, kCFBooleanTrue!], - forKeys:[kSec.class, kSec.attrService, kSec.attrAccount, kSec.returnAttributes]) + let query = NSMutableDictionary(objects: [kSec.classGenericPassword, service, key, kCFBooleanTrue!], + forKeys: [ + kSec.class, + kSec.attrService, + kSec.attrAccount, + kSec.returnAttributes, + ]) // Delete first and old entry let status = SecItemDelete(query as CFDictionary) if status != errSecSuccess { @@ -119,13 +128,12 @@ public class KeychainService { } public extension KeychainService { - /// Custom getter for Decodable conforming types. /// /// - Parameter key: The key. /// - Returns: The type stored in the keychain or nil. - func get(_ key: String) ->T? where T:Decodable { - guard let data : Data = get(key) else { + func get(_ key: String) -> T? where T: Decodable { + guard let data: Data = get(key) else { return nil } do { @@ -135,14 +143,14 @@ public extension KeychainService { return nil } } - + /// Custom setter for Encodable conforming types. /// /// - Parameters: /// - value: The Encodable conforming value. /// - key: The key. /// - Returns: The operation result. - func set(_ value: T, forKey key: String) -> Result where T:Encodable { + func set(_ value: T, forKey key: String) -> Result where T: Encodable { do { let data = try PropertyListEncoder().encode(value) return set(data, forKey: key) @@ -150,15 +158,15 @@ public extension KeychainService { return KeychainService.Result.failed(0) } } - + /// Custom getter for NSCoding conforming types. /// /// - Parameter key: The key. /// - Returns: The NSCoding conforming type stored in the keychain or nil. func get(_ key: String) -> T? where T: NSCoding, T: NSObject { - if let data : Data = get(key) { -// if let value = try? NSKeyedUnarchiver.unarchivedObject(ofClass: T.self, from: data) { -// return value + if let data: Data = get(key) { + // if let value = try? NSKeyedUnarchiver.unarchivedObject(ofClass: T.self, from: data) { + // return value if let value = NSKeyedUnarchiver.unarchiveObject(with: data) { return (value as! T) } else { @@ -167,7 +175,7 @@ public extension KeychainService { } return nil } - + /// Custom setter for NSCoding conforming types. /// /// - Parameters: @@ -176,7 +184,7 @@ public extension KeychainService { /// - Returns: The operation result. @discardableResult func set(_ value: T, forKey key: String) throws -> Result { // where T: NSCoding { -// let data = try NSKeyedArchiver.archivedData(withRootObject: value, requiringSecureCoding: false) + // let data = try NSKeyedArchiver.archivedData(withRootObject: value, requiringSecureCoding: false) let data = NSKeyedArchiver.archivedData(withRootObject: value) return set(data, forKey: key) } diff --git a/Sources/Harmony/Security/KeychainSetInteractor.swift b/Sources/Harmony/Security/KeychainSetInteractor.swift index 24c897a2..7b214d36 100644 --- a/Sources/Harmony/Security/KeychainSetInteractor.swift +++ b/Sources/Harmony/Security/KeychainSetInteractor.swift @@ -23,42 +23,45 @@ private let defaultExecutor = DispatchQueueExecutor() /// @available(*, deprecated, message: "Use the KeychainDataSource and Repository pattern instead.") public class KeychainSetInteractor { - - private let executor : Executor - private let keychain : KeychainService - private let key : String - + private let executor: Executor + private let keychain: KeychainService + private let key: String + /// Default initializer /// /// - Parameters: /// - executor: The executor to run the interactor /// - keychain: The keychain instance /// - key: The key to access the user defaults - public init(_ executor: Executor, _ keychain: KeychainService, _ key : String) { + public init(_ executor: Executor, _ keychain: KeychainService, _ key: String) { self.executor = executor self.keychain = keychain self.key = key } - + /// Convenience initializer. - /// This initializer uses a shared executor on all KeychainSetInteractor instances and the default service of Keychain(). + /// This initializer uses a shared executor on all KeychainSetInteractor instances and the default service of + // Keychain(). /// /// - Parameter key: The key to access the user defaults - public convenience init(_ key : String) { + public convenience init(_ key: String) { self.init(defaultExecutor, KeychainService(), key) } - + /// Main execution method /// /// - Returns: A future for the result value or error - public func execute(_ value : T) -> Future where T:Encodable { + public func execute(_ value: T) -> Future where T: Encodable { return executor.submit { resolver in let result = self.keychain.set(value, forKey: self.key) switch result { case .success: resolver.set(true) - case .failed(let status): - resolver.set(CoreError.OSStatusFailure(status, "Keychain failed to set value for key \(self.key) (OSStatus \(status))")) + case let .failed(status): + resolver + .set(CoreError + .OSStatusFailure(status, + "Keychain failed to set value for key \(self.key) (OSStatus \(status))")) } } } diff --git a/Sources/Harmony/Security/SecureKey.swift b/Sources/Harmony/Security/SecureKey.swift index 96aec3be..5790a34b 100644 --- a/Sources/Harmony/Security/SecureKey.swift +++ b/Sources/Harmony/Security/SecureKey.swift @@ -21,25 +21,24 @@ import Security /// Stores securely inside the Keychain an auto-generated data blob (aka. Key). /// public class SecureKey { - /// Arguments for the keychain queries - private struct kSec { - static let `class` = NSString(format: kSecClass) - static let classKey = NSString(format: kSecClassKey) - static let attrApplicationTag = NSString(format: kSecAttrApplicationTag) - static let attrKeySizeInBits = NSString(format: kSecAttrKeySizeInBits) - static let returnData = NSString(format: kSecReturnData) - static let attrAccessible = NSString(format: kSecAttrAccessible) + private enum kSec { + static let `class` = NSString(format: kSecClass) + static let classKey = NSString(format: kSecClassKey) + static let attrApplicationTag = NSString(format: kSecAttrApplicationTag) + static let attrKeySizeInBits = NSString(format: kSecAttrKeySizeInBits) + static let returnData = NSString(format: kSecReturnData) + static let attrAccessible = NSString(format: kSecAttrAccessible) static let attrAccessibleAlways = NSString(format: kSecAttrAccessibleAfterFirstUnlock) - static let valueData = NSString(format: kSecValueData) + static let valueData = NSString(format: kSecValueData) } - + /// The key identifier - public let identifier : String - + public let identifier: String + /// The key length - public let length : size_t - + public let length: size_t + /// Main initializer /// /// - Parameters: @@ -49,7 +48,7 @@ public class SecureKey { self.identifier = identifier self.length = length } - + /// Generates a new key and stores it inside the keychain. /// /// - Throws: CoreError.OSStatusFailure if fails. @@ -57,23 +56,23 @@ public class SecureKey { guard let tag = identifier.data(using: String.Encoding.utf8) else { throw CoreError.Failed("Failed to convert the SecureKey identifier to data") } - + guard let keyData = Data(randomOfLength: UInt(length)) else { throw CoreError.Failed("Failed to generate random key") } - - let query = NSDictionary(objects:[kSec.classKey, tag], - forKeys:[kSec.class, kSec.attrApplicationTag]) - - let attributesToUpdate = NSDictionary(objects:[keyData], - forKeys:[kSec.valueData]) - + + let query = NSDictionary(objects: [kSec.classKey, tag], + forKeys: [kSec.class, kSec.attrApplicationTag]) + + let attributesToUpdate = NSDictionary(objects: [keyData], + forKeys: [kSec.valueData]) + let status = SecItemUpdate(query as CFDictionary, attributesToUpdate as CFDictionary) guard status == errSecSuccess else { throw CoreError.OSStatusFailure(status) } } - + /// Removes the stored key from the keychain. /// /// - Throws: CoreError.OSStatusFailure if fails. @@ -81,16 +80,16 @@ public class SecureKey { guard let tag = identifier.data(using: String.Encoding.utf8) else { throw CoreError.Failed("Failed to convert the SecureKey identifier to data") } - - let query = NSDictionary(objects:[kSec.classKey, tag], + + let query = NSDictionary(objects: [kSec.classKey, tag], forKeys: [kSec.class, kSec.attrApplicationTag]) - + let status = SecItemDelete(query as CFDictionary) guard status == errSecSuccess else { throw CoreError.OSStatusFailure(status) } } - + /// Returns the key. /// Note that if there is no key previously stored, this method will generate a new key. /// @@ -100,9 +99,14 @@ public class SecureKey { guard let tag = identifier.data(using: String.Encoding.utf8) else { throw CoreError.Failed("Failed to convert the SecureKey identifier to data") } - let query = NSDictionary(objects:[kSec.classKey, tag, length, true], - forKeys: [kSec.class, kSec.attrApplicationTag, kSec.attrKeySizeInBits, kSec.returnData]) - var dataRef : CFTypeRef? + let query = NSDictionary(objects: [kSec.classKey, tag, length, true], + forKeys: [ + kSec.class, + kSec.attrApplicationTag, + kSec.attrKeySizeInBits, + kSec.returnData, + ]) + var dataRef: CFTypeRef? let status = SecItemCopyMatching(query as CFDictionary, &dataRef) switch status { case errSecSuccess: @@ -111,16 +115,25 @@ public class SecureKey { case -25308: // errKCInteractionNotAllowed // If reading fails because app is not allowed (device locked) // Fix cannot be applied because we cannot read the current keychain item. - throw CoreError.OSStatusFailure(status, "Failed to fetch Keychain because the device is locked (OSStatus: \(status)).") + throw CoreError.OSStatusFailure( + status, + "Failed to fetch Keychain because the device is locked (OSStatus: \(status))." + ) default: // If no pre-existing key from this application guard let keyData = Data(randomOfLength: UInt(length)) else { throw CoreError.Failed("Failed to generate random key") } - - let query = NSDictionary(objects:[kSec.classKey, tag, length, kSec.attrAccessibleAlways, keyData], - forKeys:[kSec.class, kSec.attrApplicationTag, kSec.attrKeySizeInBits, kSec.attrAccessible, kSec.valueData]) + + let query = NSDictionary(objects: [kSec.classKey, tag, length, kSec.attrAccessibleAlways, keyData], + forKeys: [ + kSec.class, + kSec.attrApplicationTag, + kSec.attrKeySizeInBits, + kSec.attrAccessible, + kSec.valueData, + ]) let status = SecItemAdd(query as CFDictionary, nil) guard status == errSecSuccess else { throw CoreError.OSStatusFailure(status, "Failed to insert key data with OSStatus: \(status)") diff --git a/Sources/HarmonyTesting/Data/DataSourceSpy.swift b/Sources/HarmonyTesting/Data/DataSourceSpy.swift index 1234cca8..21a26d44 100644 --- a/Sources/HarmonyTesting/Data/DataSourceSpy.swift +++ b/Sources/HarmonyTesting/Data/DataSourceSpy.swift @@ -18,22 +18,21 @@ import Foundation import Harmony /// A GetDataSource spy that records all calls. -public class GetDataSourceSpy : GetDataSource where D.T == T { - +public class GetDataSourceSpy: GetDataSource where D.T == T { public private(set) var getCalls: [Query] = [] public private(set) var getAllCalls: [Query] = [] - + private let dataSource: D - + public init(_ dataSource: D) { self.dataSource = dataSource } - + public func get(_ query: Query) -> Future { getCalls.append(query) return dataSource.get(query) } - + public func getAll(_ query: Query) -> Future<[T]> { getAllCalls.append(query) return dataSource.getAll(query) @@ -41,22 +40,21 @@ public class GetDataSourceSpy : GetDataSource where D.T == } /// A PutDataSource spy that records all calls. -public class PutDataSourceSpy : PutDataSource where D.T == T { - +public class PutDataSourceSpy: PutDataSource where D.T == T { public private(set) var putCalls: [(value: T?, query: Query)] = [] public private(set) var putAllCalls: [(array: [T], query: Query)] = [] - + private let dataSource: D - + public init(_ dataSource: D) { self.dataSource = dataSource } - + public func put(_ value: T?, in query: Query) -> Future { putCalls.append((value, query)) return dataSource.put(value, in: query) } - + public func putAll(_ array: [T], in query: Query) -> Future<[T]> { putAllCalls.append((array, query)) return dataSource.putAll(array, in: query) @@ -64,22 +62,21 @@ public class PutDataSourceSpy : PutDataSource where D.T == } /// A DeleteDataSource spy that records all calls. -public class DeleteDataSourceSpy : DeleteDataSource { - +public class DeleteDataSourceSpy: DeleteDataSource { public private(set) var deleteCalls: [Query] = [] public private(set) var deleteAllCalls: [Query] = [] - + private let dataSource: D - + public init(_ dataSource: D) { self.dataSource = dataSource } - + public func delete(_ query: Query) -> Future { deleteCalls.append(query) return dataSource.delete(query) } - + public func deleteAll(_ query: Query) -> Future { deleteAllCalls.append(query) return dataSource.deleteAll(query) @@ -87,48 +84,48 @@ public class DeleteDataSourceSpy : DeleteDataSource { } /// A DataSource spy that records all calls. -public class DataSourceSpy : GetDataSource, PutDataSource, DeleteDataSource where D:GetDataSource, D:PutDataSource, D:DeleteDataSource, D.T == T { - +public class DataSourceSpy: GetDataSource, PutDataSource, DeleteDataSource where D: GetDataSource, + D: PutDataSource, D: DeleteDataSource, D.T == T { public private(set) var getCalls: [Query] = [] public private(set) var getAllCalls: [Query] = [] - + private let dataSource: D - + public init(_ dataSource: D) { self.dataSource = dataSource } - + public func get(_ query: Query) -> Future { getCalls.append(query) return dataSource.get(query) } - + public func getAll(_ query: Query) -> Future<[T]> { getAllCalls.append(query) return dataSource.getAll(query) } - + public private(set) var putCalls: [(value: T?, query: Query)] = [] public private(set) var putAllCalls: [(array: [T], query: Query)] = [] - + public func put(_ value: T?, in query: Query) -> Future { putCalls.append((value, query)) return dataSource.put(value, in: query) } - + public func putAll(_ array: [T], in query: Query) -> Future<[T]> { putAllCalls.append((array, query)) return dataSource.putAll(array, in: query) } - + public private(set) var deleteCalls: [Query] = [] public private(set) var deleteAllCalls: [Query] = [] - + public func delete(_ query: Query) -> Future { deleteCalls.append(query) return dataSource.delete(query) } - + public func deleteAll(_ query: Query) -> Future { deleteAllCalls.append(query) return dataSource.deleteAll(query) diff --git a/Sources/HarmonyTesting/Data/MockObjectValidation.swift b/Sources/HarmonyTesting/Data/MockObjectValidation.swift index 7c99f44a..3dab276c 100644 --- a/Sources/HarmonyTesting/Data/MockObjectValidation.swift +++ b/Sources/HarmonyTesting/Data/MockObjectValidation.swift @@ -12,7 +12,7 @@ import Harmony public struct MockObjectValidation: ObjectValidation { public let objectValid: Bool public let arrayValid: Bool - + /// Default initializer. /// - Parameters: /// - objectValid: The bool value to return upon object validation. Default value is true. @@ -21,12 +21,12 @@ public struct MockObjectValidation: ObjectValidation { self.objectValid = objectValid self.arrayValid = arrayValid } - - public func isObjectValid(_ object: T) -> Bool { + + public func isObjectValid(_: T) -> Bool { return objectValid } - - public func isArrayValid(_ objects: [T]) -> Bool { + + public func isArrayValid(_: [T]) -> Bool { return arrayValid } } diff --git a/Sources/HarmonyTesting/Data/RepositorySpy.swift b/Sources/HarmonyTesting/Data/RepositorySpy.swift index bab5460f..4a76cc65 100644 --- a/Sources/HarmonyTesting/Data/RepositorySpy.swift +++ b/Sources/HarmonyTesting/Data/RepositorySpy.swift @@ -18,22 +18,23 @@ import Foundation import Harmony /// A GetRepository spy that records all calls. -public class GetRepositorySpy : GetRepository where D.T == T { - +public class GetRepositorySpy: GetRepository where D.T == T { + // swiftlint:disable large_tuple + public private(set) var getCalls: [(query: Query, operation: Harmony.Operation)] = [] public private(set) var getAllCalls: [(query: Query, operation: Harmony.Operation)] = [] - + private let repository: D - + public init(_ dataSource: D) { - self.repository = dataSource + repository = dataSource } - + public func get(_ query: Query, operation: Harmony.Operation) -> Future { getCalls.append((query, operation)) return repository.get(query, operation: operation) } - + public func getAll(_ query: Query, operation: Harmony.Operation) -> Future<[T]> { getAllCalls.append((query, operation)) return repository.getAll(query, operation: operation) @@ -41,22 +42,21 @@ public class GetRepositorySpy : GetRepository where D.T == } /// A PutRepository spy that records all calls. -public class PutRepositorySpy : PutRepository where D.T == T { - +public class PutRepositorySpy: PutRepository where D.T == T { public private(set) var putCalls: [(value: T?, query: Query, operation: Harmony.Operation)] = [] public private(set) var putAllCalls: [(array: [T], query: Query, operation: Harmony.Operation)] = [] - + private let repository: D - + public init(_ dataSource: D) { - self.repository = dataSource + repository = dataSource } - + public func put(_ value: T?, in query: Query, operation: Harmony.Operation) -> Future { putCalls.append((value, query, operation)) return repository.put(value, in: query, operation: operation) } - + public func putAll(_ array: [T], in query: Query, operation: Harmony.Operation) -> Future<[T]> { putAllCalls.append((array, query, operation)) return repository.putAll(array, in: query, operation: operation) @@ -64,22 +64,21 @@ public class PutRepositorySpy : PutRepository where D.T == } /// A DeleteRepository spy that records all calls. -public class DeleteRepositorySpy : DeleteRepository { - +public class DeleteRepositorySpy: DeleteRepository { public private(set) var deleteCalls: [(query: Query, operation: Harmony.Operation)] = [] public private(set) var deleteAllCalls: [(query: Query, operation: Harmony.Operation)] = [] - + private let repository: D - + public init(_ dataSource: D) { - self.repository = dataSource + repository = dataSource } - + public func delete(_ query: Query, operation: Harmony.Operation) -> Future { deleteCalls.append((query, operation)) return repository.delete(query, operation: operation) } - + public func deleteAll(_ query: Query, operation: Harmony.Operation) -> Future { deleteAllCalls.append((query, operation)) return repository.deleteAll(query, operation: operation) @@ -87,48 +86,48 @@ public class DeleteRepositorySpy : DeleteRepository { } /// A Repository spy that records all calls. -public class RepositorySpy : GetRepository, PutRepository, DeleteRepository where D:GetRepository, D:PutRepository, D:DeleteRepository, D.T == T { - +public class RepositorySpy: GetRepository, PutRepository, DeleteRepository where D: GetRepository, + D: PutRepository, D: DeleteRepository, D.T == T { public private(set) var getCalls: [(query: Query, operation: Harmony.Operation)] = [] public private(set) var getAllCalls: [(query: Query, operation: Harmony.Operation)] = [] - + private let repository: D - + public init(_ dataSource: D) { - self.repository = dataSource + repository = dataSource } - + public func get(_ query: Query, operation: Harmony.Operation) -> Future { getCalls.append((query, operation)) return repository.get(query, operation: operation) } - + public func getAll(_ query: Query, operation: Harmony.Operation) -> Future<[T]> { getAllCalls.append((query, operation)) return repository.getAll(query, operation: operation) } - + public private(set) var putCalls: [(value: T?, query: Query, operation: Harmony.Operation)] = [] public private(set) var putAllCalls: [(array: [T], query: Query, operation: Harmony.Operation)] = [] - + public func put(_ value: T?, in query: Query, operation: Harmony.Operation) -> Future { putCalls.append((value, query, operation)) return repository.put(value, in: query, operation: operation) } - + public func putAll(_ array: [T], in query: Query, operation: Harmony.Operation) -> Future<[T]> { putAllCalls.append((array, query, operation)) return repository.putAll(array, in: query, operation: operation) } - + public private(set) var deleteCalls: [(query: Query, operation: Harmony.Operation)] = [] public private(set) var deleteAllCalls: [(query: Query, operation: Harmony.Operation)] = [] - + public func delete(_ query: Query, operation: Harmony.Operation) -> Future { deleteCalls.append((query, operation)) return repository.delete(query, operation: operation) } - + public func deleteAll(_ query: Query, operation: Harmony.Operation) -> Future { deleteAllCalls.append((query, operation)) return repository.deleteAll(query, operation: operation) diff --git a/Sources/HarmonyTesting/Interactor/MockInteractorDelete.swift b/Sources/HarmonyTesting/Interactor/MockInteractorDelete.swift index 69b85fed..b19cd74a 100644 --- a/Sources/HarmonyTesting/Interactor/MockInteractorDelete.swift +++ b/Sources/HarmonyTesting/Interactor/MockInteractorDelete.swift @@ -17,20 +17,22 @@ import Harmony public extension Interactor { - + // swiftlint:disable unavailable_function class MockDeleteByQuery: DeleteByQuery { private let expectedResult: Result public required init(expectedResult: Result) { self.expectedResult = expectedResult - super.init(DirectExecutor(), SingleDataSourceRepository, Any>(InMemoryDataSource())) + super.init( + DirectExecutor(), + SingleDataSourceRepository, Any>(InMemoryDataSource()) + ) } - public required init(_ executor: Executor, _ repository: DeleteRepository) { + public required init(_: Executor, _: DeleteRepository) { fatalError("init(_:_:) has not been implemented") } - public var spyQuery: [Query?] = [] public var spyOperation: [Harmony.Operation] = [] public var executeCounter: Int { @@ -38,15 +40,18 @@ public extension Interactor { } @discardableResult - override public func execute(_ query: Query, _ operation: Harmony.Operation = DefaultOperation(), in executor: Executor? = nil) -> Future { - - self.spyOperation.append(operation) - self.spyQuery.append(query) - - switch self.expectedResult { - case .success(let value): + override public func execute( + _ query: Query, + _ operation: Harmony.Operation = DefaultOperation(), + in _: Executor? = nil + ) -> Future { + spyOperation.append(operation) + spyQuery.append(query) + + switch expectedResult { + case let .success(value): return Future(value) - case .failure(let error): + case let .failure(error): return Future(error) } } @@ -54,15 +59,15 @@ public extension Interactor { public var spyId: [Any] = [] @discardableResult - override public func execute(_ id: K, _ operation: Harmony.Operation = DefaultOperation(), in executor: Executor? = nil) -> Future where K: Hashable { + override public func execute(_ id: K, _ operation: Harmony.Operation = DefaultOperation(), + in _: Executor? = nil) -> Future where K: Hashable { + spyOperation.append(operation) + spyId.append(id) - self.spyOperation.append(operation) - self.spyId.append(id) - - switch self.expectedResult { - case .success(let value): + switch expectedResult { + case let .success(value): return Future(value) - case .failure(let error): + case let .failure(error): return Future(error) } } @@ -72,12 +77,15 @@ public extension Interactor { private let expectedResult: Result public required init(expectedResult: Result) { - self.expectedResult = expectedResult - super.init(DirectExecutor(), SingleDataSourceRepository, Any>(InMemoryDataSource()), VoidQuery()) + super.init( + DirectExecutor(), + SingleDataSourceRepository, Any>(InMemoryDataSource()), + VoidQuery() + ) } - public required init(_ executor: Executor, _ repository: DeleteRepository, _ query: Query) { + public required init(_: Executor, _: DeleteRepository, _: Query) { fatalError("init(_:_:_:) has not been implemented") } @@ -87,14 +95,14 @@ public extension Interactor { } @discardableResult - override public func execute(_ operation: Harmony.Operation = DefaultOperation(), in executor: Executor? = nil) -> Future { - - self.spyOperation.append(operation) + override public func execute(_ operation: Harmony.Operation = DefaultOperation(), + in _: Executor? = nil) -> Future { + spyOperation.append(operation) - switch self.expectedResult { - case .success(let value): + switch expectedResult { + case let .success(value): return Future(value) - case .failure(let error): + case let .failure(error): return Future(error) } } @@ -104,12 +112,14 @@ public extension Interactor { private let expectedResult: Result public required init(expectedResult: Result) { - self.expectedResult = expectedResult - super.init(DirectExecutor(), SingleDataSourceRepository, Any>(InMemoryDataSource())) + super.init( + DirectExecutor(), + SingleDataSourceRepository, Any>(InMemoryDataSource()) + ) } - public required init(_ executor: Executor, _ repository: DeleteRepository) { + public required init(_: Executor, _: DeleteRepository) { fatalError("init(_:_:) has not been implemented") } @@ -120,15 +130,18 @@ public extension Interactor { } @discardableResult - override public func execute(_ query: Query, _ operation: Harmony.Operation = DefaultOperation(), in executor: Executor? = nil) -> Future { - - self.spyOperation.append(operation) - self.spyQuery.append(query) - - switch self.expectedResult { - case .success(let value): + override public func execute( + _ query: Query, + _ operation: Harmony.Operation = DefaultOperation(), + in _: Executor? = nil + ) -> Future { + spyOperation.append(operation) + spyQuery.append(query) + + switch expectedResult { + case let .success(value): return Future(value) - case .failure(let error): + case let .failure(error): return Future(error) } } @@ -136,15 +149,15 @@ public extension Interactor { public var spyId: [Any] = [] @discardableResult - override public func execute(_ id: K, _ operation: Harmony.Operation = DefaultOperation(), in executor: Executor? = nil) -> Future where K: Hashable { + override public func execute(_ id: K, _ operation: Harmony.Operation = DefaultOperation(), + in _: Executor? = nil) -> Future where K: Hashable { + spyOperation.append(operation) + spyId.append(id) - self.spyOperation.append(operation) - self.spyId.append(id) - - switch self.expectedResult { - case .success(let value): + switch expectedResult { + case let .success(value): return Future(value) - case .failure(let error): + case let .failure(error): return Future(error) } } @@ -154,12 +167,15 @@ public extension Interactor { private let expectedResult: Result public required init(expectedResult: Result) { - self.expectedResult = expectedResult - super.init(DirectExecutor(), SingleDataSourceRepository, Any>(InMemoryDataSource()), VoidQuery()) + super.init( + DirectExecutor(), + SingleDataSourceRepository, Any>(InMemoryDataSource()), + VoidQuery() + ) } - public required init(_ executor: Executor, _ repository: DeleteRepository, _ query: Query) { + public required init(_: Executor, _: DeleteRepository, _: Query) { fatalError("init(_:_:_:) has not been implemented") } @@ -169,14 +185,14 @@ public extension Interactor { } @discardableResult - override public func execute(_ operation: Harmony.Operation = DefaultOperation(), in executor: Executor? = nil) -> Future { - - self.spyOperation.append(operation) + override public func execute(_ operation: Harmony.Operation = DefaultOperation(), + in _: Executor? = nil) -> Future { + spyOperation.append(operation) - switch self.expectedResult { - case .success(let value): + switch expectedResult { + case let .success(value): return Future(value) - case .failure(let error): + case let .failure(error): return Future(error) } } diff --git a/Sources/HarmonyTesting/Interactor/MockInteractorGet.swift b/Sources/HarmonyTesting/Interactor/MockInteractorGet.swift index b851a963..cd97aee5 100644 --- a/Sources/HarmonyTesting/Interactor/MockInteractorGet.swift +++ b/Sources/HarmonyTesting/Interactor/MockInteractorGet.swift @@ -17,7 +17,7 @@ import Harmony public extension Interactor { - + // swiftlint:disable unavailable_function class MockGetByQuery: GetByQuery { private let expectedResult: Result @@ -26,7 +26,7 @@ public extension Interactor { super.init(DirectExecutor(), SingleDataSourceRepository(InMemoryDataSource())) } - public required init(_ executor: Executor, _ repository: R) where T == R.T, R: GetRepository { + public required init(_: Executor, _: R) where T == R.T, R: GetRepository { fatalError("init(_:_:) has not been implemented") } @@ -37,15 +37,18 @@ public extension Interactor { } @discardableResult - override public func execute(_ query: Query = VoidQuery(), _ operation: Harmony.Operation = DefaultOperation(), in executor: Executor? = nil) -> Future { - - self.spyOperation.append(operation) - self.spyQuery.append(query) - - switch self.expectedResult { - case .success(let value): + override public func execute( + _ query: Query = VoidQuery(), + _ operation: Harmony.Operation = DefaultOperation(), + in _: Executor? = nil + ) -> Future { + spyOperation.append(operation) + spyQuery.append(query) + + switch expectedResult { + case let .success(value): return Future(value) - case .failure(let error): + case let .failure(error): return Future(error) } } @@ -53,15 +56,15 @@ public extension Interactor { public var spyId: [Any] = [] @discardableResult - override public func execute(_ id: K, _ operation: Harmony.Operation = DefaultOperation(), in executor: Executor? = nil) -> Future where K: Hashable { - - self.spyOperation.append(operation) - self.spyId.append(id) + override public func execute(_ id: K, _ operation: Harmony.Operation = DefaultOperation(), + in _: Executor? = nil) -> Future where K: Hashable { + spyOperation.append(operation) + spyId.append(id) - switch self.expectedResult { - case .success(let value): + switch expectedResult { + case let .success(value): return Future(value) - case .failure(let error): + case let .failure(error): return Future(error) } } @@ -75,26 +78,24 @@ public extension Interactor { super.init(DirectExecutor(), SingleDataSourceRepository(InMemoryDataSource()), VoidQuery()) } - - public required init(_ executor: Executor, _ repository: R, _ query: Query) where T == R.T, R: GetRepository { + public required init(_: Executor, _: R, _: Query) where T == R.T, R: GetRepository { fatalError("init(_:_:_:) has not been implemented") } - public var spyOperation: [Harmony.Operation] = [] public var executeCounter: Int { spyOperation.count } @discardableResult - override public func execute(_ operation: Harmony.Operation = DefaultOperation(), in executor: Executor? = nil) -> Future { + override public func execute(_ operation: Harmony.Operation = DefaultOperation(), + in _: Executor? = nil) -> Future { + spyOperation.append(operation) - self.spyOperation.append(operation) - - switch self.expectedResult { - case .success(let value): + switch expectedResult { + case let .success(value): return Future(value) - case .failure(let error): + case let .failure(error): return Future(error) } } @@ -108,7 +109,7 @@ public extension Interactor { super.init(DirectExecutor(), SingleDataSourceRepository(InMemoryDataSource())) } - public required init(_ executor: Executor, _ repository: R) where T == R.T, R: GetRepository { + public required init(_: Executor, _: R) where T == R.T, R: GetRepository { fatalError("init(_:_:) has not been implemented") } @@ -119,15 +120,18 @@ public extension Interactor { } @discardableResult - override public func execute(_ query: Query = AllObjectsQuery(), _ operation: Harmony.Operation = DefaultOperation(), in executor: Executor? = nil) -> Future<[T]> { - - self.spyOperation.append(operation) - self.spyQuery.append(query) - - switch self.expectedResult { - case .success(let value): + override public func execute( + _ query: Query = AllObjectsQuery(), + _ operation: Harmony.Operation = DefaultOperation(), + in _: Executor? = nil + ) -> Future<[T]> { + spyOperation.append(operation) + spyQuery.append(query) + + switch expectedResult { + case let .success(value): return Future(value) - case .failure(let error): + case let .failure(error): return Future(error) } } @@ -135,15 +139,15 @@ public extension Interactor { public var spyId: [Any] = [] @discardableResult - override public func execute(_ id: K, _ operation: Harmony.Operation = DefaultOperation(), in executor: Executor? = nil) -> Future<[T]> where K: Hashable { + override public func execute(_ id: K, _ operation: Harmony.Operation = DefaultOperation(), + in _: Executor? = nil) -> Future<[T]> where K: Hashable { + spyOperation.append(operation) + spyId.append(id) - self.spyOperation.append(operation) - self.spyId.append(id) - - switch self.expectedResult { - case .success(let value): + switch expectedResult { + case let .success(value): return Future(value) - case .failure(let error): + case let .failure(error): return Future(error) } } @@ -157,7 +161,7 @@ public extension Interactor { super.init(DirectExecutor(), SingleDataSourceRepository(InMemoryDataSource()), VoidQuery()) } - public required init(_ executor: Executor, _ repository: R, _ query: Query) where T == R.T, R: GetRepository { + public required init(_: Executor, _: R, _: Query) where T == R.T, R: GetRepository { fatalError("init(_:_:_:) has not been implemented") } @@ -167,14 +171,14 @@ public extension Interactor { } @discardableResult - override public func execute(_ operation: Harmony.Operation = DefaultOperation(), in executor: Executor? = nil) -> Future<[T]> { - - self.spyOperation.append(operation) + override public func execute(_ operation: Harmony.Operation = DefaultOperation(), + in _: Executor? = nil) -> Future<[T]> { + spyOperation.append(operation) - switch self.expectedResult { - case .success(let value): + switch expectedResult { + case let .success(value): return Future(value) - case .failure(let error): + case let .failure(error): return Future(error) } } diff --git a/Sources/HarmonyTesting/Interactor/MockInteractorPut.swift b/Sources/HarmonyTesting/Interactor/MockInteractorPut.swift index 78c092ec..3daad665 100644 --- a/Sources/HarmonyTesting/Interactor/MockInteractorPut.swift +++ b/Sources/HarmonyTesting/Interactor/MockInteractorPut.swift @@ -17,7 +17,7 @@ import Harmony public extension Interactor { - + // swiftlint:disable unavailable_function class MockPutByQuery: PutByQuery { private let expectedResult: Result @@ -26,7 +26,7 @@ public extension Interactor { super.init(DirectExecutor(), SingleDataSourceRepository(InMemoryDataSource())) } - public required init(_ executor: Executor, _ repository: R) where T == R.T, R: PutRepository { + public required init(_: Executor, _: R) where T == R.T, R: PutRepository { fatalError("init(_:_:) has not been implemented") } @@ -39,16 +39,20 @@ public extension Interactor { public var spyOperation: [Harmony.Operation] = [] @discardableResult - override public func execute(_ value: T? = nil, query: Query = VoidQuery(), _ operation: Harmony.Operation = DefaultOperation(), in executor: Harmony.Executor? = nil) -> Future { - - self.spyExecuteValue.append(value) - self.spyQuery.append(query) - self.spyOperation.append(operation) - - switch self.expectedResult { - case .success(let value): + override public func execute( + _ value: T? = nil, + query: Query = VoidQuery(), + _ operation: Harmony.Operation = DefaultOperation(), + in _: Harmony.Executor? = nil + ) -> Future { + spyExecuteValue.append(value) + spyQuery.append(query) + spyOperation.append(operation) + + switch expectedResult { + case let .success(value): return Future(value) - case .failure(let error): + case let .failure(error): return Future(error) } } @@ -56,16 +60,18 @@ public extension Interactor { public var spyId: [Any] = [] @discardableResult - override public func execute(_ value: T?, forId id: K, _ operation: Harmony.Operation = DefaultOperation(), in executor: Executor? = nil) -> Future where K:Hashable { - - self.spyExecuteValue.append(value) - self.spyId.append(id) - self.spyOperation.append(operation) - - switch self.expectedResult { - case .success(let value): + override public func execute(_ value: T?, forId id: K, + _ operation: Harmony.Operation = DefaultOperation(), + in _: Executor? = nil) -> Future + where K: Hashable { + spyExecuteValue.append(value) + spyId.append(id) + spyOperation.append(operation) + + switch expectedResult { + case let .success(value): return Future(value) - case .failure(let error): + case let .failure(error): return Future(error) } } @@ -79,7 +85,7 @@ public extension Interactor { super.init(DirectExecutor(), SingleDataSourceRepository(InMemoryDataSource()), VoidQuery()) } - public required init(_ executor: Executor, _ repository: R, _ query: Query) where T == R.T, R: PutRepository { + public required init(_: Executor, _: R, _: Query) where T == R.T, R: PutRepository { fatalError("init(_:_:_:) has not been implemented") } @@ -91,15 +97,18 @@ public extension Interactor { public var spyOperation: [Harmony.Operation] = [] @discardableResult - override public func execute(_ value: T? = nil, _ operation: Harmony.Operation = DefaultOperation(), in executor: Executor? = nil) -> Future { - - self.spyExecuteValue.append(value) - self.spyOperation.append(operation) - - switch self.expectedResult { - case .success(let value): + override public func execute( + _ value: T? = nil, + _ operation: Harmony.Operation = DefaultOperation(), + in _: Executor? = nil + ) -> Future { + spyExecuteValue.append(value) + spyOperation.append(operation) + + switch expectedResult { + case let .success(value): return Future(value) - case .failure(let error): + case let .failure(error): return Future(error) } } @@ -112,8 +121,8 @@ public extension Interactor { self.expectedResult = expectedResult super.init(DirectExecutor(), SingleDataSourceRepository(InMemoryDataSource())) } - - public required init(_ executor: Executor, _ repository: R) where T == R.T, R: PutRepository { + + public required init(_: Executor, _: R) where T == R.T, R: PutRepository { fatalError("init(_:_:) has not been implemented") } @@ -126,16 +135,20 @@ public extension Interactor { public var spyOperation: [Harmony.Operation] = [] @discardableResult - override public func execute(_ array: [T] = [], query: Query = VoidQuery(), _ operation: Harmony.Operation = DefaultOperation(), in executor: Executor? = nil) -> Future<[T]> { - - self.spyExecuteValue.append(array) - self.spyQuery.append(query) - self.spyOperation.append(operation) - - switch self.expectedResult { - case .success(let value): + override public func execute( + _ array: [T] = [], + query: Query = VoidQuery(), + _ operation: Harmony.Operation = DefaultOperation(), + in _: Executor? = nil + ) -> Future<[T]> { + spyExecuteValue.append(array) + spyQuery.append(query) + spyOperation.append(operation) + + switch expectedResult { + case let .success(value): return Future(value) - case .failure(let error): + case let .failure(error): return Future(error) } } @@ -143,16 +156,18 @@ public extension Interactor { public var spyId: [Any] = [] @discardableResult - override public func execute(_ array: [T] = [], forId id: K, _ operation: Harmony.Operation = DefaultOperation(), in executor: Executor? = nil) -> Future<[T]> where K: Hashable { - - self.spyExecuteValue.append(array) - self.spyOperation.append(operation) - self.spyId.append(id) - - switch self.expectedResult { - case .success(let value): + override public func execute(_ array: [T] = [], forId id: K, + _ operation: Harmony.Operation = DefaultOperation(), + in _: Executor? = nil) -> Future<[T]> + where K: Hashable { + spyExecuteValue.append(array) + spyOperation.append(operation) + spyId.append(id) + + switch expectedResult { + case let .success(value): return Future(value) - case .failure(let error): + case let .failure(error): return Future(error) } } @@ -166,7 +181,7 @@ public extension Interactor { super.init(DirectExecutor(), SingleDataSourceRepository(InMemoryDataSource()), VoidQuery()) } - public required init(_ executor: Executor, _ repository: R, _ query: Query) where T == R.T, R: PutRepository { + public required init(_: Executor, _: R, _: Query) where T == R.T, R: PutRepository { fatalError("init(_:_:_:) has not been implemented") } @@ -178,15 +193,18 @@ public extension Interactor { public var spyOperation: [Harmony.Operation] = [] @discardableResult - override public func execute(_ array: [T] = [], _ operation: Harmony.Operation = DefaultOperation(), in executor: Executor? = nil) -> Future<[T]> { - - self.spyExecuteValue.append(array) - self.spyOperation.append(operation) - - switch self.expectedResult { - case .success(let value): + override public func execute( + _ array: [T] = [], + _ operation: Harmony.Operation = DefaultOperation(), + in _: Executor? = nil + ) -> Future<[T]> { + spyExecuteValue.append(array) + spyOperation.append(operation) + + switch expectedResult { + case let .success(value): return Future(value) - case .failure(let error): + case let .failure(error): return Future(error) } } diff --git a/Sources/HarmonyTesting/ObjectMother/BoolObjectMother.swift b/Sources/HarmonyTesting/ObjectMother/BoolObjectMother.swift index ea0db43f..54007fc5 100644 --- a/Sources/HarmonyTesting/ObjectMother/BoolObjectMother.swift +++ b/Sources/HarmonyTesting/ObjectMother/BoolObjectMother.swift @@ -17,5 +17,5 @@ import Foundation public func randomBool() -> Bool { - return Int.random(in: 0...1) != 0 + return Int.random(in: 0 ... 1) != 0 } diff --git a/Sources/HarmonyTesting/ObjectMother/DoubleObjectMother.swift b/Sources/HarmonyTesting/ObjectMother/DoubleObjectMother.swift index b7e62cfb..c7861707 100644 --- a/Sources/HarmonyTesting/ObjectMother/DoubleObjectMother.swift +++ b/Sources/HarmonyTesting/ObjectMother/DoubleObjectMother.swift @@ -17,5 +17,5 @@ import Foundation public func anyDouble(minValue: Double = -32000, maxValue: Double = 32000) -> Double { - return Double.random(in: minValue...maxValue) + return Double.random(in: minValue ... maxValue) } diff --git a/Sources/HarmonyTesting/ObjectMother/IntegerObjectModel.swift b/Sources/HarmonyTesting/ObjectMother/IntegerObjectModel.swift index c837a83f..aed0d2fb 100644 --- a/Sources/HarmonyTesting/ObjectMother/IntegerObjectModel.swift +++ b/Sources/HarmonyTesting/ObjectMother/IntegerObjectModel.swift @@ -17,5 +17,5 @@ import Foundation public func anyInt(minValue: Int = -32000, maxValue: Int = 32000) -> Int { - return Int.random(in: minValue...maxValue) + return Int.random(in: minValue ... maxValue) } diff --git a/Sources/HarmonyTesting/TestUtils.swift b/Sources/HarmonyTesting/TestUtils.swift index 197b2494..3137c17a 100644 --- a/Sources/HarmonyTesting/TestUtils.swift +++ b/Sources/HarmonyTesting/TestUtils.swift @@ -19,9 +19,9 @@ import Harmony public enum TestUtils { public static func result(expectedResult: Result) -> Future { switch expectedResult { - case .success(let value): + case let .success(value): return Future(value) - case .failure(let error): + case let .failure(error): return Future(error) } } diff --git a/Tests/HarmonyTests/CacheRepositoryTests.swift b/Tests/HarmonyTests/CacheRepositoryTests.swift index 24812e03..01c9484a 100644 --- a/Tests/HarmonyTests/CacheRepositoryTests.swift +++ b/Tests/HarmonyTests/CacheRepositoryTests.swift @@ -15,21 +15,20 @@ // import Foundation -import Nimble -import XCTest import Harmony import HarmonyTesting +import Nimble +import XCTest class CacheRepositoryTests: XCTestCase { - private var main: InMemoryDataSource! private var cache: InMemoryDataSource! - + private typealias DataSourceSpyType = DataSourceSpy, Int> - + private var mainSpy: DataSourceSpyType! private var cacheSpy: DataSourceSpyType! - + private var cacheRepository: CacheRepository! private func setup( @@ -39,31 +38,31 @@ class CacheRepositoryTests: XCTestCase { ) throws { main = InMemoryDataSource() cache = InMemoryDataSource() - + mainSpy = DataSourceSpy(main) cacheSpy = DataSourceSpy(cache) - + cacheRepository = CacheRepository( main: mainSpy, cache: cacheSpy, validator: MockObjectValidation(objectValid: objectValid, arrayValid: true) ) - + if let mainKeyValue = mainKeyValue { _ = try main.put(mainKeyValue.value, in: mainKeyValue.query).result.get() } - + if let cacheKeyValue = cacheKeyValue { _ = try cache.put(cacheKeyValue.value, in: cacheKeyValue.query).result.get() } } - + func test_getValue_withMain_withEmptyMain() throws { // Given let query = IdQuery(String(randomOfLength: 8)) try setup(mainData: nil, cacheData: nil) let operation = MainOperation() - + expect { // When try self.cacheRepository.get(query, operation: operation).result.get() @@ -74,30 +73,30 @@ class CacheRepositoryTests: XCTestCase { expect(self.mainSpy.getCalls.count).to(equal(1)) expect(self.mainSpy.getCalls[0] as! IdQuery).to(be(query)) } - + func test_getValue_withMain_withMainValue() throws { // Given let value = Int.random() let query = IdQuery(String(randomOfLength: 8)) try setup(mainData: (query, value), cacheData: nil) let operation = MainOperation() - + // When let result = try cacheRepository.get(query, operation: operation).result.get() - + // Then expect(result).to(equal(value)) expect(self.cacheSpy.getCalls.count).to(equal(0)) expect(self.mainSpy.getCalls.count).to(equal(1)) expect(self.mainSpy.getCalls[0] as! IdQuery).to(be(query)) } - + func test_getValue_withCache_withEmptyCache() throws { // Given let query = IdQuery(String(randomOfLength: 8)) try setup(mainData: nil, cacheData: nil) let operation = CacheOperation() - + expect { // When try self.cacheRepository.get(query, operation: operation).result.get() @@ -108,34 +107,34 @@ class CacheRepositoryTests: XCTestCase { expect(self.cacheSpy.getCalls.count).to(equal(1)) expect(self.cacheSpy.getCalls[0] as! IdQuery).to(be(query)) } - + func test_getValue_withCache_withCacheValue() throws { // Given let value = Int.random() let query = IdQuery(String(randomOfLength: 8)) try setup(mainData: nil, cacheData: (query, value)) let operation = CacheOperation() - + // When let result = try cacheRepository.get(query, operation: operation).result.get() - + // Then expect(result).to(equal(value)) expect(self.mainSpy.getCalls.count).to(equal(0)) expect(self.cacheSpy.getCalls.count).to(equal(1)) expect(self.cacheSpy.getCalls[0] as! IdQuery).to(be(query)) } - + func test_getValue_withCacheSync_withEmptyCache_withMainValue() throws { // Given let value = Int.random() let query = IdQuery(String(randomOfLength: 8)) try setup(mainData: (query, value), cacheData: nil) let operation = CacheSyncOperation() - + // When let result = try cacheRepository.get(query, operation: operation).result.get() - + // Then expect(result).to(equal(value)) expect(self.mainSpy.getCalls.count).to(equal(1)) @@ -144,24 +143,24 @@ class CacheRepositoryTests: XCTestCase { expect(self.cacheSpy.putCalls[0].value).to(equal(value)) expect(self.cacheSpy.putCalls[0].query as! IdQuery).to(be(query)) } - + func test_getValue_withCacheSync_withValidCacheValue() throws { // Given let value = Int.random() let query = IdQuery(String(randomOfLength: 8)) try setup(mainData: nil, cacheData: (query, value)) let operation = CacheSyncOperation() - + // When let result = try cacheRepository.get(query, operation: operation).result.get() - + // Then expect(result).to(equal(value)) expect(self.mainSpy.getCalls.count).to(equal(0)) expect(self.cacheSpy.getCalls.count).to(equal(1)) expect(self.cacheSpy.getCalls[0] as! IdQuery).to(be(query)) } - + func test_getValue_withCacheSync_withInvalidCacheValue_withMainValue() throws { // Given let validValue = Int.random() @@ -169,10 +168,10 @@ class CacheRepositoryTests: XCTestCase { let query = IdQuery(String(randomOfLength: 8)) try setup(mainData: (query, validValue), cacheData: (query, invalidValue), objectValid: false) let operation = CacheSyncOperation() - + // When let result = try cacheRepository.get(query, operation: operation).result.get() - + // Then expect(result).to(equal(validValue)) expect(self.mainSpy.getCalls.count).to(equal(1)) @@ -183,14 +182,14 @@ class CacheRepositoryTests: XCTestCase { expect(self.cacheSpy.putCalls[0].value).to(equal(validValue)) expect(self.cacheSpy.putCalls[0].query as! IdQuery).to(be(query)) } - + func test_getValue_withCacheSync_withInvalidCacheValue_withEmptyMain() throws { // Given let invalidValue = Int.random() let query = IdQuery(String(randomOfLength: 8)) try setup(mainData: nil, cacheData: (query, invalidValue), objectValid: false) let operation = CacheSyncOperation() - + expect { // When try self.cacheRepository.get(query, operation: operation).result.get() @@ -203,5 +202,3 @@ class CacheRepositoryTests: XCTestCase { expect(self.cacheSpy.getCalls[0] as! IdQuery).to(be(query)) } } - - diff --git a/Tests/HarmonyTests/DelayedMainQueueExecutorTests.swift b/Tests/HarmonyTests/DelayedMainQueueExecutorTests.swift index 28950842..fb446e40 100644 --- a/Tests/HarmonyTests/DelayedMainQueueExecutorTests.swift +++ b/Tests/HarmonyTests/DelayedMainQueueExecutorTests.swift @@ -5,56 +5,55 @@ // Created by Borja Arias Drake on 21.09.2022.. // -import XCTest -import Nimble import Harmony +import Nimble +import XCTest final class DelayedMainQueueExecutorTests: XCTestCase { - func test_submit() throws { // Given let executor = DelayedMainQueueExecutor() let closureIsRun = XCTestExpectation() - + // When executor.submit { resolver in resolver.set() XCTAssertTrue(Thread.isMainThread) closureIsRun.fulfill() } - + // Then wait(for: [closureIsRun], timeout: 1) } - + func test_submit_with_delay_overriding_delay() throws { // Given let executor = DelayedMainQueueExecutor(overrideDelay: true) let closureIsRun = XCTestExpectation() - + // When executor.submit(after: .now() + 2) { end in end() XCTAssertTrue(Thread.isMainThread) closureIsRun.fulfill() } - + // Then wait(for: [closureIsRun], timeout: 1) } - + func test_submit_with_delay_without_overriding() throws { // Given let executor = DelayedMainQueueExecutor() let closureIsRun = XCTestExpectation() - + // When executor.submit(after: .now() + 0.5) { end in end() XCTAssertTrue(Thread.isMainThread) closureIsRun.fulfill() } - + // Then wait(for: [closureIsRun], timeout: 0.75) } diff --git a/Tests/HarmonyTests/DeviceStorageDataSourcePrefixTests.swift b/Tests/HarmonyTests/DeviceStorageDataSourcePrefixTests.swift index c2d33b1c..479d8425 100644 --- a/Tests/HarmonyTests/DeviceStorageDataSourcePrefixTests.swift +++ b/Tests/HarmonyTests/DeviceStorageDataSourcePrefixTests.swift @@ -9,79 +9,80 @@ import Foundation import XCTest class DeviceStorageDataSourcePrefixTests: XCTestCase { - - private let tester = DeviceStorageDataSourceTester(DeviceStorageDataSourceObjectMother(deviceStorageType: .prefix("test-prefix"))) - + private let tester = + DeviceStorageDataSourceTester( + DeviceStorageDataSourceObjectMother(deviceStorageType: .prefix("test-prefix")) + ) + override func tearDown() { tester.tearDown() } - + func test_get_value() throws { try tester.test_get_value() } - + func test_getAll_value() throws { try tester.test_getAll_value() } - + func test_put_value() throws { try tester.test_put_value() } - + func test_put_dictionary_value() throws { try tester.test_put_dictionary_value() } - + func test_put_array_value() throws { try tester.test_put_array_value() } - + func test_putAll_array_value_with_idQuery() throws { try tester.test_putAll_array_value_with_idQuery() } - + func test_putAll_array_value_with_idsQuery() throws { try tester.test_putAll_array_value_with_idsQuery() } - + func test_delete_value() throws { try tester.test_delete_value() } - + func test_delete_all_values() throws { try tester.test_delete_all_values() } - + func test_get_value_not_found() throws { try tester.test_get_value_not_found() } - + func test_getAll_value_not_found() throws { try tester.test_getAll_value_not_found() } - + func test_get_non_valid_query() throws { try tester.test_get_non_valid_query() } - + func test_getAll_non_valid_query() throws { try tester.test_getAll_non_valid_query() } - + func test_put_non_valid_query() throws { try tester.test_put_non_valid_query() } - + func test_putAll_non_valid_query() throws { try tester.test_putAll_non_valid_query() } - + func test_delete_non_valid_query() throws { try tester.test_delete_non_valid_query() } - + func test_should_replace_previous_value_when_inserting_with_existing_key() throws { try tester.test_should_replace_previous_value_when_inserting_with_existing_key() } - } diff --git a/Tests/HarmonyTests/DeviceStorageDataSourceRegularTests.swift b/Tests/HarmonyTests/DeviceStorageDataSourceRegularTests.swift index 1788567f..168f6b98 100644 --- a/Tests/HarmonyTests/DeviceStorageDataSourceRegularTests.swift +++ b/Tests/HarmonyTests/DeviceStorageDataSourceRegularTests.swift @@ -9,81 +9,79 @@ import Foundation import XCTest class DeviceStorageDataSourceRegularTests: XCTestCase { - - private let tester = DeviceStorageDataSourceTester(DeviceStorageDataSourceObjectMother(deviceStorageType: .regular)) - + private let tester = + DeviceStorageDataSourceTester(DeviceStorageDataSourceObjectMother(deviceStorageType: .regular)) + override func tearDown() { tester.tearDown() } - + func test_get_value() throws { try tester.test_get_value() } - + func test_getAll_value() throws { try tester.test_getAll_value() } - + func test_put_value() throws { try tester.test_put_value() } - + func test_put_dictionary_value() throws { try tester.test_put_dictionary_value() } - + func test_put_array_value() throws { try tester.test_put_array_value() } - + func test_putAll_array_value_with_idQuery() throws { try tester.test_putAll_array_value_with_idQuery() } - + func test_putAll_array_value_with_idsQuery() throws { try tester.test_putAll_array_value_with_idsQuery() } - + func test_delete_value() throws { try tester.test_delete_value() } - + func test_delete_all_values() throws { // TODO: decide what to do with deleteAll implementation when deviceStorageType is regular -// try tester.test_delete_all_values() - + // try tester.test_delete_all_values() } - + func test_get_value_not_found() throws { try tester.test_get_value_not_found() } - + func test_getAll_value_not_found() throws { try tester.test_getAll_value_not_found() } - + func test_get_non_valid_query() throws { try tester.test_get_non_valid_query() } - + func test_getAll_non_valid_query() throws { try tester.test_getAll_non_valid_query() } - + func test_put_non_valid_query() throws { try tester.test_put_non_valid_query() } - + func test_putAll_non_valid_query() throws { try tester.test_putAll_non_valid_query() } - + func test_delete_non_valid_query() throws { try tester.test_delete_non_valid_query() } - + func test_should_replace_previous_value_when_inserting_with_existing_key() throws { try tester.test_should_replace_previous_value_when_inserting_with_existing_key() } - } diff --git a/Tests/HarmonyTests/DeviceStorageDataSourceRootKeyTests.swift b/Tests/HarmonyTests/DeviceStorageDataSourceRootKeyTests.swift index 1732ad9c..d7a01b4b 100644 --- a/Tests/HarmonyTests/DeviceStorageDataSourceRootKeyTests.swift +++ b/Tests/HarmonyTests/DeviceStorageDataSourceRootKeyTests.swift @@ -9,79 +9,80 @@ import Foundation import XCTest class DeviceStorageDataSourceRootKeyTests: XCTestCase { - - private let tester = DeviceStorageDataSourceTester(DeviceStorageDataSourceObjectMother(deviceStorageType: .rootKey("test-root-key"))) - + private let tester = + DeviceStorageDataSourceTester( + DeviceStorageDataSourceObjectMother(deviceStorageType: .rootKey("test-root-key")) + ) + override func tearDown() { tester.tearDown() } - + func test_get_value() throws { try tester.test_get_value() } - + func test_getAll_value() throws { try tester.test_getAll_value() } - + func test_put_value() throws { try tester.test_put_value() } - + func test_put_dictionary_value() throws { try tester.test_put_dictionary_value() } - + func test_put_array_value() throws { try tester.test_put_array_value() } - + func test_putAll_array_value_with_idQuery() throws { try tester.test_putAll_array_value_with_idQuery() } - + func test_putAll_array_value_with_idsQuery() throws { try tester.test_putAll_array_value_with_idsQuery() } - + func test_delete_value() throws { try tester.test_delete_value() } - + func test_delete_all_values() throws { try tester.test_delete_all_values() } - + func test_get_value_not_found() throws { try tester.test_get_value_not_found() } - + func test_getAll_value_not_found() throws { try tester.test_getAll_value_not_found() } - + func test_get_non_valid_query() throws { try tester.test_get_non_valid_query() } - + func test_getAll_non_valid_query() throws { try tester.test_getAll_non_valid_query() } - + func test_put_non_valid_query() throws { try tester.test_put_non_valid_query() } - + func test_putAll_non_valid_query() throws { try tester.test_putAll_non_valid_query() } - + func test_delete_non_valid_query() throws { try tester.test_delete_non_valid_query() } - + func test_should_replace_previous_value_when_inserting_with_existing_key() throws { try tester.test_should_replace_previous_value_when_inserting_with_existing_key() } - } diff --git a/Tests/HarmonyTests/DeviceStorageDataSourceTester.swift b/Tests/HarmonyTests/DeviceStorageDataSourceTester.swift index 6f55f18d..f04b80a4 100644 --- a/Tests/HarmonyTests/DeviceStorageDataSourceTester.swift +++ b/Tests/HarmonyTests/DeviceStorageDataSourceTester.swift @@ -6,50 +6,59 @@ // import Foundation +import Harmony import Nimble import XCTest -import Harmony struct DeviceStorageDataSourceObjectMother { let deviceStorageType: DeviceStorageType - func provideDataSource(userDefaults: UserDefaults, insertValue: (IdQuery, T)? = nil, insertValues: (IdQuery, [T])? = nil) throws -> DeviceStorageDataSource { + func provideDataSource( + userDefaults: UserDefaults, + insertValue: (IdQuery, T)? = nil, + insertValues: (IdQuery, [T])? = nil + ) throws -> DeviceStorageDataSource { let dataSource = DeviceStorageDataSource(userDefaults, storageType: deviceStorageType) - + if let insertValue = insertValue { try dataSource.put(insertValue.1, in: insertValue.0).result.get() } - + if let insertValues = insertValues { - try dataSource.putAll(insertValues.1,in: insertValues.0).result.get() + try dataSource.putAll(insertValues.1, in: insertValues.0).result.get() } - + return dataSource } } - class DeviceStorageDataSourceTester { - let dataSourceObjectMother: DeviceStorageDataSourceObjectMother - + init(_ dataSourceObjectMother: DeviceStorageDataSourceObjectMother) { self.dataSourceObjectMother = dataSourceObjectMother } - + let userDefaults = UserDefaults.standard - - private func provideDataSource(insertValue: (IdQuery, T)? = nil, insertValues: (IdQuery, [T])? = nil) throws -> DeviceStorageDataSource { - return try dataSourceObjectMother.provideDataSource(userDefaults: userDefaults, insertValue: insertValue, insertValues: insertValues) + + private func provideDataSource( + insertValue: (IdQuery, T)? = nil, + insertValues: (IdQuery, [T])? = nil + ) throws -> DeviceStorageDataSource { + return try dataSourceObjectMother.provideDataSource( + userDefaults: userDefaults, + insertValue: insertValue, + insertValues: insertValues + ) } - + func tearDown() { let dictionary = userDefaults.dictionaryRepresentation() dictionary.keys.forEach { key in userDefaults.removeObject(forKey: key) } } - + func test_get_value() throws { // Given let query = IdQuery(String(randomOfLength: 8)) @@ -62,7 +71,7 @@ class DeviceStorageDataSourceTester { // Then expect(actualValue).to(equal(expectedValue)) } - + func test_getAll_value() throws { // Given let query = IdQuery(String(randomOfLength: 8)) @@ -75,7 +84,7 @@ class DeviceStorageDataSourceTester { // Then expect(actualValue).to(equal(expectedValue)) } - + func test_put_value() throws { // Given let query = IdQuery(String(randomOfLength: 8)) @@ -83,14 +92,14 @@ class DeviceStorageDataSourceTester { let dataSource: DeviceStorageDataSource = try provideDataSource() // When - try dataSource.put(expectedValue, in:query).result.get() + try dataSource.put(expectedValue, in: query).result.get() // Then - expect{ - try dataSource.get(query).result.get() + expect { + try dataSource.get(query).result.get() }.to(equal(expectedValue)) } - + func test_put_dictionary_value() throws { // Given let query = IdQuery(String(randomOfLength: 8)) @@ -98,14 +107,14 @@ class DeviceStorageDataSourceTester { let dataSource: DeviceStorageDataSource<[String: Int]> = try provideDataSource() // When - try dataSource.put(expectedValue, in:query).result.get() + try dataSource.put(expectedValue, in: query).result.get() // Then - expect{ - try dataSource.get(query).result.get() + expect { + try dataSource.get(query).result.get() }.to(equal(expectedValue)) } - + func test_put_array_value() throws { // Given let query = IdQuery(String(randomOfLength: 8)) @@ -113,14 +122,14 @@ class DeviceStorageDataSourceTester { let dataSource: DeviceStorageDataSource<[Int]> = try provideDataSource() // When - try dataSource.put(expectedValue, in:query).result.get() + try dataSource.put(expectedValue, in: query).result.get() // Then - expect{ - try dataSource.get(query).result.get() + expect { + try dataSource.get(query).result.get() }.to(equal(expectedValue)) } - + func test_putAll_array_value_with_idQuery() throws { // Given let query = IdQuery(String(randomOfLength: 8)) @@ -128,14 +137,14 @@ class DeviceStorageDataSourceTester { let dataSource: DeviceStorageDataSource = try provideDataSource() // When - try dataSource.putAll(expectedValue, in:query).result.get() + try dataSource.putAll(expectedValue, in: query).result.get() // Then - expect{ - try dataSource.getAll(query).result.get() + expect { + try dataSource.getAll(query).result.get() }.to(equal(expectedValue)) } - + func test_putAll_array_value_with_idsQuery() throws { // Given let query = IdsQuery([String(randomOfLength: 8), String(randomOfLength: 8)]) @@ -143,59 +152,62 @@ class DeviceStorageDataSourceTester { let dataSource: DeviceStorageDataSource = try provideDataSource() // When - try dataSource.putAll(expectedValue, in:query).result.get() + try dataSource.putAll(expectedValue, in: query).result.get() // Then - expect{ - try dataSource.getAll(query).result.get() + expect { + try dataSource.getAll(query).result.get() }.to(equal(expectedValue)) } - + func test_delete_value() throws { // Given let value = Int.random() let query = IdQuery(String(randomOfLength: 8)) let dataSource = try provideDataSource(insertValue: (query, value)) - + // When try dataSource.delete(query).result.get() - + // Then expect { try dataSource.get(query).result.get() } .to(throwError(errorType: CoreError.NotFound.self)) } - + func test_delete_all_values() throws { // Given let value = Int.random() let valueQuery = IdQuery(String(randomOfLength: 8)) let values = [Int.random(), Int.random()] let valuesQuery = IdQuery(String(randomOfLength: 8)) - let dataSource = try provideDataSource(insertValue: (valueQuery, value), insertValues: (valuesQuery, values)) - + let dataSource = try provideDataSource( + insertValue: (valueQuery, value), + insertValues: (valuesQuery, values) + ) + // When try dataSource.delete(AllObjectsQuery()).result.get() - + // Then expect { try dataSource.get(valueQuery).result.get() } .to(throwError(errorType: CoreError.NotFound.self)) - + // Then expect { try dataSource.get(valuesQuery).result.get() } .to(throwError(errorType: CoreError.NotFound.self)) } - + func test_get_value_not_found() throws { // Given let dataSource: DeviceStorageDataSource = try provideDataSource() let query = IdQuery(String(randomOfLength: 8)) - + expect { // When try dataSource.get(query).result.get() @@ -203,12 +215,12 @@ class DeviceStorageDataSourceTester { // Then .to(throwError(errorType: CoreError.NotFound.self)) } - + func test_getAll_value_not_found() throws { // Given let dataSource: DeviceStorageDataSource = try provideDataSource() let query = IdQuery(String(randomOfLength: 8)) - + expect { // When try dataSource.getAll(query).result.get() @@ -216,12 +228,12 @@ class DeviceStorageDataSourceTester { // Then .to(throwError(errorType: CoreError.NotFound.self)) } - + func test_get_non_valid_query() throws { // Given let dataSource: DeviceStorageDataSource = try provideDataSource() let query = VoidQuery() - + expect { // When try dataSource.get(query).result.get() @@ -229,12 +241,12 @@ class DeviceStorageDataSourceTester { // Then .to(throwAssertion()) } - + func test_getAll_non_valid_query() throws { // Given let dataSource: DeviceStorageDataSource = try provideDataSource() let query = VoidQuery() - + expect { // When try dataSource.getAll(query).result.get() @@ -242,13 +254,13 @@ class DeviceStorageDataSourceTester { // Then .to(throwAssertion()) } - + func test_put_non_valid_query() throws { // Given let dataSource: DeviceStorageDataSource = try provideDataSource() let query = VoidQuery() let value = Int.random() - + expect { // When try dataSource.put(value, in: query).result.get() @@ -256,13 +268,13 @@ class DeviceStorageDataSourceTester { // Then .to(throwAssertion()) } - + func test_putAll_non_valid_query() throws { // Given let dataSource: DeviceStorageDataSource = try provideDataSource() let query = VoidQuery() let value = [Int.random(), Int.random()] - + expect { // When try dataSource.putAll(value, in: query).result.get() @@ -270,7 +282,7 @@ class DeviceStorageDataSourceTester { // Then .to(throwAssertion()) } - + func test_delete_non_valid_query() throws { // Given let dataSource: DeviceStorageDataSource = try provideDataSource() @@ -283,17 +295,17 @@ class DeviceStorageDataSourceTester { // Then .to(throwAssertion()) } - + func test_should_replace_previous_value_when_inserting_with_existing_key() throws { // Given let query = IdQuery(String(randomOfLength: 8)) let firstValue = [Int.random(), Int.random()] let secondValue = Int.random() let dataSource = try provideDataSource(insertValues: (query, firstValue)) - + expect { // When - _ = dataSource.put(secondValue,in: query) // Put a new value using the same key + _ = dataSource.put(secondValue, in: query) // Put a new value using the same key return try dataSource.get(query).result.get() } // Then @@ -306,5 +318,4 @@ class DeviceStorageDataSourceTester { // Then .to(throwError(errorType: CoreError.NotFound.self)) // The old value (list) is not there anymore } - } diff --git a/Tests/HarmonyTests/FileSystemStorageDataSourceTests.swift b/Tests/HarmonyTests/FileSystemStorageDataSourceTests.swift index ce96efad..21b5050d 100644 --- a/Tests/HarmonyTests/FileSystemStorageDataSourceTests.swift +++ b/Tests/HarmonyTests/FileSystemStorageDataSourceTests.swift @@ -6,25 +6,28 @@ // import Foundation +import Harmony import Nimble import XCTest -import Harmony class FileSystemStorageDataSourceTests: XCTestCase { - private func provideDataSource(insertValue: (IdQuery, Data)? = nil, insertValues: (IdQuery, [Data])? = nil) throws -> FileSystemStorageDataSource { + private func provideDataSource( + insertValue: (IdQuery, Data)? = nil, + insertValues: (IdQuery, [Data])? = nil + ) throws -> FileSystemStorageDataSource { let dataSource = FileSystemStorageDataSource(fileManager: FileManager.default, relativePath: "test")! - + if let insertValue = insertValue { try dataSource.put(insertValue.1, in: insertValue.0).result.get() } - + if let insertValues = insertValues { - try dataSource.putAll(insertValues.1,in: insertValues.0).result.get() + try dataSource.putAll(insertValues.1, in: insertValues.0).result.get() } - + return dataSource } - + override func tearDown() { do { try provideDataSource().delete(AllObjectsQuery()).result.get() @@ -32,7 +35,7 @@ class FileSystemStorageDataSourceTests: XCTestCase { // The directory was not created by a particular test (e.g: no inserted value) } } - + func test_get_value() throws { // Given let query = IdQuery(String(randomOfLength: 8)) @@ -45,11 +48,14 @@ class FileSystemStorageDataSourceTests: XCTestCase { // Then expect(actualValue).to(equal(expectedValue)) } - + func test_getAll_value() throws { // Given let query = IdQuery(String(randomOfLength: 8)) - let expectedValue = [String(randomOfLength: 8).data(using: .utf8)!, String(randomOfLength: 8).data(using: .utf8)!] + let expectedValue = [ + String(randomOfLength: 8).data(using: .utf8)!, + String(randomOfLength: 8).data(using: .utf8)!, + ] let dataSource = try provideDataSource(insertValues: (query, expectedValue)) // When @@ -58,7 +64,7 @@ class FileSystemStorageDataSourceTests: XCTestCase { // Then expect(actualValue).to(equal(expectedValue)) } - + func test_put_value() throws { // Given let query = IdQuery(String(randomOfLength: 8)) @@ -66,74 +72,80 @@ class FileSystemStorageDataSourceTests: XCTestCase { let dataSource = try provideDataSource() // When - try dataSource.put(expectedValue, in:query).result.get() + try dataSource.put(expectedValue, in: query).result.get() // Then - expect{ - try dataSource.get(query).result.get() + expect { + try dataSource.get(query).result.get() }.to(equal(expectedValue)) } - + func test_putAll_value() throws { // Given let query = IdQuery(String(randomOfLength: 8)) - let expectedValue = [String(randomOfLength: 8).data(using: .utf8)!, String(randomOfLength: 8).data(using: .utf8)!] + let expectedValue = [ + String(randomOfLength: 8).data(using: .utf8)!, + String(randomOfLength: 8).data(using: .utf8)!, + ] let dataSource = try provideDataSource() // When - try dataSource.putAll(expectedValue, in:query).result.get() + try dataSource.putAll(expectedValue, in: query).result.get() // Then - expect{ - try dataSource.getAll(query).result.get() + expect { + try dataSource.getAll(query).result.get() }.to(equal(expectedValue)) } - + func test_delete_value() throws { // Given let value = String(randomOfLength: 8).data(using: .utf8)! let valueQuery = IdQuery(String(randomOfLength: 8)) let dataSource = try provideDataSource(insertValue: (valueQuery, value)) - + // When try dataSource.delete(valueQuery).result.get() - + // Then expect { try dataSource.get(valueQuery).result.get() } .to(throwError(errorType: CoreError.NotFound.self)) } - + func test_delete_all_values() throws { // Given let value = String(randomOfLength: 8).data(using: .utf8)! let valueQuery = IdQuery(String(randomOfLength: 8)) let values = [String(randomOfLength: 8).data(using: .utf8)!, String(randomOfLength: 8).data(using: .utf8)!] let valuesQuery = IdQuery(String(randomOfLength: 8)) - let dataSource = try provideDataSource(insertValue: (valueQuery, value), insertValues: (valuesQuery, values)) - + let dataSource = try provideDataSource( + insertValue: (valueQuery, value), + insertValues: (valuesQuery, values) + ) + // When try dataSource.delete(AllObjectsQuery()).result.get() - + // Then expect { try dataSource.get(valueQuery).result.get() } .to(throwError(errorType: CoreError.NotFound.self)) - + // Then expect { try dataSource.get(valuesQuery).result.get() } .to(throwError(errorType: CoreError.NotFound.self)) } - + func test_get_value_not_found() throws { // Given let dataSource = try provideDataSource() let query = IdQuery(String(randomOfLength: 8)) - + expect { // When try dataSource.get(query).result.get() @@ -141,12 +153,12 @@ class FileSystemStorageDataSourceTests: XCTestCase { // Then .to(throwError(errorType: CoreError.NotFound.self)) } - + func test_getAll_value_not_found() throws { // Given let dataSource = try provideDataSource() let query = IdQuery(String(randomOfLength: 8)) - + expect { // When try dataSource.getAll(query).result.get() @@ -154,12 +166,12 @@ class FileSystemStorageDataSourceTests: XCTestCase { // Then .to(throwError(errorType: CoreError.NotFound.self)) } - + func test_get_non_valid_query() throws { // Given let dataSource = try provideDataSource() let query = VoidQuery() - + expect { // When try dataSource.get(query).result.get() @@ -167,12 +179,12 @@ class FileSystemStorageDataSourceTests: XCTestCase { // Then .to(throwAssertion()) } - + func test_getAll_non_valid_query() throws { // Given let dataSource = try provideDataSource() let query = VoidQuery() - + expect { // When try dataSource.getAll(query).result.get() @@ -180,13 +192,13 @@ class FileSystemStorageDataSourceTests: XCTestCase { // Then .to(throwAssertion()) } - + func test_put_non_valid_query() throws { // Given let dataSource = try provideDataSource() let query = VoidQuery() let value = String(randomOfLength: 8).data(using: .utf8)! - + expect { // When try dataSource.put(value, in: query).result.get() @@ -194,13 +206,13 @@ class FileSystemStorageDataSourceTests: XCTestCase { // Then .to(throwAssertion()) } - + func test_putAll_non_valid_query() throws { // Given let dataSource = try provideDataSource() let query = VoidQuery() let value = [String(randomOfLength: 8).data(using: .utf8)!, String(randomOfLength: 8).data(using: .utf8)!] - + expect { // When try dataSource.putAll(value, in: query).result.get() @@ -208,7 +220,7 @@ class FileSystemStorageDataSourceTests: XCTestCase { // Then .to(throwAssertion()) } - + func test_delete_non_valid_query() throws { // Given let dataSource = try provideDataSource() @@ -221,17 +233,20 @@ class FileSystemStorageDataSourceTests: XCTestCase { // Then .to(throwAssertion()) } - + func test_should_replace_previous_value_when_inserting_with_existing_key() throws { // Given let query = IdQuery(String(randomOfLength: 8)) - let firstValue = [String(randomOfLength: 8).data(using: .utf8)!, String(randomOfLength: 8).data(using: .utf8)!] + let firstValue = [ + String(randomOfLength: 8).data(using: .utf8)!, + String(randomOfLength: 8).data(using: .utf8)!, + ] let secondValue = String(randomOfLength: 8).data(using: .utf8)! let dataSource = try provideDataSource(insertValues: (query, firstValue)) - + expect { // When - _ = dataSource.put(secondValue,in: query) // Put a new value using the same key + _ = dataSource.put(secondValue, in: query) // Put a new value using the same key return try dataSource.get(query).result.get() } // Then @@ -244,6 +259,4 @@ class FileSystemStorageDataSourceTests: XCTestCase { // Then .to(throwError(errorType: CoreError.NotFound.self)) // The old value (list) is not there anymore } - - } diff --git a/Tests/HarmonyTests/FutureTests.swift b/Tests/HarmonyTests/FutureTests.swift index 6cd34054..58ec937a 100644 --- a/Tests/HarmonyTests/FutureTests.swift +++ b/Tests/HarmonyTests/FutureTests.swift @@ -5,13 +5,12 @@ // Created by Joan Martin on 16/6/22. // -import XCTest import Harmony import HarmonyTesting import Nimble +import XCTest class FutureTests: XCTestCase { - override func setUpWithError() throws { // Put setup code here. This method is called before the invocation of each test method in the class. } @@ -23,7 +22,7 @@ class FutureTests: XCTestCase { // Success Errors func test_future_value_set_on_init() throws { // Given - let anyValue = Int.random(in: Int.min...Int.max) + let anyValue = Int.random(in: Int.min ... Int.max) // When let future = Future(anyValue) @@ -34,7 +33,7 @@ class FutureTests: XCTestCase { func test_future_value_set_on_set_value() throws { // Given - let anyValue = Int.random(in: Int.min...Int.max) + let anyValue = Int.random(in: Int.min ... Int.max) let future = Future() // When @@ -46,7 +45,7 @@ class FutureTests: XCTestCase { func test_future_value_set_on_set_value_or_error() throws { // Given - let anyValue = Int.random(in: Int.min...Int.max) + let anyValue = Int.random(in: Int.min ... Int.max) let future = Future() // When @@ -93,7 +92,7 @@ class FutureTests: XCTestCase { func test_future_states_on_value_first_then_after() throws { // Given - let anyValue = Int.random(in: Int.min...Int.max) + let anyValue = Int.random(in: Int.min ... Int.max) let future = Future() // When @@ -113,7 +112,7 @@ class FutureTests: XCTestCase { func test_future_states_on_then_first_value_after() throws { // Given - let anyValue = Int.random(in: Int.min...Int.max) + let anyValue = Int.random(in: Int.min ... Int.max) let future = Future() // When @@ -133,7 +132,7 @@ class FutureTests: XCTestCase { func test_future_states_result_first_value_after() throws { // Given - let anyValue = Int.random(in: Int.min...Int.max) + let anyValue = Int.random(in: Int.min ... Int.max) let queue = DispatchQueue(label: "") let future = Future() let expectation = expectation(description: "") @@ -158,7 +157,7 @@ class FutureTests: XCTestCase { func test_future_states_value_first_result_after() throws { // Given - let anyValue = Int.random(in: Int.min...Int.max) + let anyValue = Int.random(in: Int.min ... Int.max) let future = Future() // When diff --git a/Tests/HarmonyTests/InMemoryDataSourceTests.swift b/Tests/HarmonyTests/InMemoryDataSourceTests.swift index f5f189c1..b9ace276 100644 --- a/Tests/HarmonyTests/InMemoryDataSourceTests.swift +++ b/Tests/HarmonyTests/InMemoryDataSourceTests.swift @@ -15,68 +15,65 @@ // import Foundation +import Harmony import Nimble import XCTest -import Harmony class InMemoryDataSourceTests: XCTestCase { - private func provideEmptyDataSource() -> InMemoryDataSource { return InMemoryDataSource() } - + func test_put_value() throws { // Given let dataSource = provideEmptyDataSource() let query = IdQuery(String(randomOfLength: 8)) let value = Int.random() - + // When _ = try dataSource.put(value, in: query).result.get() - + // Then let result = try dataSource.get(query).result.get() expect(result).to(equal(value)) } - - + func test_putAll_value() throws { // Given let dataSource = provideEmptyDataSource() let query = IdQuery(String(randomOfLength: 8)) let value = [Int.random(), Int.random()] - + // When _ = try dataSource.putAll(value, in: query).result.get() - + // Then let result = try dataSource.getAll(query).result.get() expect(result).to(equal(value)) } - + func test_delete_value() throws { // Given let dataSource = provideEmptyDataSource() let query = IdQuery(String(randomOfLength: 8)) let value = Int.random() _ = try dataSource.put(value, in: query).result.get() - + // When try dataSource.delete(query).result.get() - + // Then expect { try dataSource.get(query).result.get() } .to(throwError(errorType: CoreError.NotFound.self)) - } - + func test_get_value_not_found() throws { // Given let dataSource = provideEmptyDataSource() let query = IdQuery(String(randomOfLength: 8)) - + expect { // When try dataSource.get(query).result.get() @@ -84,12 +81,12 @@ class InMemoryDataSourceTests: XCTestCase { // Then .to(throwError(errorType: CoreError.NotFound.self)) } - + func test_getAll_value_not_found() throws { // Given let dataSource = provideEmptyDataSource() let query = IdQuery(String(randomOfLength: 8)) - + expect { // When try dataSource.getAll(query).result.get() @@ -97,12 +94,12 @@ class InMemoryDataSourceTests: XCTestCase { // Then .to(throwError(errorType: CoreError.NotFound.self)) } - + func test_get_non_valid_query() throws { // Given let dataSource = provideEmptyDataSource() let query = VoidQuery() - + expect { // When try dataSource.get(query).result.get() @@ -110,12 +107,12 @@ class InMemoryDataSourceTests: XCTestCase { // Then .to(throwError(errorType: CoreError.QueryNotSupported.self)) } - + func test_getAll_non_valid_query() throws { // Given let dataSource = provideEmptyDataSource() let query = VoidQuery() - + expect { // When try dataSource.getAll(query).result.get() @@ -123,13 +120,13 @@ class InMemoryDataSourceTests: XCTestCase { // Then .to(throwError(errorType: CoreError.QueryNotSupported.self)) } - + func test_put_non_valid_query() throws { // Given let dataSource = provideEmptyDataSource() let query = VoidQuery() let value = Int.random() - + expect { // When try dataSource.put(value, in: query).result.get() @@ -137,13 +134,13 @@ class InMemoryDataSourceTests: XCTestCase { // Then .to(throwError(errorType: CoreError.QueryNotSupported.self)) } - + func test_putAll_non_valid_query() throws { // Given let dataSource = provideEmptyDataSource() let query = VoidQuery() let value = [Int.random(), Int.random()] - + expect { // When try dataSource.putAll(value, in: query).result.get() @@ -151,12 +148,12 @@ class InMemoryDataSourceTests: XCTestCase { // Then .to(throwError(errorType: CoreError.QueryNotSupported.self)) } - + func test_delete_non_valid_query() throws { // Given let dataSource = provideEmptyDataSource() let query = VoidQuery() - + expect { // When try dataSource.delete(query).result.get() @@ -164,7 +161,7 @@ class InMemoryDataSourceTests: XCTestCase { // Then .to(throwError(errorType: CoreError.QueryNotSupported.self)) } - + func test_should_replace_previous_value_when_inserting_with_existing_key() throws { // Given let dataSource = provideEmptyDataSource() @@ -172,10 +169,10 @@ class InMemoryDataSourceTests: XCTestCase { let firstValue = [Int.random(), Int.random()] let secondValue = Int.random() dataSource.putAll(firstValue, in: query) // Put a list - + expect { // When - dataSource.put(secondValue,in: query) // Put a new value using the same key + dataSource.put(secondValue, in: query) // Put a new value using the same key return try dataSource.get(query).result.get() } // Then @@ -188,5 +185,4 @@ class InMemoryDataSourceTests: XCTestCase { // Then .to(throwError(errorType: CoreError.NotFound.self)) // The old value (list) is not there anymore } - } diff --git a/Tests/HarmonyTests/TimedCacheDataSourceTests.swift b/Tests/HarmonyTests/TimedCacheDataSourceTests.swift index e514f052..945265c7 100644 --- a/Tests/HarmonyTests/TimedCacheDataSourceTests.swift +++ b/Tests/HarmonyTests/TimedCacheDataSourceTests.swift @@ -6,70 +6,66 @@ // import Foundation -import Foundation +import Harmony import Nimble import XCTest -import Harmony // TODO: Improve TimedCacheDataSourceTests to take into account expiration date. Use a spy to check wether the value is obtained from cache or not. class TimedCacheDataSourceTests: XCTestCase { - private func provideEmptyDataSource() -> TimedCacheDataSource> { return TimedCacheDataSource(InMemoryDataSource()) } - + func test_put_value() throws { // Given let dataSource = provideEmptyDataSource() let query = IdQuery(String(randomOfLength: 8)) let value = Int.random() - + // When _ = try dataSource.put(value, in: query).result.get() - + // Then let result = try dataSource.get(query).result.get() expect(result).to(equal(value)) } - - + func test_putAll_value() throws { // Given let dataSource = provideEmptyDataSource() let query = IdQuery(String(randomOfLength: 8)) let value = [Int.random(), Int.random()] - + // When _ = try dataSource.putAll(value, in: query).result.get() - + // Then let result = try dataSource.getAll(query).result.get() expect(result).to(equal(value)) } - + func test_delete_value() throws { // Given let dataSource = provideEmptyDataSource() let query = IdQuery(String(randomOfLength: 8)) let value = Int.random() _ = try dataSource.put(value, in: query).result.get() - + // When try dataSource.delete(query).result.get() - + // Then expect { try dataSource.get(query).result.get() } .to(throwError(errorType: CoreError.NotFound.self)) - } - + func test_get_value_not_found() throws { // Given let dataSource = provideEmptyDataSource() let query = IdQuery(String(randomOfLength: 8)) - + expect { // When try dataSource.get(query).result.get() @@ -77,12 +73,12 @@ class TimedCacheDataSourceTests: XCTestCase { // Then .to(throwError(errorType: CoreError.NotFound.self)) } - + func test_getAll_value_not_found() throws { // Given let dataSource = provideEmptyDataSource() let query = IdQuery(String(randomOfLength: 8)) - + expect { // When try dataSource.getAll(query).result.get() @@ -90,12 +86,12 @@ class TimedCacheDataSourceTests: XCTestCase { // Then .to(throwError(errorType: CoreError.NotFound.self)) } - + func test_get_non_valid_query() throws { // Given let dataSource = provideEmptyDataSource() let query = VoidQuery() - + expect { // When try dataSource.get(query).result.get() @@ -103,12 +99,12 @@ class TimedCacheDataSourceTests: XCTestCase { // Then .to(throwError(errorType: CoreError.QueryNotSupported.self)) } - + func test_getAll_non_valid_query() throws { // Given let dataSource = provideEmptyDataSource() let query = VoidQuery() - + expect { // When try dataSource.getAll(query).result.get() @@ -116,13 +112,13 @@ class TimedCacheDataSourceTests: XCTestCase { // Then .to(throwError(errorType: CoreError.QueryNotSupported.self)) } - + func test_put_non_valid_query() throws { // Given let dataSource = provideEmptyDataSource() let query = VoidQuery() let value = Int.random() - + expect { // When try dataSource.put(value, in: query).result.get() @@ -130,13 +126,13 @@ class TimedCacheDataSourceTests: XCTestCase { // Then .to(throwError(errorType: CoreError.QueryNotSupported.self)) } - + func test_putAll_non_valid_query() throws { // Given let dataSource = provideEmptyDataSource() let query = VoidQuery() let value = [Int.random(), Int.random()] - + expect { // When try dataSource.putAll(value, in: query).result.get() @@ -144,12 +140,12 @@ class TimedCacheDataSourceTests: XCTestCase { // Then .to(throwError(errorType: CoreError.QueryNotSupported.self)) } - + func test_delete_non_valid_query() throws { // Given let dataSource = provideEmptyDataSource() let query = VoidQuery() - + expect { // When try dataSource.delete(query).result.get() @@ -157,7 +153,7 @@ class TimedCacheDataSourceTests: XCTestCase { // Then .to(throwError(errorType: CoreError.QueryNotSupported.self)) } - + func test_should_replace_previous_value_when_inserting_with_existing_key() throws { // Given let dataSource = provideEmptyDataSource() @@ -165,10 +161,10 @@ class TimedCacheDataSourceTests: XCTestCase { let firstValue = [Int.random(), Int.random()] let secondValue = Int.random() dataSource.putAll(firstValue, in: query) // Put a list - + expect { // When - dataSource.put(secondValue,in: query) // Put a new value using the same key + dataSource.put(secondValue, in: query) // Put a new value using the same key return try dataSource.get(query).result.get() } // Then @@ -181,5 +177,4 @@ class TimedCacheDataSourceTests: XCTestCase { // Then .to(throwError(errorType: CoreError.NotFound.self)) // The old value (list) is not there anymore } - }