Skip to content

Instantly share code, notes, and snippets.

@rjchatfield
Last active February 16, 2020 12:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rjchatfield/12071f629c0d8df26bf5a7d5c4953e06 to your computer and use it in GitHub Desktop.
Save rjchatfield/12071f629c0d8df26bf5a7d5c4953e06 to your computer and use it in GitHub Desktop.
Async fetcher
final class BoardShortFetcher {
let projectRepo: ProjectRepository
let projectGateway: ProjectGateway
let boardShortRepo: BoardShortRepository
let boardShortGateway: BoardShortGateway
init() { fatalError() }
// Only entry point
func retreiveBoardShort(boardFeatureLocator: BoardFeatureLocator) -> Future<BoardShortEntity, Error> {
retieveBoardID(boardFeatureLocator: boardFeatureLocator)
.flatMap { [boardShortRepo, boardShortGateway] boardID -> Future<BoardShortEntity, Error> in
// Try from Cache...
if let boardShort = boardShortRepo.retrieveBoardShort(boardID: boardID) {
return .just(boardShort)
} else {
// ... else, from Network
return boardShortGateway.fetchBoardShort(boardID: boardID)
}
}
}
// MARK: - Private methods
private func retieveBoardID(boardFeatureLocator: BoardFeatureLocator) -> Future<String, Error> {
switch boardFeatureLocator {
case .board(let boardID),
.backlog(let boardID):
return .just(boardID)
case .coreProject(let projectKey):
// Note: Core Board ID == Project ID
// Try from Cache...
if let projectID = projectRepo.retrieveProjectID(projectKey: projectKey) {
return .just(projectID)
} else {
// ... else, from Network
return projectGateway.fetchProjectID(projectKey: projectKey)
}
}
}
}
import Combine
// + 40 lines of Combine extensions
enum BoardShortFetcher2 {
struct State {
let boardFeatureLocator: BoardFeatureLocator
var boardID: String?
var boardShort: BoardShortEntity?
}
enum Action {
case start
case retreivedCoreProjectID(projectID: String?)
case retreivedBoardShort(BoardShortEntity?)
}
enum Effect {
case retreiveProjectIDFromCache(String)
case retreiveProjectIDFromNetwork(String)
case retreiveBoardShortFromCache(boardID: String)
case retreiveBoardFromNetwork(boardID: String)
}
// Notes: Pure function, but no async calls
static func reduce(state: inout State, action: Action) -> [Effect] {
switch action {
case .start:
switch state.boardFeatureLocator {
case .board(let boardID),
.backlog(let boardID):
state.boardID = boardID
return [.retreiveBoardShortFromCache(boardID: boardID)]
case .coreProject(let projectKey):
return [.retreiveProjectIDFromCache(projectKey)]
}
case .retreivedCoreProjectID(let projectID?):
// Note: Core Board ID == Project ID
state.boardID = projectID
return [.retreiveBoardShortFromCache(boardID: projectID)]
case .retreivedCoreProjectID(nil):
guard case .coreProject(let projectKey) = state.boardFeatureLocator else { return [] }
return [.retreiveProjectIDFromNetwork(projectKey)]
case .retreivedBoardShort(nil):
guard let boardID = state.boardID else { return [] }
return [.retreiveBoardFromNetwork(boardID: boardID)]
case .retreivedBoardShort(let boardShort?):
state.boardShort = boardShort
}
return []
}
// TODO: Make this useful
class EffectHandler {
let projectRepo: ProjectRepository
let projectGateway: ProjectGateway
let boardShortRepo: BoardShortRepository
let boardShortGateway: BoardShortGateway
init() { fatalError() }
// Notes: Async calls, but very little logic
func handleEffect(effect: Effect) -> AnyPublisher<Action, Never> {
switch effect {
case .retreiveProjectIDFromCache(let projectKey):
let projectID = projectRepo.retrieveProjectID(projectKey: projectKey)
return .just(.retreivedCoreProjectID(projectID: projectID))
case .retreiveProjectIDFromNetwork(let projectKey):
return projectGateway.fetchProjectID(projectKey: projectKey)
.map { .retreivedCoreProjectID(projectID: $0) }
.mapError { _ in fatalError() }
.eraseToAnyPublisher()
case .retreiveBoardShortFromCache(let boardID):
let boardShort = boardShortRepo.retrieveBoardShort(boardID: boardID)
return .just(.retreivedBoardShort(boardShort))
case .retreiveBoardFromNetwork(let boardID):
return boardShortGateway.fetchBoardShort(boardID: boardID)
.map { .retreivedBoardShort($0) }
.mapError { _ in fatalError() }
.eraseToAnyPublisher()
}
}
}
}
import Combine
public final class BoardShortFetcher3 {
let projectRepo: ProjectRepository
let projectGateway: ProjectGateway
let boardShortRepo: BoardShortRepository
let boardShortGateway: BoardShortGateway
init() { fatalError() }
func retreiveBoardShort(boardFeatureLocator: BoardFeatureLocator) throws async -> BoardShortEntity {
let boardID = try await retieveBoardID(boardFeatureLocator: boardFeatureLocator)
// Try from Cache...
return boardShortRepository.retrieveBoardShort(boardID: boardID)
// ... else, from Network
?? try await boardShortGateway.fetchBoardShort(boardID: boardID)
}
private func retieveBoardID(boardFeatureLocator: BoardFeatureLocator) throws async -> String {
switch boardFeatureLocator {
case .board(let boardID),
.backlog(let boardID):
return boardID
case .coreProject(let projectKey):
// Note: Core Board ID == Project ID
// Try from Cache...
return projectRepository.retrieveProjectID(projectKey: projectKey)
// ... else, from Network
?? try await projectGateway.fetchProjectID(projectKey: projectKey)
}
}
}
public extension Future {
static func just(_ output: Output) -> Self {
Self { callback in
callback(.success(output))
}
}
public func flatMap<U>(_ transform: @escaping (Output) -> Future<U, Failure>) -> Future<U, Failure> {
Future<U, Failure>({ [self] callback in
self
.flatMap({ output in
transform(output)
})
.sink(
receiveCompletion: { completionResult in
switch completionResult {
case .finished: break
case .failure(let error): callback(.failure(error))
}
},
receiveValue: { callback(.success($0)) }
)
})
}
public func flatMap<U>(transform: @escaping (Output) -> Result<U, Failure>) -> Future<U, Failure> {
flatMap({ output in
Future<U, Failure> { callback in
callback(transform(output))
}
})
}
}
public extension AnyPublisher {
static func just(_ output: Output) -> Self {
Self(Future.just(output))
}
}
import Combine
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment