Skip to content

Commit d39b685

Browse files
committed
feat(realtime): add convenience types for handling broadcast changes
Add usage example in SlackClone app
1 parent 2b59f10 commit d39b685

File tree

14 files changed

+471
-48
lines changed

14 files changed

+471
-48
lines changed

Examples/Examples.xcodeproj/project.pbxproj

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
7956406A2955AFBD0088A06F /* ErrorText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 795640692955AFBD0088A06F /* ErrorText.swift */; };
2929
7956406D2955B3500088A06F /* SwiftUINavigation in Frameworks */ = {isa = PBXBuildFile; productRef = 7956406C2955B3500088A06F /* SwiftUINavigation */; };
3030
795640702955B5190088A06F /* IdentifiedCollections in Frameworks */ = {isa = PBXBuildFile; productRef = 7956406F2955B5190088A06F /* IdentifiedCollections */; };
31+
795E90A12DE87AA3009F8C11 /* AsyncAlgorithms in Frameworks */ = {isa = PBXBuildFile; productRef = 795E90A02DE87AA3009F8C11 /* AsyncAlgorithms */; };
3132
796298992AEBBA77000AA957 /* MFAFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 796298982AEBBA77000AA957 /* MFAFlow.swift */; };
3233
7962989D2AEBC6F9000AA957 /* SVGView in Frameworks */ = {isa = PBXBuildFile; productRef = 7962989C2AEBC6F9000AA957 /* SVGView */; };
3334
79719ECE2ADF26C400737804 /* Supabase in Frameworks */ = {isa = PBXBuildFile; productRef = 79719ECD2ADF26C400737804 /* Supabase */; };
@@ -174,6 +175,7 @@
174175
files = (
175176
79D884D92B3C18E90009EA4A /* Supabase in Frameworks */,
176177
79B8F4242B5FED7C0000E839 /* IdentifiedCollections in Frameworks */,
178+
795E90A12DE87AA3009F8C11 /* AsyncAlgorithms in Frameworks */,
177179
);
178180
runOnlyForDeploymentPostprocessing = 0;
179181
};
@@ -397,6 +399,7 @@
397399
packageProductDependencies = (
398400
79D884D82B3C18E90009EA4A /* Supabase */,
399401
79B8F4232B5FED7C0000E839 /* IdentifiedCollections */,
402+
795E90A02DE87AA3009F8C11 /* AsyncAlgorithms */,
400403
);
401404
productName = SlackClone;
402405
productReference = 79D884C72B3C18830009EA4A /* SlackClone.app */;
@@ -458,6 +461,7 @@
458461
7962989B2AEBC6F9000AA957 /* XCRemoteSwiftPackageReference "SVGView" */,
459462
79E2B5562B97890F0042CD21 /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS" */,
460463
79BE429F2D942EFD00B9DDF4 /* XCRemoteSwiftPackageReference "clerk-ios" */,
464+
795E909F2DE87AA3009F8C11 /* XCRemoteSwiftPackageReference "swift-async-algorithms" */,
461465
);
462466
productRefGroup = 793895C72954ABFF0044F2B8 /* Products */;
463467
projectDirPath = "";
@@ -991,6 +995,14 @@
991995
minimumVersion = 1.0.0;
992996
};
993997
};
998+
795E909F2DE87AA3009F8C11 /* XCRemoteSwiftPackageReference "swift-async-algorithms" */ = {
999+
isa = XCRemoteSwiftPackageReference;
1000+
repositoryURL = "https://github.com/apple/swift-async-algorithms.git";
1001+
requirement = {
1002+
kind = upToNextMajorVersion;
1003+
minimumVersion = 1.0.4;
1004+
};
1005+
};
9941006
7962989B2AEBC6F9000AA957 /* XCRemoteSwiftPackageReference "SVGView" */ = {
9951007
isa = XCRemoteSwiftPackageReference;
9961008
repositoryURL = "https://github.com/exyte/SVGView";
@@ -1028,6 +1040,11 @@
10281040
package = 7956406E2955B5190088A06F /* XCRemoteSwiftPackageReference "swift-identified-collections" */;
10291041
productName = IdentifiedCollections;
10301042
};
1043+
795E90A02DE87AA3009F8C11 /* AsyncAlgorithms */ = {
1044+
isa = XCSwiftPackageProductDependency;
1045+
package = 795E909F2DE87AA3009F8C11 /* XCRemoteSwiftPackageReference "swift-async-algorithms" */;
1046+
productName = AsyncAlgorithms;
1047+
};
10311048
7962989C2AEBC6F9000AA957 /* SVGView */ = {
10321049
isa = XCSwiftPackageProductDependency;
10331050
package = 7962989B2AEBC6F9000AA957 /* XCRemoteSwiftPackageReference "SVGView" */;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"recommendations": ["denoland.vscode-deno"]
3+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"deno.enablePaths": [
3+
"supabase/functions"
4+
],
5+
"deno.lint": true,
6+
"deno.unstable": [
7+
"bare-node-builtins",
8+
"byonm",
9+
"sloppy-imports",
10+
"unsafe-proto",
11+
"webgpu",
12+
"broadcast-channel",
13+
"worker-options",
14+
"cron",
15+
"kv",
16+
"ffi",
17+
"fs",
18+
"http",
19+
"net"
20+
],
21+
"[typescript]": {
22+
"editor.defaultFormatter": "denoland.vscode-deno"
23+
}
24+
}

Examples/SlackClone/ChannelStore.swift

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
// Created by Guilherme Souza on 18/01/24.
66
//
77

8+
import AsyncAlgorithms
89
import Foundation
10+
import OSLog
911
import Supabase
1012

1113
@MainActor
@@ -22,22 +24,21 @@ final class ChannelStore {
2224
Task {
2325
channels = await fetchChannels()
2426

25-
let channel = supabase.channel("public:channels")
27+
await supabase.realtimeV2.setAuth()
2628

27-
let insertions = channel.postgresChange(InsertAction.self, table: "channels")
28-
let deletions = channel.postgresChange(DeleteAction.self, table: "channels")
29+
let realtimeChannel = supabase.channel("channel:*") {
30+
$0.isPrivate = true
31+
}
2932

30-
await channel.subscribe()
33+
let insertions = realtimeChannel.broadcastStream(event: "INSERT")
34+
let updates = realtimeChannel.broadcastStream(event: "UPDATE")
35+
let deletions = realtimeChannel.broadcastStream(event: "DELETE")
3136

32-
Task {
33-
for await insertion in insertions {
34-
handleInsertedChannel(insertion)
35-
}
36-
}
37+
await realtimeChannel.subscribe()
3738

3839
Task {
39-
for await delete in deletions {
40-
handleDeletedChannel(delete)
40+
for await event in merge(insertions, updates, deletions) {
41+
handleBroadcastEvent(event)
4142
}
4243
}
4344
}
@@ -52,7 +53,7 @@ final class ChannelStore {
5253
.insert(channel)
5354
.execute()
5455
} catch {
55-
dump(error)
56+
Logger.main.error("Failed to add channel: \(error.localizedDescription)")
5657
toast = .init(status: .error, title: "Error", description: error.localizedDescription)
5758
}
5859
}
@@ -62,7 +63,8 @@ final class ChannelStore {
6263
return channel
6364
}
6465

65-
let channel: Channel = try await supabase
66+
let channel: Channel =
67+
try await supabase
6668
.from("channels")
6769
.select()
6870
.eq("id", value: id)
@@ -72,27 +74,40 @@ final class ChannelStore {
7274
return channel
7375
}
7476

75-
private func handleInsertedChannel(_ action: InsertAction) {
77+
private func handleBroadcastEvent(_ event: BroadcastEvent) {
7678
do {
77-
let channel = try action.decodeRecord(decoder: decoder) as Channel
78-
channels.append(channel)
79+
let change = try event.broadcastChange()
80+
switch change.operation {
81+
case .insert(let channel):
82+
channels.append(try channel.decode(decoder: decoder))
83+
84+
case .update(let new, _):
85+
let channel = try new.decode(decoder: decoder) as Channel
86+
if let index = channels.firstIndex(where: { $0.id == channel.id }) {
87+
channels[index] = channel
88+
} else {
89+
Logger.main.warning("Channel with ID \(channel.id) not found for update")
90+
}
91+
92+
case .delete(let old):
93+
guard let id = old["id"]?.intValue else {
94+
Logger.main.error("Missing channel ID in delete operation")
95+
return
96+
}
97+
channels.removeAll { $0.id == id }
98+
messages.removeMessages(for: id)
99+
}
79100
} catch {
80-
dump(error)
101+
Logger.main.error("Failed to handle broadcast event: \(error.localizedDescription)")
81102
toast = .init(status: .error, title: "Error", description: error.localizedDescription)
82103
}
83104
}
84105

85-
private func handleDeletedChannel(_ action: DeleteAction) {
86-
guard let id = action.oldRecord["id"]?.intValue else { return }
87-
channels.removeAll { $0.id == id }
88-
messages.removeMessages(for: id)
89-
}
90-
91106
private func fetchChannels() async -> [Channel] {
92107
do {
93108
return try await supabase.from("channels").select().execute().value
94109
} catch {
95-
dump(error)
110+
Logger.main.error("Failed to fetch channels: \(error.localizedDescription)")
96111
toast = .init(status: .error, title: "Error", description: error.localizedDescription)
97112
return []
98113
}

Examples/SlackClone/supabase/.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,12 @@
22
.branches
33
.temp
44
.env
5+
6+
# Supabase
7+
.branches
8+
.temp
9+
10+
# dotenvx
11+
.env.keys
12+
.env.local
13+
.env.*.local

0 commit comments

Comments
 (0)