From e50505ad306eaa8d10ae11bd898f33cbd6cc8dfd Mon Sep 17 00:00:00 2001 From: Brian Drelling Date: Fri, 1 Mar 2024 17:43:10 -0600 Subject: [PATCH] Fixed cross-platform compilation issues --- Examples/StormView.swift | 6 +++--- .../Extensions/Font+Convenience.swift | 12 ++++++++++++ Sources/KippleFont/Views/FontIterator.swift | 2 +- ...ft => View+NavigationBarConvenience.swift} | 19 ++++++++++++++----- .../Kipple/String+CopyToClipboard.swift | 10 +++++++++- Sources/KippleUI/Kipple/View+Copying.swift | 7 +++++-- .../KippleUI/Kipple/View+PreventDimming.swift | 2 +- .../ViewRepresentable+StaticPreview.swift | 2 +- .../View+NavigationBarBackground.swift | 6 ++++++ .../Core/ViewModifiers/View+Snapshot.swift | 19 ++++++++++++++----- .../SwiftUI/Core/Views/BackButton.swift | 4 +++- .../KippleUI/SwiftUI/Core/Views/Modal.swift | 6 ++++++ .../SwiftUI/Core/Views/NavigatorStack.swift | 14 +++++++------- .../Haptics/Helpers/HapticGenerator.swift | 4 ++++ 14 files changed, 86 insertions(+), 27 deletions(-) rename Sources/KippleUI/AppKit/CrossPlatformSupport/{View+NavigationBarTitleDisplayMode.swift => View+NavigationBarConvenience.swift} (56%) diff --git a/Examples/StormView.swift b/Examples/StormView.swift index 822c3fe..406a8df 100644 --- a/Examples/StormView.swift +++ b/Examples/StormView.swift @@ -2,7 +2,7 @@ import SwiftUI -@available(iOS 17, macOS 14, tvOS 14, watchOS 10, *) +@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) private struct StormView: View { @State private var storm = Storm() let rainColor = Color(red: 0.25, green: 0.5, blue: 0.75) @@ -31,7 +31,7 @@ private struct Raindrop: Hashable, Equatable { var speed: Double } -@available(iOS 17, macOS 14, tvOS 14, watchOS 10, *) +@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Observable private class Storm { var drops = Set() @@ -44,7 +44,7 @@ private class Storm { // MARK: - Previews -@available(iOS 17, macOS 14, tvOS 14, watchOS 10, *) +@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) struct StormView_Previews: PreviewProvider { static var previews: some View { StormView() diff --git a/Sources/KippleFont/Extensions/Font+Convenience.swift b/Sources/KippleFont/Extensions/Font+Convenience.swift index 29cdefc..745d057 100644 --- a/Sources/KippleFont/Extensions/Font+Convenience.swift +++ b/Sources/KippleFont/Extensions/Font+Convenience.swift @@ -56,6 +56,18 @@ public extension UXFont { static func printNamesAndVariations() { familyNamesAndVariations.forEach { print($0) } } + + /// A wrapper around `familyName` that converts to a `String` on all platforms. + /// + /// This is necessary because `NSFont.familyName` is optional, but `UIFont.familyName` is not. + /// On macOS, if a family name is not found, a value of `"Unknown"` is returned. + var safeFamilyName: String { + #if canImport(UIKit) + self.familyName + #else + self.familyName ?? "Unknown" + #endif + } } // MARK: - Platform Conformance diff --git a/Sources/KippleFont/Views/FontIterator.swift b/Sources/KippleFont/Views/FontIterator.swift index a1d34e9..918bb2e 100644 --- a/Sources/KippleFont/Views/FontIterator.swift +++ b/Sources/KippleFont/Views/FontIterator.swift @@ -8,7 +8,7 @@ public struct FontIterator: View where Content: View { public var body: some View { ForEach(self.fonts, id: \.self) { font in - self.content(Font(font), font.familyName ?? "Unknown") + self.content(Font(font), font.safeFamilyName) } } diff --git a/Sources/KippleUI/AppKit/CrossPlatformSupport/View+NavigationBarTitleDisplayMode.swift b/Sources/KippleUI/AppKit/CrossPlatformSupport/View+NavigationBarConvenience.swift similarity index 56% rename from Sources/KippleUI/AppKit/CrossPlatformSupport/View+NavigationBarTitleDisplayMode.swift rename to Sources/KippleUI/AppKit/CrossPlatformSupport/View+NavigationBarConvenience.swift index 95b2a32..4e42345 100644 --- a/Sources/KippleUI/AppKit/CrossPlatformSupport/View+NavigationBarTitleDisplayMode.swift +++ b/Sources/KippleUI/AppKit/CrossPlatformSupport/View+NavigationBarConvenience.swift @@ -2,8 +2,7 @@ import SwiftUI -#if os(macOS) - +#if os(macOS) || os(tvOS) || os(watchOS) public enum PlatformSafeTitleDisplayMode { case automatic case inline @@ -11,10 +10,10 @@ public enum PlatformSafeTitleDisplayMode { @available(tvOS, unavailable) case large } +#endif + +#if os(macOS) -@available(iOS, unavailable) -@available(watchOS, unavailable) -@available(tvOS, unavailable) public extension View { func navigationBarTitleDisplayMode(_: PlatformSafeTitleDisplayMode) -> some View { self @@ -22,3 +21,13 @@ public extension View { } #endif + +#if os(tvOS) || os(watchOS) + +extension View { + public func navigationBarTitle(_ titleKey: LocalizedStringKey, displayMode: NavigationBarItem.TitleDisplayMode) -> some View { + self.navigationTitle(titleKey) + } +} + +#endif diff --git a/Sources/KippleUI/Kipple/String+CopyToClipboard.swift b/Sources/KippleUI/Kipple/String+CopyToClipboard.swift index f206ed0..ac8af3b 100644 --- a/Sources/KippleUI/Kipple/String+CopyToClipboard.swift +++ b/Sources/KippleUI/Kipple/String+CopyToClipboard.swift @@ -1,6 +1,6 @@ // Copyright © 2024 Brian Drelling. All rights reserved. -#if canImport(UIKit) +#if os(iOS) || os(visionOS) import UIKit @@ -23,4 +23,12 @@ public extension String { } } +#else + +public extension String { + func copyToClipboard() { + print("WARNING: String.copyToClipboard is only available on iOS, macOS, and visionOS.") + } +} + #endif diff --git a/Sources/KippleUI/Kipple/View+Copying.swift b/Sources/KippleUI/Kipple/View+Copying.swift index 104c41b..2316883 100644 --- a/Sources/KippleUI/Kipple/View+Copying.swift +++ b/Sources/KippleUI/Kipple/View+Copying.swift @@ -9,10 +9,13 @@ private struct CopyingModifier: ViewModifier { private let text: String - #if canImport(UIKit) + #if os(iOS) private let backgroundColor: Color = .init(uiColor: .secondarySystemBackground) - #else + #elseif os(macOS) private let backgroundColor: Color = .init(nsColor: .windowBackgroundColor) + #else + #warning("backgroundColor is not defined on tvOS and watchOS!") + private let backgroundColor: Color = .init(uiColor: .clear) #endif func body(content: Content) -> some View { diff --git a/Sources/KippleUI/Kipple/View+PreventDimming.swift b/Sources/KippleUI/Kipple/View+PreventDimming.swift index ee5ebd2..d6d0b70 100644 --- a/Sources/KippleUI/Kipple/View+PreventDimming.swift +++ b/Sources/KippleUI/Kipple/View+PreventDimming.swift @@ -7,7 +7,7 @@ public extension View { /// When the `View` disappears, the screen is able to dim again. @ViewBuilder func preventScreenDimming() -> some View { - #if canImport(UIKit) + #if canImport(UIKit) && (os(iOS) || os(tvOS) || os(visionOS)) self.onAppear { UIApplication.shared.isIdleTimerDisabled = true } diff --git a/Sources/KippleUI/Kipple/ViewRepresentable+StaticPreview.swift b/Sources/KippleUI/Kipple/ViewRepresentable+StaticPreview.swift index 21efcc4..f059524 100644 --- a/Sources/KippleUI/Kipple/ViewRepresentable+StaticPreview.swift +++ b/Sources/KippleUI/Kipple/ViewRepresentable+StaticPreview.swift @@ -1,6 +1,6 @@ // Copyright © 2024 Brian Drelling. All rights reserved. -#if canImport(UIKit) +#if canImport(UIKit) && (os(iOS) || os(tvOS)) import SwiftUI diff --git a/Sources/KippleUI/SwiftUI/Core/ViewModifiers/View+NavigationBarBackground.swift b/Sources/KippleUI/SwiftUI/Core/ViewModifiers/View+NavigationBarBackground.swift index bc4a00e..64f4ac2 100644 --- a/Sources/KippleUI/SwiftUI/Core/ViewModifiers/View+NavigationBarBackground.swift +++ b/Sources/KippleUI/SwiftUI/Core/ViewModifiers/View+NavigationBarBackground.swift @@ -34,10 +34,16 @@ public extension View { #if canImport(UIKit) struct NavigationBarBackground_Previews: PreviewProvider { + #if os(iOS) private static let displayModes: [NavigationBarItem.TitleDisplayMode] = [ .inline, .large, ] + #else + private static let displayModes: [NavigationBarItem.TitleDisplayMode] = [ + .inline, + ] + #endif static var previews: some View { ForEach(displayModes, id: \.self) { displayMode in diff --git a/Sources/KippleUI/SwiftUI/Core/ViewModifiers/View+Snapshot.swift b/Sources/KippleUI/SwiftUI/Core/ViewModifiers/View+Snapshot.swift index 799f819..67e8ad0 100644 --- a/Sources/KippleUI/SwiftUI/Core/ViewModifiers/View+Snapshot.swift +++ b/Sources/KippleUI/SwiftUI/Core/ViewModifiers/View+Snapshot.swift @@ -1,15 +1,15 @@ // Copyright © 2024 Brian Drelling. All rights reserved. -#if canImport(UIKit) - import SwiftUI +#if canImport(UIKit) && (os(iOS) || os(tvOS)) + public extension View { func snapshot() -> Image { .init(uiImage: self.uiImage()) } - func uiImage() -> UIImage { + private func uiImage() -> UIImage { let controller = UIHostingController(rootView: self) let view = controller.view @@ -25,6 +25,17 @@ public extension View { } } +#else + +public extension View { + func snapshot() -> Image { + print("WARNING: View.snapshot() is only available on iOS and tvOS.") + return .init(systemName: "photo") + } +} + +#endif + // MARK: - Previews struct ViewSnapshot_Previews: PreviewProvider { @@ -37,5 +48,3 @@ struct ViewSnapshot_Previews: PreviewProvider { .snapshot() } } - -#endif diff --git a/Sources/KippleUI/SwiftUI/Core/Views/BackButton.swift b/Sources/KippleUI/SwiftUI/Core/Views/BackButton.swift index 4795436..e54a8a2 100644 --- a/Sources/KippleUI/SwiftUI/Core/Views/BackButton.swift +++ b/Sources/KippleUI/SwiftUI/Core/Views/BackButton.swift @@ -68,7 +68,8 @@ public struct BackButtonPreviewer: View where Content: View { // MARK: - Extensions -@available(iOS 15, macOS 12, tvOS 15, watchOS 8, *) +@available(iOS 16, macOS 13, tvOS 16, watchOS 9, *) +@available(watchOS, unavailable) public extension View { func withBackButton(@ViewBuilder content: @escaping () -> Content) -> some View { navigationBarBackButtonHidden(true) @@ -89,6 +90,7 @@ public extension View { // MARK: - Previews @available(iOS 15, macOS 12, tvOS 15, watchOS 8, *) +@available(watchOS, unavailable) struct BackButton_Previews: PreviewProvider { static var previews: some View { BackButtonPreviewer { diff --git a/Sources/KippleUI/SwiftUI/Core/Views/Modal.swift b/Sources/KippleUI/SwiftUI/Core/Views/Modal.swift index 0529d30..9e67329 100644 --- a/Sources/KippleUI/SwiftUI/Core/Views/Modal.swift +++ b/Sources/KippleUI/SwiftUI/Core/Views/Modal.swift @@ -2,6 +2,7 @@ import SwiftUI +@available(watchOS, unavailable) public struct Modal: View where Content: View { private let content: () -> Content @@ -16,7 +17,11 @@ public struct Modal: View where Content: View { self.content() .withNavigationBarBackground { Rectangle() + #if os(watchOS) + .fill(.background) + #else .fill(.regularMaterial) + #endif } // .navigationBarTitleDisplayMode(.inline) .withNavigationBarDoneButton() @@ -35,6 +40,7 @@ public struct Modal: View where Content: View { // MARK: - Extensions +@available(watchOS, unavailable) public extension View { func inModal() -> some View { Modal { diff --git a/Sources/KippleUI/SwiftUI/Core/Views/NavigatorStack.swift b/Sources/KippleUI/SwiftUI/Core/Views/NavigatorStack.swift index 23d1d45..d927ba5 100644 --- a/Sources/KippleUI/SwiftUI/Core/Views/NavigatorStack.swift +++ b/Sources/KippleUI/SwiftUI/Core/Views/NavigatorStack.swift @@ -2,7 +2,7 @@ import SwiftUI -@available(iOS 17, macOS 14, tvOS 14, watchOS 10, *) +@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) public struct NavigatorStack: View where Content: View { @Bindable private var navigator: Navigator private let content: (Navigator) -> Content @@ -46,7 +46,7 @@ public struct NavigatorStack: View where Content: View { // MARK: - Supporting Types -@available(iOS 17, macOS 14, tvOS 14, watchOS 10, *) +@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Observable public final class Navigator { public var path: NavigationPath @@ -74,12 +74,12 @@ public final class Navigator { } } -@available(iOS 17, macOS 14, tvOS 14, watchOS 10, *) +@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) private struct NavigatorKey: EnvironmentKey { static let defaultValue: Navigator = .init() } -@available(iOS 17, macOS 14, tvOS 14, watchOS 10, *) +@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) public extension EnvironmentValues { var navigator: Navigator { get { self[NavigatorKey.self] } @@ -87,7 +87,7 @@ public extension EnvironmentValues { } } -@available(iOS 17, macOS 14, tvOS 14, watchOS 10, *) +@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) public protocol Navigable: Hashable { associatedtype View: SwiftUI.View @ViewBuilder var view: View { get } @@ -95,14 +95,14 @@ public protocol Navigable: Hashable { // MARK: - Extensions -@available(iOS 17, macOS 14, tvOS 14, watchOS 10, *) +@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) public extension Navigator { convenience init(path: [PathComponent]) where PathComponent: Hashable { self.init(path: .init(path)) } } -@available(iOS 17, macOS 14, tvOS 14, watchOS 10, *) +@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) public extension View { func navigationDestination(for _: N.Type) -> some View where N: Navigable { self.navigationDestination(for: N.self) { $0.view } diff --git a/Sources/KippleUI/SwiftUI/Haptics/Helpers/HapticGenerator.swift b/Sources/KippleUI/SwiftUI/Haptics/Helpers/HapticGenerator.swift index e226712..655061c 100644 --- a/Sources/KippleUI/SwiftUI/Haptics/Helpers/HapticGenerator.swift +++ b/Sources/KippleUI/SwiftUI/Haptics/Helpers/HapticGenerator.swift @@ -1,5 +1,7 @@ // Copyright © 2024 Brian Drelling. All rights reserved. +#if canImport(CoreHaptics) + import Combine import CoreHaptics @@ -127,3 +129,5 @@ public extension CHHapticEvent { ) } } + +#endif