diff --git a/SwiftLeeds.xcodeproj/project.pbxproj b/SwiftLeeds.xcodeproj/project.pbxproj index f62da03..8b2d04f 100644 --- a/SwiftLeeds.xcodeproj/project.pbxproj +++ b/SwiftLeeds.xcodeproj/project.pbxproj @@ -92,6 +92,7 @@ 7B31C8F12ED0A7BF00FEEDF7 /* Settings in Frameworks */ = {isa = PBXBuildFile; productRef = 7B31C8F02ED0A7BF00FEEDF7 /* Settings */; }; 7B31C8F32ED0AB0E00FEEDF7 /* Settings in Frameworks */ = {isa = PBXBuildFile; productRef = 7B31C8F22ED0AB0E00FEEDF7 /* Settings */; }; 7B31C8F52ED0AB1600FEEDF7 /* Settings in Frameworks */ = {isa = PBXBuildFile; productRef = 7B31C8F42ED0AB1600FEEDF7 /* Settings */; }; + 7B8C3ECF2EE4A9F70089C6CF /* SharedAssets in Frameworks */ = {isa = PBXBuildFile; productRef = 7B8C3ECE2EE4A9F70089C6CF /* SharedAssets */; }; AE1C8010289E9F3800996659 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE1C800F289E9F3800996659 /* String.swift */; }; AE1C801428A7BCD000996659 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = AE1C801328A7BCD000996659 /* Settings.bundle */; }; AE32CDF0286CCF9D00DF0AFF /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE32CDEF286CCF9D00DF0AFF /* Constants.swift */; }; @@ -307,6 +308,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 7B8C3ECF2EE4A9F70089C6CF /* SharedAssets in Frameworks */, 7B31C8E52ED08DD100FEEDF7 /* Networking in Frameworks */, 7B31C8F12ED0A7BF00FEEDF7 /* Settings in Frameworks */, 74F5EF8C2A4B4352008D9413 /* ReadabilityModifier in Frameworks */, @@ -676,6 +678,7 @@ 7B31C8E42ED08DD100FEEDF7 /* Networking */, 7B31C8EA2ED0959000FEEDF7 /* DesignKit */, 7B31C8F02ED0A7BF00FEEDF7 /* Settings */, + 7B8C3ECE2EE4A9F70089C6CF /* SharedAssets */, ); productName = SwiftLeeds; productReference = AECB295327417F9D00CDC983 /* SwiftLeeds.app */; @@ -1525,6 +1528,10 @@ isa = XCSwiftPackageProductDependency; productName = Settings; }; + 7B8C3ECE2EE4A9F70089C6CF /* SharedAssets */ = { + isa = XCSwiftPackageProductDependency; + productName = SharedAssets; + }; AE5EFD72289DC1D000464FE1 /* CachedAsyncImage */ = { isa = XCSwiftPackageProductDependency; package = AE5EFD71289DC1D000464FE1 /* XCRemoteSwiftPackageReference "swiftui-cached-async-image" */; diff --git a/SwiftLeeds.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/SwiftLeeds.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 6794260..d1a00f3 100644 --- a/SwiftLeeds.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/SwiftLeeds.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "14f2f2f8f24b8e0498b003261f3de0cbd1af8008855390449874ffab4bdbbd32", + "originHash" : "7b97790ca80d08686d9c9fcc756f857d9951541bb61167ef5df024519c64b182", "pins" : [ { "identity" : "readabilitymodifier", @@ -10,6 +10,15 @@ "version" : "1.0.0" } }, + { + "identity" : "swiftgenplugin", + "kind" : "remoteSourceControl", + "location" : "https://github.com/shadone/SwiftGenPlugin", + "state" : { + "branch" : "6.6.2+deriveddatafix", + "revision" : "9eb26960d2ebecbe74feecc695e6125eb39e797e" + } + }, { "identity" : "swiftui-cached-async-image", "kind" : "remoteSourceControl", diff --git a/SwiftLeedsPackage/Package.resolved b/SwiftLeedsPackage/Package.resolved new file mode 100644 index 0000000..73ddc0e --- /dev/null +++ b/SwiftLeedsPackage/Package.resolved @@ -0,0 +1,15 @@ +{ + "originHash" : "e72f8f7e5964e57cc3bd7cf4731a74f3869e73bfce1ee20f37f3216c7ed82a39", + "pins" : [ + { + "identity" : "swiftgenplugin", + "kind" : "remoteSourceControl", + "location" : "https://github.com/SwiftGen/SwiftGenPlugin", + "state" : { + "revision" : "879b85a470cacd70c19e22eb7e11a3aed66f4068", + "version" : "6.6.2" + } + } + ], + "version" : 3 +} diff --git a/SwiftLeedsPackage/Package.swift b/SwiftLeedsPackage/Package.swift index e05f43c..92a76d5 100644 --- a/SwiftLeedsPackage/Package.swift +++ b/SwiftLeedsPackage/Package.swift @@ -32,6 +32,15 @@ let package = Package( "Settings", ] ), + .library( + name: "SharedAssets", + targets: [ + "SharedAssets", + ] + ) + ], + dependencies: [ + .package(url: "https://github.com/shadone/SwiftGenPlugin", branch: "6.6.2+deriveddatafix"), ], targets: [ .target( @@ -51,7 +60,13 @@ let package = Package( ), .target( name: "ColorTheme" - ) + ), + .target( + name: "SharedAssets", + plugins: [ + .plugin(name: "SwiftGenPlugin", package: "SwiftGenPlugin"), + ] + ), ], // Set to v5 to avoid strict concurrency checking in pre swift 6 code swiftLanguageModes: [ diff --git a/SwiftLeedsPackage/Sources/SharedAssets/Models/SLColorResource.swift b/SwiftLeedsPackage/Sources/SharedAssets/Models/SLColorResource.swift new file mode 100644 index 0000000..9f05f4a --- /dev/null +++ b/SwiftLeedsPackage/Sources/SharedAssets/Models/SLColorResource.swift @@ -0,0 +1,76 @@ +// swiftlint:disable all +#if canImport(UIKit) +import UIKit +#endif +#if canImport(SwiftUI) +import SwiftUI +#endif + +// MARK: - Backwards Deployment Support - + +/// A color resource. +public struct SLColorResource: Swift.Hashable, Swift.Sendable { + /// An asset catalog color resource name. + let name: Swift.String + + /// An asset catalog color resource bundle. + let bundle: Foundation.Bundle + + /// Initialize a `SLColorResource` with `name` and `bundle`. + init(name: Swift.String, bundle: Foundation.Bundle) { + self.name = name + self.bundle = bundle + } +} + +#if canImport(SwiftUI) +@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *) +extension SwiftUI.Color { + private init?(thinnableResource: SLColorResource?) { + if let resource = thinnableResource { + self.init(resource) + } else { + return nil + } + } + +} + +@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *) +extension SwiftUI.ShapeStyle where Self == SwiftUI.Color { + private init?(thinnableResource: SLColorResource?) { + if let resource = thinnableResource { + self.init(resource) + } else { + return nil + } + } + +} + +@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *) +public extension SwiftUI.Color { + /// Initialize a `Color` with a color resource. + init(_ resource: SLColorResource) { + self.init(resource.name, bundle: resource.bundle) + } + +} +#endif + +#if canImport(UIKit) +@available(iOS 11.0, tvOS 11.0, *) +@available(watchOS, unavailable) +public extension UIKit.UIColor { + /// Initialize a `UIColor` with a color resource. + convenience init(resource: SLColorResource) { +#if !os(watchOS) + self.init(named: resource.name, in: resource.bundle, compatibleWith: nil)! +#else + self.init() +#endif + } + +} +#endif +// swiftlint:enable all diff --git a/SwiftLeedsPackage/Sources/SharedAssets/Models/SLImageResource.swift b/SwiftLeedsPackage/Sources/SharedAssets/Models/SLImageResource.swift new file mode 100644 index 0000000..df01f9c --- /dev/null +++ b/SwiftLeedsPackage/Sources/SharedAssets/Models/SLImageResource.swift @@ -0,0 +1,105 @@ +// swiftlint:disable all +#if canImport(UIKit) +import UIKit +#endif +#if canImport(SwiftUI) +import SwiftUI +#endif + +// MARK: - Backwards Deployment Support - + +/// An image resource. +public struct SLImageResource: Swift.Hashable, Swift.Sendable { + /// An asset catalog image resource name. + fileprivate let name: Swift.String + + /// An asset catalog image resource bundle. + fileprivate let bundle: Foundation.Bundle + + /// Initialize an `SLImageResource` with `name` and `bundle`. + init(name: Swift.String, bundle: Foundation.Bundle) { + self.name = name + self.bundle = bundle + } +} + +#if canImport(SwiftUI) +@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *) +public extension SwiftUI.Image { + /// Initialize an `Image` with an image resource. + init(_ resource: SLImageResource) { + self.init(resource.name, bundle: resource.bundle) + } + +} +#endif + +#if canImport(UIKit) +@available(iOS 11.0, tvOS 11.0, *) +@available(watchOS, unavailable) +public extension UIKit.UIImage { + /// Initialize a `UIImage` with an image resource. + convenience init(resource: SLImageResource) { +#if !os(watchOS) + self.init(named: resource.name, in: resource.bundle, compatibleWith: nil)! +#else + self.init() +#endif + } + +} + +@available(iOS 11.0, tvOS 11.0, *) +@available(watchOS, unavailable) +extension UIKit.UIImage { + private convenience init?(thinnableResource: SLImageResource?) { +#if !os(watchOS) + if let resource = thinnableResource { + self.init(resource: resource) + } else { + return nil + } +#else + return nil +#endif + } + +} +#endif + +#if canImport(AppKit) +@available(macOS 10.7, *) +@available(macCatalyst, unavailable) +extension AppKit.NSImage { + private convenience init?(thinnableResource: SLImageResource?) { +#if !targetEnvironment(macCatalyst) + if let resource = thinnableResource { + self.init(resource: resource) + } else { + return nil + } +#else + return nil +#endif + } + +} +#endif + +@available(iOS 11.0, macOS 10.7, tvOS 11.0, *) +@available(watchOS, unavailable) +extension SLImageResource { + private init?(thinnableName: Swift.String, bundle: Foundation.Bundle) { +#if canImport(UIKit) && !os(watchOS) + if UIKit.UIImage(named: thinnableName, in: bundle, compatibleWith: nil) != nil { + self.init(name: thinnableName, bundle: bundle) + } else { + return nil + } +#else + return nil +#endif + } + +} +// swiftlint:enable all diff --git a/SwiftLeedsPackage/Sources/SharedAssets/RequiredSwiftFileForSPM.swift b/SwiftLeedsPackage/Sources/SharedAssets/RequiredSwiftFileForSPM.swift new file mode 100644 index 0000000..d7a440f --- /dev/null +++ b/SwiftLeedsPackage/Sources/SharedAssets/RequiredSwiftFileForSPM.swift @@ -0,0 +1,15 @@ +// File needed to avoid Swift Package compilation error + +import SwiftUI + +struct Test: View { + let color: SLColorResource = .accent + + var body: some View { + Color(color) + } +} + +#Preview { + Test() +} diff --git a/SwiftLeedsPackage/Sources/SharedAssets/Resources/Colors.xcassets/Accent.colorset/Contents.json b/SwiftLeedsPackage/Sources/SharedAssets/Resources/Colors.xcassets/Accent.colorset/Contents.json new file mode 100644 index 0000000..622f9df --- /dev/null +++ b/SwiftLeedsPackage/Sources/SharedAssets/Resources/Colors.xcassets/Accent.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x30", + "green" : "0x3B", + "red" : "0xFF" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SwiftLeedsPackage/Sources/SharedAssets/Resources/Colors.xcassets/Contents.json b/SwiftLeedsPackage/Sources/SharedAssets/Resources/Colors.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/SwiftLeedsPackage/Sources/SharedAssets/Resources/Colors.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SwiftLeedsPackage/Sources/SharedAssets/Resources/Images.xcassets/Contents.json b/SwiftLeedsPackage/Sources/SharedAssets/Resources/Images.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/SwiftLeedsPackage/Sources/SharedAssets/Resources/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SwiftLeedsPackage/Sources/SharedAssets/Resources/Images.xcassets/SwiftLeedsIcon.imageset/Contents.json b/SwiftLeedsPackage/Sources/SharedAssets/Resources/Images.xcassets/SwiftLeedsIcon.imageset/Contents.json new file mode 100644 index 0000000..62e9408 --- /dev/null +++ b/SwiftLeedsPackage/Sources/SharedAssets/Resources/Images.xcassets/SwiftLeedsIcon.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "SwiftLeedsIcon.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "original" + } +} diff --git a/SwiftLeedsPackage/Sources/SharedAssets/Resources/Images.xcassets/SwiftLeedsIcon.imageset/SwiftLeedsIcon.pdf b/SwiftLeedsPackage/Sources/SharedAssets/Resources/Images.xcassets/SwiftLeedsIcon.imageset/SwiftLeedsIcon.pdf new file mode 100644 index 0000000..ffd8991 Binary files /dev/null and b/SwiftLeedsPackage/Sources/SharedAssets/Resources/Images.xcassets/SwiftLeedsIcon.imageset/SwiftLeedsIcon.pdf differ diff --git a/SwiftLeedsPackage/Sources/SharedAssets/Templates/assets.stencil b/SwiftLeedsPackage/Sources/SharedAssets/Templates/assets.stencil new file mode 100644 index 0000000..db40e5e --- /dev/null +++ b/SwiftLeedsPackage/Sources/SharedAssets/Templates/assets.stencil @@ -0,0 +1,102 @@ +// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen +// swiftlint:disable all + +{% if catalogs %} +{% set resourcePrefix %}{{param.resourcePrefix|default:""}}{% endset %} +{% set forceNamespaces %}{{param.forceProvidesNamespaces|default:"false"}}{% endset %} +{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %} +{% macro casesBlock assets assetType %} + {% for asset in assets %} + {% if asset.type == assetType %} + /// The "{{ asset.name }}" asset catalog {{ assetType }} resource. + static let {{ asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords }} = {{ resourcePrefix }}{{ assetType|capitalize }}Resource(name: "{{ asset.name }}", bundle: resourceBundle) + {% elif asset.items %} + {% call casesBlock asset.items assetType %} + {% endif %} + {% endfor %} +{% endmacro %} +{% macro allValuesBlock catalogs assetType %} + {% if catalogs.count > 1 %} + {% for catalog in catalogs %} + {% if catalog.assets %} + {% call casesBlock catalog.assets assetType %} + {% endif %} + {% endfor %} + {% else %} + {% call casesBlock catalogs.first.assets assetType %} + {% endif %} +{% endmacro %} +{% macro generateAssetProperty asset assetType platform %} + /// The "{{ asset.name }}" asset catalog {{ assetType }}. + static var {{ asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords }}: {{ platform }} { + {% if platform == "Color" %} + Color(theme: .{{ asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords }}) + {% elif platform == "Image" %} + Image(theme: .{{ asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords }}) + {% elif platform == "NSColor" or platform == "UIColor" or platform == "NSImage" or platform == "UIImage" %} + #if !os(watchOS) && !targetEnvironment(macCatalyst) + {{ platform }}(theme: .{{ asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords }}) + #else + {{ platform }}() + #endif + {% else %} + fatalError("Unsupported platform: {{ platform }}") + {% endif %} + } +{% endmacro %} +{% macro processAssets assets assetType platform %} + {% for asset in assets %} + {% if asset.type == assetType %} + {% call generateAssetProperty asset assetType platform %} + {% elif asset.items %} + {% call processAssets asset.items assetType platform %} + {% endif %} + {% endfor %} +{% endmacro %} +{% macro extensionBlock catalogs assetType platform %} + {% if catalogs.count > 1 %} + {% for catalog in catalogs %} + {% if catalog.assets %} + {% call processAssets catalog.assets assetType platform %} + {% endif %} + {% endfor %} + {% else %} + {% call processAssets catalogs.first.assets assetType platform %} + {% endif %} +{% endmacro %} + +import Foundation +#if canImport(AppKit) +import AppKit +#endif +#if canImport(UIKit) +import UIKit +#endif +#if canImport(SwiftUI) +import SwiftUI +#endif + +#if SWIFT_PACKAGE +private let resourceBundle = Bundle.module +#else +private class ResourceBundleClass {} +private let resourceBundle = Foundation.Bundle(for: ResourceBundleClass.self) +#endif + +// MARK: - Color Symbols - + +@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) +public extension SLColorResource { + {% call allValuesBlock catalogs "color" %} +} + +// MARK: - Image Symbols - + +@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) +public extension SLImageResource { + {% call allValuesBlock catalogs "image" %} +} +{% else %} +// No assets found +{% endif %} +// swiftlint:enable all diff --git a/SwiftLeedsPackage/Sources/SharedAssets/swiftgen.yml b/SwiftLeedsPackage/Sources/SharedAssets/swiftgen.yml new file mode 100644 index 0000000..1d44143 --- /dev/null +++ b/SwiftLeedsPackage/Sources/SharedAssets/swiftgen.yml @@ -0,0 +1,14 @@ +input_dir: Resources +output_dir: ${DERIVED_SOURCES_DIR} + +xcassets: + inputs: + - . + outputs: + - templatePath: Templates/assets.stencil + output: Assets+Generated.swift + params: + resourcePrefix: SL + bundle: Bundle.module + publicAccess: true + forceProvidesNamespaces: true diff --git a/SwiftLeedsPackage/Sources/SwiftLeedsCore/SwiftLeedsCore.swift b/SwiftLeedsPackage/Sources/SwiftLeedsCore/SwiftLeedsCore.swift index 8b13789..de958c7 100644 --- a/SwiftLeedsPackage/Sources/SwiftLeedsCore/SwiftLeedsCore.swift +++ b/SwiftLeedsPackage/Sources/SwiftLeedsCore/SwiftLeedsCore.swift @@ -1 +1 @@ - +// This file is needed to satisfy the compiler for an empty package