Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ jobs:
runs-on: macos-15
strategy:
matrix:
xcode: ["16.3"]
xcode: ["26.0"]
steps:
- uses: actions/checkout@v5
- name: Select Xcode ${{ matrix.xcode }}
Expand All @@ -182,8 +182,8 @@ jobs:
deriveddata-examples-${{ hashFiles('**/Sources/**/*.swift', '**/Tests/**/*.swift', '**/Examples/**/*.swift') }}
restore-keys: |
deriveddata-examples-
- name: Select Xcode 16.3
run: sudo xcode-select -s /Applications/Xcode_16.3.app
- name: Select Xcode 26.0
run: sudo xcode-select -s /Applications/Xcode_26.0.app
- name: Set IgnoreFileSystemDeviceInodeChanges flag
run: defaults write com.apple.dt.XCBuild IgnoreFileSystemDeviceInodeChanges -bool YES
- name: Update mtime for incremental builds
Expand Down
4 changes: 2 additions & 2 deletions Examples/Examples.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -660,7 +660,7 @@
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_VERSION = 5.0;
SWIFT_VERSION = 6.0;
};
name = Debug;
};
Expand Down Expand Up @@ -729,7 +729,7 @@
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_VERSION = 5.0;
SWIFT_VERSION = 6.0;
};
name = Release;
};
Expand Down
4 changes: 2 additions & 2 deletions Examples/Examples/Auth/AuthWithMagicLink.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ struct AuthWithMagicLink: View {
actionState = .inFlight

actionState = await .result(
Result {
Result { @Sendable in
try await supabase.auth.signInWithOTP(
email: email,
redirectTo: Constants.redirectToURL
Expand All @@ -63,7 +63,7 @@ struct AuthWithMagicLink: View {

actionState = .inFlight
actionState = await .result(
Result {
Result { @Sendable in
try await supabase.auth.session(from: url)
}
)
Expand Down
2 changes: 1 addition & 1 deletion Examples/Examples/Auth/SignInWithApple.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ struct SignInWithApple: View {

private func signInWithApple(using idToken: String, fullName: String?) async {
actionState = .inFlight
let result = await Result {
let result = await Result { @Sendable in
_ = try await supabase.auth.signInWithIdToken(
credentials: .init(
provider: .apple,
Expand Down
2 changes: 1 addition & 1 deletion Examples/Examples/Auth/SignInWithOAuth.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ struct SignInWithOAuth: View {
VStack {
Picker("Provider", selection: $provider) {
ForEach(providers) { provider in
Text("\(provider)").tag(provider)
Text("\(provider.rawValue)").tag(provider)
}
}

Expand Down
2 changes: 1 addition & 1 deletion Examples/Examples/Storage/BucketDetailView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ struct BucketDetailView: View {
private func load() async {
fileObjects = .inFlight
fileObjects = await .result(
Result {
Result { @Sendable in
try await supabase.storage.from(bucket.id).list()
}
)
Expand Down
14 changes: 8 additions & 6 deletions Examples/SlackClone/Logger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ extension Logger {

struct SupaLogger: SupabaseLogger {
func log(message: SupabaseLogMessage) {
let logger = Logger.supabase
Task {
let logger = await Logger.supabase

switch message.level {
case .debug: logger.debug("\(message, privacy: .public)")
case .error: logger.error("\(message, privacy: .public)")
case .verbose: logger.info("\(message, privacy: .public)")
case .warning: logger.notice("\(message, privacy: .public)")
switch message.level {
case .debug: logger.debug("\(message, privacy: .public)")
case .error: logger.error("\(message, privacy: .public)")
case .verbose: logger.info("\(message, privacy: .public)")
case .warning: logger.notice("\(message, privacy: .public)")
}
}
}
}
2 changes: 0 additions & 2 deletions Examples/SlackClone/UserStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,6 @@ final class UserStore {
case let .delete(action):
guard let id = action.oldRecord["id"]?.stringValue else { return }
users[UUID(uuidString: id)!] = nil
default:
break
}
} catch {
dump(error)
Expand Down
6 changes: 3 additions & 3 deletions Examples/UserManagement/AvatarImage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ struct AvatarImage: Transferable, Equatable {

static var transferRepresentation: some TransferRepresentation {
DataRepresentation(importedContentType: .image) { data in
guard let image = AvatarImage(data: data) else {
guard let image = await AvatarImage(data: data) else {
throw TransferError.importFailed
}

Expand All @@ -40,12 +40,12 @@ struct AvatarImage: Transferable, Equatable {
}

extension AvatarImage {
init?(data: Data) {
init?(data: Data) async {
guard let uiImage = PlatformImage(data: data) else {
return nil
}

let image = Image(platformImage: uiImage)
let image = await Image(platformImage: uiImage)
self.init(image: image, data: data)
}
}
Expand Down
2 changes: 1 addition & 1 deletion Examples/UserManagement/ProfileView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ struct ProfileView: View {

private func downloadImage(path: String) async throws {
let data = try await supabase.storage.from("avatars").download(path: path)
avatarImage = AvatarImage(data: data)
avatarImage = await AvatarImage(data: data)
}

private func uploadImage() async throws -> String? {
Expand Down
12 changes: 6 additions & 6 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version:5.10
// swift-tools-version:6.0
// The swift-tools-version declares the minimum version of Swift required to build this package.

import Foundation
Expand All @@ -7,11 +7,11 @@ import PackageDescription
let package = Package(
name: "Supabase",
platforms: [
.iOS(.v13),
.macCatalyst(.v13),
.macOS(.v10_15),
.watchOS(.v6),
.tvOS(.v13),
.iOS(.v16),
.macCatalyst(.v16),
.macOS(.v13),
.watchOS(.v9),
.tvOS(.v16),
],
products: [
.library(name: "Auth", targets: ["Auth"]),
Expand Down
8 changes: 5 additions & 3 deletions Sources/Auth/AuthClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ private actor GlobalJWKSCache {
private let globalJWKSCache = GlobalJWKSCache()

public actor AuthClient {
static var globalClientID = 0
static let globalClientID = LockIsolated(0)
nonisolated let clientID: AuthClientID

nonisolated private var api: APIClient { Dependencies[clientID].api }
Expand Down Expand Up @@ -121,8 +121,10 @@ public actor AuthClient {
/// - Parameters:
/// - configuration: The client configuration.
public init(configuration: Configuration) {
AuthClient.globalClientID += 1
clientID = AuthClient.globalClientID
clientID = AuthClient.globalClientID.withValue {
$0 += 1
return $0
}

Dependencies[clientID] = Dependencies(
configuration: configuration,
Expand Down
57 changes: 9 additions & 48 deletions Sources/Helpers/DateFormatter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,6 @@

import Foundation

extension DateFormatter {
fileprivate static func iso8601(includingFractionalSeconds: Bool) -> DateFormatter {
includingFractionalSeconds ? iso8601Fractional : iso8601Whole
}

fileprivate static let iso8601Fractional: DateFormatter = {
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .iso8601)
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS"
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
return formatter
}()

fileprivate static let iso8601Whole: DateFormatter = {
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .iso8601)
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
return formatter
}()
}

@available(iOS 15, macOS 12, tvOS 15, watchOS 8, *)
extension Date.ISO8601FormatStyle {
fileprivate func currentTimestamp(includingFractionalSeconds: Bool) -> Self {
year().month().day()
Expand All @@ -42,35 +17,21 @@ extension Date.ISO8601FormatStyle {

extension Date {
package var iso8601String: String {
if #available(iOS 15, macOS 12, tvOS 15, watchOS 8, *) {
return formatted(.iso8601.currentTimestamp(includingFractionalSeconds: true))
} else {
return DateFormatter.iso8601(includingFractionalSeconds: true).string(from: self)
}
formatted(.iso8601.currentTimestamp(includingFractionalSeconds: true))
}
}

extension String {
package var date: Date? {
if #available(iOS 15, macOS 12, tvOS 15, watchOS 8, *) {
if let date = try? Date(
self,
strategy: .iso8601.currentTimestamp(includingFractionalSeconds: true)
) {
return date
}
return try? Date(
self,
strategy: .iso8601.currentTimestamp(includingFractionalSeconds: false)
)
} else {
guard
let date = DateFormatter.iso8601(includingFractionalSeconds: true).date(from: self)
?? DateFormatter.iso8601(includingFractionalSeconds: false).date(from: self)
else {
return nil
}
if let date = try? Date(
self,
strategy: .iso8601.currentTimestamp(includingFractionalSeconds: true)
) {
return date
}
return try? Date(
self,
strategy: .iso8601.currentTimestamp(includingFractionalSeconds: false)
)
}
}
1 change: 0 additions & 1 deletion Sources/Helpers/Logger/OSLogSupabaseLogger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import Foundation
/// options: .init(global: .init(logger: supabaseLogger))
/// )
/// ```
@available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *)
public struct OSLogSupabaseLogger: SupabaseLogger {
private let logger: Logger

Expand Down
2 changes: 1 addition & 1 deletion Sources/Helpers/Task+withTimeout.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ package func withTimeout<R: Sendable>(
group.addTask {
let interval = deadline.timeIntervalSinceNow
if interval > 0 {
try await _clock.sleep(for: interval)
try await _clock.sleep(for: .seconds(interval))
}
try Task.checkCancellation()
throw TimeoutError()
Expand Down
39 changes: 3 additions & 36 deletions Sources/Helpers/_Clock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,43 +9,10 @@ import Clocks
import ConcurrencyExtras
import Foundation

package protocol _Clock: Sendable {
func sleep(for duration: TimeInterval) async throws
}

@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
extension ContinuousClock: _Clock {
package func sleep(for duration: TimeInterval) async throws {
try await sleep(for: .seconds(duration))
}
}
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
extension TestClock<Duration>: _Clock {
package func sleep(for duration: TimeInterval) async throws {
try await sleep(for: .seconds(duration))
}
}

/// `_Clock` used on platforms where ``Clock`` protocol isn't available.
struct FallbackClock: _Clock {
func sleep(for duration: TimeInterval) async throws {
try await Task.sleep(nanoseconds: NSEC_PER_SEC * UInt64(duration))
}
}

// Resolves clock instance based on platform availability.
let _resolveClock: @Sendable () -> any _Clock = {
if #available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) {
ContinuousClock()
} else {
FallbackClock()
}
}

private let __clock = LockIsolated(_resolveClock())
private let __clock = LockIsolated(ContinuousClock() as any Clock<Duration>)

#if DEBUG
package var _clock: any _Clock {
package var _clock: any Clock<Duration> {
get {
__clock.value
}
Expand All @@ -54,7 +21,7 @@ private let __clock = LockIsolated(_resolveClock())
}
}
#else
package var _clock: any _Clock {
package var _clock: any Clock<Duration> {
__clock.value
}
#endif
2 changes: 1 addition & 1 deletion Sources/Realtime/RealtimeChannelV2.swift
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ public final class RealtimeChannelV2: Sendable, RealtimeChannelProtocol {
)

do {
try await _clock.sleep(for: delay)
try await _clock.sleep(for: .seconds(delay))
} catch {
// If sleep is cancelled, break out of retry loop
logger?.debug("Subscription retry cancelled for channel '\(topic)'")
Expand Down
4 changes: 2 additions & 2 deletions Sources/Realtime/RealtimeClientV2.swift
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ public final class RealtimeClientV2: Sendable, RealtimeClientProtocol {
if status == .disconnected {
let connectionTask = Task {
if reconnect {
try? await _clock.sleep(for: options.reconnectDelay)
try? await _clock.sleep(for: .seconds(options.reconnectDelay))

if Task.isCancelled {
options.logger?.debug("Reconnect cancelled, returning")
Expand Down Expand Up @@ -398,7 +398,7 @@ public final class RealtimeClientV2: Sendable, RealtimeClientProtocol {
private func startHeartbeating() {
let heartbeatTask = Task { [weak self, options] in
while !Task.isCancelled {
try? await _clock.sleep(for: options.heartbeatInterval)
try? await _clock.sleep(for: .seconds(options.heartbeatInterval))
if Task.isCancelled {
break
}
Expand Down
6 changes: 3 additions & 3 deletions Tests/AuthTests/AuthClientTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ final class AuthClientTests: XCTestCase {
let validSession = Session.validSession
Dependencies[sut.clientID].sessionStorage.store(validSession)

let eventsTask = Task {
let eventsTask = Task { [sut] in
await sut.authStateChanges.prefix(2).collect()
}

Expand Down Expand Up @@ -236,7 +236,7 @@ final class AuthClientTests: XCTestCase {
let validSession = Session.validSession
Dependencies[sut.clientID].sessionStorage.store(validSession)

let eventsTask = Task {
let eventsTask = Task { [sut] in
await sut.authStateChanges.prefix(2).collect()
}

Expand Down Expand Up @@ -282,7 +282,7 @@ final class AuthClientTests: XCTestCase {
let validSession = Session.validSession
Dependencies[sut.clientID].sessionStorage.store(validSession)

let eventsTask = Task {
let eventsTask = Task { [sut] in
await sut.authStateChanges.prefix(2).collect()
}

Expand Down
Loading
Loading