Skip to content

Commit

Permalink
🛠 Essential Feed v1.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
AlfredoHernandez authored Dec 2, 2020
2 parents 77e50b8 + 8e7ed14 commit eea1f62
Show file tree
Hide file tree
Showing 93 changed files with 1,681 additions and 1,530 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/CI-iOS.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
name: CI-iOS

# Controls when the action will run. Triggers the workflow on push or pull request
# events but only for the master branch
# events but only for the main branch
on:
pull_request:
branches: [ master, develop ]
branches: [ main, develop ]

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/CI-macOS.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
name: CI-macOS

# Controls when the action will run. Triggers the workflow on push or pull request
# events but only for the master branch
# events but only for the main branch
on:
pull_request:
branches: [ master, develop ]
branches: [ main, develop ]

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
Expand Down
52 changes: 8 additions & 44 deletions EssentialApp/EssentialApp.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

142 changes: 142 additions & 0 deletions EssentialApp/EssentialApp/CombineHelpers.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
//
// Copyright © 2020 Jesús Alfredo Hernández Alarcón. All rights reserved.
//

import Combine
import EssentialFeed
import Foundation

public extension HTTPClient {
typealias Publisher = AnyPublisher<(Data, HTTPURLResponse), Error>

func getPublisher(from url: URL) -> Publisher {
var task: HTTPClientTask?

return Deferred {
Future { completion in
task = self.get(from: url, completion: completion)
}
}
.handleEvents(receiveCancel: { task?.cancel() })
.eraseToAnyPublisher()
}
}

public extension FeedImageDataLoader {
typealias Publisher = AnyPublisher<Data, Swift.Error>

func loadImageDataPublisher(from url: URL) -> Publisher {
var task: FeedImageDataLoaderTask?

return Deferred {
Future { completion in
task = loadImageData(from: url, completion: completion)
}
}
.handleEvents(receiveCancel: { task?.cancel() })
.eraseToAnyPublisher()
}
}

public extension Publisher where Output == Data {
func catching(to cache: FeedImageDataCache, using url: URL) -> AnyPublisher<Output, Failure> {
handleEvents(receiveOutput: { data in
cache.saveIgnoringResult(data: data, for: url)
}).eraseToAnyPublisher()
}
}

public extension FeedImageDataCache {
func saveIgnoringResult(data: Data, for url: URL) {
save(data, for: url, completion: { _ in })
}
}

public extension LocalFeedLoader {
typealias Publisher = AnyPublisher<[FeedImage], Swift.Error>

func loadPublisher() -> Publisher {
Deferred { Future(self.load) }.eraseToAnyPublisher()
}
}

public extension Publisher where Output == [FeedImage] {
func catching(to cache: FeedCache) -> AnyPublisher<Output, Failure> {
handleEvents(receiveOutput: { feed in
cache.saveIgnoringResult(feed)
}).eraseToAnyPublisher()
}
}

private extension FeedCache {
func saveIgnoringResult(_ feed: [FeedImage]) {
save(feed) { _ in }
}
}

public extension Publisher {
func fallback(to fallbackPublisher: @escaping () -> AnyPublisher<Output, Failure>) -> AnyPublisher<Output, Failure> {
self.catch { _ in fallbackPublisher() }.eraseToAnyPublisher()
}
}

extension Publisher {
func dispatchOnMainQueue() -> AnyPublisher<Output, Failure> {
receive(on: DispatchQueue.immediateWhenOnMainQueueScheduler).eraseToAnyPublisher()
}
}

extension DispatchQueue {
static var immediateWhenOnMainQueueScheduler = ImmediateWhenOnMainQueueScheduler.shared

struct ImmediateWhenOnMainQueueScheduler: Scheduler {
typealias SchedulerTimeType = DispatchQueue.SchedulerTimeType
typealias SchedulerOptions = DispatchQueue.SchedulerOptions
var now: DispatchQueue.SchedulerTimeType {
DispatchQueue.main.now
}

var minimumTolerance: DispatchQueue.SchedulerTimeType.Stride {
DispatchQueue.main.minimumTolerance
}

static let shared = Self()

private static let key = DispatchSpecificKey<UInt8>()
private static let value = UInt8.max

private init() {
DispatchQueue.main.setSpecific(key: Self.key, value: Self.value)
}

private func isMainQueue() -> Bool {
DispatchQueue.getSpecific(key: Self.key) == Self.value
}

func schedule(options: DispatchQueue.SchedulerOptions?, _ action: @escaping () -> Void) {
guard isMainQueue() else {
return DispatchQueue.main.schedule(options: options, action)
}
action()
}

func schedule(
after date: DispatchQueue.SchedulerTimeType,
tolerance: DispatchQueue.SchedulerTimeType.Stride,
options: DispatchQueue.SchedulerOptions?,
_ action: @escaping () -> Void
) {
DispatchQueue.main.schedule(after: date, tolerance: tolerance, options: options, action)
}

func schedule(
after date: DispatchQueue.SchedulerTimeType,
interval: DispatchQueue.SchedulerTimeType.Stride,
tolerance: DispatchQueue.SchedulerTimeType.Stride,
options: DispatchQueue.SchedulerOptions?,
_ action: @escaping () -> Void
) -> Cancellable {
DispatchQueue.main.schedule(after: date, interval: interval, tolerance: tolerance, options: options, action)
}
}
}
31 changes: 0 additions & 31 deletions EssentialApp/EssentialApp/FeedImageDataLoaderCacheDecorator.swift

This file was deleted.

This file was deleted.

This file was deleted.

30 changes: 0 additions & 30 deletions EssentialApp/EssentialApp/FeedLoaderCacheDecorator.swift

This file was deleted.

32 changes: 19 additions & 13 deletions EssentialApp/EssentialApp/FeedLoaderPresentationAdapter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,32 @@
// Copyright © 2020 Jesús Alfredo Hernández Alarcón. All rights reserved.
//

import Combine
import EssentialFeed
import EssentialFeediOS
import Foundation

final class FeedLoaderPresentationAdapter: FeedViewControllerDelegate {
private let feedLoader: FeedLoader
var presenter: FeedPresenter?
final class LoadResourcePresentationAdapter: FeedViewControllerDelegate {
private let feedLoader: () -> AnyPublisher<[FeedImage], Error>
var presenter: LoadResourcePresenter<[FeedImage], FeedViewAdapter>?
var cancellable: AnyCancellable?

init(feedLoader: FeedLoader) {
init(feedLoader: @escaping () -> AnyPublisher<[FeedImage], Error>) {
self.feedLoader = feedLoader
}

func didRequestFeedRefresh() {
presenter?.didStartLoadingFeed()
feedLoader.load { [weak self] result in
switch result {
case let .success(feed):
self?.presenter?.didFinishLoadingFeed(with: feed)
case let .failure(error):
self?.presenter?.didFinishLoadingFeed(with: error)
}
}
presenter?.didStartLoading()

cancellable = feedLoader()
.sink(receiveCompletion: { [weak self] completion in
switch completion {
case let .failure(error):
self?.presenter?.didFinishLoading(with: error)
default: break
}
}, receiveValue: { [weak self] feed in
self?.presenter?.didFinishLoading(with: feed)
})
}
}
26 changes: 0 additions & 26 deletions EssentialApp/EssentialApp/FeedLoaderWithFallbackComposite.swift

This file was deleted.

Loading

0 comments on commit eea1f62

Please sign in to comment.