Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ jobs:
- name: Persistence
schemes: "Persistence"
- name: Presentation
schemes: "Presentation HomeTab"
schemes: "Entry PresentationShared HomeTab"
- name: Widget
schemes: "Widget WidgetCore"
steps:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,15 @@ flowchart TD
| `WidgetCore` | widget snapshot models, factories, app-group keys/defaults store, deep links, pure snapshot logic | Core | Adding Domain, Data, Infra, Persistence, Presentation, App, or Widget dependency |
| `WidgetExtension` | WidgetKit rendering and timeline plumbing | WidgetCore | Calling app/domain services directly |

## Presentation target structure

- `Presentation` preserves `App -> Presentation` imports and re-exports the entry API through `Application/Presentation/Sources/**/*.swift`.
- `Entry` owns root, auth, login, main tab shell, window, and global route responsibilities. It owns the `Domain` references needed by those flows.
- `EntryTests` validates `Entry` through `Application/Presentation/Entry/Tests/**/*.swift`.
- `HomeTab`, `TodayTab`, `NotificationTab`, and `ProfileTab` remain tab-specific feature targets and each target owns the `Domain` references it needs.
- `PresentationShared` owns shared Todo, Search, Loading UI, and presentation contracts.
- `App` owns composition root, lifecycle, and assembler wiring. It must not take ownership of presentation feature or root flows.

## Layer-internal dependency injection

Do not inject dependencies between types that belong to the same layer.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ This reference holds DevLog-specific working rules that should live with the pro
- Prefer a Data-side boundary value plus repository mapping when Infra should not import Domain.
- For example, keep the app-facing Domain query separate from an Infra-facing Data query when that avoids Domain coupling in service protocols.
- Firebase-specific error detection belongs in Infra; Data should handle domain-level errors after mapping.
- Data and Presentation currently keep narrow social-login cancellation classification in `DataLayerError` and `Error+SocialLogin`. Do not expand that into concrete login implementation or broader SDK ownership without explicit approval.
- Infra currently keeps narrow social-login cancellation classification in `InfraLayerError`. Do not expand that into concrete login implementation or broader SDK ownership outside Infra without explicit approval.

## Presentation StorePattern

Expand Down
1 change: 1 addition & 0 deletions Application/Presentation/Entry/Sources/.swiftlint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
parent_config: ../../../../.swiftlint.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
//
// LoginButton.swift
// Presentation
// Entry
//
// Created by opfic on 4/25/25.
//

import SwiftUI
import Domain

struct LoginButton: View {
@State private var logo: Image?
Expand All @@ -26,7 +25,7 @@ struct LoginButton: View {
self.showsProgressView = showsProgressView
self.action = action
}

var body: some View {
Button {
action()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
//
// LoginFeature.swift
// Presentation
// Entry
//
// Created by opfic on 6/5/26.
//

import Domain
import Foundation
import Domain
import PresentationShared

@Reducer
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//
// LoginView.swift
// Presentation
// Entry
//
// Created by opfic on 12/30/24.
//
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//
// MainFeature.swift
// Presentation
// Entry
//
// Created by opfic on 6/16/26.
//
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//
// MainView.swift
// Presentation
// Entry
//
// Created by opfic on 5/8/25.
//
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//
// RootFeature.swift
// Presentation
// Entry
//
// Created by opfic on 6/17/26.
//
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//
// RootView.swift
// Presentation
// Entry
//
// Created by opfic on 5/2/25.
//
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//
// MainTab.swift
// Presentation
// Entry
//
// Created by opfic on 4/30/26.
//
Expand Down
8 changes: 8 additions & 0 deletions Application/Presentation/Entry/Sources/SharedExports.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//
// SharedExports.swift
// Entry
//
// Created by opfic on 7/2/26.
//

@_exported import PresentationShared
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//
// TodoEditorWindowView.swift
// Presentation
// Entry
//
// Created by opfic on 5/31/26.
//
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//
// TodoWindowCoordinator.swift
// Presentation
// Entry
//
// Created by opfic on 5/31/26.
//
Expand Down
1 change: 1 addition & 0 deletions Application/Presentation/Entry/Tests/.swiftlint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
parent_config: ../../../../.swiftlint-tests.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//
// LoginFeatureTests.swift
// PresentationTests
// EntryTests
//
// Created by opfic on 6/5/26.
//
Expand All @@ -9,7 +9,7 @@ import Testing
import PresentationShared
import Foundation
import Domain
@testable import Presentation
@testable import Entry

@MainActor
struct LoginFeatureTests {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//
// MainFeatureTests.swift
// PresentationTests
// EntryTests
//
// Created by opfic on 6/16/26.
//
Expand All @@ -10,7 +10,7 @@ import PresentationShared
import Domain
import Foundation
import Testing
@testable import Presentation
@testable import Entry

@MainActor
struct MainFeatureTests {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//
// RootFeatureTestSupport.swift
// PresentationTests
// EntryTests
//
// Created by opfic on 6/17/26.
//
Expand All @@ -11,7 +11,7 @@ import Core
import Domain
import Foundation
import Testing
@testable import Presentation
@testable import Entry

@MainActor
protocol RootStateDriving {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//
// RootFeatureTests.swift
// PresentationTests
// EntryTests
//
// Created by opfic on 6/17/26.
//
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//
// EntryTestSupport.swift
// EntryTests
//
// Created by opfic on 7/6/26.
//

import Domain

@MainActor
func waitUntil(
timeout: Duration = .seconds(1),
pollInterval: Duration = .milliseconds(20),
_ condition: @escaping () -> Bool
) async {
let continuousClock = ContinuousClock()
let deadline = continuousClock.now + timeout

while !condition() && continuousClock.now < deadline {
try? await Task.sleep(for: pollInterval)
}
}

final class SignInUseCaseSpy: SignInUseCase {
Comment thread
opficdev marked this conversation as resolved.
var error: Error?
var signedIn = true
var shouldSuspend = false
private(set) var calledProviders = [AuthProvider]()
private(set) var successfulProviders = [AuthProvider]()
private var continuation: CheckedContinuation<Void, Never>?
private var shouldResume = false

func execute(_ provider: AuthProvider) async throws -> Bool {
calledProviders.append(provider)

if shouldSuspend {
await withCheckedContinuation { continuation in
if shouldResume {
shouldResume = false
continuation.resume()
} else {
self.continuation = continuation
}
}
}

if let error {
throw error
}

if signedIn {
successfulProviders.append(provider)
}

return signedIn
}

func resume() {
guard let continuation else {
shouldResume = true
return
}
self.continuation = nil
continuation.resume()
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
//
// LoadingState.swift
// Presentation
// PresentationShared
//
// Created by opfic on 3/16/26.
//

import Foundation
import Domain

@MainActor
public final class LoadingState {
Expand All @@ -26,7 +25,7 @@ public final class LoadingState {
private var visibleDelayedTargets = Set<AnyHashable>()
private var visibleTargets = Set<AnyHashable>()

nonisolated init(delay: Duration = .seconds(0.3)) {
public nonisolated init(delay: Duration = .seconds(0.3)) {
self.delay = delay
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
parent_config: ../../../../.swiftlint-tests.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
//
// LoadingFeatureTests.swift
// PresentationTests
// PresentationSharedTests
//
// Created by opfic on 6/11/26.
//

import Testing
import PresentationShared
@testable import Presentation
@testable import PresentationShared

@MainActor
struct LoadingFeatureTests {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
//
// SearchFeatureTestDoubles.swift
// PresentationTests
// PresentationSharedTests
//
// Created by opfic on 6/12/26.
//

import Foundation
import PresentationShared
import Core
import Domain
@testable import Presentation
@testable import PresentationShared

@MainActor
struct SearchStoreTestAdapter {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
//
// SearchFeatureTests.swift
// PresentationTests
// PresentationSharedTests
//
// Created by opfic on 6/12/26.
//

import Testing
import PresentationShared
import Foundation
import Core
import Domain
@testable import Presentation
@testable import PresentationShared

@MainActor
struct SearchFeatureTests {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//
// TestSupport.swift
// PresentationTests
// PresentationSharedTests
//
// Created by opfic on 4/6/26.
//
Expand All @@ -10,7 +10,7 @@ import Foundation
import Combine
import Core
import Domain
@testable import Presentation
@testable import PresentationShared

@MainActor
func waitUntil(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
//
// TodoDetailFeatureTests.swift
// PresentationTests
// PresentationSharedTests
//
// Created by opfic on 6/11/26.
//

import Testing
import PresentationShared
import Foundation
import Domain
@testable import Presentation
@testable import PresentationShared

@MainActor
struct TodoDetailFeatureTests {
Expand Down
Loading
Loading