Skip to content
Open
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
368742c
[Enhancement] Wire StreamCore into the StreamVideo target
Jul 1, 2026
efb1894
[StreamCore]Migrate SDK signaling & core infra onto stream-core-swift…
Jul 2, 2026
6d9163f
Fix websocket interval
Jul 13, 2026
640a656
Remove duplicated reconnection policies
Jul 13, 2026
b651fc1
Address feedback
Jul 13, 2026
ae23567
fix e2e tests
Jul 13, 2026
b6c5f91
Fix StreamCore imports for library evolution
Jul 13, 2026
4bebc4f
Fix StreamCore enum resilience
Jul 14, 2026
1b6386f
[Enhancement] Restore WebRTC log level synchronization (#1194)
ipavlidakis Jul 14, 2026
5f24a32
[Enhancement]Remove unused background task scheduler (#1193)
ipavlidakis Jul 14, 2026
54a1687
[StreamCore]Migrate to using RawJSON, JsonDecoder and ReflectiveStrin…
ipavlidakis Jul 15, 2026
3291ac0
[StreamCore]Migrate DisposableBag (#1196)
ipavlidakis Jul 15, 2026
d6ad915
[StreamCore] Migrate shared runtime primitives to StreamCore (#1197)
ipavlidakis Jul 16, 2026
1a44ddf
[StreamCore]Use StreamCore WebSocket state for coordinator connection…
ipavlidakis Jul 17, 2026
9a5ef66
[StreamCore] Preserve SFU reconnect close codes (#1202)
ipavlidakis Jul 20, 2026
fffe5f6
[StreamCore]Manual import StreamCore in generated models (#1203)
ipavlidakis Jul 20, 2026
940e94e
[StreamCore] Use core WebSocket state for SFU (#1204)
ipavlidakis Jul 20, 2026
7d887e4
Fix failing test
Jul 20, 2026
99c7b04
[Fix] Import StreamCore Event in SwiftUI
Jul 20, 2026
2f7c929
[Hygiene] Disable queue debugging in DemoApp scheme
Jul 20, 2026
60fbc28
[StreamCore] Remove obsolete SFU event wrapper (#1205)
ipavlidakis Jul 21, 2026
3be4960
Adding test to ensure connection recovery flow
ipavlidakis Jul 23, 2026
ae2d232
[StreamCore]Unify APIError (#1208)
ipavlidakis Jul 23, 2026
ecfda6a
Fix sonar analysis failure
ipavlidakis Jul 23, 2026
5ad7433
[Clean up & fixing production code and tests
ipavlidakis Jul 23, 2026
46f5d24
[Migrated]Timer entities
ipavlidakis Jul 23, 2026
764dab3
@_exported StreamCore
ipavlidakis Jul 24, 2026
4bc4583
Remove remaining StreamCore.* prefixed imports
ipavlidakis Jul 24, 2026
0ee0aa9
Fix WebRTCTrace distribution build
ipavlidakis Jul 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions DemoApp/Sources/Models/TokenResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@
//

import Foundation
import StreamVideo

struct TokenResponse: Codable, ReflectiveStringConvertible {
struct TokenResponse: Codable {
let userId: String
let token: String
let apiKey: String
Expand Down
24 changes: 20 additions & 4 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,42 @@ let package = Package(
],
dependencies: [
.package(url: "https://github.com/apple/swift-protobuf.git", exact: "1.30.0"),
.package(url: "https://github.com/GetStream/stream-video-swift-webrtc.git", exact: "145.8.0")
.package(url: "https://github.com/GetStream/stream-video-swift-webrtc.git", exact: "145.8.0"),
// Shared low-level SDK. Provides the logger, WebSocket client, error
// model, etc. that StreamVideo is migrating onto.
.package(url: "https://github.com/GetStream/stream-core-swift.git", branch: "develop")
],
targets: [
.target(
name: "StreamVideo",
dependencies: [
.product(name: "SwiftProtobuf", package: "swift-protobuf"),
.product(name: "StreamWebRTC", package: "stream-video-swift-webrtc")
.product(name: "StreamWebRTC", package: "stream-video-swift-webrtc"),
.product(name: "StreamCore", package: "stream-core-swift")
]
),
.target(
name: "StreamVideoSwiftUI",
dependencies: ["StreamVideo"],
// Links StreamCore directly: StreamVideo's public logger types are
// now typealiases to StreamCore's, so this module references
// StreamCore symbols (e.g. `LogSubsystem`, `Publisher.log`) at link
// time even though it only imports StreamVideo.
dependencies: [
"StreamVideo",
.product(name: "StreamCore", package: "stream-core-swift")
],
resources: [
.process("Resources")
]
),
.target(
name: "StreamVideoUIKit",
dependencies: ["StreamVideo", "StreamVideoSwiftUI"]
// Links StreamCore for the same reason as StreamVideoSwiftUI.
dependencies: [
"StreamVideo",
"StreamVideoSwiftUI",
.product(name: "StreamCore", package: "stream-core-swift")
]
)
]
)
1 change: 1 addition & 0 deletions Sources/StreamVideo/Call.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import AVFoundation
import Combine
import Foundation
import StreamCore
import StreamWebRTC

/// Observable object that provides info about the call state, as well as methods for updating it.
Expand Down
1 change: 1 addition & 0 deletions Sources/StreamVideo/CallState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import Combine
import Foundation
import StreamCore

@MainActor
public class CallState: ObservableObject {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import Combine
import Foundation
import StreamCore

extension Call.StateMachine {
/// Represents a stage in the call state machine.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//
// Copyright Β© 2026 Stream.io Inc. All rights reserved.
//

import StreamCore

extension APIError: ReflectiveStringConvertible {}
40 changes: 32 additions & 8 deletions Sources/StreamVideo/Errors/Errors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
//

import Foundation
import StreamCore

extension Stream_Video_Sfu_Models_Error: Error, ReflectiveStringConvertible {}
extension Stream_Video_Sfu_Models_Error:
Error,
ReflectiveStringConvertible {}

/// A Client error.
public class ClientError: Error, CustomStringConvertible, @unchecked Sendable {
Expand All @@ -22,6 +25,7 @@ public class ClientError: Error, CustomStringConvertible, @unchecked Sendable {
/// An underlying error.
public let underlyingError: Error?

/// The associated API error, if this wraps one.
public let apiError: APIError?

var errorDescription: String? {
Expand Down Expand Up @@ -63,8 +67,19 @@ public class ClientError: Error, CustomStringConvertible, @unchecked Sendable {
underlyingError = error
message = error?.localizedDescription ?? nil
location = .init(file: "\(file)", line: Int(line))
if let aErr = error as? APIError {
apiError = aErr
if let apiError = error as? APIError {
self.apiError = apiError
} else if let apiError = error as? StreamAPIError {
self.apiError = APIError(
code: apiError.code,
details: apiError.details,
duration: apiError.duration,
exceptionFields: apiError.exceptionFields,
message: apiError.message,
moreInfo: apiError.moreInfo,
statusCode: apiError.statusCode,
unrecoverable: apiError.unrecoverable
)
} else {
apiError = nil
}
Expand Down Expand Up @@ -115,8 +130,11 @@ extension ClientError: Equatable {
extension ClientError {
/// Returns `true` if underlaying error is `ErrorPayload` with code is inside invalid token codes range.
var isInvalidTokenError: Bool {
(underlyingError as? ErrorPayload)?.isInvalidTokenError == true
|| apiError?.isTokenExpiredError == true
if (underlyingError as? ErrorPayload)?.isInvalidTokenError == true {
return true
}
guard let apiError else { return false }
return ClosedRange.tokenInvalidErrorCodes.contains(apiError.code)
}
}

Expand All @@ -131,15 +149,18 @@ extension Error {
}

extension Error {
// Casts to `StreamAPIError` (StreamCore's) because that is the type the
// transport now throws. Only the stored `code`/`statusCode` are read, so no
// `import StreamCore` is required here.
var isTokenExpiredError: Bool {
if let error = self as? APIError, ClosedRange.tokenInvalidErrorCodes ~= error.code {
if let error = self as? StreamAPIError, ClosedRange.tokenInvalidErrorCodes ~= error.code {
return true
}
return false
}

var hasClientErrors: Bool {
if let apiError = self as? APIError,
if let apiError = self as? StreamAPIError,
ClosedRange.clientErrorCodes ~= apiError.statusCode {
return false
}
Expand All @@ -156,7 +177,10 @@ extension ClosedRange where Bound == Int {
}

struct APIErrorContainer: Codable {
let error: APIError
// Decodes the server error envelope directly into `StreamAPIError`
// (identical `CodingKeys` to video's `APIError`), so WebSocket-surfaced API
// errors converge on StreamCore's type too.
let error: StreamAPIError
}

extension APIError: Error {}
23 changes: 23 additions & 0 deletions Sources/StreamVideo/Errors/StreamAPIError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// Copyright Β© 2026 Stream.io Inc. All rights reserved.
//

import Foundation
import StreamCore

/// StreamVideo-facing name for StreamCore's `APIError`.
///
/// This is the canonical API error type for hand-written code; the generated
/// OpenAPI layer keeps its own `APIError`. A distinct alias name is required
/// because the generated `APIError` already occupies the bare name in this
/// module, and referencing `StreamCore.APIError` directly would force
/// `import StreamCore` (which reintroduces `log`/`ClientError`/`APIError`
/// ambiguities) in every consumer. Only stored properties (`code`,
/// `statusCode`, `message`, `unrecoverable`) are read through it, so no import
/// is needed at the use sites.
///
/// - TODO: [StreamCore migration] This alias (and the split between hand-written
Comment thread
ipavlidakis marked this conversation as resolved.
Outdated
/// `StreamAPIError` and the generated `APIError`) goes away once the OpenAPI
/// generator is changed to emit `APIError` as `StreamCore.APIError`. Then the
/// whole app uses one API error type and this file can be deleted.
public typealias StreamAPIError = StreamCore.APIError

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't we use the APIError directly?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are currently two nominally different APIError types: the generated StreamVideo.APIError and StreamCore.APIError. Using bare APIError resolves to the generated model, not StreamCore’s type. StreamAPIError therefore disambiguates Core errors without requiring import StreamCore in every consumer, which would also introduce symbol collisions. ClientError.apiError remains the generated type for source compatibility, with Core errors converted at the boundary. The alias can be removed once the generated model migrates to StreamCore (which i'm not even sure if it's possible to happen as it will require backend to align the apiErrors between chat and video and potentially feeds).

1 change: 1 addition & 0 deletions Sources/StreamVideo/HTTPClient/HTTPUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//

import Foundation
import StreamCore

extension URLRequest {
var queryItems: [URLQueryItem] {
Expand Down
1 change: 1 addition & 0 deletions Sources/StreamVideo/Models/CallsQuery.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//

import Foundation
import StreamCore

/// Defines a query to retrieve a list of calls from the server, with specified page size, sort parameters,
/// filters and watch flag.
Expand Down
1 change: 1 addition & 0 deletions Sources/StreamVideo/Models/CoordinatorModels.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//

import Foundation
import StreamCore

public struct FetchingLocationError: Error {}

Expand Down
1 change: 1 addition & 0 deletions Sources/StreamVideo/Models/Member.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//

import Foundation
import StreamCore

/// Represents a member in the call.
public struct Member: Identifiable, Equatable, Sendable, Codable {
Expand Down
1 change: 1 addition & 0 deletions Sources/StreamVideo/Models/User.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//

import Foundation
import StreamCore

/// Model for the user's info.
public struct User: Identifiable, Hashable, Sendable, Codable {
Expand Down
5 changes: 4 additions & 1 deletion Sources/StreamVideo/OpenApi/URLSessionTransport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,10 @@ final class URLSessionTransport: DefaultAPITransport, @unchecked Sendable {
}

do {
return try JSONDecoder.default.decode(APIError.self, from: data)
// Decode server errors into `StreamAPIError` (StreamCore's, identical
// `CodingKeys`) so every thrown API error downstream is StreamCore's
// type. The generated OpenAPI layer keeps video's `APIError`.
return try JSONDecoder.default.decode(StreamAPIError.self, from: data)
} catch {
return ClientError.NetworkError(response.description)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import Foundation

public final class APIError: @unchecked Sendable, Codable, JSONEncodable, Hashable, ReflectiveStringConvertible {
public final class APIError: @unchecked Sendable, Codable, JSONEncodable, Hashable {

public var code: Int
public var details: [Int]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Copyright Β© 2026 Stream.io Inc. All rights reserved.
//

import StreamCore
import Foundation

public final class AppUpdatedEvent: @unchecked Sendable, Event, Codable, JSONEncodable, Hashable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//

import Foundation
import StreamCore

public final class BlockedUserEvent: @unchecked Sendable, Event, Codable, JSONEncodable, Hashable, WSCallEvent {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//

import Foundation
import StreamCore

public final class CallAcceptedEvent: @unchecked Sendable, Event, Codable, JSONEncodable, Hashable, WSCallEvent {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//

import Foundation
import StreamCore

public final class CallClosedCaptionsFailedEvent: @unchecked Sendable, Event, Codable, JSONEncodable, Hashable, WSCallEvent {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//

import Foundation
import StreamCore

public final class CallClosedCaptionsStartedEvent: @unchecked Sendable, Event, Codable, JSONEncodable, Hashable, WSCallEvent {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//

import Foundation
import StreamCore

public final class CallClosedCaptionsStoppedEvent: @unchecked Sendable, Event, Codable, JSONEncodable, Hashable, WSCallEvent {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//

import Foundation
import StreamCore

public final class CallCreatedEvent: @unchecked Sendable, Event, Codable, JSONEncodable, Hashable, WSCallEvent {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//

import Foundation
import StreamCore

public final class CallDeletedEvent: @unchecked Sendable, Event, Codable, JSONEncodable, Hashable, WSCallEvent {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//

import Foundation
import StreamCore

public final class CallEndedEvent: @unchecked Sendable, Event, Codable, JSONEncodable, Hashable, WSCallEvent {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//

import Foundation
import StreamCore

public final class CallFrameRecordingFailedEvent: @unchecked Sendable, Event, Codable, JSONEncodable, Hashable, WSCallEvent {
public var call: CallResponse
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//

import Foundation
import StreamCore

public final class CallFrameRecordingFrameReadyEvent: @unchecked Sendable, Event, Codable, JSONEncodable, Hashable, WSCallEvent {
public var callCid: String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//

import Foundation
import StreamCore

public final class CallFrameRecordingStartedEvent: @unchecked Sendable, Event, Codable, JSONEncodable, Hashable, WSCallEvent {
public var call: CallResponse
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//

import Foundation
import StreamCore

public final class CallFrameRecordingStoppedEvent: @unchecked Sendable, Event, Codable, JSONEncodable, Hashable, WSCallEvent {
public var call: CallResponse
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//

import Foundation
import StreamCore

public final class CallHLSBroadcastingFailedEvent: @unchecked Sendable, Event, Codable, JSONEncodable, Hashable, WSCallEvent {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//

import Foundation
import StreamCore

public final class CallHLSBroadcastingStartedEvent: @unchecked Sendable, Event, Codable, JSONEncodable, Hashable, WSCallEvent {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//

import Foundation
import StreamCore

public final class CallHLSBroadcastingStoppedEvent: @unchecked Sendable, Event, Codable, JSONEncodable, Hashable, WSCallEvent {

Expand Down
Loading
Loading