import BrightFutures import Result /// ContentLoader is a lightweight state manager for loading and displaying content and/or errors. /// It allows you to repeatedly make requests and display new content or cached content in the event of an error. /// Commonly known as RemoteData or LCE (Loading / Content / Error). /// Inspired by https://tech.instacart.com/lce-modeling-data-loading-in-rxjava-b798ac98d80 /// final class ContentLoader { init() { } private var loader: (() -> Void)? private var update: (() -> Void)? var loading: Bool = false var content: T? var error: Error? var hasContent: Bool { return content != nil } var hasError: Bool { return error != nil } @discardableResult func loader(_ loader: (() -> Void)?) -> ContentLoader { self.loader = loader return self } @discardableResult func onUpdate(_ update: (() -> Void)?) -> ContentLoader { self.update = update return self } func load(loader: (() -> Void)?) { loading = true if let loader = loader { loader() } else if let loader = self.loader { loader() } else { loading = false failure(error: ContentLoaderError.noLoadingMethod) } } func load(future: Future) { loading = true future .onSuccess { self.success(content: $0) } .onFailure { self.failure(error: $0.error) } .onComplete { _ in self.loading = false } } func success(content: T) { self.content = content self.loading = false update?() } func failure(error: Error?) { self.error = error self.loading = false update?() } /// Does not cancel any ongoing async work func reset() { error = nil content = nil loading = false } } enum ContentLoaderError: Error { case noLoadingMethod }