diff --git a/Examples/Examples.xcodeproj/project.pbxproj b/Examples/Examples.xcodeproj/project.pbxproj
index 95c39f3c..c2524506 100644
--- a/Examples/Examples.xcodeproj/project.pbxproj
+++ b/Examples/Examples.xcodeproj/project.pbxproj
@@ -709,7 +709,6 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = Examples/Examples.entitlements;
- CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "\"Examples/Preview Content\"";
DEVELOPMENT_TEAM = ELTTE7K8TT;
ENABLE_HARDENED_RUNTIME = YES;
@@ -719,7 +718,12 @@
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
PRODUCT_BUNDLE_IDENTIFIER = "com.supabase.swift-examples";
PRODUCT_NAME = "$(TARGET_NAME)";
+ REGISTER_APP_GROUPS = NO;
+ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
+ SUPPORTS_MACCATALYST = NO;
+ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_EMIT_LOC_STRINGS = YES;
+ TARGETED_DEVICE_FAMILY = 1;
};
name = Debug;
};
@@ -729,7 +733,6 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = Examples/Examples.entitlements;
- CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "\"Examples/Preview Content\"";
DEVELOPMENT_TEAM = ELTTE7K8TT;
ENABLE_HARDENED_RUNTIME = YES;
@@ -739,7 +742,12 @@
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
PRODUCT_BUNDLE_IDENTIFIER = "com.supabase.swift-examples";
PRODUCT_NAME = "$(TARGET_NAME)";
+ REGISTER_APP_GROUPS = NO;
+ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
+ SUPPORTS_MACCATALYST = NO;
+ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_EMIT_LOC_STRINGS = YES;
+ TARGETED_DEVICE_FAMILY = 1;
};
name = Release;
};
diff --git a/Examples/Examples/Auth/AuthView.swift b/Examples/Examples/Auth/AuthView.swift
index e6dae5a8..88830900 100644
--- a/Examples/Examples/Auth/AuthView.swift
+++ b/Examples/Examples/Auth/AuthView.swift
@@ -14,7 +14,9 @@ struct AuthView: View {
case signInWithPhone
case signInWithApple
case signInWithOAuth
- case signInWithOAuthUsingUIKit
+ #if canImport(UIKit)
+ case signInWithOAuthUsingUIKit
+ #endif
case googleSignInSDKFlow
case signInAnonymously
@@ -25,7 +27,9 @@ struct AuthView: View {
case .signInWithPhone: "Sign in with Phone"
case .signInWithApple: "Sign in with Apple"
case .signInWithOAuth: "Sign in with OAuth flow"
- case .signInWithOAuthUsingUIKit: "Sign in with OAuth flow (UIKit)"
+ #if canImport(UIKit)
+ case .signInWithOAuthUsingUIKit: "Sign in with OAuth flow (UIKit)"
+ #endif
case .googleSignInSDKFlow: "Google Sign in (GIDSignIn SDK Flow)"
case .signInAnonymously: "Sign in Anonymously"
}
@@ -43,7 +47,9 @@ struct AuthView: View {
options
.navigationTitle(options.title)
}
+ #if !os(macOS)
.navigationBarTitleDisplayMode(.inline)
+ #endif
}
}
}
@@ -56,8 +62,10 @@ extension AuthView.Option: View {
case .signInWithPhone: SignInWithPhone()
case .signInWithApple: SignInWithApple()
case .signInWithOAuth: SignInWithOAuth()
- case .signInWithOAuthUsingUIKit: UIViewControllerWrapper(SignInWithOAuthViewController())
- .edgesIgnoringSafeArea(.all)
+ #if canImport(UIKit)
+ case .signInWithOAuthUsingUIKit: UIViewControllerWrapper(SignInWithOAuthViewController())
+ .edgesIgnoringSafeArea(.all)
+ #endif
case .googleSignInSDKFlow: GoogleSignInSDKFlow()
case .signInAnonymously: SignInAnonymously()
}
diff --git a/Examples/Examples/Auth/AuthWithEmailAndPassword.swift b/Examples/Examples/Auth/AuthWithEmailAndPassword.swift
index 5a0fe82f..3db18342 100644
--- a/Examples/Examples/Auth/AuthWithEmailAndPassword.swift
+++ b/Examples/Examples/Auth/AuthWithEmailAndPassword.swift
@@ -27,15 +27,19 @@ struct AuthWithEmailAndPassword: View {
Form {
Section {
TextField("Email", text: $email)
- .keyboardType(.emailAddress)
.textContentType(.emailAddress)
.autocorrectionDisabled()
+ #if !os(macOS)
+ .keyboardType(.emailAddress)
.textInputAutocapitalization(.never)
+ #endif
SecureField("Password", text: $password)
.textContentType(.password)
.autocorrectionDisabled()
+ #if !os(macOS)
.textInputAutocapitalization(.never)
+ #endif
}
Section {
diff --git a/Examples/Examples/Auth/AuthWithMagicLink.swift b/Examples/Examples/Auth/AuthWithMagicLink.swift
index 7cbb3707..1267e223 100644
--- a/Examples/Examples/Auth/AuthWithMagicLink.swift
+++ b/Examples/Examples/Auth/AuthWithMagicLink.swift
@@ -15,10 +15,12 @@ struct AuthWithMagicLink: View {
Form {
Section {
TextField("Email", text: $email)
- .keyboardType(.emailAddress)
.textContentType(.emailAddress)
.autocorrectionDisabled()
+ #if !os(macOS)
+ .keyboardType(.emailAddress)
.textInputAutocapitalization(.never)
+ #endif
}
Section {
diff --git a/Examples/Examples/Auth/GoogleSignInSDKFlow.swift b/Examples/Examples/Auth/GoogleSignInSDKFlow.swift
index f7fb7fc7..09035f34 100644
--- a/Examples/Examples/Auth/GoogleSignInSDKFlow.swift
+++ b/Examples/Examples/Auth/GoogleSignInSDKFlow.swift
@@ -19,7 +19,7 @@ struct GoogleSignInSDKFlow: View {
func handleSignIn() {
Task {
do {
- let result = try await GIDSignIn.sharedInstance.signIn(withPresenting: rootViewController)
+ let result = try await GIDSignIn.sharedInstance.signIn(withPresenting: root)
guard let idToken = result.user.idToken?.tokenString else {
debug("No 'idToken' returned by GIDSignIn call.")
@@ -38,9 +38,15 @@ struct GoogleSignInSDKFlow: View {
}
}
- var rootViewController: UIViewController {
- UIApplication.shared.firstKeyWindow?.rootViewController ?? UIViewController()
- }
+ #if canImport(AppKit)
+ var root: NSWindow {
+ NSApplication.shared.windows.first ?? NSWindow()
+ }
+ #else
+ var root: UIViewController {
+ UIApplication.shared.firstKeyWindow?.rootViewController ?? UIViewController()
+ }
+ #endif
}
#Preview {
diff --git a/Examples/Examples/Auth/SignInWithOAuth.swift b/Examples/Examples/Auth/SignInWithOAuth.swift
index cc325a6f..295c73eb 100644
--- a/Examples/Examples/Auth/SignInWithOAuth.swift
+++ b/Examples/Examples/Auth/SignInWithOAuth.swift
@@ -45,78 +45,81 @@ struct SignInWithOAuth: View {
}
}
-final class SignInWithOAuthViewController: UIViewController, UIPickerViewDataSource,
- UIPickerViewDelegate
-{
- let providers = Provider.allCases
- var provider = Provider.allCases[0]
+#if canImport(UIKit)
+ final class SignInWithOAuthViewController: UIViewController, UIPickerViewDataSource,
+ UIPickerViewDelegate
+ {
+ let providers = Provider.allCases
+ var provider = Provider.allCases[0]
+
+ let providerPicker = UIPickerView()
+ let signInButton = UIButton(type: .system)
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ setupViews()
+ }
- let providerPicker = UIPickerView()
- let signInButton = UIButton(type: .system)
+ func setupViews() {
+ view.backgroundColor = .white
+
+ providerPicker.dataSource = self
+ providerPicker.delegate = self
+ view.addSubview(providerPicker)
+ providerPicker.translatesAutoresizingMaskIntoConstraints = false
+ NSLayoutConstraint.activate([
+ providerPicker.centerXAnchor.constraint(equalTo: view.centerXAnchor),
+ providerPicker.centerYAnchor.constraint(equalTo: view.centerYAnchor),
+ providerPicker.widthAnchor.constraint(equalToConstant: 200),
+ providerPicker.heightAnchor.constraint(equalToConstant: 100),
+ ])
+
+ signInButton.setTitle("Start Sign-in Flow", for: .normal)
+ signInButton.addTarget(self, action: #selector(signInButtonTapped), for: .touchUpInside)
+ view.addSubview(signInButton)
+ signInButton.translatesAutoresizingMaskIntoConstraints = false
+ NSLayoutConstraint.activate([
+ signInButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
+ signInButton.topAnchor.constraint(equalTo: providerPicker.bottomAnchor, constant: 20),
+ ])
+ }
- override func viewDidLoad() {
- super.viewDidLoad()
- setupViews()
- }
+ @objc func signInButtonTapped() {
+ Task {
+ do {
+ try await supabase.auth.signInWithOAuth(
+ provider: provider,
+ redirectTo: Constants.redirectToURL
+ )
+ } catch {
+ debug("Failed to sign-in with OAuth flow: \(error)")
+ }
+ }
+ }
- func setupViews() {
- view.backgroundColor = .white
-
- providerPicker.dataSource = self
- providerPicker.delegate = self
- view.addSubview(providerPicker)
- providerPicker.translatesAutoresizingMaskIntoConstraints = false
- NSLayoutConstraint.activate([
- providerPicker.centerXAnchor.constraint(equalTo: view.centerXAnchor),
- providerPicker.centerYAnchor.constraint(equalTo: view.centerYAnchor),
- providerPicker.widthAnchor.constraint(equalToConstant: 200),
- providerPicker.heightAnchor.constraint(equalToConstant: 100),
- ])
-
- signInButton.setTitle("Start Sign-in Flow", for: .normal)
- signInButton.addTarget(self, action: #selector(signInButtonTapped), for: .touchUpInside)
- view.addSubview(signInButton)
- signInButton.translatesAutoresizingMaskIntoConstraints = false
- NSLayoutConstraint.activate([
- signInButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
- signInButton.topAnchor.constraint(equalTo: providerPicker.bottomAnchor, constant: 20),
- ])
- }
+ func numberOfComponents(in _: UIPickerView) -> Int {
+ 1
+ }
- @objc func signInButtonTapped() {
- Task {
- do {
- try await supabase.auth.signInWithOAuth(
- provider: provider,
- redirectTo: Constants.redirectToURL
- )
- } catch {
- debug("Failed to sign-in with OAuth flow: \(error)")
- }
+ func pickerView(_: UIPickerView, numberOfRowsInComponent _: Int) -> Int {
+ providers.count
}
- }
- func numberOfComponents(in _: UIPickerView) -> Int {
- 1
- }
+ func pickerView(_: UIPickerView, titleForRow row: Int, forComponent _: Int) -> String? {
+ "\(providers[row])"
+ }
- func pickerView(_: UIPickerView, numberOfRowsInComponent _: Int) -> Int {
- providers.count
+ func pickerView(_: UIPickerView, didSelectRow row: Int, inComponent _: Int) {
+ provider = providers[row]
+ }
}
- func pickerView(_: UIPickerView, titleForRow row: Int, forComponent _: Int) -> String? {
- "\(providers[row])"
+ #Preview("UIKit") {
+ SignInWithOAuthViewController()
}
- func pickerView(_: UIPickerView, didSelectRow row: Int, inComponent _: Int) {
- provider = providers[row]
- }
-}
+#endif
#Preview("SwiftUI") {
SignInWithOAuth()
}
-
-#Preview("UIKit") {
- SignInWithOAuthViewController()
-}
diff --git a/Examples/Examples/Examples.entitlements b/Examples/Examples/Examples.entitlements
index 4210463c..625af03d 100644
--- a/Examples/Examples/Examples.entitlements
+++ b/Examples/Examples/Examples.entitlements
@@ -2,13 +2,11 @@
- com.apple.developer.applesignin
-
- Default
-
com.apple.security.app-sandbox
com.apple.security.files.user-selected.read-only
+ com.apple.security.network.client
+
diff --git a/Examples/Examples/ExamplesApp.swift b/Examples/Examples/ExamplesApp.swift
index 2ee2b7b9..4ea4624b 100644
--- a/Examples/Examples/ExamplesApp.swift
+++ b/Examples/Examples/ExamplesApp.swift
@@ -9,40 +9,60 @@ import GoogleSignIn
import Supabase
import SwiftUI
-class AppDelegate: UIResponder, UIApplicationDelegate {
- func application(
- _: UIApplication,
- didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
- ) -> Bool {
- if let url = launchOptions?[.url] as? URL {
+#if canImport(AppKit)
+ class AppDelegate: NSResponder, NSApplicationDelegate {
+ func applicationDidFinishLaunching(_ notification: Notification) {
+ print(notification)
+// if let url = launchOptions?[.url] as? URL {
+// supabase.handle(url)
+// }
+ }
+
+ func application(_: NSApplication, open urls: [URL]) {
+ guard let url = urls.first else { return }
supabase.handle(url)
}
- return true
}
+#else
+ class AppDelegate: UIResponder, UIApplicationDelegate {
+ func application(
+ _: UIApplication,
+ didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
+ ) -> Bool {
+ if let url = launchOptions?[.url] as? URL {
+ supabase.handle(url)
+ }
+ return true
+ }
- func application(_: UIApplication, open url: URL, options _: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
- supabase.handle(url)
- return true
- }
+ func application(_: UIApplication, open url: URL, options _: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
+ supabase.handle(url)
+ return true
+ }
- func application(_: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options _: UIScene.ConnectionOptions) -> UISceneConfiguration {
- let configuration = UISceneConfiguration(name: nil, sessionRole: connectingSceneSession.role)
- configuration.delegateClass = SceneDelegate.self
- return configuration
+ func application(_: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options _: UIScene.ConnectionOptions) -> UISceneConfiguration {
+ let configuration = UISceneConfiguration(name: nil, sessionRole: connectingSceneSession.role)
+ configuration.delegateClass = SceneDelegate.self
+ return configuration
+ }
}
-}
-class SceneDelegate: UIResponder, UISceneDelegate {
- func scene(_: UIScene, openURLContexts URLContexts: Set) {
- guard let url = URLContexts.first?.url else { return }
+ class SceneDelegate: UIResponder, UISceneDelegate {
+ func scene(_: UIScene, openURLContexts URLContexts: Set) {
+ guard let url = URLContexts.first?.url else { return }
- supabase.handle(url)
+ supabase.handle(url)
+ }
}
-}
+#endif
@main
struct ExamplesApp: App {
- @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
+ #if canImport(AppKit)
+ @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
+ #else
+ @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
+ #endif
var body: some Scene {
WindowGroup {
diff --git a/Examples/Examples/Profile/UpdateProfileView.swift b/Examples/Examples/Profile/UpdateProfileView.swift
index 49ecf14f..ce02bb7d 100644
--- a/Examples/Examples/Profile/UpdateProfileView.swift
+++ b/Examples/Examples/Profile/UpdateProfileView.swift
@@ -34,14 +34,18 @@ struct UpdateProfileView: View {
Section {
TextField("Email", text: $email)
.textContentType(.emailAddress)
- .keyboardType(.emailAddress)
.autocorrectionDisabled()
+ #if !os(macOS)
+ .keyboardType(.emailAddress)
.textInputAutocapitalization(.never)
+ #endif
TextField("Phone", text: $phone)
.textContentType(.telephoneNumber)
- .keyboardType(.phonePad)
.autocorrectionDisabled()
+ #if !os(macOS)
+ .keyboardType(.phonePad)
.textInputAutocapitalization(.never)
+ #endif
}
Section {
diff --git a/Examples/Examples/UIApplicationExtensions.swift b/Examples/Examples/UIApplicationExtensions.swift
index e39ae820..e3a26aa5 100644
--- a/Examples/Examples/UIApplicationExtensions.swift
+++ b/Examples/Examples/UIApplicationExtensions.swift
@@ -5,14 +5,16 @@
// Created by Guilherme Souza on 05/03/24.
//
-import UIKit
+#if canImport(UIKit)
+ import UIKit
-extension UIApplication {
- var firstKeyWindow: UIWindow? {
- UIApplication.shared
- .connectedScenes
- .compactMap { $0 as? UIWindowScene }
- .filter { $0.activationState == .foregroundActive }
- .first?.keyWindow
+ extension UIApplication {
+ var firstKeyWindow: UIWindow? {
+ UIApplication.shared
+ .connectedScenes
+ .compactMap { $0 as? UIWindowScene }
+ .filter { $0.activationState == .foregroundActive }
+ .first?.keyWindow
+ }
}
-}
+#endif
diff --git a/Examples/Examples/UIViewControllerWrapper.swift b/Examples/Examples/UIViewControllerWrapper.swift
index 7738a041..4e7ffda6 100644
--- a/Examples/Examples/UIViewControllerWrapper.swift
+++ b/Examples/Examples/UIViewControllerWrapper.swift
@@ -7,20 +7,23 @@
import SwiftUI
-struct UIViewControllerWrapper: UIViewControllerRepresentable {
- typealias UIViewControllerType = T
+#if canImport(UIKit)
- let viewController: T
+ struct UIViewControllerWrapper: UIViewControllerRepresentable {
+ typealias UIViewControllerType = T
- init(_ viewController: T) {
- self.viewController = viewController
- }
+ let viewController: T
- func makeUIViewController(context _: Context) -> T {
- viewController
- }
+ init(_ viewController: T) {
+ self.viewController = viewController
+ }
+
+ func makeUIViewController(context _: Context) -> T {
+ viewController
+ }
- func updateUIViewController(_: T, context _: Context) {
- // Update the view controller if needed
+ func updateUIViewController(_: T, context _: Context) {
+ // Update the view controller if needed
+ }
}
-}
+#endif
diff --git a/Examples/SlackClone/SlackClone.entitlements b/Examples/SlackClone/SlackClone.entitlements
index 625af03d..5776a3a2 100644
--- a/Examples/SlackClone/SlackClone.entitlements
+++ b/Examples/SlackClone/SlackClone.entitlements
@@ -8,5 +8,7 @@
com.apple.security.network.client
+ keychain-access-groups
+
diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift
index d775eb55..15978ff4 100644
--- a/Sources/Auth/AuthClient.swift
+++ b/Sources/Auth/AuthClient.swift
@@ -12,17 +12,17 @@ import Helpers
typealias AuthClientID = UUID
-public final class AuthClient: Sendable {
- let clientID = AuthClientID()
+public actor AuthClient {
+ nonisolated let clientID = AuthClientID()
+ nonisolated var configuration: AuthClient.Configuration { Dependencies[clientID].configuration }
+ private nonisolated var codeVerifierStorage: CodeVerifierStorage { Dependencies[clientID].codeVerifierStorage }
+ private nonisolated var sessionStorage: SessionStorage { Dependencies[clientID].sessionStorage }
private var api: APIClient { Dependencies[clientID].api }
- var configuration: AuthClient.Configuration { Dependencies[clientID].configuration }
- private var codeVerifierStorage: CodeVerifierStorage { Dependencies[clientID].codeVerifierStorage }
private var date: @Sendable () -> Date { Dependencies[clientID].date }
- private var sessionManager: SessionManager { Dependencies[clientID].sessionManager }
private var eventEmitter: AuthStateChangeEventEmitter { Dependencies[clientID].eventEmitter }
- private var logger: (any SupabaseLogger)? { Dependencies[clientID].configuration.logger }
- private var sessionStorage: SessionStorage { Dependencies[clientID].sessionStorage }
+ private nonisolated var logger: (any SupabaseLogger)? { Dependencies[clientID].configuration.logger }
+ private var sessionManager: SessionManager { Dependencies[clientID].sessionManager }
/// Returns the session, refreshing it if necessary.
///
@@ -36,26 +36,26 @@ public final class AuthClient: Sendable {
/// Returns the current session, if any.
///
/// The session returned by this property may be expired. Use ``session`` for a session that is guaranteed to be valid.
- public var currentSession: Session? {
+ public nonisolated var currentSession: Session? {
try? sessionStorage.get()
}
/// Returns the current user, if any.
///
/// The user returned by this property may be outdated. Use ``user(jwt:)`` method to get an up-to-date user instance.
- public var currentUser: User? {
+ public nonisolated var currentUser: User? {
try? sessionStorage.get()?.user
}
/// Namespace for accessing multi-factor authentication API.
- public var mfa: AuthMFA {
+ public nonisolated var mfa: AuthMFA {
AuthMFA(clientID: clientID)
}
/// Namespace for the GoTrue admin methods.
/// - Warning: This methods requires `service_role` key, be careful to never expose `service_role`
/// key in the client.
- public var admin: AuthAdmin {
+ public nonisolated var admin: AuthAdmin {
AuthAdmin(clientID: clientID)
}
@@ -93,7 +93,7 @@ public final class AuthClient: Sendable {
/// Listen for auth state changes.
///
/// An `.initialSession` is always emitted when this method is called.
- public var authStateChanges: AsyncStream<(
+ public nonisolated var authStateChanges: AsyncStream<(
event: AuthChangeEvent,
session: Session?
)> {
@@ -487,7 +487,7 @@ public final class AuthClient: Sendable {
/// If that isn't the case, you should consider using
/// ``signInWithOAuth(provider:redirectTo:scopes:queryParams:launchFlow:)`` or
/// ``signInWithOAuth(provider:redirectTo:scopes:queryParams:configure:)``.
- public func getOAuthSignInURL(
+ public nonisolated func getOAuthSignInURL(
provider: Provider,
scopes: String? = nil,
redirectTo: URL? = nil,
@@ -657,7 +657,7 @@ public final class AuthClient: Sendable {
/// supabase.auth.handle(url)
/// }
/// ```
- public func handle(_ url: URL) {
+ public nonisolated func handle(_ url: URL) {
Task {
do {
try await session(from: url)
@@ -1174,7 +1174,7 @@ public final class AuthClient: Sendable {
eventEmitter.emit(.initialSession, session: session, token: token)
}
- private func prepareForPKCE() -> (codeChallenge: String?, codeChallengeMethod: String?) {
+ private nonisolated func prepareForPKCE() -> (codeChallenge: String?, codeChallengeMethod: String?) {
guard configuration.flowType == .pkce else {
return (nil, nil)
}
@@ -1197,7 +1197,7 @@ public final class AuthClient: Sendable {
return params["code"] != nil && currentCodeVerifier != nil
}
- private func getURLForProvider(
+ private nonisolated func getURLForProvider(
url: URL,
provider: Provider,
scopes: String? = nil,
diff --git a/Sources/Auth/AuthClientConfiguration.swift b/Sources/Auth/AuthClientConfiguration.swift
index 4b724a0a..4fcc3bd3 100644
--- a/Sources/Auth/AuthClientConfiguration.swift
+++ b/Sources/Auth/AuthClientConfiguration.swift
@@ -93,7 +93,7 @@ extension AuthClient {
/// - decoder: The JSON decoder to use for decoding responses.
/// - fetch: The asynchronous fetch handler for network requests.
/// - autoRefreshToken: Set to `true` if you want to automatically refresh the token before expiring.
- public convenience init(
+ public init(
url: URL,
headers: [String: String] = [:],
flowType: AuthFlowType = AuthClient.Configuration.defaultFlowType,
diff --git a/Sources/Auth/Deprecated.swift b/Sources/Auth/Deprecated.swift
index ac7c1fca..3f1eba1d 100644
--- a/Sources/Auth/Deprecated.swift
+++ b/Sources/Auth/Deprecated.swift
@@ -105,7 +105,7 @@ extension AuthClient {
deprecated,
message: "Replace usages of this initializer with new init(url:headers:flowType:localStorage:logger:encoder:decoder:fetch)"
)
- public convenience init(
+ public init(
url: URL,
headers: [String: String] = [:],
flowType: AuthFlowType = Configuration.defaultFlowType,
diff --git a/Sources/Auth/Internal/EventEmitter.swift b/Sources/Auth/Internal/EventEmitter.swift
index 0c2aedf2..7bc40dfb 100644
--- a/Sources/Auth/Internal/EventEmitter.swift
+++ b/Sources/Auth/Internal/EventEmitter.swift
@@ -3,7 +3,10 @@ import Foundation
import Helpers
struct AuthStateChangeEventEmitter {
- var emitter = EventEmitter<(AuthChangeEvent, Session?)?>(initialEvent: nil, emitsLastEventWhenAttaching: false)
+ let emitter = EventEmitter<(AuthChangeEvent, Session?)?>(
+ initialEvent: nil,
+ emitsLastEventWhenAttaching: false
+ )
func attach(_ listener: @escaping AuthStateChangeListener) -> ObservationToken {
emitter.attach { event in
diff --git a/Sources/Auth/Internal/Keychain.swift b/Sources/Auth/Internal/Keychain.swift
index 0d84378f..9be9f905 100644
--- a/Sources/Auth/Internal/Keychain.swift
+++ b/Sources/Auth/Internal/Keychain.swift
@@ -67,10 +67,12 @@
query[kSecAttrAccessGroup as String] = accessGroup
}
- // this is highly recommended for all keychain operations and makes the
- // macOS keychain item behave like an iOS keychain item
- // https://developer.apple.com/documentation/security/ksecusedataprotectionkeychain
- query[kSecUseDataProtectionKeychain as String] = kCFBooleanTrue
+ #if os(macOS)
+ // this is highly recommended for all keychain operations and makes the
+ // macOS keychain item behave like an iOS keychain item
+ // https://developer.apple.com/documentation/security/ksecusedataprotectionkeychain
+ query[kSecUseDataProtectionKeychain as String] = kCFBooleanTrue
+ #endif
return query
}
@@ -85,7 +87,14 @@
func setQuery(forKey key: String, data: Data) -> [String: Any] {
var query = baseQuery(withKey: key, data: data)
- query[kSecAttrAccessible as String] = kSecAttrAccessibleAfterFirstUnlock
+ #if os(macOS)
+ // See https://developer.apple.com/documentation/security/ksecattraccessible
+ if query[kSecUseDataProtectionKeychain as String] as? Bool == true {
+ query[kSecAttrAccessible as String] = kSecAttrAccessibleAfterFirstUnlock
+ }
+ #else
+ query[kSecAttrAccessible as String] = kSecAttrAccessibleAfterFirstUnlock
+ #endif
return query
}
@@ -102,6 +111,7 @@
case itemNotFound
case interactionNotAllowed
case decodeFailed
+ case missingEntitlement
case other(status: OSStatus)
case unknown(message: String)
@@ -116,6 +126,7 @@
case errSecItemNotFound: self = .itemNotFound
case errSecInteractionNotAllowed: self = .interactionNotAllowed
case errSecDecode: self = .decodeFailed
+ case errSecMissingEntitlement: self = .missingEntitlement
default: self = .other(status: rawValue)
}
}
@@ -131,6 +142,7 @@
case .itemNotFound: errSecItemNotFound
case .interactionNotAllowed: errSecInteractionNotAllowed
case .decodeFailed: errSecDecode
+ case .missingEntitlement: errSecMissingEntitlement
case let .other(status): status
case .unknown: errSecSuccess // This is not a Keychain error
}
@@ -171,6 +183,8 @@
"errSecInteractionNotAllowed: Interaction with the Security Server is not allowed."
case .decodeFailed:
"errSecDecode: Unable to decode the provided data."
+ case .missingEntitlement:
+ "errSecMissingEntitlement: A required entitlement is missing."
case .other:
"Unspecified Keychain error: \(status)."
case let .unknown(message):
@@ -216,6 +230,10 @@
/// See [errSecDecode](https://developer.apple.com/documentation/security/errsecdecode).
static let decodeFailed: KeychainError = .init(code: .decodeFailed)
+ /// A required entitlement is missing.
+ /// See [errSecMissingEntitlement](https://developer.apple.com/documentation/security/errsecmissingentitlement)
+ static let missingEntitlement: KeychainError = .init(code: .missingEntitlement)
+
/// Other Keychain error.
/// The `OSStatus` of the Keychain operation can be accessed via the ``status`` property.
static let other: KeychainError = .init(code: .other(status: 0))