From 6dd4d9ec8986d28655d4d4b96a9bf80588fc4c33 Mon Sep 17 00:00:00 2001 From: fhasse95 <49185957+fhasse95@users.noreply.github.com> Date: Sun, 4 Oct 2026 23:44:09 +0200 Subject: [PATCH] watchOS Adjustments --- Sources/SkipKit/BiometricAuthentication.swift | 10 +++++-- Sources/SkipKit/DeviceInfo.swift | 23 +++++++++++----- Sources/SkipKit/DocumentPicker.swift | 2 +- Sources/SkipKit/PermissionManager.swift | 27 +++++++++++++++++-- 4 files changed, 50 insertions(+), 12 deletions(-) diff --git a/Sources/SkipKit/BiometricAuthentication.swift b/Sources/SkipKit/BiometricAuthentication.swift index 7636cc6..83e9314 100644 --- a/Sources/SkipKit/BiometricAuthentication.swift +++ b/Sources/SkipKit/BiometricAuthentication.swift @@ -31,7 +31,9 @@ public enum BiometricAuthentication { // MARK: - Properties public static var authenticationType: BiometricAuthenticationType { - #if !SKIP + #if os(watchOS) + return .none + #elseif !SKIP let context = LAContext() var error: NSError? @@ -83,7 +85,11 @@ public enum BiometricAuthentication { allowsDeviceCredentialFallback: Bool = false, completion: @escaping @MainActor (BiometricAuthenticationResult) -> Void ) { - #if !SKIP + #if os(watchOS) + Task { @MainActor in + completion(.unavailable) + } + #elseif !SKIP let context = LAContext() let policy: LAPolicy = allowsDeviceCredentialFallback ? .deviceOwnerAuthentication diff --git a/Sources/SkipKit/DeviceInfo.swift b/Sources/SkipKit/DeviceInfo.swift index d91ee80..b9cd27c 100644 --- a/Sources/SkipKit/DeviceInfo.swift +++ b/Sources/SkipKit/DeviceInfo.swift @@ -2,6 +2,9 @@ // SPDX-License-Identifier: MPL-2.0 #if !SKIP_BRIDGE import Foundation +#if os(watchOS) +import WatchKit +#endif import SwiftUI #if SKIP @@ -14,7 +17,7 @@ import android.os.Build import android.util.DisplayMetrics import android.view.WindowManager #else -#if canImport(UIKit) +#if canImport(UIKit) && !os(watchOS) import UIKit #endif #if canImport(Network) @@ -89,7 +92,9 @@ public final class DeviceInfo { let context = ProcessInfo.processInfo.androidContext let dm = context.getResources().getDisplayMetrics() return Double(dm.widthPixels) / Double(dm.density) - #elseif canImport(UIKit) + #elseif os(watchOS) + return Double(WKInterfaceDevice.current().screenBounds.width) + #elseif canImport(UIKit) && !os(watchOS) return MainActor.assumeIsolated { Double(UIScreen.main.bounds.width) } #elseif os(macOS) return Double(NSScreen.main?.frame.width ?? 0) @@ -104,7 +109,9 @@ public final class DeviceInfo { let context = ProcessInfo.processInfo.androidContext let dm = context.getResources().getDisplayMetrics() return Double(dm.heightPixels) / Double(dm.density) - #elseif canImport(UIKit) + #elseif os(watchOS) + return Double(WKInterfaceDevice.current().screenBounds.height) + #elseif canImport(UIKit) && !os(watchOS) return MainActor.assumeIsolated { Double(UIScreen.main.bounds.height) } #elseif os(macOS) return Double(NSScreen.main?.frame.height ?? 0) @@ -118,7 +125,9 @@ public final class DeviceInfo { #if SKIP let context = ProcessInfo.processInfo.androidContext return Double(context.getResources().getDisplayMetrics().density) - #elseif canImport(UIKit) + #elseif os(watchOS) + return Double(WKInterfaceDevice.current().screenScale) + #elseif canImport(UIKit) && !os(watchOS) return MainActor.assumeIsolated { Double(UIScreen.main.scale) } #elseif os(macOS) return Double(NSScreen.main?.backingScaleFactor ?? 1.0) @@ -145,7 +154,7 @@ public final class DeviceInfo { } else { return .phone } - #elseif canImport(UIKit) + #elseif canImport(UIKit) && !os(watchOS) return MainActor.assumeIsolated { switch UIDevice.current.userInterfaceIdiom { case .phone: return .phone @@ -232,7 +241,7 @@ public final class DeviceInfo { let level = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY) if level < 0 { return nil } return Double(level) / 100.0 - #elseif canImport(UIKit) + #elseif canImport(UIKit) && !os(watchOS) return MainActor.assumeIsolated { let device = UIDevice.current let wasEnabled = device.isBatteryMonitoringEnabled @@ -261,7 +270,7 @@ public final class DeviceInfo { return level >= 100 ? .full : .charging } return .unplugged - #elseif canImport(UIKit) + #elseif canImport(UIKit) && !os(watchOS) return MainActor.assumeIsolated { let device = UIDevice.current let wasEnabled = device.isBatteryMonitoringEnabled diff --git a/Sources/SkipKit/DocumentPicker.swift b/Sources/SkipKit/DocumentPicker.swift index 39e64b7..9339543 100644 --- a/Sources/SkipKit/DocumentPicker.swift +++ b/Sources/SkipKit/DocumentPicker.swift @@ -1,6 +1,6 @@ // Copyright 2025–2026 Skip // SPDX-License-Identifier: MPL-2.0 -#if !SKIP_BRIDGE +#if !SKIP_BRIDGE && !os(watchOS) import Foundation import SwiftUI diff --git a/Sources/SkipKit/PermissionManager.swift b/Sources/SkipKit/PermissionManager.swift index e4d4256..55571bf 100644 --- a/Sources/SkipKit/PermissionManager.swift +++ b/Sources/SkipKit/PermissionManager.swift @@ -4,11 +4,13 @@ import Foundation import SwiftUI #if !SKIP +#if canImport(Photos) import Photos +#endif import AVFoundation // NOTE: CoreLocation is intentionally NOT imported; see the comment on LocationDelegate below import UserNotifications -import SystemConfiguration + #else import android.Manifest import android.os.Build @@ -143,6 +145,8 @@ public final class PermissionManager: Sendable { public static func queryCameraPermission() -> PermissionAuthorization { #if SKIP return queryPermission(.CAMERA) + #elseif os(watchOS) + return .restricted #else return queryAVPermission(for: .video) #endif @@ -152,6 +156,8 @@ public final class PermissionManager: Sendable { public static func requestCameraPermission() async -> PermissionAuthorization { #if SKIP return await requestPermission(.CAMERA) + #elseif os(watchOS) + return .restricted #else return await requestAVPermission(for: .video) #endif @@ -161,6 +167,13 @@ public final class PermissionManager: Sendable { public static func queryRecordAudioPermission() -> PermissionAuthorization { #if SKIP return queryPermission(.RECORD_AUDIO) + #elseif os(watchOS) + switch AVAudioSession.sharedInstance().recordPermission { + case .granted: return .authorized + case .denied: return .denied + case .undetermined: return .unknown + @unknown default: return .unknown + } #else return queryAVPermission(for: .audio) #endif @@ -170,12 +183,18 @@ public final class PermissionManager: Sendable { public static func requestRecordAudioPermission() async -> PermissionAuthorization { #if SKIP return await requestPermission(.RECORD_AUDIO) + #elseif os(watchOS) + return await withCheckedContinuation { continuation in + AVAudioSession.sharedInstance().requestRecordPermission { granted in + continuation.resume(returning: granted ? .authorized : .denied) + } + } #else return await requestAVPermission(for: .audio) #endif } - #if !SKIP + #if !SKIP && !os(watchOS) private static func queryAVPermission(for mediaType: AVMediaType) -> PermissionAuthorization { let status: AVAuthorizationStatus = AVCaptureDevice.authorizationStatus(for: mediaType) switch status { @@ -246,6 +265,8 @@ public final class PermissionManager: Sendable { public static func queryPhotoLibraryPermission(readWrite: Bool = true) -> PermissionAuthorization { #if SKIP return queryPermission(readWrite ? .WRITE_EXTERNAL_STORAGE : .READ_EXTERNAL_STORAGE) + #elseif os(watchOS) + return .restricted #else let status: PHAuthorizationStatus = PHPhotoLibrary.authorizationStatus(for: readWrite ? .readWrite : .addOnly) @@ -270,6 +291,8 @@ public final class PermissionManager: Sendable { public static func requestPhotoLibraryPermission(readWrite: Bool = true) async -> PermissionAuthorization { #if SKIP return await requestPermission(readWrite ? .WRITE_EXTERNAL_STORAGE : .READ_EXTERNAL_STORAGE) + #elseif os(watchOS) + return .restricted #else let status = queryPhotoLibraryPermission(readWrite: readWrite) if status != .unknown {