Skip to content

Commit 83a66ee

Browse files
Updated combine
1 parent 3a425ac commit 83a66ee

5 files changed

Lines changed: 86 additions & 0 deletions

File tree

.DS_Store

0 Bytes
Binary file not shown.

SwiftUIExamples/.DS_Store

0 Bytes
Binary file not shown.
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
//
2+
// CustomPublisher.swift
3+
// SwiftUI-CommandLineTool
4+
//
5+
// Created by Janesh Suthar on 01/08/25.
6+
//
7+
8+
import Foundation
9+
@preconcurrency import Combine
10+
11+
//Build a custom Combine operator
12+
//Let’s build an operator called .isEmptyOrWhitespace() for Publisher<String, Failure>
13+
//It emits:
14+
//true if the string is empty or whitespace
15+
//false otherwise
16+
17+
// 1. Define a custom Publisher wrapper
18+
struct IsEmptyOrWhitespacePublisher<Upstream: Publisher>: Publisher where Upstream.Output == String {
19+
typealias Output = Bool
20+
typealias Failure = Upstream.Failure
21+
22+
let upstream: Upstream
23+
24+
func receive<S>(subscriber: S) where S : Subscriber, Failure == S.Failure, Output == S.Input {
25+
upstream
26+
.map { str in
27+
str.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
28+
}
29+
.receive(subscriber: subscriber)
30+
}
31+
}
32+
// 2. Add a convenience extension on Publisher
33+
extension Publisher where Output == String {
34+
func isEmptyOrWhitespace() -> IsEmptyOrWhitespacePublisher<Self> {
35+
return IsEmptyOrWhitespacePublisher(upstream: self)
36+
}
37+
}
38+
// 3. Usage
39+
func printIsEmptyOrWhitespace() {
40+
_ = Just(" ")
41+
.isEmptyOrWhitespace()
42+
.sink { isEmpty in
43+
print("Is empty or whitespace:", isEmpty) // true
44+
}
45+
}
46+
47+
// MARK: - A custom Combine operator that retries a failing publisher N times, with a delay between attempts. Super useful for: Network requests, Database retries, Resilient logic in unstable environments
48+
49+
extension Publisher {
50+
func retryWithDelay(retries: Int, delay: TimeInterval, scheduler: DispatchQueue = .main) -> AnyPublisher<Output, Failure> {
51+
self.catch { error -> AnyPublisher<Output, Failure> in
52+
guard retries > 0 else {
53+
return Fail(error: error).eraseToAnyPublisher()
54+
}
55+
56+
return Just(())
57+
.delay(for: .seconds(delay), scheduler: scheduler)
58+
.flatMap { _ in
59+
self.retryWithDelay(retries: retries - 1, delay: delay, scheduler: scheduler)
60+
}
61+
.eraseToAnyPublisher()
62+
}
63+
.eraseToAnyPublisher()
64+
}
65+
}
66+
67+
68+
func testFailingPublisher() async throws {
69+
let failingPublisher = Future<Int, Error> { promise in
70+
print("Attempting request...")
71+
promise(.failure(NSError(domain: "Network", code: -1)))
72+
}
73+
_ = failingPublisher
74+
.retryWithDelay(retries: 3, delay: 2)
75+
.sink(receiveCompletion: { completion in
76+
print("Completion:", completion)
77+
}, receiveValue: { value in
78+
print("Got value:", value)
79+
})
80+
}

SwiftUIExamples/SwiftUI-CommandLineTool/main.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,3 +311,5 @@ func performTransactions() async {
311311
print("Current balance: \(currentBalance)")
312312
}
313313
await performTransactions()
314+
printIsEmptyOrWhitespace()
315+
try! await testFailingPublisher()

SwiftUIExamples/SwiftUIExamples.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
10AB45412DD24B7D00967614 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 10AB45402DD24B7D00967614 /* Assets.xcassets */; };
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 */; };
31+
4F313CD02E3D2782009D56B9 /* CustomPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F313CCF2E3D2782009D56B9 /* CustomPublisher.swift */; };
3132
4FB02AD62E3B3B320010B8D8 /* DependencyInjectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FB02AD52E3B3B320010B8D8 /* DependencyInjectionTests.swift */; };
3233
4FEBFAB62E3C85BC00D72E78 /* MemoryLeakExamples.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FEBFAB52E3C85BC00D72E78 /* MemoryLeakExamples.swift */; };
3334
4FEBFAB82E3CBDA500D72E78 /* ActorsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FEBFAB72E3CBDA500D72E78 /* ActorsExample.swift */; };
@@ -80,6 +81,7 @@
8081
10AB45402DD24B7D00967614 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
8182
10AB45432DD24B7D00967614 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
8283
10AB454A2DD257A100967614 /* BindingExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BindingExample.swift; sourceTree = "<group>"; };
84+
4F313CCF2E3D2782009D56B9 /* CustomPublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomPublisher.swift; sourceTree = "<group>"; };
8385
4FB02AD52E3B3B320010B8D8 /* DependencyInjectionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DependencyInjectionTests.swift; sourceTree = "<group>"; };
8486
4FEBFAB52E3C85BC00D72E78 /* MemoryLeakExamples.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryLeakExamples.swift; sourceTree = "<group>"; };
8587
4FEBFAB72E3CBDA500D72E78 /* ActorsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActorsExample.swift; sourceTree = "<group>"; };
@@ -161,6 +163,7 @@
161163
children = (
162164
1099980D2E24DAA600A8A3C9 /* main.swift */,
163165
1082B7262E26ABB800A74AA6 /* CombineExamples.swift */,
166+
4F313CCF2E3D2782009D56B9 /* CustomPublisher.swift */,
164167
);
165168
path = "SwiftUI-CommandLineTool";
166169
sourceTree = "<group>";
@@ -348,6 +351,7 @@
348351
files = (
349352
1082B7272E26ABB800A74AA6 /* CombineExamples.swift in Sources */,
350353
1099980E2E24DAA600A8A3C9 /* main.swift in Sources */,
354+
4F313CD02E3D2782009D56B9 /* CustomPublisher.swift in Sources */,
351355
);
352356
runOnlyForDeploymentPostprocessing = 0;
353357
};

0 commit comments

Comments
 (0)