Skip to content

Commit 9176333

Browse files
authored
Swift 6: Room and Track state (#652)
This is the last 🎉 portion of Swift 6 warnings, mostly around `State`. The algorithm is still pretty simple: - can be `final (internal)`/immutable → make it `Sendable` - is already public/inherited → make it `@unchecked Sendable` Unit tests are mostly `@unchecked` because of inheritance, IMO gradually moving to Swift Testing is more important. Fortunately, for most generic/async stuff `T: Sendable` is just works (tm), just needs proper annotations.
1 parent 810a037 commit 9176333

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+122
-97
lines changed

.nanpa/swift-6-state.kdl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
patch type="fixed" "Swift 6: Fixed warnings for mutable state"

Sources/LiveKit/Broadcast/BroadcastBundleInfo.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
import Foundation
2020

21-
enum BroadcastBundleInfo {
21+
actor BroadcastBundleInfo {
2222
/// Identifier of the app group shared by the primary app and broadcast extension.
2323
static var groupIdentifier: String? {
2424
if let override = groupIdentifierOverride { return override }

Sources/LiveKit/Broadcast/BroadcastScreenCapturer.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ internal import LiveKitWebRTC
2828
@_implementationOnly import LiveKitWebRTC
2929
#endif
3030

31-
class BroadcastScreenCapturer: BufferCapturer {
31+
class BroadcastScreenCapturer: BufferCapturer, @unchecked Sendable {
3232
private let appAudio: Bool
3333
private var receiver: BroadcastReceiver?
3434

Sources/LiveKit/Broadcast/IPC/BroadcastImageCodec.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@
1717
#if os(iOS)
1818

1919
import AVFoundation
20-
import CoreImage
20+
@preconcurrency import CoreImage
2121

2222
/// Encode and decode image samples for transport.
23-
struct BroadcastImageCodec {
23+
struct BroadcastImageCodec: Sendable {
2424
struct Metadata: Codable {
2525
let width: Int
2626
let height: Int

Sources/LiveKit/Broadcast/LKSampleHandler.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import LKObjCHelpers
3434
#endif
3535

3636
@available(macCatalyst 13.1, *)
37-
open class LKSampleHandler: RPBroadcastSampleHandler {
37+
open class LKSampleHandler: RPBroadcastSampleHandler, @unchecked Sendable {
3838
private var uploader: BroadcastUploader?
3939
private var cancellable = Set<AnyCancellable>()
4040

Sources/LiveKit/Core/DataChannelPair.swift

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ protocol DataChannelDelegate: Sendable {
2929
func dataChannel(_ dataChannelPair: DataChannelPair, didReceiveDataPacket dataPacket: Livekit_DataPacket)
3030
}
3131

32-
class DataChannelPair: NSObject, Loggable {
32+
class DataChannelPair: NSObject, @unchecked Sendable, Loggable {
3333
// MARK: - Public
3434

3535
public let delegates = MulticastDelegate<DataChannelDelegate>(label: "DataChannelDelegate")
@@ -48,6 +48,8 @@ class DataChannelPair: NSObject, Loggable {
4848
guard let lossy, let reliable else { return false }
4949
return reliable.readyState == .open && lossy.readyState == .open
5050
}
51+
52+
var eventContinuation: AsyncStream<ChannelEvent>.Continuation?
5153
}
5254

5355
private let _state: StateSync<State>
@@ -61,24 +63,22 @@ class DataChannelPair: NSObject, Loggable {
6163
var amount: UInt64 = 0
6264
}
6365

64-
private struct PublishDataRequest {
66+
private struct PublishDataRequest: Sendable {
6567
let data: LKRTCDataBuffer
6668
let continuation: CheckedContinuation<Void, any Error>?
6769
}
6870

69-
private struct ChannelEvent {
71+
private struct ChannelEvent: Sendable {
7072
let channelKind: ChannelKind
7173
let detail: Detail
7274

73-
enum Detail {
75+
enum Detail: Sendable {
7476
case publishData(PublishDataRequest)
7577
case bufferedAmountChanged(UInt64)
7678
}
7779
}
7880

79-
private var eventContinuation: AsyncStream<ChannelEvent>.Continuation?
80-
81-
@Sendable private func handleEvents(
81+
private func handleEvents(
8282
events: AsyncStream<ChannelEvent>
8383
) async {
8484
var lossyBuffering = BufferingState()
@@ -175,7 +175,7 @@ class DataChannelPair: NSObject, Loggable {
175175

176176
Task {
177177
let eventStream = AsyncStream<ChannelEvent> { continuation in
178-
self.eventContinuation = continuation
178+
_state.mutate { $0.eventContinuation = continuation }
179179
}
180180
await handleEvents(events: eventStream)
181181
}
@@ -241,7 +241,7 @@ class DataChannelPair: NSObject, Loggable {
241241
channelKind: ChannelKind(packet.kind), // TODO: field is deprecated
242242
detail: .publishData(request)
243243
)
244-
eventContinuation?.yield(event)
244+
_state.eventContinuation?.yield(event)
245245
}
246246
}
247247

@@ -255,7 +255,7 @@ class DataChannelPair: NSObject, Loggable {
255255
private static let lossyLowThreshold: UInt64 = reliableLowThreshold
256256

257257
deinit {
258-
eventContinuation?.finish()
258+
_state.eventContinuation?.finish()
259259
}
260260
}
261261

@@ -267,7 +267,7 @@ extension DataChannelPair: LKRTCDataChannelDelegate {
267267
channelKind: dataChannel.kind,
268268
detail: .bufferedAmountChanged(amount)
269269
)
270-
eventContinuation?.yield(event)
270+
_state.eventContinuation?.yield(event)
271271
}
272272

273273
func dataChannelDidChangeState(_: LKRTCDataChannel) {

Sources/LiveKit/Core/RPC.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ let MAX_RPC_PAYLOAD_BYTES = 15360 // 15 KB
120120
/// Throwing an `RpcError` will send the error back to the requester.
121121
///
122122
/// - SeeAlso: `LocalParticipant.registerRpcMethod`
123-
public typealias RpcHandler = (RpcInvocationData) async throws -> String
123+
public typealias RpcHandler = @Sendable (RpcInvocationData) async throws -> String
124124

125125
public struct RpcInvocationData {
126126
/// A unique identifier for this RPC request
@@ -136,9 +136,9 @@ public struct RpcInvocationData {
136136
public let responseTimeout: TimeInterval
137137
}
138138

139-
struct PendingRpcResponse {
139+
struct PendingRpcResponse: Sendable {
140140
let participantIdentity: Participant.Identity
141-
let onResolve: (_ payload: String?, _ error: RpcError?) -> Void
141+
let onResolve: @Sendable (_ payload: String?, _ error: RpcError?) -> Void
142142
}
143143

144144
actor RpcStateManager: Loggable {

Sources/LiveKit/Core/Room+Engine.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ internal import LiveKitWebRTC
3030
extension Room {
3131
// MARK: - Public
3232

33-
typealias ConditionEvalFunc = (_ newState: State, _ oldState: State?) -> Bool
33+
typealias ConditionEvalFunc = @Sendable (_ newState: State, _ oldState: State?) -> Bool
3434

3535
// MARK: - Private
3636

@@ -207,7 +207,7 @@ extension Room {
207207
extension Room {
208208
func execute(when condition: @escaping ConditionEvalFunc,
209209
removeWhen removeCondition: @escaping ConditionEvalFunc,
210-
_ block: @escaping () -> Void)
210+
_ block: @Sendable @escaping () -> Void)
211211
{
212212
// already matches condition, execute immediately
213213
if _state.read({ condition($0, nil) }) {

Sources/LiveKit/Core/Room+EngineDelegate.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ extension Room {
9696
}
9797

9898
// Notify change when engine's state mutates
99-
Task.detached { @MainActor in
99+
Task { @MainActor in
100100
self.objectWillChange.send()
101101
}
102102
}

Sources/LiveKit/Core/Room.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import Network
2121
#endif
2222

2323
@objc
24-
public class Room: NSObject, ObservableObject, Loggable {
24+
public class Room: NSObject, @unchecked Sendable, ObservableObject, Loggable {
2525
// MARK: - MulticastDelegate
2626

2727
public let delegates = MulticastDelegate<RoomDelegate>(label: "RoomDelegate")
@@ -133,7 +133,7 @@ public class Room: NSObject, ObservableObject, Loggable {
133133

134134
// MARK: - State
135135

136-
struct State: Equatable {
136+
struct State: Equatable, Sendable {
137137
// Options
138138
var connectOptions: ConnectOptions
139139
var roomOptions: RoomOptions
@@ -228,7 +228,9 @@ public class Room: NSObject, ObservableObject, Loggable {
228228
}
229229

230230
// listen to app states
231-
AppStateListener.shared.delegates.add(delegate: self)
231+
Task { @MainActor in
232+
AppStateListener.shared.delegates.add(delegate: self)
233+
}
232234

233235
// trigger events when state mutates
234236
_state.onDidMutate = { [weak self] newState, oldState in
@@ -290,7 +292,7 @@ public class Room: NSObject, ObservableObject, Loggable {
290292
}
291293

292294
// Notify Room when state mutates
293-
Task.detached { @MainActor in
295+
Task { @MainActor in
294296
self.objectWillChange.send()
295297
}
296298
}

0 commit comments

Comments
 (0)