Skip to content
Open
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
4 changes: 2 additions & 2 deletions Binding/Binding/Model/DeviceBindingAuthenticationType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ public enum DeviceBindingAuthenticationType: String, Codable, Sendable {
func getAuthType(pinCollector: PinCollector? = nil) -> DeviceAuthenticator {
switch self {
case .biometricOnly:
return BiometricOnlyAuthenticator()
return BiometricOnlyAuthenticator(config: BiometricAuthenticatorConfig())
case .biometricAllowFallback:
return BiometricDeviceCredentialAuthenticator()
return BiometricDeviceCredentialAuthenticator(config: BiometricAuthenticatorConfig())
case .applicationPin:
fatalError("Application PIN is not supported on this platform.")
case .none:
Expand Down
4 changes: 4 additions & 0 deletions Browser/Browser/BrowserLauncher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
import Foundation
import AuthenticationServices
import PingLogger
#if canImport(UIKit)
import SafariServices
import Combine
import UIKit
#endif

// MARK: - Enums
/// BrowserType enum to specify the type of external user-agent;
Expand Down Expand Up @@ -65,6 +67,7 @@ public protocol BrowserLauncherProtocol: Sendable {

// MARK: - BrowserLauncher

#if canImport(UIKit)
/// BrowserLauncher class to launch external user-agent for web requests
@MainActor
public final class BrowserLauncher: NSObject, BrowserLauncherProtocol {
Expand Down Expand Up @@ -443,3 +446,4 @@ public final class OpenURLMonitor: NSObject {
return true
}
}
#endif
18 changes: 14 additions & 4 deletions DeviceProfile/DeviceProfile/Collectors/HardwareCollector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@
// HardwareCollector.swift
// DeviceProfile
//
// Copyright (c) 2025 Ping Identity Corporation. All rights reserved.
// Copyright (c) 2025 - 2026 Ping Identity Corporation. All rights reserved.
//
// This software may be modified and distributed under the terms
// of the MIT license. See the LICENSE file for details.
//

import Foundation
import UIKit
import AVFoundation
#if canImport(UIKit)
import UIKit
#endif

// MARK: - HardwareCollector

Expand Down Expand Up @@ -93,14 +95,18 @@ public struct HardwareInfo: Codable, Sendable {
/// - Values represent logical screen dimensions
@MainActor
private static func getDisplayInfo() -> [String: Int] {
#if canImport(UIKit)
let screenBounds = UIScreen.main.bounds
let isPortrait = UIDevice.current.orientation.isPortrait

return [
"width": Int(screenBounds.width),
"height": Int(screenBounds.height),
"orientation": isPortrait ? 1 : 0
]
#else
return [:]
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.

The macOS #else path returns [:] (empty dictionary) for display info. Since macOS is a build-only target for 3rd-party library compatibility and this code won't run in production, the empty return is fine — but a comment would help future readers distinguish intent from omission:

#else
// macOS: build target only for 3rd-party library compatibility — not a supported runtime platform
return [:]
#endif

#endif
}

/// Retrieves camera system information
Expand All @@ -119,6 +125,7 @@ public struct HardwareInfo: Codable, Sendable {
/// - May return 0 if camera access is restricted
/// - Includes specialty cameras (telephoto, ultra-wide, etc.)
private static func getCameraInfo() -> [String: Int] {
#if canImport(UIKit)
let discoverySession = AVCaptureDevice.DiscoverySession(
deviceTypes: [
.builtInTelephotoCamera,
Expand All @@ -128,8 +135,11 @@ public struct HardwareInfo: Codable, Sendable {
mediaType: .video,
position: .unspecified
)

let cameraCount = discoverySession.devices.count
return ["numberOfCameras": cameraCount]
#else
return [:]
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.

Same as the display info fallback above — macOS is a build-only target so the empty return is intentional. Worth adding the same comment here for consistency:

#else
// macOS: build target only for 3rd-party library compatibility — not a supported runtime platform
return [:]
#endif

#endif
}
}
15 changes: 12 additions & 3 deletions DeviceProfile/DeviceProfile/Collectors/PlatformCollector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@
// PlatformCollector.swift
// DeviceProfile
//
// Copyright (c) 2025 Ping Identity Corporation. All rights reserved.
// Copyright (c) 2025 - 2026 Ping Identity Corporation. All rights reserved.
//
// This software may be modified and distributed under the terms
// of the MIT license. See the LICENSE file for details.
//

import Foundation
import UIKit
import PingTamperDetector
#if canImport(UIKit)
import UIKit
#endif

// MARK: - PlatformCollector

Expand Down Expand Up @@ -72,15 +74,22 @@ public struct PlatformInfo: Codable, Sendable {

/// Initializes platform information by collecting system details
init() async {
#if canImport(UIKit)
self.platform = await UIDevice.current.systemName
self.version = await UIDevice.current.systemVersion
self.device = await UIDevice.current.model
self.deviceName = await UIDevice.current.name
#else
self.platform = "macOS"
self.version = ProcessInfo.processInfo.operatingSystemVersionString
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.

ProcessInfo.processInfo.operatingSystemVersionString returns a verbose string like "Version 13.0 (Build 22A380)", while the iOS path returns "16.0" via UIDevice.current.systemVersion. Since macOS is a build-only target this won't affect production, but if macOS is ever promoted or this data is sent to a server, the structural difference will break any version string parsing. Worth aligning the format now:

let v = ProcessInfo.processInfo.operatingSystemVersion
self.version = "\(v.majorVersion).\(v.minorVersion).\(v.patchVersion)"

self.device = "Mac"
self.deviceName = Host.current().localizedName ?? "Mac"
#endif
self.model = Self.getDeviceModel()
self.brand = "Apple"
self.locale = Locale.current.language.languageCode?.identifier
self.timeZone = TimeZone.current.identifier

self.jailBreakScore = await TamperDetector().analyze()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// TelephonyCollector.swift
// DeviceProfile
//
// Copyright (c) 2025 Ping Identity Corporation. All rights reserved.
// Copyright (c) 2025 - 2026 Ping Identity Corporation. All rights reserved.
//
// This software may be modified and distributed under the terms
// of the MIT license. See the LICENSE file for details.
Expand Down Expand Up @@ -61,17 +61,18 @@
/// - Selects the primary or most complete carrier information
/// - Prioritizes carriers with complete information
init() {
#if canImport(UIKit)
let networkInfo = CTTelephonyNetworkInfo()

var selectedCarrier: (carrierName: String?, isoCountryCode: String?)?

// Check for available cellular service providers
if let providers = networkInfo.serviceSubscriberCellularProviders,

Check warning on line 70 in DeviceProfile/DeviceProfile/Collectors/TelephonyCollector.swift

View workflow job for this annotation

GitHub Actions / Build and test / build-and-test

'serviceSubscriberCellularProviders' was deprecated in iOS 16.0: Deprecated with no replacement
!providers.isEmpty {

// Extract carrier information from all providers
let carriers = providers.map {
(carrierName: $0.value.carrierName, isoCountryCode: $0.value.isoCountryCode)

Check warning on line 75 in DeviceProfile/DeviceProfile/Collectors/TelephonyCollector.swift

View workflow job for this annotation

GitHub Actions / Build and test / build-and-test

'isoCountryCode' was deprecated in iOS 16.0: Deprecated; returns '--' at some point in the future

Check warning on line 75 in DeviceProfile/DeviceProfile/Collectors/TelephonyCollector.swift

View workflow job for this annotation

GitHub Actions / Build and test / build-and-test

'carrierName' was deprecated in iOS 16.0: Deprecated; returns '--' at some point in the future
}

// Select the best carrier using custom sorting logic
Expand All @@ -86,6 +87,10 @@
self.carrierName = DeviceProfileConstants.unknown
self.networkCountryIso = DeviceProfileConstants.unknown
}
#else
self.carrierName = DeviceProfileConstants.unknown
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.

Since macOS is a build-only target and this code won't run in production, returning DeviceProfileConstants.unknown is acceptable. A brief comment would clarify that this is intentional (not a missing implementation), and flag that nil would be more semantically accurate if macOS ever gets promoted to a real target — telephony doesn't exist on Mac at all, whereas "Unknown" implies data was collected but unreadable:

#else
// macOS: build target only — telephony not available on this platform
self.carrierName = DeviceProfileConstants.unknown
self.networkCountryIso = DeviceProfileConstants.unknown
#endif

self.networkCountryIso = DeviceProfileConstants.unknown
#endif
}
}

Expand Down
31 changes: 22 additions & 9 deletions DeviceProfile/DeviceProfile/LocationManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -249,27 +249,40 @@ public class LocationManager: NSObject, ObservableObject, @unchecked Sendable {
}

// Handle different authorization states
switch await authorizationStatus {
case .authorizedAlways, .authorizedWhenInUse:
// Already authorized, fetch location
let currentStatus = await authorizationStatus
let isAuthorized: Bool
#if canImport(UIKit)
isAuthorized = (currentStatus == .authorizedAlways || currentStatus == .authorizedWhenInUse)
#else
isAuthorized = (currentStatus == .authorizedAlways)
#endif
if isAuthorized {
return try await fetchLocationWithAuthorization()

}

switch currentStatus {
case .notDetermined:
// Need to request authorization first
let status = try await requestAuthorizationIfNeeded()
if status == .authorizedAlways || status == .authorizedWhenInUse {
let statusAuthorized: Bool
#if canImport(UIKit)
statusAuthorized = (status == .authorizedAlways || status == .authorizedWhenInUse)
#else
statusAuthorized = (status == .authorizedAlways)
#endif
if statusAuthorized {
return try await fetchLocationWithAuthorization()
} else {
throw authorizationErrorForStatus(status)
}

case .denied:
throw LocationError.authorizationDenied

case .restricted:
throw LocationError.authorizationRestricted
@unknown default:

default:
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.

The original code used @unknown default, which makes the compiler emit a warning if Apple adds a new CLAuthorizationStatus case in a future OS. This refactor replaces it with a plain default, silently swallowing any future enum cases (mapping them to authorizationDenied with no logging). The split into #if canImport(UIKit) for authorizedWhenInUse above is correct, but the switch itself should restore exhaustiveness protection:

@unknown default:
    throw LocationError.authorizationDenied

throw LocationError.authorizationDenied
}
}
Expand Down
2 changes: 2 additions & 0 deletions ExternalIdP/ExternalIdP/Handlers/BrowserHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import PingOrchestrate
import PingNetwork
import PingLogger

#if canImport(UIKit)
/// A handler class for managing browser-based Identity Provider (IdP) authorization.
@MainActor
public class BrowserHandler: IdpRequestHandler {
Expand Down Expand Up @@ -68,3 +69,4 @@ public class BrowserHandler: IdpRequestHandler {
}
}
}
#endif
4 changes: 4 additions & 0 deletions ExternalIdP/ExternalIdP/IdpClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
//

import Foundation
#if canImport(UIKit)
import UIKit
#endif

/// Represents the IdpClient struct. The IdpClient struct represents the client configuration for the IDP.
/// - property clientId: The client ID.
Expand All @@ -26,6 +28,7 @@ public struct IdpClient: Sendable {
public var continueUrl: String? = nil
}

#if canImport(UIKit)
extension IdpClient {
@MainActor
public static func getTopViewController() -> UIViewController? {
Expand All @@ -47,3 +50,4 @@ extension IdpClient {
return topController
}
}
#endif
4 changes: 4 additions & 0 deletions ExternalIdP/ExternalIdP/IdpCollector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ open class IdpCollector: NSObject, Collector, ContinueNodeAware, RequestIntercep
/// - url: The URL for the IdP authentication.
/// - Returns: A Result of type Bool or An IdpExceptions error.
public func fallbackToBrowserHandler(callbackURLScheme: String? = nil, url: URL) async -> Result<Bool, IdpExceptions> {
#if canImport(UIKit)
let urlScheme: String
if let customScheme = callbackURLScheme {
urlScheme = customScheme
Expand All @@ -200,6 +201,9 @@ open class IdpCollector: NSObject, Collector, ContinueNodeAware, RequestIntercep
} catch {
return .failure(.idpCanceledException(message: error.localizedDescription))
}
#else
return .failure(.illegalStateException(message: "Browser authentication is not supported on this platform"))
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.

Since macOS is a build-only target and browser-based IdP is iOS-only, returning a failure here is correct and intentional. Worth adding a comment so it's obvious this isn't a gap to fill:

#else
// macOS: build target only — browser-based IdP authentication requires UIKit and is not a supported use case
return .failure(.illegalStateException(message: "Browser authentication is not supported on this platform"))
#endif

#endif
}

/// Authorizes the IdP
Expand Down
4 changes: 3 additions & 1 deletion ExternalIdP/ExternalIdP/IdpValidationUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
// IdpValidationUtils.swift
// ExternalIdP
//
// Copyright (c) 2025 Ping Identity Corporation. All rights reserved.
// Copyright (c) 2025 - 2026 Ping Identity Corporation. All rights reserved.
//
// This software may be modified and distributed under the terms
// of the MIT license. See the LICENSE file for details.
//

import Foundation
#if canImport(UIKit)
import UIKit

/// Utility class for common IdP validation operations
Expand All @@ -35,6 +36,7 @@ public struct IdpValidationUtils {
}
}
}
#endif

/// Centralized error messages for IdP operations
public struct IdpErrorMessages {
Expand Down
2 changes: 2 additions & 0 deletions ExternalIdPApple/ExternalIdPApple/Handlers/AppleHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Foundation
import AuthenticationServices
import PingExternalIdP

#if canImport(UIKit)
///IdpHandler for Apple
@MainActor
@objc public final class AppleHandler: NSObject, @preconcurrency IdpHandler, Sendable {
Expand Down Expand Up @@ -50,3 +51,4 @@ import PingExternalIdP
extension AppleHandler {
public static let acceptsJSON = "acceptsJSON"
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import PingNetwork
import AuthenticationServices
import PingExternalIdP

#if canImport(UIKit)
///IdpHandler for Apple
@MainActor
@objc class AppleRequestHandler: NSObject, IdpRequestHandler {
Expand Down Expand Up @@ -74,3 +75,4 @@ import PingExternalIdP
throw IdpExceptions.illegalStateException(message: IdpErrorMessages.appleSignInFailed)
}
}
#endif
Loading
Loading