diff --git a/Package.swift b/Package.swift index 818b20253..2c5576e35 100644 --- a/Package.swift +++ b/Package.swift @@ -63,6 +63,7 @@ var package = Package( .target(name: "OktaClientMacros"), .product(name: "SwiftSyntaxMacros", package: "swift-syntax"), .product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax"), + .product(name: "SwiftSyntaxMacrosGenericTestSupport", package: "swift-syntax"), ]), .target(name: "OktaClientMacros", dependencies: [ @@ -72,9 +73,7 @@ var package = Package( path: "Sources/OktaClientMacros/Interface"), // Concurrency & locking - .target(name: "OktaConcurrency", - exclude: exclude(), - resources: include()), + .target(name: "OktaConcurrency"), .testTarget(name: "OktaConcurrencyTests", dependencies: [ .target(name: "OktaConcurrency"), @@ -90,9 +89,7 @@ var package = Package( dependencies: [ .target(name: "OktaConcurrency"), .target(name: "OktaClientMacros") - ], - exclude: exclude(), - resources: include()), + ]), .target(name: "KeychainTestCommon", dependencies: [ .target(name: "Keychain") @@ -110,9 +107,7 @@ var package = Package( dependencies: [ .target(name: "OktaConcurrency"), .target(name: "OktaClientMacros") - ], - exclude: exclude(), - resources: include()), + ]), .testTarget(name: "OktaUtilitiesTests", dependencies: [ .target(name: "OktaUtilities"), @@ -129,9 +124,7 @@ var package = Package( .target(name: "APIClient", dependencies: [ .target(name: "OktaUtilities"), - ], - exclude: exclude(), - resources: include()), + ]), .testTarget(name: "APIClientTests", dependencies: [ .target(name: "APIClient"), @@ -155,8 +148,7 @@ var package = Package( .target(name: "OktaConcurrency"), .target(name: "APIClient") ], - exclude: exclude(), - resources: include(.process("Resources"))), + resources: [ .process("Resources") ]), .testTarget(name: "JWTTests", dependencies: [ .target(name: "OktaConcurrency"), @@ -239,7 +231,9 @@ var package = Package( #if compiler(>=6) for target in package.targets where target.type != .system && target.type != .test { - target.swiftSettings = target.swiftSettings ?? [] + target.swiftSettings = target.swiftSettings ?? [ + .enableExperimentalFeature("StrictConcurrency=complete") + ] target.swiftSettings?.append(contentsOf: [ .enableExperimentalFeature("StrictConcurrency"), .enableUpcomingFeature("ExistentialAny"), diff --git a/Sources/APIClient/PrivacyInfo.xcprivacy b/Sources/APIClient/PrivacyInfo.xcprivacy deleted file mode 100644 index c6bdf9c11..000000000 --- a/Sources/APIClient/PrivacyInfo.xcprivacy +++ /dev/null @@ -1,14 +0,0 @@ - - - - - NSPrivacyAccessedAPITypes - - NSPrivacyTracking - - NSPrivacyTrackingDomains - - NSPrivacyCollectedDataTypes - - - diff --git a/Sources/AuthFoundation/Migration/Migrators/OIDCLegacyMigrator.swift b/Sources/AuthFoundation/Migration/Migrators/OIDCLegacyMigrator.swift index bd517cb84..b29ce00b5 100644 --- a/Sources/AuthFoundation/Migration/Migrators/OIDCLegacyMigrator.swift +++ b/Sources/AuthFoundation/Migration/Migrators/OIDCLegacyMigrator.swift @@ -15,7 +15,7 @@ import OktaUtilities import Keychain import JWT -#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) || os(visionOS) +#if canImport(Darwin) private let accountIdRegex = try? NSRegularExpression(pattern: "0oa[0-9a-zA-Z]{17}") diff --git a/Sources/AuthFoundation/Token Management/Internal/KeychainTokenStorage.swift b/Sources/AuthFoundation/Token Management/Internal/KeychainTokenStorage.swift index 1a0015d6f..467bc11d4 100644 --- a/Sources/AuthFoundation/Token Management/Internal/KeychainTokenStorage.swift +++ b/Sources/AuthFoundation/Token Management/Internal/KeychainTokenStorage.swift @@ -10,7 +10,7 @@ // See the License for the specific language governing permissions and limitations under the License. // -#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) || os(visionOS) +#if canImport(Darwin) import Foundation import Keychain @@ -56,6 +56,10 @@ final class KeychainTokenStorage: TokenStorage { _defaultTokenID = id try saveDefault() + + DispatchQueue.global().async { + self.delegate?.token(storage: self, defaultChanged: id) + } } var defaultTokenID: String? { diff --git a/Sources/AuthFoundation/Token Management/Internal/UserDefaultsTokenStorage.swift b/Sources/AuthFoundation/Token Management/Internal/UserDefaultsTokenStorage.swift index 257f67eb5..e8b040fce 100644 --- a/Sources/AuthFoundation/Token Management/Internal/UserDefaultsTokenStorage.swift +++ b/Sources/AuthFoundation/Token Management/Internal/UserDefaultsTokenStorage.swift @@ -78,6 +78,10 @@ final class UserDefaultsTokenStorage: TokenStorage { userDefaults.removeObject(forKey: UserDefaultsKeys.defaultTokenKey) } userDefaults.synchronize() + + DispatchQueue.global().async { + self.delegate?.token(storage: self, defaultChanged: id) + } } var defaultTokenID: String? { diff --git a/Sources/AuthFoundation/Token Management/TokenStorage.swift b/Sources/AuthFoundation/Token Management/TokenStorage.swift index 71cfb8f70..4800d7e49 100644 --- a/Sources/AuthFoundation/Token Management/TokenStorage.swift +++ b/Sources/AuthFoundation/Token Management/TokenStorage.swift @@ -67,6 +67,9 @@ public protocol TokenStorage: Sendable { /// Protocol that custom ``TokenStorage`` instances are required to communicate changes to. public protocol TokenStorageDelegate: AnyObject, Sendable { + /// Sent when the default token has been changed. + func token(storage: any TokenStorage, defaultChanged id: String?) + /// Sent when a new token has been added. /// /// > Important: This message should only be sent when a token is actually new. If the token is semantically identical to another one already in storage, the ``token(storage:replaced:with:)`` message should be sent instead. diff --git a/Sources/AuthFoundation/User Management/CredentialSecurity+StaticProperties.swift b/Sources/AuthFoundation/User Management/CredentialSecurity+StaticProperties.swift index 472b7c7be..6a29a9b15 100644 --- a/Sources/AuthFoundation/User Management/CredentialSecurity+StaticProperties.swift +++ b/Sources/AuthFoundation/User Management/CredentialSecurity+StaticProperties.swift @@ -18,7 +18,7 @@ import OktaClientMacros fileprivate let staticLock = Lock() nonisolated(unsafe) fileprivate var _isDefaultSynchronizable: Bool = false -#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) || os(visionOS) +#if canImport(Darwin) nonisolated(unsafe) fileprivate var _standard: [Credential.Security] = [.accessibility(.afterFirstUnlockThisDeviceOnly)] #else nonisolated(unsafe) fileprivate var _standard: [Credential.Security] = [] diff --git a/Sources/AuthFoundation/User Management/CredentialSecurity.swift b/Sources/AuthFoundation/User Management/CredentialSecurity.swift index e0280f17e..dce54932d 100644 --- a/Sources/AuthFoundation/User Management/CredentialSecurity.swift +++ b/Sources/AuthFoundation/User Management/CredentialSecurity.swift @@ -27,7 +27,7 @@ extension Credential { /// /// On Apple platforms, this controls the Keychain security settings for the underlying token's keychain item. public enum Security { - #if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) || os(visionOS) + #if canImport(Darwin) /// Defines the accessibility level for a credential. case accessibility(_ option: Keychain.Accessibility) diff --git a/Sources/AuthFoundation/User Management/Internal/CredentialCoordinatorImpl.swift b/Sources/AuthFoundation/User Management/Internal/CredentialCoordinatorImpl.swift index af77e15ed..e5a192ca8 100644 --- a/Sources/AuthFoundation/User Management/Internal/CredentialCoordinatorImpl.swift +++ b/Sources/AuthFoundation/User Management/Internal/CredentialCoordinatorImpl.swift @@ -111,7 +111,7 @@ final class CredentialCoordinatorImpl: Sendable, CredentialCoordinator { } static func defaultTokenStorage() -> any TokenStorage { - #if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) || os(visionOS) + #if canImport(Darwin) KeychainTokenStorage() #else UserDefaultsTokenStorage() @@ -216,23 +216,17 @@ extension CredentialCoordinatorImpl: OAuth2ClientDelegate { } extension CredentialCoordinatorImpl: TokenStorageDelegate { -// func token(storage: any TokenStorage, defaultTokenChanged token: Token?) { -// withLock { -// // Ensure the default matches what the Token Storage says, -// // and that the new token is indeed different from the local default. -// guard _tokenStorage.defaultTokenID == token?.id, -// _default?.id != token?.id -// else { -// return -// } -// -// if let token = token { -// _default = _credentialDataSource.credential(for: token, coordinator: self) -// } else { -// _default = nil -// } -// } -// } + func token(storage: any TokenStorage, defaultChanged id: String?) { + // Almost all default ID changes will be triggered from the Credential Coordinator. + // In the rare event that the underlying value is changed from some other means, + // this operation will ensure the statei of this class will remain consistent. + withLock { + // Ensure the new token is indeed different from the local default. + if _default?.id != id { + _default = nil + } + } + } func token(storage: any TokenStorage, added id: String, token: Token) { } @@ -243,7 +237,6 @@ extension CredentialCoordinatorImpl: TokenStorageDelegate { func token(storage: any TokenStorage, replaced id: String, with newToken: Token) { // Doing nothing with this, for now... } - } extension CredentialCoordinatorImpl: CredentialDataSourceDelegate { diff --git a/Sources/AuthFoundation/User Management/Internal/CredentialSecurity+Internal.swift b/Sources/AuthFoundation/User Management/Internal/CredentialSecurity+Internal.swift index 32049ecec..6d3cb3d89 100644 --- a/Sources/AuthFoundation/User Management/Internal/CredentialSecurity+Internal.swift +++ b/Sources/AuthFoundation/User Management/Internal/CredentialSecurity+Internal.swift @@ -18,7 +18,7 @@ import LocalAuthentication #endif extension Array where Element == Credential.Security { - #if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) || os(visionOS) + #if canImport(Darwin) #if canImport(LocalAuthentication) && !os(tvOS) var context: LAContext? { for case let Credential.Security.context(value) in self { diff --git a/Sources/JWT/Enums/JWTClaim.swift b/Sources/JWT/Enums/JWTClaim.swift index b21011c47..0ff025d6b 100644 --- a/Sources/JWT/Enums/JWTClaim.swift +++ b/Sources/JWT/Enums/JWTClaim.swift @@ -277,7 +277,7 @@ public extension HasClaims where ClaimType == JWTClaim { /// The person's preferred username. var preferredUsername: String? { self[.preferredUsername] } -#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) || os(visionOS) +#if canImport(Darwin) /// The person's name components, pre-assigned to a PersonNameComponents object. /// /// This property can be used as a convenience to generate a string representation of the user's name, based on the user's current locale. diff --git a/Sources/JWT/PrivacyInfo.xcprivacy b/Sources/JWT/PrivacyInfo.xcprivacy deleted file mode 100644 index c6bdf9c11..000000000 --- a/Sources/JWT/PrivacyInfo.xcprivacy +++ /dev/null @@ -1,14 +0,0 @@ - - - - - NSPrivacyAccessedAPITypes - - NSPrivacyTracking - - NSPrivacyTrackingDomains - - NSPrivacyCollectedDataTypes - - - diff --git a/Sources/Keychain/Internal/Keychain+Extensions.swift b/Sources/Keychain/Internal/Keychain+Extensions.swift index 143f83e5c..2087a45e9 100644 --- a/Sources/Keychain/Internal/Keychain+Extensions.swift +++ b/Sources/Keychain/Internal/Keychain+Extensions.swift @@ -10,7 +10,7 @@ // See the License for the specific language governing permissions and limitations under the License. // -#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) || os(visionOS) +#if canImport(Darwin) @preconcurrency import Foundation diff --git a/Sources/Keychain/Internal/KeychainProtocol.swift b/Sources/Keychain/Internal/KeychainProtocol.swift index 89d45bc15..7bbd4914c 100644 --- a/Sources/Keychain/Internal/KeychainProtocol.swift +++ b/Sources/Keychain/Internal/KeychainProtocol.swift @@ -10,7 +10,7 @@ // See the License for the specific language governing permissions and limitations under the License. // -#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) || os(visionOS) +#if canImport(Darwin) import Foundation diff --git a/Sources/Keychain/Keychain.swift b/Sources/Keychain/Keychain.swift index 46e176b02..806e3849d 100644 --- a/Sources/Keychain/Keychain.swift +++ b/Sources/Keychain/Keychain.swift @@ -14,7 +14,7 @@ import Foundation import OktaConcurrency import OktaClientMacros -#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) || os(visionOS) +#if canImport(Darwin) #if canImport(LocalAuthentication) && !os(tvOS) import LocalAuthentication diff --git a/Sources/Keychain/KeychainError.swift b/Sources/Keychain/KeychainError.swift index 884e5988a..baa118cfb 100644 --- a/Sources/Keychain/KeychainError.swift +++ b/Sources/Keychain/KeychainError.swift @@ -12,7 +12,7 @@ import Foundation -#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) || os(visionOS) +#if canImport(Darwin) /// Describes errors that may occur when interacting with the keychain. public enum KeychainError: Error { diff --git a/Sources/OktaConcurrency/PrivacyInfo.xcprivacy b/Sources/OktaConcurrency/PrivacyInfo.xcprivacy deleted file mode 100644 index c6bdf9c11..000000000 --- a/Sources/OktaConcurrency/PrivacyInfo.xcprivacy +++ /dev/null @@ -1,14 +0,0 @@ - - - - - NSPrivacyAccessedAPITypes - - NSPrivacyTracking - - NSPrivacyTrackingDomains - - NSPrivacyCollectedDataTypes - - - diff --git a/Sources/OktaUtilities/PrivacyInfo.xcprivacy b/Sources/OktaUtilities/PrivacyInfo.xcprivacy deleted file mode 100644 index c6bdf9c11..000000000 --- a/Sources/OktaUtilities/PrivacyInfo.xcprivacy +++ /dev/null @@ -1,14 +0,0 @@ - - - - - NSPrivacyAccessedAPITypes - - NSPrivacyTracking - - NSPrivacyTrackingDomains - - NSPrivacyCollectedDataTypes - - - diff --git a/Tests/AuthFoundationTests/CredentialCoordinatorTests.swift b/Tests/AuthFoundationTests/CredentialCoordinatorTests.swift index f7c3a4d8c..be5933558 100644 --- a/Tests/AuthFoundationTests/CredentialCoordinatorTests.swift +++ b/Tests/AuthFoundationTests/CredentialCoordinatorTests.swift @@ -19,7 +19,7 @@ import FoundationNetworking @testable import TestCommon @testable import AuthFoundation -final class UserCoordinatorTests: XCTestCase { +final class CredentialCoordinatorTests: XCTestCase { var userDefaults: UserDefaults! var storage: UserDefaultsTokenStorage! var coordinator: CredentialCoordinatorImpl! @@ -80,10 +80,31 @@ final class UserCoordinatorTests: XCTestCase { } func testImplicitCredentialForToken() throws { + XCTAssertNil(storage.defaultTokenID) + XCTAssertNil(coordinator.default) + let credential = try coordinator.store(token: token, security: []) XCTAssertEqual(storage.allIDs, [token.id]) XCTAssertEqual(storage.defaultTokenID, token.id) XCTAssertEqual(coordinator.default, credential) + + XCTAssertNoThrow(try credential.remove()) + XCTAssertNil(storage.defaultTokenID) + XCTAssertNil(coordinator.default) + } + + func testRespondToStorageDelegate() throws { + coordinator.default = try coordinator.store(token: token, security: []) + + XCTAssertEqual(storage.allIDs, [token.id]) + XCTAssertEqual(storage.defaultTokenID, token.id) + XCTAssertEqual(coordinator.default?.id, token.id) + + XCTAssertNoThrow(try storage.remove(id: token.id)) + XCTAssertNil(storage.defaultTokenID) + + sleep(for: .standard) + XCTAssertNil(coordinator.default) } } diff --git a/Tests/AuthFoundationTests/CredentialNotificationTests.swift b/Tests/AuthFoundationTests/CredentialNotificationTests.swift index 0922fd12e..eb54f2173 100644 --- a/Tests/AuthFoundationTests/CredentialNotificationTests.swift +++ b/Tests/AuthFoundationTests/CredentialNotificationTests.swift @@ -57,25 +57,22 @@ final class CredentialNotificationTests: XCTestCase { } func testNotifications() throws { - let wait = expectation(description: "Main actor") + let oldCredential = coordinator.default - Task { @MainActor in - let oldCredential = coordinator.default - - let recorder = NotificationRecorder(observing: [.defaultCredentialChanged]) - - let credential = try coordinator.store(token: token, security: []) - self.wait(for: .standard) - XCTAssertEqual(recorder.notifications.count, 1) - XCTAssertEqual(recorder.notifications.first?.object as? Credential, credential) - XCTAssertNotEqual(oldCredential, credential) - - recorder.reset() - coordinator.default = nil - self.wait(for: .standard) - XCTAssertEqual(recorder.notifications.count, 1) - XCTAssertNil(recorder.notifications.first?.object) - } - waitForExpectations(timeout: .long) + let recorder = NotificationRecorder(observing: [.defaultCredentialChanged]) + + let credential = try coordinator.store(token: token, security: []) + sleep(for: .short) + + XCTAssertEqual(recorder.notifications.count, 1) + XCTAssertEqual(recorder.notifications.first?.object as? Credential, credential) + XCTAssertNotEqual(oldCredential, credential) + + recorder.reset() + coordinator.default = nil + sleep(for: .short) + + XCTAssertEqual(recorder.notifications.count, 1) + XCTAssertNil(recorder.notifications.first?.object) } } diff --git a/Tests/AuthFoundationTests/CredentialRefreshTests.swift b/Tests/AuthFoundationTests/CredentialRefreshTests.swift index 0c13ab780..32893bbc8 100644 --- a/Tests/AuthFoundationTests/CredentialRefreshTests.swift +++ b/Tests/AuthFoundationTests/CredentialRefreshTests.swift @@ -310,7 +310,7 @@ final class CredentialRefreshTests: XCTestCase, OAuth2ClientDelegate { // Stopping should prevent subsequent refreshes credential.automaticRefresh = false - sleep(1) + sleep(for: .short) XCTAssertEqual(urlSession.requests.count, 0) } diff --git a/Tests/AuthFoundationTests/KeychainTokenStorageTests.swift b/Tests/AuthFoundationTests/KeychainTokenStorageTests.swift index 1c8c4a9d8..54dd9f905 100644 --- a/Tests/AuthFoundationTests/KeychainTokenStorageTests.swift +++ b/Tests/AuthFoundationTests/KeychainTokenStorageTests.swift @@ -10,7 +10,7 @@ // See the License for the specific language governing permissions and limitations under the License. // -#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) || os(visionOS) +#if canImport(Darwin) import XCTest @testable import AuthFoundation diff --git a/Tests/AuthFoundationTests/OIDCLegacyMigratorTests.swift b/Tests/AuthFoundationTests/OIDCLegacyMigratorTests.swift index 4d6deacf0..9422204eb 100644 --- a/Tests/AuthFoundationTests/OIDCLegacyMigratorTests.swift +++ b/Tests/AuthFoundationTests/OIDCLegacyMigratorTests.swift @@ -18,7 +18,7 @@ import XCTest @testable import Keychain @testable import KeychainTestCommon -#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) || os(visionOS) +#if canImport(Darwin) final class OIDCLegacyMigratorTests: XCTestCase { typealias LegacyOIDC = SDKVersion.Migration.LegacyOIDC diff --git a/Tests/AuthFoundationTests/UserDefaultsTokenStorageTests.swift b/Tests/AuthFoundationTests/UserDefaultsTokenStorageTests.swift index 8d85a2294..658ab23e2 100644 --- a/Tests/AuthFoundationTests/UserDefaultsTokenStorageTests.swift +++ b/Tests/AuthFoundationTests/UserDefaultsTokenStorageTests.swift @@ -10,7 +10,7 @@ // See the License for the specific language governing permissions and limitations under the License. // -#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) || os(visionOS) +#if canImport(Darwin) import XCTest @testable import AuthFoundation import TestCommon @@ -73,7 +73,7 @@ final class UserDefaultTokenStorageTests: XCTestCase { XCTAssertThrowsError(try storage.add(token: token, security: [])) XCTAssertEqual(storage.allIDs.count, 1) - XCTAssertNoThrow(try storage.update(token: newToken, security: nil)) + XCTAssertThrowsError(try storage.update(token: newToken, security: nil)) XCTAssertEqual(storage.allIDs.count, 1) XCTAssertNoThrow(try storage.remove(id: token.id)) @@ -85,11 +85,12 @@ final class UserDefaultTokenStorageTests: XCTestCase { func testImplicitDefaultToken() throws { XCTAssertNil(storage.defaultTokenID) - + XCTAssertTrue(storage.allIDs.isEmpty) + XCTAssertNoThrow(try storage.add(token: token, security: [])) XCTAssertEqual(storage.allIDs.count, 1) - XCTAssertEqual(storage.defaultTokenID, token.id) + XCTAssertNil(storage.defaultTokenID) } func testRemoveDefaultToken() throws { diff --git a/Tests/AuthFoundationTests/UserInfoTests.swift b/Tests/AuthFoundationTests/UserInfoTests.swift index 55783fb7f..af318205f 100644 --- a/Tests/AuthFoundationTests/UserInfoTests.swift +++ b/Tests/AuthFoundationTests/UserInfoTests.swift @@ -30,7 +30,7 @@ final class UserInfoTests: XCTestCase { XCTAssertTrue(info.emailVerified!) XCTAssertEqual(info.address?["street_address"], "155 Country Lane") - #if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) || os(visionOS) + #if canImport(Darwin) if #available(iOS 15, macCatalyst 15, macOS 12.0, tvOS 15, watchOS 8, *) { let formatter = PersonNameComponentsFormatter() formatter.style = .long diff --git a/Tests/KeychainTestCommon/MockKeychain.swift b/Tests/KeychainTestCommon/MockKeychain.swift index 6728c7a1b..478952184 100644 --- a/Tests/KeychainTestCommon/MockKeychain.swift +++ b/Tests/KeychainTestCommon/MockKeychain.swift @@ -10,7 +10,7 @@ // See the License for the specific language governing permissions and limitations under the License. // -#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) || os(visionOS) +#if canImport(Darwin) import Foundation @testable import Keychain diff --git a/Tests/KeychainTests/KeychainErrorTests.swift b/Tests/KeychainTests/KeychainErrorTests.swift index 6bd020487..64f5a1e43 100644 --- a/Tests/KeychainTests/KeychainErrorTests.swift +++ b/Tests/KeychainTests/KeychainErrorTests.swift @@ -15,7 +15,7 @@ import XCTest final class KeychainErrorTests: XCTestCase { func testKeychainError() { - #if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) || os(visionOS) + #if canImport(Darwin) XCTAssertNotEqual(KeychainError.cannotGet(code: noErr).errorDescription, "keychain_cannot_get") XCTAssertNotEqual(KeychainError.cannotList(code: noErr).errorDescription, diff --git a/Tests/KeychainTests/KeychainTests.swift b/Tests/KeychainTests/KeychainTests.swift index f4ab7e019..3ce7309df 100644 --- a/Tests/KeychainTests/KeychainTests.swift +++ b/Tests/KeychainTests/KeychainTests.swift @@ -10,7 +10,7 @@ // See the License for the specific language governing permissions and limitations under the License. // -#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) || os(visionOS) +#if canImport(Darwin) import XCTest @testable import Keychain diff --git a/Tests/OktaDirectAuthTests/ErrorTests.swift b/Tests/OktaDirectAuthTests/ErrorTests.swift index d0cd956ec..7c509add2 100644 --- a/Tests/OktaDirectAuthTests/ErrorTests.swift +++ b/Tests/OktaDirectAuthTests/ErrorTests.swift @@ -67,7 +67,7 @@ final class ErrorTests: XCTestCase { XCTAssertEqual(DirectAuthenticationFlowError(APIClientError.serverError(serverError)), .server(error: serverError)) - #if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) || os(visionOS) + #if canImport(Darwin) // Ensure a generic error embedded in OAuth2Error becomes the appropriate error type XCTAssertEqual(DirectAuthenticationFlowError(OAuth2Error.error(KeychainError.invalidFormat)), .other(error: KeychainError.invalidFormat)) diff --git a/Tests/TestCommon/XCTestCase+Extensions.swift b/Tests/TestCommon/XCTestCase+Extensions.swift index e0726dd92..49e26a712 100644 --- a/Tests/TestCommon/XCTestCase+Extensions.swift +++ b/Tests/TestCommon/XCTestCase+Extensions.swift @@ -104,12 +104,11 @@ public extension XCTestCase { _ = group.wait(timeout: .short) } - @MainActor - func wait(for interval: TimeInterval) { - let waitExpectation = expectation(description: "Wait for \(interval)s") - DispatchQueue.main.asyncAfter(deadline: .now() + interval) { - waitExpectation.fulfill() + func sleep(for duration: TimeInterval) { + let sleepExpectation = expectation(description: "Sleep for \(duration) seconds") + DispatchQueue.main.asyncAfter(deadline: .now() + duration) { + sleepExpectation.fulfill() } - waitForExpectations(timeout: interval * 1.5) + wait(for: [sleepExpectation]) } } diff --git a/Tests/WebAuthenticationUITests/WebAuthenticationFlowTests.swift b/Tests/WebAuthenticationUITests/WebAuthenticationFlowTests.swift index f8e11ed4d..ca86964a7 100644 --- a/Tests/WebAuthenticationUITests/WebAuthenticationFlowTests.swift +++ b/Tests/WebAuthenticationUITests/WebAuthenticationFlowTests.swift @@ -53,7 +53,7 @@ class WebAuthenticationUITests: XCTestCase { let webAuth = WebAuthentication(loginFlow: loginFlow, logoutFlow: logoutFlow) webAuth.signIn(from: nil, options: [.state("qwe")]) { _ in } - wait(for: .short) + sleep(for: .short) let webAuthProvider = try XCTUnwrap(webAuth.provider as? WebAuthenticationProviderMock) @@ -65,7 +65,7 @@ class WebAuthenticationUITests: XCTestCase { let webAuth = WebAuthentication(loginFlow: loginFlow, logoutFlow: logoutFlow) webAuth.signOut(from: nil, token: "idToken", options: [.state("qwe")]) { _ in } - wait(for: .short) + sleep(for: .short) let provider = try XCTUnwrap(webAuth.provider as? WebAuthenticationProviderMock) XCTAssertNil(webAuth.completionBlock) @@ -78,7 +78,7 @@ class WebAuthenticationUITests: XCTestCase { XCTAssertNil(webAuth.provider) webAuth.signIn(from: nil, options: [.state("qwe")]) { _ in } - wait(for: .short) + sleep(for: .short) let webAuthProvider = try XCTUnwrap(webAuth.provider as? WebAuthenticationProviderMock)