Skip to content

Commit 7d864fc

Browse files
Added dependency injection example
1 parent 3644861 commit 7d864fc

3 files changed

Lines changed: 91 additions & 2 deletions

File tree

SwiftUIExamples/SwiftUIExamples.xcodeproj/project.pbxproj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
10525C852E2D2D9F00D7C510 /* git_hub_repos_async_tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10525C742E2D2D1A00D7C510 /* git_hub_repos_async_tests.swift */; };
1717
107A5D2D2E1D4252004C4BE6 /* SchoolApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 107A5D2C2E1D4252004C4BE6 /* SchoolApp.swift */; };
1818
1082B7272E26ABB800A74AA6 /* CombineExamples.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1082B7262E26ABB800A74AA6 /* CombineExamples.swift */; };
19+
1089EA622E38695900B1E312 /* Dependency-Injection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1089EA612E38695900B1E312 /* Dependency-Injection.swift */; };
1920
1099980E2E24DAA600A8A3C9 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1099980D2E24DAA600A8A3C9 /* main.swift */; };
2021
109998132E252E7400A8A3C9 /* FlatMapVsSwitchToLatest1.swift in Sources */ = {isa = PBXBuildFile; fileRef = 109998122E252E7400A8A3C9 /* FlatMapVsSwitchToLatest1.swift */; };
2122
109998152E25444C00A8A3C9 /* FlatMapVsSwitchToLatest2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 109998142E25444C00A8A3C9 /* FlatMapVsSwitchToLatest2.swift */; };
@@ -62,6 +63,7 @@
6263
10525C7E2E2D2D9800D7C510 /* SwiftUIExamplesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIExamplesTests.swift; sourceTree = "<group>"; };
6364
107A5D2C2E1D4252004C4BE6 /* SchoolApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SchoolApp.swift; sourceTree = "<group>"; };
6465
1082B7262E26ABB800A74AA6 /* CombineExamples.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CombineExamples.swift; sourceTree = "<group>"; };
66+
1089EA612E38695900B1E312 /* Dependency-Injection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Dependency-Injection.swift"; sourceTree = "<group>"; };
6567
1099980B2E24DAA600A8A3C9 /* SwiftUI-CommandLineTool */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "SwiftUI-CommandLineTool"; sourceTree = BUILT_PRODUCTS_DIR; };
6668
1099980D2E24DAA600A8A3C9 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
6769
109998122E252E7400A8A3C9 /* FlatMapVsSwitchToLatest1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlatMapVsSwitchToLatest1.swift; sourceTree = "<group>"; };
@@ -138,6 +140,14 @@
138140
path = SwiftUIExamplesTests;
139141
sourceTree = "<group>";
140142
};
143+
1089EA602E38694300B1E312 /* Archs */ = {
144+
isa = PBXGroup;
145+
children = (
146+
1089EA612E38695900B1E312 /* Dependency-Injection.swift */,
147+
);
148+
path = Archs;
149+
sourceTree = "<group>";
150+
};
141151
1099980C2E24DAA600A8A3C9 /* SwiftUI-CommandLineTool */ = {
142152
isa = PBXGroup;
143153
children = (
@@ -170,6 +180,7 @@
170180
10AB453B2DD24B7B00967614 /* SwiftUIExamples */ = {
171181
isa = PBXGroup;
172182
children = (
183+
1089EA602E38694300B1E312 /* Archs */,
173184
10AB453C2DD24B7B00967614 /* SwiftUIExamplesApp.swift */,
174185
10525C712E2D2A4A00D7C510 /* AsyncAwait */,
175186
072EEEAD2E1E8CD10007B3A4 /* Combine */,
@@ -334,6 +345,7 @@
334345
072EEEAF2E1E8CE40007B3A4 /* SearchViewUsingCombine.swift in Sources */,
335346
072EEEC42E1E98DA0007B3A4 /* DataTaskPublisher.swift in Sources */,
336347
109998132E252E7400A8A3C9 /* FlatMapVsSwitchToLatest1.swift in Sources */,
348+
1089EA622E38695900B1E312 /* Dependency-Injection.swift in Sources */,
337349
10AB453D2DD24B7B00967614 /* SwiftUIExamplesApp.swift in Sources */,
338350
109998152E25444C00A8A3C9 /* FlatMapVsSwitchToLatest2.swift in Sources */,
339351
10525C732E2D2A6000D7C510 /* UserListAsyncStaticData.swift in Sources */,
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//
2+
// RepoListDIApp.swift
3+
// Complete iOS App using MVVM with Dependency Injection
4+
//
5+
6+
import SwiftUI
7+
8+
// MARK: - View
9+
10+
struct ProjectsListView: View {
11+
@StateObject var viewModel: RepoViewModel
12+
13+
var body: some View {
14+
NavigationView {
15+
List(viewModel.repos, id: \.self) { repo in
16+
Text(repo)
17+
}
18+
.navigationTitle("Projects")
19+
.onAppear {
20+
viewModel.loadRepos()
21+
}
22+
}
23+
}
24+
}
25+
26+
27+
// MARK: - ViewModel
28+
29+
class RepoViewModel: ObservableObject {
30+
@Published var repos: [String] = []
31+
private let service: RepoServiceProtocol
32+
33+
init(service: RepoServiceProtocol) {
34+
self.service = service
35+
}
36+
37+
func loadRepos() {
38+
service.fetchRepos { [weak self] result in
39+
DispatchQueue.main.async {
40+
self?.repos = result
41+
}
42+
}
43+
}
44+
}
45+
46+
47+
// MARK: - Protocol
48+
49+
protocol RepoServiceProtocol {
50+
func fetchRepos(completion: @escaping ([String]) -> Void)
51+
}
52+
53+
54+
// MARK: - Real Service
55+
56+
class RepoService: RepoServiceProtocol {
57+
func fetchRepos(completion: @escaping ([String]) -> Void) {
58+
// Simulate API delay
59+
DispatchQueue.global().asyncAfter(deadline: .now() + 1.0) {
60+
completion(["Swift", "Combine", "UIKit", "CoreData"])
61+
}
62+
}
63+
}
64+
65+
66+
// MARK: - Mock Service (for unit tests or previews)
67+
68+
class MockRepoService: RepoServiceProtocol {
69+
func fetchRepos(completion: @escaping ([String]) -> Void) {
70+
completion(["MockRepo1", "MockRepo2"])
71+
}
72+
}

SwiftUIExamples/SwiftUIExamples/SwiftUIExamplesApp.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,14 @@ struct DashboardView: View {
3434
ExampleItem(title: "ButtonThrottle ", destination: AnyView(ButtonThrottle())),
3535
ExampleItem(title: "Handle events ", destination: AnyView(GitHubSearchView3())),
3636
ExampleItem(title: "Aync Await with Static Data protocols", destination: AnyView(UserListView(viewModel: UserListViewModel(userService: MockUserService())))),
37-
ExampleItem(title: "Aync Await with API and protocols tests", destination: AnyView(RepoListView(username: "janeshsutharios", viewModel: RepoListViewModel(service: GitHubService()))))
38-
37+
ExampleItem(title: "Aync Await with API and protocols tests", destination: AnyView(RepoListView(username: "janeshsutharios", viewModel: RepoListViewModel(service: GitHubService())))),
38+
ExampleItem(title: "Dependency-Injection ", destination: AnyView(ProjectsListView(viewModel: RepoViewModel(service: RepoService())))),
3939
]
40+
41+
var getViewModelForDI: RepoViewModel {
42+
let service = RepoService() // Swap with MockRepoService() for testing
43+
return RepoViewModel(service: service)
44+
}
4045

4146
var body: some View {
4247
NavigationView {

0 commit comments

Comments
 (0)