From 5b7e88bc73e5bbf66d8f989530bbdf7d218f1cab Mon Sep 17 00:00:00 2001 From: marchidalgo Date: Wed, 21 Jan 2026 19:55:41 +0100 Subject: [PATCH 1/4] feat: improve PhotoView for cross-platform support Refactor PhotoView to add Android support and apply platform-specific layouts. Introduce `applyPhotoLayout` to handle conditional layout logic for different platforms. Create `iosPhotoView` and `androidPhotoView` functions to streamline image loading methods dependent on the OS. These changes enable better flexibility and performance on both iOS and Android, ensuring a consistent user experience across platforms. --- .../SwiftUI/Views/PhotoView.swift | 88 +++++++++++++++++-- 1 file changed, 80 insertions(+), 8 deletions(-) diff --git a/Sources/BSWInterfaceKit/SwiftUI/Views/PhotoView.swift b/Sources/BSWInterfaceKit/SwiftUI/Views/PhotoView.swift index d90de771..d9425122 100644 --- a/Sources/BSWInterfaceKit/SwiftUI/Views/PhotoView.swift +++ b/Sources/BSWInterfaceKit/SwiftUI/Views/PhotoView.swift @@ -40,8 +40,8 @@ public struct PhotoView: View { public var body: some View { contentView - .aspectRatio( - configuration.aspectRatio, + .applyPhotoLayout( + aspectRatio: configuration.aspectRatio, contentMode: configuration.contentMode ) } @@ -72,19 +72,42 @@ public struct PhotoView: View { private var photoView: some View { switch photo.kind { case .url(let url): - #if canImport(Nuke) - nukePhotoView(url: url) + #if os(Android) + androidPhotoView(url) #else - AsyncImage(url: url) + iosPhotoView(url) #endif case .image(let image): - image - .resizable() + #if os(Android) + image.photoStyle() + #else + image.resizable() + #endif default: placeholder } } + @ViewBuilder + private func iosPhotoView(_ url: URL) -> some View { + #if canImport(Nuke) + nukePhotoView(url: url) + #else + AsyncImage(url: url) + #endif + } + + #if os(Android) + @ViewBuilder + private func androidPhotoView(_ url: URL) -> some View { + #if canImport(Nuke) + nukePhotoView(url: url) + #else + basicAsyncImage(url: url) + #endif + } + #endif + var isRunningTests: Bool { #if canImport(UIKit.UIApplication) UIApplication.shared.isRunningTests @@ -113,7 +136,23 @@ public struct PhotoView: View { } else { placeholder } - #endif + #endif + } + } + #endif + + #if os(Android) + @ViewBuilder + private func basicAsyncImage(url: URL) -> some View { + AsyncImage(url: url) { phase in + switch phase { + case .success(let image): + image.photoStyle() + case .failure(_): + configuration.placeholder.body() + default: + configuration.placeholder.body() + } } } #endif @@ -180,6 +219,39 @@ extension PhotoView.Configuration: Sendable {} extension PhotoView.Configuration.Placeholder: Sendable {} extension PhotoView.Configuration.Placeholder.Shape: Sendable {} +#if os(Android) +private extension Image { + func photoStyle() -> some View { + self + .resizable() + .interpolation(.high) + .antialiased(true) + } +} +#endif + +private extension View { + @ViewBuilder + func applyPhotoLayout(aspectRatio: CGFloat?, contentMode: ContentMode) -> some View { + #if os(Android) + if let ratio = aspectRatio { + self.aspectRatio(ratio, contentMode: contentMode) + } else { + switch contentMode { + case .fit: + self.scaledToFit() + case .fill: + self.scaledToFill().clipped() + @unknown default: + self.scaledToFit() + } + } + #else + self.aspectRatio(aspectRatio, contentMode: contentMode) + #endif + } +} + #if canImport(UIKit) private extension PhotoView { From da6304d5f729d50933f99985d949b1dfa160a37a Mon Sep 17 00:00:00 2001 From: marchidalgo Date: Wed, 21 Jan 2026 19:57:58 +0100 Subject: [PATCH 2/4] refactor: improve code readability and consistency in PhotoView Indent preprocessor directives inside ViewBuilder functions to improve code readability. This change ensures consistent indenting across platform-specific View implementations and maintains uniformity in formatting. This refactoring does not alter any functionality but enhances maintainability. --- .../BSWInterfaceKit/SwiftUI/Views/PhotoView.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Sources/BSWInterfaceKit/SwiftUI/Views/PhotoView.swift b/Sources/BSWInterfaceKit/SwiftUI/Views/PhotoView.swift index d9425122..cef6afea 100644 --- a/Sources/BSWInterfaceKit/SwiftUI/Views/PhotoView.swift +++ b/Sources/BSWInterfaceKit/SwiftUI/Views/PhotoView.swift @@ -90,21 +90,21 @@ public struct PhotoView: View { @ViewBuilder private func iosPhotoView(_ url: URL) -> some View { - #if canImport(Nuke) + #if canImport(Nuke) nukePhotoView(url: url) - #else + #else AsyncImage(url: url) - #endif + #endif } #if os(Android) @ViewBuilder private func androidPhotoView(_ url: URL) -> some View { - #if canImport(Nuke) + #if canImport(Nuke) nukePhotoView(url: url) - #else + #else basicAsyncImage(url: url) - #endif + #endif } #endif From 371bbb7822898fe6ddbabebbe6cefb9168eccae1 Mon Sep 17 00:00:00 2001 From: marchidalgo Date: Wed, 21 Jan 2026 20:01:52 +0100 Subject: [PATCH 3/4] fix: correct iOS function naming for photo views Change the function name from `iosPhotoView` to `iOSPhotoView` to maintain consistency with Swift naming conventions and improve code readability. This change ensures that the case of 'iOS' follows standard practices, reflecting platform-specific coding style. --- Sources/BSWInterfaceKit/SwiftUI/Views/PhotoView.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/BSWInterfaceKit/SwiftUI/Views/PhotoView.swift b/Sources/BSWInterfaceKit/SwiftUI/Views/PhotoView.swift index cef6afea..661bb984 100644 --- a/Sources/BSWInterfaceKit/SwiftUI/Views/PhotoView.swift +++ b/Sources/BSWInterfaceKit/SwiftUI/Views/PhotoView.swift @@ -75,7 +75,7 @@ public struct PhotoView: View { #if os(Android) androidPhotoView(url) #else - iosPhotoView(url) + iOSPhotoView(url) #endif case .image(let image): #if os(Android) @@ -89,7 +89,7 @@ public struct PhotoView: View { } @ViewBuilder - private func iosPhotoView(_ url: URL) -> some View { + private func iOSPhotoView(_ url: URL) -> some View { #if canImport(Nuke) nukePhotoView(url: url) #else From a4b1cddbde8cde15995282e33afecc47d85ee072 Mon Sep 17 00:00:00 2001 From: marchidalgo Date: Mon, 26 Jan 2026 19:02:13 +0100 Subject: [PATCH 4/4] chore: update `skip` package and refactor `PhotoView` Update the `skip` package dependency to version 1.7.0 for additional features and improvements. Refactor the `PhotoView.swift` file to remove platform-specific function implementations (`iOSPhotoView` and `androidPhotoView`), consolidating loading logic. Enhance the use of AsyncImage for Android and streamline the aspect ratio application by removing the `applyPhotoLayout` extension method in favor of directly using `.aspectRatio`. --- Package.resolved | 6 +- .../SwiftUI/Views/PhotoView.swift | 98 +++++++------------ 2 files changed, 36 insertions(+), 68 deletions(-) diff --git a/Package.resolved b/Package.resolved index 7c9c5ff5..1ad1e6ca 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "0da1e4bb6b1f40a19b1a311a4da8056b660bb61b6f28d9a75a414b06a47187d3", + "originHash" : "0997dd5a67de5508db332755539bfceccd0c1ce547ea5f5484b928b9c5eb3540", "pins" : [ { "identity" : "bswfoundation", @@ -33,8 +33,8 @@ "kind" : "remoteSourceControl", "location" : "https://source.skip.tools/skip.git", "state" : { - "revision" : "9e88c24b32ab42b03e90e3c8fc484f883690fd9c", - "version" : "1.6.27" + "revision" : "8e0c0421c04b00dd55795c913d94279a71a8c3f9", + "version" : "1.7.0" } }, { diff --git a/Sources/BSWInterfaceKit/SwiftUI/Views/PhotoView.swift b/Sources/BSWInterfaceKit/SwiftUI/Views/PhotoView.swift index 661bb984..65ba2898 100644 --- a/Sources/BSWInterfaceKit/SwiftUI/Views/PhotoView.swift +++ b/Sources/BSWInterfaceKit/SwiftUI/Views/PhotoView.swift @@ -40,8 +40,8 @@ public struct PhotoView: View { public var body: some View { contentView - .applyPhotoLayout( - aspectRatio: configuration.aspectRatio, + .aspectRatio( + configuration.aspectRatio, contentMode: configuration.contentMode ) } @@ -73,41 +73,25 @@ public struct PhotoView: View { switch photo.kind { case .url(let url): #if os(Android) - androidPhotoView(url) + androidPhotoView(url: url) #else - iOSPhotoView(url) + #if canImport(Nuke) + nukePhotoView(url: url) + #else + AsyncImage(url: url) + #endif #endif case .image(let image): #if os(Android) image.photoStyle() #else - image.resizable() + image + .resizable() #endif default: placeholder } } - - @ViewBuilder - private func iOSPhotoView(_ url: URL) -> some View { - #if canImport(Nuke) - nukePhotoView(url: url) - #else - AsyncImage(url: url) - #endif - } - - #if os(Android) - @ViewBuilder - private func androidPhotoView(_ url: URL) -> some View { - #if canImport(Nuke) - nukePhotoView(url: url) - #else - basicAsyncImage(url: url) - #endif - } - #endif - var isRunningTests: Bool { #if canImport(UIKit.UIApplication) UIApplication.shared.isRunningTests @@ -116,6 +100,26 @@ public struct PhotoView: View { #endif } + // MARK: - Android + + #if os(Android) + @ViewBuilder + private func androidPhotoView(url: URL) -> some View { + AsyncImage(url: url) { phase in + switch phase { + case .success(let image): + image.photoStyle() + case .failure(_): + configuration.placeholder.body() + default: + configuration.placeholder.body() + } + } + } + #endif + + // MARK: - iOS + #if canImport(Nuke) @ViewBuilder func nukePhotoView(url: URL) -> some View { @@ -136,23 +140,7 @@ public struct PhotoView: View { } else { placeholder } - #endif - } - } - #endif - - #if os(Android) - @ViewBuilder - private func basicAsyncImage(url: URL) -> some View { - AsyncImage(url: url) { phase in - switch phase { - case .success(let image): - image.photoStyle() - case .failure(_): - configuration.placeholder.body() - default: - configuration.placeholder.body() - } + #endif } } #endif @@ -219,6 +207,8 @@ extension PhotoView.Configuration: Sendable {} extension PhotoView.Configuration.Placeholder: Sendable {} extension PhotoView.Configuration.Placeholder.Shape: Sendable {} +// MARK: - Android image styling + #if os(Android) private extension Image { func photoStyle() -> some View { @@ -230,28 +220,6 @@ private extension Image { } #endif -private extension View { - @ViewBuilder - func applyPhotoLayout(aspectRatio: CGFloat?, contentMode: ContentMode) -> some View { - #if os(Android) - if let ratio = aspectRatio { - self.aspectRatio(ratio, contentMode: contentMode) - } else { - switch contentMode { - case .fit: - self.scaledToFit() - case .fill: - self.scaledToFill().clipped() - @unknown default: - self.scaledToFit() - } - } - #else - self.aspectRatio(aspectRatio, contentMode: contentMode) - #endif - } -} - #if canImport(UIKit) private extension PhotoView {