Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
fabe9c7
Group Feed UI tests in a new folder
PortoCode Mar 16, 2025
500b9ca
Add `CellController` protocol in preparation to support any cell cont…
PortoCode Mar 16, 2025
cbf7677
Rename FeedViewController to ListViewController since it can now rend…
PortoCode Mar 16, 2025
15c8f80
Replace FeedViewControllerDelegate with a closure
PortoCode Mar 16, 2025
d1766b0
Move Shared UI to new folder
PortoCode Mar 17, 2025
0bdb752
Extract shared ListViewController snapshot tests
PortoCode Mar 17, 2025
7ba98f9
Add XCTFail to remind us to call assert after recording a snapshot
PortoCode Mar 17, 2025
b8aafce
Implement Image Comments UI
PortoCode Mar 17, 2025
eb03a36
Add default implementation for optional methods in the CellController…
PortoCode Mar 17, 2025
b37dd34
Replace custom CellController protocol with a composition of the UITa…
PortoCode Mar 17, 2025
1ab9509
Replace protocol composition in a single type with a struct compositi…
PortoCode Mar 17, 2025
bd021d7
Weakify self within `onRetry` closure to prevent cell from holding st…
PortoCode Mar 17, 2025
2bdd84f
Configure `ErrorView` programmatically so we don't duplicate layout l…
PortoCode Mar 18, 2025
d2de713
Run ListSnapshotTests without storyboard
PortoCode Mar 18, 2025
67a0985
Enable content size category config when taking a snapshot
PortoCode Mar 18, 2025
61e8085
Replace hardcoded fonts with dynamic fonts
PortoCode Mar 18, 2025
1b050cd
Use diffable data source to only reload cells when needed
PortoCode Mar 18, 2025
a0511b8
Set data source default row animation to fade
PortoCode Mar 18, 2025
f0642a3
Add `UIView.makeContainer` helper
PortoCode Mar 18, 2025
93840bd
Extract table view configuration into a helper method
PortoCode Mar 18, 2025
da4f0aa
Configure Error View with the new `UIButton.Configuration` APIs
PortoCode Mar 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 6 additions & 8 deletions EssentialApp/EssentialApp/FeedUIComposer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,11 @@ public final class FeedUIComposer {
public static func feedComposedWith(
feedLoader: @escaping () -> AnyPublisher<[FeedImage], Error>,
imageLoader: @escaping (URL) -> FeedImageDataLoader.Publisher
) -> FeedViewController {
) -> ListViewController {
let presentationAdapter = FeedPresentationAdapter(loader: feedLoader)

let feedController = makeFeedViewController(
delegate: presentationAdapter,
title: FeedPresenter.title)
let feedController = makeFeedViewController(title: FeedPresenter.title)
feedController.onRefresh = presentationAdapter.loadResource

presentationAdapter.presenter = LoadResourcePresenter(
resourceView: FeedViewAdapter(
Expand All @@ -34,11 +33,10 @@ public final class FeedUIComposer {
return feedController
}

private static func makeFeedViewController(delegate: FeedViewControllerDelegate, title: String) -> FeedViewController {
let bundle = Bundle(for: FeedViewController.self)
private static func makeFeedViewController(title: String) -> ListViewController {
let bundle = Bundle(for: ListViewController.self)
let storyboard = UIStoryboard(name: "Feed", bundle: bundle)
let feedController = storyboard.instantiateInitialViewController() as! FeedViewController
feedController.delegate = delegate
let feedController = storyboard.instantiateInitialViewController() as! ListViewController
feedController.title = title
return feedController
}
Expand Down
6 changes: 3 additions & 3 deletions EssentialApp/EssentialApp/FeedViewAdapter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import EssentialFeed
import EssentialFeediOS

final class FeedViewAdapter: ResourceView {
private weak var controller: FeedViewController?
private weak var controller: ListViewController?
private let imageLoader: (URL) -> FeedImageDataLoader.Publisher

private typealias ImageDataPresentationAdapter = LoadResourcePresentationAdapter<Data, WeakRefVirtualProxy<FeedImageCellController>>

init(controller: FeedViewController? = nil, imageLoader: @escaping (URL) -> FeedImageDataLoader.Publisher) {
init(controller: ListViewController? = nil, imageLoader: @escaping (URL) -> FeedImageDataLoader.Publisher) {
self.controller = controller
self.imageLoader = imageLoader
}
Expand All @@ -34,7 +34,7 @@ final class FeedViewAdapter: ResourceView {
errorView: WeakRefVirtualProxy(view),
mapper: UIImage.tryMake)

return view
return CellController(id: model, view)
})
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,6 @@ final class LoadResourcePresentationAdapter<Resource, View: ResourceView> {
}
}

extension LoadResourcePresentationAdapter: FeedViewControllerDelegate {
func didRequestFeedRefresh() {
loadResource()
}
}

extension LoadResourcePresentationAdapter: FeedImageCellControllerDelegate {
func didRequestImage() {
loadResource()
Expand Down
4 changes: 2 additions & 2 deletions EssentialApp/EssentialAppTests/FeedAcceptanceTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,13 @@ class FeedAcceptanceTests: XCTestCase {
private func launch(
httpClient: HTTPClientStub = .offline,
store: InMemoryFeedStore = .empty
) -> FeedViewController {
) -> ListViewController {
let sut = SceneDelegate(httpClient: httpClient, store: store)
sut.window = UIWindow()
sut.configureWindow()

let nav = sut.window?.rootViewController as? UINavigationController
let vc = nav?.topViewController as! FeedViewController
let vc = nav?.topViewController as! ListViewController
vc.simulateAppearance()
return vc
}
Expand Down
15 changes: 14 additions & 1 deletion EssentialApp/EssentialAppTests/FeedUIIntegrationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,19 @@ final class FeedUIIntegrationTests: XCTestCase {
XCTAssertEqual(sut.errorMessage, nil)
}

func test_tapOnErrorView_hidesErrorMessage() {
let (sut, loader) = makeSUT()

sut.simulateAppearance()
XCTAssertEqual(sut.errorMessage, nil)

loader.completeFeedLoadingWithError(at: 0)
XCTAssertEqual(sut.errorMessage, loadError)

sut.simulateErrorViewTap()
XCTAssertEqual(sut.errorMessage, nil)
}

func test_feedImageView_loadsImageURLWhenVisible() {
let image0 = makeImage(url: URL(string: "http://url-0.com")!)
let image1 = makeImage(url: URL(string: "http://url-1.com")!)
Expand Down Expand Up @@ -354,7 +367,7 @@ final class FeedUIIntegrationTests: XCTestCase {

// MARK: - Helpers

private func makeSUT(file: StaticString = #file, line: UInt = #line) -> (sut: FeedViewController, loader: LoaderSpy) {
private func makeSUT(file: StaticString = #file, line: UInt = #line) -> (sut: ListViewController, loader: LoaderSpy) {
let loader = LoaderSpy()
let sut = FeedUIComposer.feedComposedWith(feedLoader: loader.loadPublisher, imageLoader: loader.loadImageDataPublisher)
trackForMemoryLeaks(loader, file: file, line: line)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import EssentialFeediOS

extension FeedUIIntegrationTests {

func assertThat(_ sut: FeedViewController, isRendering feed: [FeedImage], file: StaticString = #file, line: UInt = #line) {
func assertThat(_ sut: ListViewController, isRendering feed: [FeedImage], file: StaticString = #file, line: UInt = #line) {
sut.view.enforceLayoutCycle()

guard sut.numberOfRenderedFeedImageViews() == feed.count else {
Expand All @@ -23,7 +23,7 @@ extension FeedUIIntegrationTests {
executeRunLoopToCleanUpReferences()
}

func assertThat(_ sut: FeedViewController, hasViewConfiguredFor image: FeedImage, at index: Int, file: StaticString = #file, line: UInt = #line) {
func assertThat(_ sut: ListViewController, hasViewConfiguredFor image: FeedImage, at index: Int, file: StaticString = #file, line: UInt = #line) {
let view = sut.feedImageView(at: index)

guard let cell = view as? FeedImageCell else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import UIKit
import EssentialFeediOS

extension FeedViewController {
extension ListViewController {
func simulateAppearance() {
if !isViewLoaded {
loadViewIfNeeded()
Expand Down Expand Up @@ -76,16 +76,20 @@ extension FeedViewController {
return simulateFeedImageViewVisible(at: index)?.renderedImage
}

func simulateErrorViewTap() {
errorView.simulateTap()
}

var errorMessage: String? {
return errorView?.message
return errorView.message
}

var isShowingLoadingIndicator: Bool {
return refreshControl?.isRefreshing == true
}

func numberOfRenderedFeedImageViews() -> Int {
return tableView.numberOfRows(inSection: feedImagesSection)
tableView.numberOfSections == 0 ? 0 : tableView.numberOfRows(inSection: feedImagesSection)
}

func feedImageView(at row: Int) -> UITableViewCell? {
Expand Down
2 changes: 1 addition & 1 deletion EssentialApp/EssentialAppTests/SceneDelegateTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class SceneDelegateTests: XCTestCase {
let topController = rootNavigation?.topViewController

XCTAssertNotNil(rootNavigation, "Expected a navigation controller as root, got \(String(describing: root)) instead")
XCTAssertTrue(topController is FeedViewController, "Expected a feed controller as top view controller, got \(String(describing: topController)) instead")
XCTAssertTrue(topController is ListViewController, "Expected a feed controller as top view controller, got \(String(describing: topController)) instead")
}

}
Loading