Skip to content

Commit 2f62ffe

Browse files
committed
Integrate
1 parent 9231b8f commit 2f62ffe

4 files changed

Lines changed: 155 additions & 14 deletions

File tree

template/Modules/Data/Sources/AppConfig/AppConfig.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ public final class AppConfig<DecodedConfig: Sendable>: AppConfigProtocol {
5050
}
5151

5252
public func setUp() {
53-
remoteConfig = RemoteConfig.remoteConfig()
53+
// Uncomment after we set up firebase remote config
54+
// remoteConfig = RemoteConfig.remoteConfig()
5455
setUpConfigSettings()
5556
setUpDefaults()
5657
setUpListener()
@@ -122,4 +123,4 @@ extension AppConfig {
122123
#endif
123124
return true
124125
}
125-
}
126+
}

template/Tuist/Interfaces/SwiftUI/Sources/Application/App.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
1+
import Data
2+
import FactoryKit
13
import SwiftUI
24

35
@main
46
struct {PROJECT_NAME}App: App {
57

8+
@Injected(\.exampleAppConfig) private var appConfig: AppConfig<ExampleAppConfiguration>
9+
10+
init() {
11+
appConfig.setUp()
12+
}
13+
614
var body: some Scene {
715
WindowGroup {
816
LandingView()

template/Tuist/Interfaces/SwiftUI/Sources/Presentation/Modules/Home/HomeView.swift

Lines changed: 121 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,131 @@ import SwiftUI
22

33
struct HomeView: View {
44

5+
@StateObject private var viewModel: HomeViewModel
56
var onSignOut: () -> Void = {}
67

8+
init(viewModel: HomeViewModel = HomeViewModel(), onSignOut: @escaping () -> Void = {}) {
9+
_viewModel = StateObject(wrappedValue: viewModel)
10+
self.onSignOut = onSignOut
11+
}
12+
713
var body: some View {
8-
VStack(spacing: 20) {
9-
Image(systemName: "person.crop.circle.badge.checkmark")
10-
.font(.system(size: 48))
11-
.foregroundColor(.accentColor)
12-
Text("Signed In")
13-
.font(.title2.bold())
14-
Text("This starter flow demonstrates the signed-in state that teams can build on with product-specific screens.")
15-
.multilineTextAlignment(.center)
16-
.foregroundStyle(.secondary)
17-
18-
Button("Sign Out", action: onSignOut)
19-
.buttonStyle(.borderedProminent)
14+
NavigationView {
15+
VStack(spacing: 20) {
16+
Image(systemName: "person.crop.circle.badge.checkmark")
17+
.font(.system(size: 48))
18+
.foregroundColor(.accentColor)
19+
20+
Text("Signed In")
21+
.font(.title2.bold())
22+
23+
Text(viewModel.configuration.welcomeMessage)
24+
.font(.headline)
25+
.multilineTextAlignment(.center)
26+
.foregroundStyle(.primary)
27+
28+
Text("Configuration loaded from remote config")
29+
.multilineTextAlignment(.center)
30+
.foregroundStyle(.secondary)
31+
.font(.caption)
32+
33+
ConfigurationCard(
34+
configuration: ConfigurationCardUIModel(
35+
isFeatureEnabled: viewModel.configuration.isFeatureEnabled,
36+
maxRetryCount: viewModel.configuration.maxRetryCount,
37+
apiTimeout: viewModel.configuration.apiTimeout
38+
)
39+
)
40+
41+
Spacer()
42+
43+
Button("Sign Out", action: onSignOut)
44+
.buttonStyle(.borderedProminent)
45+
}
46+
.padding()
47+
.navigationTitle("Home")
48+
.navigationBarTitleDisplayMode(.inline)
49+
}
50+
}
51+
}
52+
53+
private struct ConfigurationCard: View {
54+
55+
let configuration: ConfigurationCardUIModel
56+
57+
var body: some View {
58+
VStack(alignment: .leading, spacing: 12) {
59+
Text("App Configuration")
60+
.font(.headline)
61+
.frame(maxWidth: .infinity, alignment: .leading)
62+
63+
VStack(alignment: .leading, spacing: 8) {
64+
ConfigurationRow(
65+
title: "Feature Enabled",
66+
value: configuration.isFeatureEnabled ? "Yes" : "No",
67+
systemImage: configuration.isFeatureEnabled ? "checkmark.circle.fill" : "xmark.circle.fill",
68+
color: configuration.isFeatureEnabled ? .green : .red
69+
)
70+
71+
ConfigurationRow(
72+
title: "Max Retry Count",
73+
value: "\(configuration.maxRetryCount)",
74+
systemImage: "arrow.clockwise",
75+
color: .blue
76+
)
77+
78+
ConfigurationRow(
79+
title: "API Timeout",
80+
value: "\(configuration.apiTimeout, default: "%.1f")s",
81+
systemImage: "clock",
82+
color: .orange
83+
)
84+
}
2085
}
2186
.padding()
87+
.background(Color(.systemGray6))
88+
.cornerRadius(12)
89+
}
90+
}
91+
92+
private struct ConfigurationCardUIModel {
93+
94+
let isFeatureEnabled: Bool
95+
let maxRetryCount: Int
96+
let apiTimeout: Double
97+
98+
init(
99+
isFeatureEnabled: Bool = false,
100+
maxRetryCount: Int = 3,
101+
apiTimeout: Double = 30.0
102+
) {
103+
self.isFeatureEnabled = isFeatureEnabled
104+
self.maxRetryCount = maxRetryCount
105+
self.apiTimeout = apiTimeout
106+
}
107+
}
108+
109+
private struct ConfigurationRow: View {
110+
let title: String
111+
let value: String
112+
let systemImage: String
113+
let color: Color
114+
115+
var body: some View {
116+
HStack {
117+
Image(systemName: systemImage)
118+
.foregroundColor(color)
119+
.frame(width: 20)
120+
121+
Text(title)
122+
.font(.subheadline)
123+
124+
Spacer()
125+
126+
Text(value)
127+
.font(.subheadline)
128+
.fontWeight(.medium)
129+
.foregroundColor(color)
130+
}
22131
}
23132
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import Combine
2+
import Data
3+
import Domain
4+
import FactoryKit
5+
import Foundation
6+
7+
@MainActor
8+
final class HomeViewModel: ObservableObject {
9+
10+
@Published private(set) var configuration = ExampleAppConfiguration()
11+
12+
private var disposeBag = Set<AnyCancellable>()
13+
14+
@Injected(\.exampleAppConfig) private var appConfig: AppConfig<ExampleAppConfiguration>
15+
16+
nonisolated init() {
17+
Task { @MainActor in
18+
appConfig.currentConfigPublisher
19+
.assign(to: \.configuration, on: self)
20+
.store(in: &disposeBag)
21+
}
22+
}
23+
}

0 commit comments

Comments
 (0)