Skip to content

Commit

Permalink
improve ui and refactor cancellable to cancellables (#75)
Browse files Browse the repository at this point in the history
  • Loading branch information
erolburak authored Mar 26, 2024
1 parent 0514db3 commit c17acc3
Show file tree
Hide file tree
Showing 10 changed files with 77 additions and 87 deletions.
68 changes: 24 additions & 44 deletions BobbysNews/Presentation/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,11 @@ struct ContentView: View {
}
case .emptyFetch, .emptyRead:
Section(viewModel.stateSources == .emptyFetch ? "EmptyFetchSources" : "EmptyReadSources") {
Button {
Button("CountriesLoad",
systemImage: "arrow.down.to.line.circle.fill") {
Task {
await viewModel.fetchSources(sensoryFeedback: true)
}
} label: {
Label("CountriesLoad",
systemImage: "arrow.down.to.line.circle.fill")
}
}
}
Expand All @@ -96,11 +94,10 @@ struct ContentView: View {
}

Section {
Button(role: .destructive) {
Button("Reset",
systemImage: "trash.circle.fill",
role: .destructive) {
viewModel.showConfirmationDialog = true
} label: {
Label("Reset",
systemImage: "trash.circle.fill")
}
.accessibilityIdentifier("ResetButton")
}
Expand All @@ -122,46 +119,29 @@ struct ContentView: View {
}
.overlay(alignment: .center) {
if viewModel.selectedCountry.isEmpty {
ContentUnavailableView {
Label("EmptySelectedCountry",
systemImage: "flag.circle.fill")
} description: {
Text("EmptySelectedCountryMessage")
}
ContentUnavailableView("EmptySelectedCountry",
systemImage: "flag.circle.fill",
description: Text("EmptySelectedCountryMessage"))
} else {
VStack {
switch viewModel.stateTopHeadlines {
case .isLoading:
Text("TopHeadlinesLoading")
.fontWeight(.black)
case .loaded:
EmptyView()
case .emptyFetch:
ContentUnavailableView {
Label("EmptyFetchTopHeadlines",
systemImage: "newspaper.circle.fill")
} description: {
Text("EmptyFetchTopHeadlinesMessage")
}
case .emptyRead:
ContentUnavailableView {
Label("EmptyReadTopHeadlines",
systemImage: "newspaper.circle.fill")
} description: {
Text("EmptyReadTopHeadlinesMessage")
}
}

if viewModel.stateTopHeadlines == .emptyFetch ||
viewModel.stateTopHeadlines == .emptyRead {
Button {
switch viewModel.stateTopHeadlines {
case .isLoading:
Text("TopHeadlinesLoading")
.fontWeight(.black)
case .loaded:
EmptyView()
case .emptyFetch, .emptyRead:
ContentUnavailableView {
Label(viewModel.stateTopHeadlines == .emptyFetch ? "EmptyFetchTopHeadlines" : "EmptyReadTopHeadlines",
systemImage: "newspaper.circle.fill")
} description: {
Text(viewModel.stateTopHeadlines == .emptyFetch ? "EmptyFetchTopHeadlinesMessage" : "EmptyReadTopHeadlinesMessage")
} actions: {
Button("Refresh") {
Task {
await viewModel.fetchTopHeadlines(state: .isLoading)
}
} label: {
Text("Refresh")
.textCase(.uppercase)
}
.textCase(.uppercase)
.font(.system(.subheadline,
weight: .black))
.foregroundStyle(.secondary)
Expand Down Expand Up @@ -239,7 +219,7 @@ struct ContentView: View {
.frame(width: 80,
height: 80)
.background(.bar)
.clipShape(RoundedRectangle(cornerRadius: 12))
.clipShape(.rect(cornerRadius: 12))
}
.padding(.horizontal)
.padding(.vertical, 20)
Expand Down
8 changes: 4 additions & 4 deletions BobbysNews/Presentation/ContentViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ final class ContentViewModel: Sendable {

// MARK: - Private Properties

private var cancellable = Set<AnyCancellable>()
private var cancellables = Set<AnyCancellable>()

// MARK: - Properties

Expand Down Expand Up @@ -94,7 +94,7 @@ final class ContentViewModel: Sendable {
}

func onDisappear() {
cancellable.removeAll()
cancellables.removeAll()
}

func fetchSources(sensoryFeedback: Bool? = nil) async {
Expand Down Expand Up @@ -163,7 +163,7 @@ final class ContentViewModel: Sendable {
self?.updateStateSources(completion: .finished,
state: countries.isEmpty ? self?.stateSources == .emptyFetch ? .emptyFetch : .emptyRead : .loaded)
})
.store(in: &cancellable)
.store(in: &cancellables)
}

private func readTopHeadlines() {
Expand All @@ -181,7 +181,7 @@ final class ContentViewModel: Sendable {
self?.updateStateTopHeadlines(completion: .finished,
state: articles.isEmpty ? self?.stateTopHeadlines == .emptyFetch ? .emptyFetch : .emptyRead : .loaded)
})
.store(in: &cancellable)
.store(in: &cancellables)
}

private func sensoryFeedbackTrigger(feedback: SensoryFeedback) {
Expand Down
24 changes: 9 additions & 15 deletions BobbysNews/Presentation/DetailView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ struct DetailView: View {
}
.frame(height: 280)
.background(.bar)
.clipShape(UnevenRoundedRectangle(cornerRadii: RectangleCornerRadii(topLeading: 40,
topTrailing: 40)))
.clipShape(.rect(topLeadingRadius: 40,
topTrailingRadius: 40))
.overlay {
LinearGradient(gradient: Gradient(colors: [.clear,
Color(uiColor: .systemBackground)]),
Expand Down Expand Up @@ -93,12 +93,10 @@ struct DetailView: View {
.font(.system(.caption2,
weight: .semibold))

Button {
Button("Read") {
viewModel.showWebView = true
} label: {
Text("Read")
.textCase(.uppercase)
}
.textCase(.uppercase)
.font(.system(.subheadline,
weight: .black))
.accessibilityIdentifier("ReadButton")
Expand Down Expand Up @@ -157,23 +155,19 @@ struct DetailView: View {
showError: $viewModel.webViewShowError,
url: url)
} else {
ContentUnavailableView {
Label("ErrorWebView",
systemImage: "newspaper.circle.fill")
} description: {
Text("ErrorWebViewMessage")
}
ContentUnavailableView("ErrorWebView",
systemImage: "newspaper.circle.fill",
description: Text("ErrorWebViewMessage"))
}
}
.navigationTitle("Headline")
.navigationBarTitleDisplayMode(.inline)
.ignoresSafeArea(edges: .bottom)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button {
Button("Close",
systemImage: "xmark.circle.fill") {
viewModel.showWebView = false
} label: {
Image(systemName: "xmark.circle.fill")
}
.accessibilityIdentifier("CloseButton")
}
Expand Down
16 changes: 16 additions & 0 deletions BobbysNews/Resource/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,22 @@
}
}
},
"Close" : {
"localizations" : {
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "Schließen"
}
},
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Close"
}
}
}
},
"CountriesLoad" : {
"extractionState" : "manual",
"localizations" : {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,18 @@ class SourcesPersistenceControllerTests: XCTestCase {

// MARK: - Private Properties

private var cancellable: Set<AnyCancellable>!
private var cancellables: Set<AnyCancellable>!
private var sut: SourcesPersistenceControllerMock!

// MARK: - Actions

override func setUpWithError() throws {
cancellable = Set<AnyCancellable>()
cancellables = Set<AnyCancellable>()
sut = SourcesPersistenceControllerMock()
}

override func tearDownWithError() throws {
cancellable.removeAll()
cancellables.removeAll()
sut = nil
}

Expand Down Expand Up @@ -53,7 +53,7 @@ class SourcesPersistenceControllerTests: XCTestCase {
sources = newSources
expectation.fulfill()
})
.store(in: &cancellable)
.store(in: &cancellables)
// Then
await fulfillment(of: [expectation], timeout: 1)
XCTAssertNotNil(sources)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,18 @@ class TopHeadlinesPersistenceControllerTests: XCTestCase {

// MARK: - Private Properties

private var cancellable: Set<AnyCancellable>!
private var cancellables: Set<AnyCancellable>!
private var sut: TopHeadlinesPersistenceControllerMock!

// MARK: - Actions

override func setUpWithError() throws {
cancellable = Set<AnyCancellable>()
cancellables = Set<AnyCancellable>()
sut = TopHeadlinesPersistenceControllerMock()
}

override func tearDownWithError() throws {
cancellable.removeAll()
cancellables.removeAll()
sut = nil
}

Expand Down Expand Up @@ -53,7 +53,7 @@ class TopHeadlinesPersistenceControllerTests: XCTestCase {
topHeadlines = newTopHeadlines
expectation.fulfill()
})
.store(in: &cancellable)
.store(in: &cancellables)
// Then
await fulfillment(of: [expectation], timeout: 1)
XCTAssertNotNil(topHeadlines)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,18 @@ class SourcesRepositoryTests: XCTestCase {

// MARK: - Private Properties

private var cancellable: Set<AnyCancellable>!
private var cancellables: Set<AnyCancellable>!
private var sut: SourcesRepositoryMock!

// MARK: - Actions

override func setUpWithError() throws {
cancellable = Set<AnyCancellable>()
cancellables = Set<AnyCancellable>()
sut = SourcesRepositoryMock()
}

override func tearDownWithError() throws {
cancellable.removeAll()
cancellables.removeAll()
sut = nil
}

Expand Down Expand Up @@ -58,7 +58,7 @@ class SourcesRepositoryTests: XCTestCase {
sources = newSources
expectation.fulfill()
})
.store(in: &cancellable)
.store(in: &cancellables)
// Then
await fulfillment(of: [expectation], timeout: 1)
XCTAssertNotNil(sources)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,18 @@ class TopHeadlinesRepositoryTests: XCTestCase {

// MARK: - Private Properties

private var cancellable: Set<AnyCancellable>!
private var cancellables: Set<AnyCancellable>!
private var sut: TopHeadlinesRepositoryMock!

// MARK: - Actions

override func setUpWithError() throws {
cancellable = Set<AnyCancellable>()
cancellables = Set<AnyCancellable>()
sut = TopHeadlinesRepositoryMock()
}

override func tearDownWithError() throws {
cancellable.removeAll()
cancellables.removeAll()
sut = nil
}

Expand Down Expand Up @@ -60,7 +60,7 @@ class TopHeadlinesRepositoryTests: XCTestCase {
topHeadlines = newTopHeadlines
expectation.fulfill()
})
.store(in: &cancellable)
.store(in: &cancellables)
// Then
await fulfillment(of: [expectation], timeout: 1)
XCTAssertNotNil(topHeadlines)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@ class ReadSourcesUseCaseTests: XCTestCase {

// MARK: - Private Properties

private var cancellable: Set<AnyCancellable>!
private var cancellables: Set<AnyCancellable>!
private var sut: ReadSourcesUseCase!

// MARK: - Actions

override func setUpWithError() throws {
cancellable = Set<AnyCancellable>()
cancellables = Set<AnyCancellable>()
sut = ReadSourcesUseCase(sourcesRepository: SourcesRepositoryMock())
}

override func tearDownWithError() throws {
cancellable.removeAll()
cancellables.removeAll()
sut = nil
}

Expand All @@ -41,7 +41,7 @@ class ReadSourcesUseCaseTests: XCTestCase {
sources = $0
expectation.fulfill()
})
.store(in: &cancellable)
.store(in: &cancellables)
// Then
await fulfillment(of: [expectation], timeout: 1)
XCTAssertNotNil(sources)
Expand Down
Loading

0 comments on commit c17acc3

Please sign in to comment.