Skip to content

Commit 2e1d0c3

Browse files
Added Memory Leak example
1 parent 05c8c28 commit 2e1d0c3

3 files changed

Lines changed: 69 additions & 1 deletion

File tree

SwiftUIExamples/SwiftUIExamples.xcodeproj/project.pbxproj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
10AB45442DD24B7D00967614 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 10AB45432DD24B7D00967614 /* Preview Assets.xcassets */; };
3030
10AB454B2DD257A100967614 /* BindingExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10AB454A2DD257A100967614 /* BindingExample.swift */; };
3131
4FB02AD62E3B3B320010B8D8 /* DependencyInjectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FB02AD52E3B3B320010B8D8 /* DependencyInjectionTests.swift */; };
32+
4FEBFAB62E3C85BC00D72E78 /* MemoryLeakExamples.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FEBFAB52E3C85BC00D72E78 /* MemoryLeakExamples.swift */; };
3233
/* End PBXBuildFile section */
3334

3435
/* Begin PBXContainerItemProxy section */
@@ -79,6 +80,7 @@
7980
10AB45432DD24B7D00967614 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
8081
10AB454A2DD257A100967614 /* BindingExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BindingExample.swift; sourceTree = "<group>"; };
8182
4FB02AD52E3B3B320010B8D8 /* DependencyInjectionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DependencyInjectionTests.swift; sourceTree = "<group>"; };
83+
4FEBFAB52E3C85BC00D72E78 /* MemoryLeakExamples.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryLeakExamples.swift; sourceTree = "<group>"; };
8284
/* End PBXFileReference section */
8385

8486
/* Begin PBXFrameworksBuildPhase section */
@@ -183,6 +185,7 @@
183185
10AB453B2DD24B7B00967614 /* SwiftUIExamples */ = {
184186
isa = PBXGroup;
185187
children = (
188+
4FEBFAB42E3C85A500D72E78 /* MemoryManagement */,
186189
1089EA602E38694300B1E312 /* Archs */,
187190
10AB453C2DD24B7B00967614 /* SwiftUIExamplesApp.swift */,
188191
10525C712E2D2A4A00D7C510 /* AsyncAwait */,
@@ -201,6 +204,14 @@
201204
path = "Preview Content";
202205
sourceTree = "<group>";
203206
};
207+
4FEBFAB42E3C85A500D72E78 /* MemoryManagement */ = {
208+
isa = PBXGroup;
209+
children = (
210+
4FEBFAB52E3C85BC00D72E78 /* MemoryLeakExamples.swift */,
211+
);
212+
path = MemoryManagement;
213+
sourceTree = "<group>";
214+
};
204215
/* End PBXGroup section */
205216

206217
/* Begin PBXNativeTarget section */
@@ -349,6 +360,7 @@
349360
072EEEAF2E1E8CE40007B3A4 /* SearchViewUsingCombine.swift in Sources */,
350361
072EEEC42E1E98DA0007B3A4 /* DataTaskPublisher.swift in Sources */,
351362
109998132E252E7400A8A3C9 /* FlatMapVsSwitchToLatest1.swift in Sources */,
363+
4FEBFAB62E3C85BC00D72E78 /* MemoryLeakExamples.swift in Sources */,
352364
1089EA622E38695900B1E312 /* Dependency-Injection.swift in Sources */,
353365
10AB453D2DD24B7B00967614 /* SwiftUIExamplesApp.swift in Sources */,
354366
109998152E25444C00A8A3C9 /* FlatMapVsSwitchToLatest2.swift in Sources */,
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import SwiftUI
2+
import Combine
3+
4+
// MARK: - ViewModel with Leaky Timer
5+
final class TimerViewModel: ObservableObject {
6+
@Published var count: Int = 0
7+
private var cancellables = Set<AnyCancellable>()
8+
9+
init() {
10+
Timer.publish(every: 1.0, on: .main, in: .common)
11+
.autoconnect()
12+
// To reproduce issue remove weak self here, you will observer timer still runs on background & deinit never get called.
13+
.sink { [weak self] _ in
14+
self?.count += 1
15+
print("⏱️ Timer fired: \(self?.count ?? 0)")
16+
}
17+
.store(in: &cancellables)
18+
}
19+
20+
deinit {
21+
print("🧹 TimerViewModel deallocated")
22+
}
23+
}
24+
25+
// MARK: - SwiftUI View
26+
struct LeakyTimerView: View {
27+
@StateObject private var viewModel = TimerViewModel()
28+
@Environment(\.dismiss) var dismiss
29+
30+
var body: some View {
31+
VStack {
32+
Text("Timer count: \(viewModel.count)")
33+
.font(.largeTitle)
34+
Button("Dismiss") {
35+
dismiss()
36+
}
37+
}
38+
.padding()
39+
}
40+
}
41+
42+
// MARK: - Entry Point View
43+
struct MemoryLeakExamples: View {
44+
@State private var showLeak = false
45+
46+
var body: some View {
47+
VStack {
48+
Button("Show Leaky Timer View") {
49+
showLeak = true
50+
}
51+
}
52+
.sheet(isPresented: $showLeak) {
53+
LeakyTimerView()
54+
}
55+
}
56+
}

SwiftUIExamples/SwiftUIExamples/SwiftUIExamplesApp.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ struct DashboardView: View {
3636
ExampleItem(title: "Aync Await with Static Data protocols", destination: AnyView(UserListView())),
3737
ExampleItem(title: "Aync Await with API and protocols tests", destination: AnyView(RepoListView(username: "janeshsutharios"))),
3838
ExampleItem(title: "Dependency-Injection ", destination: AnyView(ProjectsListView())),
39+
ExampleItem(title: "MemoryLeakExamples ", destination: AnyView(MemoryLeakExamples())),
3940
]
40-
4141
var body: some View {
4242
NavigationView {
4343
List(examples) { item in

0 commit comments

Comments
 (0)