From de349e213ee2f7339abdc62cbdb40ce8dd4efa49 Mon Sep 17 00:00:00 2001 From: Ashik K Date: Fri, 17 Oct 2025 16:20:04 +0200 Subject: [PATCH] Fix Release build - add SwiftGen xcassets generation - Add SwiftGen configuration for xcassets to swiftgen.yml - Generate Images+Generated.swift for type-safe image resources - Fixes 'Type ImageResource has no member' errors in Release builds - Generated file includes device icons, blob icons, and tomato ratings This was needed because the asset catalog references were missing the generated Swift code for type-safe access. --- Shared/Generated/Images+Generated.swift | 217 ++++++++++++++++++++++++ jellypig tvOS/Resources/Info.plist | 2 + swiftgen.yml | 6 + 3 files changed, 225 insertions(+) create mode 100644 Shared/Generated/Images+Generated.swift diff --git a/Shared/Generated/Images+Generated.swift b/Shared/Generated/Images+Generated.swift new file mode 100644 index 00000000..646209d2 --- /dev/null +++ b/Shared/Generated/Images+Generated.swift @@ -0,0 +1,217 @@ +// +// Swiftfin is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2025 Jellyfin & Jellyfin Contributors +// + +// swiftlint:disable all +// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen + +#if os(macOS) +import AppKit +#elseif os(iOS) +import UIKit +#elseif os(tvOS) || os(watchOS) +import UIKit +#endif +#if canImport(SwiftUI) +import SwiftUI +#endif + +// Deprecated typealiases +@available(*, deprecated, renamed: "ImageAsset.Image", message: "This typealias will be removed in SwiftGen 7.0") +typealias AssetImageTypeAlias = ImageAsset.Image + +// swiftlint:disable superfluous_disable_command file_length implicit_return + +// MARK: - Asset Catalogs + +// swiftlint:disable identifier_name line_length nesting type_body_length type_name +enum Asset { + static let deviceBrowserChrome = ImageAsset(name: "Device-browser-chrome") + static let deviceBrowserEdge = ImageAsset(name: "Device-browser-edge") + static let deviceBrowserEdgechromium = ImageAsset(name: "Device-browser-edgechromium") + static let deviceBrowserFirefox = ImageAsset(name: "Device-browser-firefox") + static let deviceBrowserHtml5 = ImageAsset(name: "Device-browser-html5") + static let deviceBrowserMsie = ImageAsset(name: "Device-browser-msie") + static let deviceBrowserOpera = ImageAsset(name: "Device-browser-opera") + static let deviceBrowserSafari = ImageAsset(name: "Device-browser-safari") + static let deviceClientAndroid = ImageAsset(name: "Device-client-android") + static let deviceClientApple = ImageAsset(name: "Device-client-apple") + static let deviceClientFinamp = ImageAsset(name: "Device-client-finamp") + static let deviceClientKodi = ImageAsset(name: "Device-client-kodi") + static let deviceClientPlaystation = ImageAsset(name: "Device-client-playstation") + static let deviceClientRoku = ImageAsset(name: "Device-client-roku") + static let deviceClientSamsungtv = ImageAsset(name: "Device-client-samsungtv") + static let deviceClientWebos = ImageAsset(name: "Device-client-webos") + static let deviceClientWindows = ImageAsset(name: "Device-client-windows") + static let deviceClientXbox = ImageAsset(name: "Device-client-xbox") + static let deviceOtherHomeassistant = ImageAsset(name: "Device-other-homeassistant") + static let deviceOtherOther = ImageAsset(name: "Device-other-other") + static let jellyfinBlobBlue = ImageAsset(name: "jellyfin-blob-blue") + static let tomatoFresh = SymbolAsset(name: "tomato.fresh") + static let tomatoRotten = SymbolAsset(name: "tomato.rotten") +} + +// swiftlint:enable identifier_name line_length nesting type_body_length type_name + +// MARK: - Implementation Details + +struct ImageAsset { + fileprivate(set) var name: String + + #if os(macOS) + typealias Image = NSImage + #elseif os(iOS) || os(tvOS) || os(watchOS) + typealias Image = UIImage + #endif + + @available(iOS 8.0, tvOS 9.0, watchOS 2.0, macOS 10.7, *) + var image: Image { + let bundle = BundleToken.bundle + #if os(iOS) || os(tvOS) + let image = Image(named: name, in: bundle, compatibleWith: nil) + #elseif os(macOS) + let name = NSImage.Name(self.name) + let image = (bundle == .main) ? NSImage(named: name) : bundle.image(forResource: name) + #elseif os(watchOS) + let image = Image(named: name) + #endif + guard let result = image else { + fatalError("Unable to load image asset named \(name).") + } + return result + } + + #if os(iOS) || os(tvOS) + @available(iOS 8.0, tvOS 9.0, *) + func image(compatibleWith traitCollection: UITraitCollection) -> Image { + let bundle = BundleToken.bundle + guard let result = Image(named: name, in: bundle, compatibleWith: traitCollection) else { + fatalError("Unable to load image asset named \(name).") + } + return result + } + #endif + + #if canImport(SwiftUI) + @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) + var swiftUIImage: SwiftUI.Image { + SwiftUI.Image(asset: self) + } + #endif +} + +extension ImageAsset.Image { + @available(iOS 8.0, tvOS 9.0, watchOS 2.0, *) + @available( + macOS, + deprecated, + message: "This initializer is unsafe on macOS, please use the ImageAsset.image property" + ) + convenience init?(asset: ImageAsset) { + #if os(iOS) || os(tvOS) + let bundle = BundleToken.bundle + self.init(named: asset.name, in: bundle, compatibleWith: nil) + #elseif os(macOS) + self.init(named: NSImage.Name(asset.name)) + #elseif os(watchOS) + self.init(named: asset.name) + #endif + } +} + +#if canImport(SwiftUI) +@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) +extension SwiftUI.Image { + init(asset: ImageAsset) { + let bundle = BundleToken.bundle + self.init(asset.name, bundle: bundle) + } + + init(asset: ImageAsset, label: Text) { + let bundle = BundleToken.bundle + self.init(asset.name, bundle: bundle, label: label) + } + + init(decorative asset: ImageAsset) { + let bundle = BundleToken.bundle + self.init(decorative: asset.name, bundle: bundle) + } +} +#endif + +struct SymbolAsset { + fileprivate(set) var name: String + + #if os(iOS) || os(tvOS) || os(watchOS) + @available(iOS 13.0, tvOS 13.0, watchOS 6.0, *) + typealias Configuration = UIImage.SymbolConfiguration + typealias Image = UIImage + + @available(iOS 12.0, tvOS 12.0, watchOS 5.0, *) + var image: Image { + let bundle = BundleToken.bundle + #if os(iOS) || os(tvOS) + let image = Image(named: name, in: bundle, compatibleWith: nil) + #elseif os(watchOS) + let image = Image(named: name) + #endif + guard let result = image else { + fatalError("Unable to load symbol asset named \(name).") + } + return result + } + + @available(iOS 13.0, tvOS 13.0, watchOS 6.0, *) + func image(with configuration: Configuration) -> Image { + let bundle = BundleToken.bundle + guard let result = Image(named: name, in: bundle, with: configuration) else { + fatalError("Unable to load symbol asset named \(name).") + } + return result + } + #endif + + #if canImport(SwiftUI) + @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) + var swiftUIImage: SwiftUI.Image { + SwiftUI.Image(asset: self) + } + #endif +} + +#if canImport(SwiftUI) +@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) +extension SwiftUI.Image { + init(asset: SymbolAsset) { + let bundle = BundleToken.bundle + self.init(asset.name, bundle: bundle) + } + + init(asset: SymbolAsset, label: Text) { + let bundle = BundleToken.bundle + self.init(asset.name, bundle: bundle, label: label) + } + + init(decorative asset: SymbolAsset) { + let bundle = BundleToken.bundle + self.init(decorative: asset.name, bundle: bundle) + } +} +#endif + +// swiftlint:disable convenience_type +private final class BundleToken { + static let bundle: Bundle = { + #if SWIFT_PACKAGE + return Bundle.module + #else + return Bundle(for: BundleToken.self) + #endif + }() +} + +// swiftlint:enable convenience_type diff --git a/jellypig tvOS/Resources/Info.plist b/jellypig tvOS/Resources/Info.plist index 00bbd172..264c6b20 100644 --- a/jellypig tvOS/Resources/Info.plist +++ b/jellypig tvOS/Resources/Info.plist @@ -25,6 +25,8 @@ NSAllowsArbitraryLoads + UIDesignRequiresCompatibility + UILaunchScreen UIColorName diff --git a/swiftgen.yml b/swiftgen.yml index 6a5d425d..4e476234 100644 --- a/swiftgen.yml +++ b/swiftgen.yml @@ -3,3 +3,9 @@ strings: outputs: - templateName: structured-swift5 output: Shared/Strings/Strings.swift + +xcassets: + inputs: "jellypig tvOS/Resources/Assets.xcassets" + outputs: + - templateName: swift5 + output: Shared/Generated/Images+Generated.swift