diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index ee1d947..5213668 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,28 +1,10 @@ -#### โœจ This PR Will: - -- -- -- +## What + -#### ๐ŸŽฏ Purpose - -- -- -#### ๐Ÿงช Unit Tests - -- -- +## Why + -#### ๐Ÿ“ธ Screenshots - -- -#### ๐Ÿ’ป Code Snippets - - - - - - - +## Changes + diff --git a/.github/scripts/extract_coverage.sh b/.github/scripts/extract_coverage.sh deleted file mode 100644 index 1611c30..0000000 --- a/.github/scripts/extract_coverage.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/bash - -# Environment variables -PACKAGE_NAME=${PACKAGE_NAME:-"SnappThemingSVGSupport"} -SOURCE_PATH=${SOURCE_PATH:-"$PACKAGE_NAME/Sources"} -OUTPUT_FILE=${COVERAGE_SUMMARY_FILE:-"pr_coverage_summary.txt"} -DECIMAL_PLACES=6 - -# Use CODECOV_PATH from environment if provided, otherwise get it -if [ -z "$CODECOV_PATH" ]; then - CODECOV_PATH=$(swift test --enable-code-coverage --show-codecov-path) -fi -echo "Using coverage report at: $CODECOV_PATH" - -# Extract all line coverage data for files containing SOURCE_PATH -FILES_LINE_COUNTS=$(jq -r --arg path "$SOURCE_PATH" '.data[0].files[] | select(.filename | contains($path)) | .summary.lines' "$CODECOV_PATH") - -total_lines=0 -covered_lines=0 - -# Loop through each file's line count data -for lines_data in $(echo "$FILES_LINE_COUNTS" | jq -c '.'); do - # Extract total and covered lines for each file - total=$(echo "$lines_data" | jq '.count') - covered=$(echo "$lines_data" | jq '.covered') - - # Add to the total lines and covered lines - total_lines=$((total_lines + total)) - covered_lines=$((covered_lines + covered)) -done - -# Calculate the average line coverage percentage -if [ $total_lines -gt 0 ]; then - average_coverage=$(echo "scale=$DECIMAL_PLACES; $covered_lines * 100 / $total_lines" | bc) -else - average_coverage=0 -fi - -average_coverage_rounded=$(echo "$average_coverage" | awk '{print int($1 * 100 + 0.5) / 100}') -average_coverage_with_percentage="${average_coverage_rounded}%" - -# Save to output file -cat < "$OUTPUT_FILE" -| ID | Name | Executable Lines | Coverage | -|----|------|-----------------:|---------:| -| 0 | $PACKAGE_NAME | $total_lines | **$average_coverage_with_percentage** | -EOF - -echo "Coverage report generated with $average_coverage_with_percentage coverage" diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 73182a0..ac469c4 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -1,17 +1,5 @@ -# This workflow will test a Swift project -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-swift - name: Package Test -env: - EXTRACT_COVERAGE: '.github/scripts/extract_coverage.sh' - CREATE_COMMENT: '.github/scripts/create_pr_comment.js' - COVERAGE_REPORT_PATH: './coverage/coverage.xcresult' - XCODE_VERSION: 'Xcode_16.2.app' - XCODE_PATH: '/Applications/Xcode_16.2.app/Contents/Developer' - COVERAGE_SUMMARY_FILE: 'pr_coverage_summary.txt' - BOT_COMMENT_HEADER: '### ๐Ÿ›ก๏ธ Code Coverage Report' - on: push: branches: [ "main" ] @@ -19,67 +7,8 @@ on: branches: [ "main" ] jobs: - build: + coverage: runs-on: macos-latest - steps: - - uses: actions/checkout@v4 - - - name: List Xcode Installations - run: sudo ls -1 /Applications | grep "Xcode" - - - name: Select Xcode - run: sudo xcode-select -s ${{ env.XCODE_PATH }} - - - name: Run Swift Tests with Coverage - run: | - swift test --enable-swift-testing --enable-code-coverage - CODECOV_PATH=$(swift test --enable-code-coverage --show-codecov-path) - echo "CODECOV_PATH=${CODECOV_PATH}" >> $GITHUB_ENV - - - name: Run Coverage Extraction Script - run: bash ${{ env.EXTRACT_COVERAGE }} - - - name: Comment on Pull Request - if: github.event_name == 'pull_request' - uses: actions/github-script@v6 - with: - script: | - const fs = require('fs'); - const coverageOutput = fs.readFileSync('${{ env.COVERAGE_SUMMARY_FILE }}', 'utf8'); - - const newComment = ` - ${{ env.BOT_COMMENT_HEADER }} - - ${coverageOutput} - - _Generated by GitHub Actions._ - `; - - // Fetch existing comments - const comments = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.payload.pull_request.number, - }); - - // Identify and delete previous bot comments - const botComments = comments.data.filter(comment => - comment.body.includes("${{ env.BOT_COMMENT_HEADER }}") - ); - - for (const botComment of botComments) { - await github.rest.issues.deleteComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: botComment.id, - }); - } - - // Create a comment on the pull request - await github.rest.issues.createComment({ - issue_number: context.payload.pull_request.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: newComment, - }); + - uses: actions/checkout@v4 + - uses: Snapp-Mobile/swift-coverage-action@v1.0.1 diff --git a/Package.swift b/Package.swift index 6c7e1ab..807da0b 100644 --- a/Package.swift +++ b/Package.swift @@ -1,5 +1,4 @@ // swift-tools-version: 6.0 -// The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription @@ -10,18 +9,21 @@ let package = Package( .macOS(.v13), ], products: [ - // Products define the executables and libraries a package produces, making them visible to other packages. .library( name: "SnappThemingSVGSupport", - targets: ["SnappThemingSVGSupport"]) + targets: ["SnappThemingSVGSupport"] + ), + .executable( + name: "ExampleApp", + targets: ["ExampleApp"] + ), ], dependencies: [ .package(url: "https://github.com/SVGKit/SVGKit.git", from: "3.0.0"), - .package(url: "https://github.com/Snapp-Mobile/SnappTheming", from: "0.1.2"), + .package(url: "https://github.com/Snapp-Mobile/SnappTheming", exact: "0.1.3"), + .package(url: "https://github.com/Snapp-Mobile/SwiftFormatLintPlugin.git", exact: "1.0.4"), ], targets: [ - // Targets are the basic building blocks of a package, defining a module or a test suite. - // Targets can depend on other targets in this package and products from dependencies. .target( name: "SnappThemingSVGSupport", dependencies: [ @@ -29,7 +31,7 @@ let package = Package( "SnappTheming", ], plugins: [ - .plugin(name: "SnappThemingSVGSupportSwiftFormatPlugin") + .plugin(name: "Lint", package: "SwiftFormatLintPlugin") ] ), .testTarget( @@ -43,11 +45,17 @@ let package = Package( .copy("Resources/images.json") ] ), - .plugin( - name: "SnappThemingSVGSupportSwiftFormatPlugin", - capability: .buildTool(), - path: "Plugins/SnappThemingSVGSupportSwiftFormatPlugin" + .executableTarget( + name: "ExampleApp", + dependencies: [ + "SnappThemingSVGSupport", + "SnappTheming", + ], + resources: [ + .copy("ExampleApp/Resources/light.json"), + .copy("ExampleApp/Resources/dark.json"), + .copy("ExampleApp/Resources/pink.json"), + ] ), - ] ) diff --git a/Plugins/SnappThemingSVGSupportSwiftFormatPlugin/SnappThemingSVGSupportSwiftFormatPlugin.swift b/Plugins/SnappThemingSVGSupportSwiftFormatPlugin/SnappThemingSVGSupportSwiftFormatPlugin.swift deleted file mode 100644 index 3db7849..0000000 --- a/Plugins/SnappThemingSVGSupportSwiftFormatPlugin/SnappThemingSVGSupportSwiftFormatPlugin.swift +++ /dev/null @@ -1,58 +0,0 @@ -// -// SwiftLintPlugin.swift -// SnappTheming -// -// Created by Oleksii Kolomiiets on 21.01.2025. -// - -import Foundation -import PackagePlugin - -/// A Swift Package Manager build tool plugin to run SwiftFormat during the build process. -@main -struct SnappThemingSVGSupportSwiftFormatPlugin: BuildToolPlugin { - /// Creates build commands for the plugin. - /// - Parameters: - /// - context: The plugin context providing details like the package directory. - /// - target: The target on which the plugin is applied. - /// - Returns: An array of build commands to execute during the build process. - /// - Throws: `PluginError` if the script is not found. - func createBuildCommands(context: PluginContext, target: Target) throws -> [Command] { - // Define the path to your script - let scriptPath = context.package.directoryURL.appending( - path: "Plugins/SnappThemingSVGSupportSwiftFormatPlugin/swift-format-script.sh" - ).path - let configurationPath = context.package.directoryURL.absoluteString - - // Validate that the script exists - guard FileManager.default.fileExists(atPath: scriptPath) else { - throw PluginError.scriptNotFound("Script not found at \(scriptPath)") - } - - // Return a build command to run your script - return [ - .buildCommand( - displayName: "Running SnappThemingSVGSupportSwiftFormatPlugin", - executable: URL(filePath: "/bin/bash"), - arguments: [scriptPath, configurationPath], - environment: [:], - inputFiles: [], - outputFiles: [] - ) - ] - } -} - -/// Custom errors for the plugin. -enum PluginError: Error, CustomStringConvertible { - /// Thrown when the script file is not found. - case scriptNotFound(String) - - /// A description of the error. - var description: String { - switch self { - case .scriptNotFound(let message): - return message - } - } -} diff --git a/Plugins/SnappThemingSVGSupportSwiftFormatPlugin/swift-format-script.sh b/Plugins/SnappThemingSVGSupportSwiftFormatPlugin/swift-format-script.sh deleted file mode 100755 index 4c54b08..0000000 --- a/Plugins/SnappThemingSVGSupportSwiftFormatPlugin/swift-format-script.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/bash - -# Locate swift-format -linter=$(xcrun --find swift-format) - -if [ -z "$linter" ]; then - echo "error: swift-format not found" >&2 - exit 1 -fi - -# Resolve paths -SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) -PACKAGE_DIRECTORY=$(cd "$SCRIPT_DIR/../.." && pwd) -CONFIG_PATH="$PACKAGE_DIRECTORY/.swiftformat" - -# Debugging outputs -echo "SCRIPT_DIR: $SCRIPT_DIR" -echo "PACKAGE_DIRECTORY: $PACKAGE_DIRECTORY" -echo "CONFIG_PATH: $CONFIG_PATH" - -# Check if the configuration file exists -if [ ! -f "$CONFIG_PATH" ]; then - echo "error: Config file not found at: $CONFIG_PATH" >&2 - exit 1 -fi - -# Verify the package directory exists -if [ ! -d "$PACKAGE_DIRECTORY" ]; then - echo "error: Directory $PACKAGE_DIRECTORY does not exist" >&2 - exit 1 -fi - -# Run swift-format lint -echo "Linting directory: $PACKAGE_DIRECTORY" -output=$("$linter" lint --configuration "$CONFIG_PATH" -r "$PACKAGE_DIRECTORY" 2>&1) - -# Debugging output -echo "Swift-format output:" -echo "$output" - -# Parse and format the output for Xcode compatibility -echo "$output" | while IFS= read -r line; do - # Match lines in the form: [file]:[line]:[column]: [level]: [message] - if [[ $line =~ ^([^:]+):([0-9]+):([0-9]+):\ (warning|error|note):\ (.+)$ ]]; then - file="${BASH_REMATCH[1]}" - line_number="${BASH_REMATCH[2]}" - column_number="${BASH_REMATCH[3]}" - level="${BASH_REMATCH[4]}" - message="${BASH_REMATCH[5]}" - - # Print the formatted output - echo "$file:$line_number:$column_number: $level: $message" - else - # Print unmatched lines as-is for debugging - echo "No match for line: $line" >&2 - fi -done diff --git a/README.md b/README.md index f0674e7..0f18691 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # SnappThemingSVGSupport

- +

Latest Version License Badge @@ -9,7 +9,8 @@ Swift Tools Version 6.0 Supported Platforms

-`SnappThemingSVGSupport` is a library that enables support for SVG asset handling in `SnappTheming` + +This library enables support for SVG asset handling in **SnappTheming** Enabling the SVG support is as easy as adding one line to your codebase. diff --git a/Sources/ExampleApp/ExampleApp.xcodeproj/project.pbxproj b/Sources/ExampleApp/ExampleApp.xcodeproj/project.pbxproj new file mode 100644 index 0000000..32ddb00 --- /dev/null +++ b/Sources/ExampleApp/ExampleApp.xcodeproj/project.pbxproj @@ -0,0 +1,384 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXBuildFile section */ + 533571662EBB430B00679688 /* SnappThemingSVGSupport in Frameworks */ = {isa = PBXBuildFile; productRef = 533571652EBB430B00679688 /* SnappThemingSVGSupport */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 539E3BB32EBA5BE4000BF002 /* ExampleApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ExampleApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 539E3BB52EBA5BE4000BF002 /* ExampleApp */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = ExampleApp; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + 539E3BB02EBA5BE4000BF002 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 533571662EBB430B00679688 /* SnappThemingSVGSupport in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 539E3BAA2EBA5BE4000BF002 = { + isa = PBXGroup; + children = ( + 539E3BB52EBA5BE4000BF002 /* ExampleApp */, + 539E3BB42EBA5BE4000BF002 /* Products */, + ); + sourceTree = ""; + }; + 539E3BB42EBA5BE4000BF002 /* Products */ = { + isa = PBXGroup; + children = ( + 539E3BB32EBA5BE4000BF002 /* ExampleApp.app */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 539E3BB22EBA5BE4000BF002 /* ExampleApp */ = { + isa = PBXNativeTarget; + buildConfigurationList = 539E3BBE2EBA5BE6000BF002 /* Build configuration list for PBXNativeTarget "ExampleApp" */; + buildPhases = ( + 539E3BAF2EBA5BE4000BF002 /* Sources */, + 539E3BB02EBA5BE4000BF002 /* Frameworks */, + 539E3BB12EBA5BE4000BF002 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 539E3BB52EBA5BE4000BF002 /* ExampleApp */, + ); + name = ExampleApp; + packageProductDependencies = ( + 533571652EBB430B00679688 /* SnappThemingSVGSupport */, + ); + productName = ExampleApp; + productReference = 539E3BB32EBA5BE4000BF002 /* ExampleApp.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 539E3BAB2EBA5BE4000BF002 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 2600; + LastUpgradeCheck = 2600; + TargetAttributes = { + 539E3BB22EBA5BE4000BF002 = { + CreatedOnToolsVersion = 26.0.1; + }; + }; + }; + buildConfigurationList = 539E3BAE2EBA5BE4000BF002 /* Build configuration list for PBXProject "ExampleApp" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 539E3BAA2EBA5BE4000BF002; + minimizedProjectReferenceProxies = 1; + packageReferences = ( + 533571642EBB430B00679688 /* XCRemoteSwiftPackageReference "SnappThemingSVGSupport" */, + ); + preferredProjectObjectVersion = 77; + productRefGroup = 539E3BB42EBA5BE4000BF002 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 539E3BB22EBA5BE4000BF002 /* ExampleApp */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 539E3BB12EBA5BE4000BF002 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 539E3BAF2EBA5BE4000BF002 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 539E3BBC2EBA5BE6000BF002 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = 44H75889S4; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 539E3BBD2EBA5BE6000BF002 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = 44H75889S4; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SWIFT_COMPILATION_MODE = wholemodule; + }; + name = Release; + }; + 539E3BBF2EBA5BE6000BF002 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 44H75889S4; + ENABLE_APP_SANDBOX = YES; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_PREVIEWS = YES; + ENABLE_USER_SELECTED_FILES = readonly; + GENERATE_INFOPLIST_FILE = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 26.0; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 26.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = io.snappmobile.ExampleApp; + PRODUCT_NAME = "$(TARGET_NAME)"; + REGISTER_APP_GROUPS = YES; + SDKROOT = auto; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator"; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,7"; + XROS_DEPLOYMENT_TARGET = 26.0; + }; + name = Debug; + }; + 539E3BC02EBA5BE6000BF002 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 44H75889S4; + ENABLE_APP_SANDBOX = YES; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_PREVIEWS = YES; + ENABLE_USER_SELECTED_FILES = readonly; + GENERATE_INFOPLIST_FILE = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 26.0; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 26.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = io.snappmobile.ExampleApp; + PRODUCT_NAME = "$(TARGET_NAME)"; + REGISTER_APP_GROUPS = YES; + SDKROOT = auto; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator"; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,7"; + XROS_DEPLOYMENT_TARGET = 26.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 539E3BAE2EBA5BE4000BF002 /* Build configuration list for PBXProject "ExampleApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 539E3BBC2EBA5BE6000BF002 /* Debug */, + 539E3BBD2EBA5BE6000BF002 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 539E3BBE2EBA5BE6000BF002 /* Build configuration list for PBXNativeTarget "ExampleApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 539E3BBF2EBA5BE6000BF002 /* Debug */, + 539E3BC02EBA5BE6000BF002 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 533571642EBB430B00679688 /* XCRemoteSwiftPackageReference "SnappThemingSVGSupport" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/Snapp-Mobile/SnappThemingSVGSupport"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 0.0.3; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 533571652EBB430B00679688 /* SnappThemingSVGSupport */ = { + isa = XCSwiftPackageProductDependency; + package = 533571642EBB430B00679688 /* XCRemoteSwiftPackageReference "SnappThemingSVGSupport" */; + productName = SnappThemingSVGSupport; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 539E3BAB2EBA5BE4000BF002 /* Project object */; +} diff --git a/Sources/ExampleApp/ExampleApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Sources/ExampleApp/ExampleApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/Sources/ExampleApp/ExampleApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Sources/ExampleApp/ExampleApp/Assets.xcassets/AccentColor.colorset/Contents.json b/Sources/ExampleApp/ExampleApp/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/Sources/ExampleApp/ExampleApp/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/ExampleApp/ExampleApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/Sources/ExampleApp/ExampleApp/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..ffdfe15 --- /dev/null +++ b/Sources/ExampleApp/ExampleApp/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,85 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/ExampleApp/ExampleApp/Assets.xcassets/Contents.json b/Sources/ExampleApp/ExampleApp/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Sources/ExampleApp/ExampleApp/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/ExampleApp/ExampleApp/ContentView.swift b/Sources/ExampleApp/ExampleApp/ContentView.swift new file mode 100644 index 0000000..85e1e34 --- /dev/null +++ b/Sources/ExampleApp/ExampleApp/ContentView.swift @@ -0,0 +1,77 @@ +// +// ContentView.swift +// ExampleApp +// +// Created by Oleksii Kolomiiets on 11/4/25. +// + +import OSLog +import SnappTheming +import SnappThemingSVGSupport +import SwiftUI + +struct ContentView: View { + @StateObject private var imageLoader = SVGImageLoader() + @State private var loadError: Error? + @State private var image: Image? + + var body: some View { + VStack(spacing: 32) { + Text("\(imageLoader.currentTheme.rawValue).json") + .font(.title2) + .fontWeight(.bold) + + if let image { + image + .resizable() + .scaledToFit() + .frame(height: 150) + .id(UUID()) + } else if let error = loadError { + Text("Error: \(error.localizedDescription)") + .font(.caption) + .foregroundColor(.red) + } else { + ProgressView() + } + + Button(action: toggleTheme) { + Text("toggle") + } + .buttonStyle(.borderedProminent) + + Text("This example demonstrates SnappThemingSVGSupport theme switching capabilities.") + .font(.caption) + .foregroundColor(.secondary) + .multilineTextAlignment(.center) + } + .padding(32) + .onAppear { + loadTheme() + } + } + + private func loadTheme() { + do { + image = try imageLoader.loadImage(from: .light) + loadError = nil + } catch { + loadError = error + os_log(.fault, "Image loading error: %{public}@", error.localizedDescription) + } + } + + private func toggleTheme() { + do { + image = try imageLoader.toggleImage() + loadError = nil + } catch { + loadError = error + os_log(.fault, "Image toggling error: %{public}@", error.localizedDescription) + } + } +} + +#Preview { + ContentView() +} diff --git a/Sources/ExampleApp/ExampleApp/ExampleApp.swift b/Sources/ExampleApp/ExampleApp/ExampleApp.swift new file mode 100644 index 0000000..75b84cf --- /dev/null +++ b/Sources/ExampleApp/ExampleApp/ExampleApp.swift @@ -0,0 +1,25 @@ +// +// ExampleApp.swift +// ExampleApp +// +// Created by Oleksii Kolomiiets on 11/4/25. +// + +import SnappTheming +import SnappThemingSVGSupport +import SwiftUI + +@main +struct ExampleApp: App { + @State private var isDarkMode = false + + init() { + SnappThemingImageProcessorsRegistry.shared.register(.svg) + } + + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/Sources/ExampleApp/ExampleApp/Resources/dark.json b/Sources/ExampleApp/ExampleApp/Resources/dark.json new file mode 100644 index 0000000..1ef1f9f --- /dev/null +++ b/Sources/ExampleApp/ExampleApp/Resources/dark.json @@ -0,0 +1,6 @@ +{ + "images": { + "svg": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGRhdGEtbmFtZT0iTGF5ZXIgMSIgd2lkdGg9IjgwIiBoZWlnaHQ9IjgwIiB2aWV3Qm94PSIwIDAgODAgODAiIHN0eWxlPSJ6b29tOiAxOyI+PHBhdGggZD0iTTYzLjI5IDE5LjFhMTUuMzkgMTUuMzkgMCAwIDAtMjIuNTkgMi4zNCAxIDEgMCAwIDEtMS41OSAwIDE1LjE0IDE1LjE0IDAgMCAwLTIyLjM4LTIuMkMxMSAyNC40MyAxMSAzMy42NCAxNS4yNiA0MC4wN2M1LjU4IDguMzYgMjIuMTkgMjMuNyAyMi4xOSAyMy43YTQuMTQgNC4xNCAwIDAgMCA1LjY2LS4xOHMxNS0xNC42NiAyMS40My0yMy41MkM2OS4wOSAzMy44MiA2OSAyNC4zIDYzLjI5IDE5LjF6IiBmaWxsPSJibGFjayIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS13aWR0aD0iMiIvPjwvc3ZnPg==", + "table": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAAOwAAADsAEnxA+tAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAA1JJREFUeJzt3T1rFFEUgOHXGAsVRIhKesEkYkzjf/CjT36KtbWVv8NCJWirVYoQECSdYGUhqBFJ0rmoRbbwY244szOZ3dzzPnBZOOLsjPfdDyx2QJIkSZIkSVL9znT8+1eA1fGjhvcF2AX2hn7iFWATGAG/XFNdI+AFsHTsjvXoAXB4ghfkmmwdAPeP2bderODmz/I6AJaLu9eDzRm4SNfx60Vx9xq0+RJ4FfgEnG3zBBrcCFgk+MVwvsWBV2ne/J/Au/HjabEMXGyYv+XoVXQazAFr48c/zQO3gTd9P+E6zW85230/0QB2aL6WNi+IWbBN83WsRw/wbz3HKX1cjFocY1Z0/f+PWVH6tw9fX5sAalJLAJ0ZQGxeLQOIzatlALF5tQwgNq+WAcTm1TKA2LxaWQMoMYAkfAcYM4DYvFoGkJwBxObVMoDYvFoGEJtXywBi82plDaDEAJLwHWDMAGLzahlAbF4tA4jNq2UAyRlAbF4tA4jNq5U1gBIDSMJ3gDEDiM2rZQCxebUMIDavlgHE5tUygOQMIDavVtYASgwgCd8BxgwgNq+WAcTm1TKA2LxaBhCbV8sAYvNqZQ2gxACSSLfRJQYQm1erj59GXQKe9nCcIV0ozJ8A+0OeSEedbxLRRwALtPht2hl3d9onMLSsHwEaM4DkDCA5A0iujy+BH4DHPRxnSE+A8w3zRxzdFue0eAhcH+rJNmi+O8XWUCfQo+80X8vNaZ7UBLZovo6N6AH8CEjOAJIzgOQMIDkDSM4AkjOA5AwgOQNIzgCSM4DkDCA5A0jOAJIzgOQMIDkDSM4AkjOA5AwgOQNIzgCSM4DkDCA5A0jOAJIzgOQMIDkDSM4AkjOA5AwgOQNIzgCSM4DkDCA5A0jOAJIzgOQMIDkDSC5rAIct59VqE8CvwryP3xse2uuG2Xvg49An0tG5wry0V/9ps3l7hfkd4C3ws8Wxpu1yw+wSsDP0iXQwB6wV/uxr9CBtbpJ0haNf0j6Nr/hMRsAi5RfsX9p8BHwFXk1yRhrUS4KbP4ll4IDmnyh3TX/tAzeKu9eTexjBLK59Brzr2RLwHPhxghfkiq0fwDMmfOV3vVPmAnALuNbxOJrMZ2AX+DbtE5EkSZIkSZI0y34Dp9katvKJ0poAAAAASUVORK5CYII=", + } +} diff --git a/Sources/ExampleApp/ExampleApp/Resources/light.json b/Sources/ExampleApp/ExampleApp/Resources/light.json new file mode 100644 index 0000000..08ee3d1 --- /dev/null +++ b/Sources/ExampleApp/ExampleApp/Resources/light.json @@ -0,0 +1,6 @@ +{ + "images": { + "svg": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGRhdGEtbmFtZT0iTGF5ZXIgMSIgd2lkdGg9IjgwIiBoZWlnaHQ9IjgwIiB2aWV3Qm94PSIwIDAgODAgODAiIHN0eWxlPSJ6b29tOiAxOyI+PHBhdGggZD0iTTYzLjI5IDE5LjFhMTUuMzkgMTUuMzkgMCAwIDAtMjIuNTkgMi4zNCAxIDEgMCAwIDEtMS41OSAwIDE1LjE0IDE1LjE0IDAgMCAwLTIyLjM4LTIuMkMxMSAyNC40MyAxMSAzMy42NCAxNS4yNiA0MC4wN2M1LjU4IDguMzYgMjIuMTkgMjMuNyAyMi4xOSAyMy43YTQuMTQgNC4xNCAwIDAgMCA1LjY2LS4xOHMxNS0xNC42NiAyMS40My0yMy41MkM2OS4wOSAzMy44MiA2OSAyNC4zIDYzLjI5IDE5LjF6IiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS13aWR0aD0iMiIvPjwvc3ZnPg==", + "table": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAE69AABOvQFzamgUAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAABIFJREFUeJzt3T2IVFcYxvG/IRgYNoiaRnZTiX2wMrVumpDC2kYJpBVFbRexESztUkSwUQgpslWakD4EUkVFCxMxrPgJCqkWtbh7cdnMvvfOveece95znh9MN3c5O8+j7zsf7ICIiIiIiNRlz4hrl4EjwGfAS+A+8DjEoSRfS8BF4A7wbs7tHnAJ2DfVASWeVeBf5ge/8/YKWENFKMYZYJN+4asIhfmKYeGrCAX4lGaxGxP+9ttr4CqwP+UvIcNdIlz4+h/BoXvEKYCK4MDndAf4BlgHnva4r4rgzHHs0J4Bh7buOwPOARsd16gIjpzEDmt9zjVtEZ50XKsiOHACO6T7xrUqQgFW6A5oueNnqAjO/YMdzKmeP+cT4Dv6v5S8202vIyR2EzuQ7xf8eSqCM98yfA+wqAhOHGb8HmBRERwItQdYVISMhd4DLCpChmLtARYVISOx9wCLipCJFHuARUWYWMo9wKIiTGSKPcCiIiQ25R5gURESmnoPsKgICeSyB1hUhIhy2wMsKkIEue4BFhUhsJz3AIuKEIiHPcCiIozkaQ+wqAgDedwDLCrCAF73AIuKsADve4BFReihlD3AoiIYStsDLCrCLkrcAywqwg4l7wEWFWFLDXuApfoi1LQHWKouQm17gKXKItS6B1iqKkLte4CliiJoD+hWfBG0B/RTbBG0ByymuCJoDximmCJoDxiniCI86jic9oBuM+A8Yf6Y1jng45SH1x4QTqgi/AYcTHVo7QHhhSjCHzTf8BKd9oB4xhbhRqqD6vWAuMYsi1+mOGDXHuDt9jdwheaBz8mQItxOcbCuPcDr7aeQD1JAi4yG/4C9sQ/UZw/wevsm4OMU2gy4RvfvcDTFYbr2AK+36yEfpEj+xP4dvt7two8CHmLMt5DKREIV4DDNV8yU6JepD2BoR8AXHffbiH2QUpfAH0M+SAFltwSW9DTwLfAQuEyCB25BQ54G3kpxML0QFNeYF4KOxT6cXgqOZ+xLwT+kOKTeDAovxJtBv5PozSC9HRxOqLeDfwUOpDq0PhAyXsgPhJwl4QdCNP/Hcf+RMM3/YdwH39L8X0wxwbf0/L+f4oIHzf8+igy+pfm/u6KDb2n+/18Vwbc0/z+oKnjQ/G9VF3yr9vlfbfCtWud/9cG3apv/Cn6bmua/gp+jhvmv4A0lz38F30OJ81/B91Ta/FfwCypl/iv4gbzPfwU/ktf5r+AD8Dj/FXxAnua/go/Aw/xX8BHlPP8VfGS5zn8Fn0hu81/BJ5bL/FfwE5l6/iv4CU05/xV8BqaY/wo+Iynnv4LPUIr5r+AzFXv+K/jMxZr/Ct6J0PNfwTsTav4reIdWGD//ZzTfYhXijx+tAftC/XLSbZXh81/BF+Akdjjrc65R8AU5jh3Sc+DQ1n3b4Dc6rlHwjvTZAV4APwNPe9xXwTt0l3HBKnjnLqLgqzaj+Q69UMHrebxDq8Am+hdftdMMK4GCL8gq8BgFX7Ul4ALwF/ODv0OzOCp4J8Z82eMycAQ4SPN6wAOaN3tEREREREQkW+8BvCnA1qaj+zAAAAAASUVORK5CYII=", + }, +} diff --git a/Sources/ExampleApp/ExampleApp/Resources/pink.json b/Sources/ExampleApp/ExampleApp/Resources/pink.json new file mode 100644 index 0000000..609504c --- /dev/null +++ b/Sources/ExampleApp/ExampleApp/Resources/pink.json @@ -0,0 +1,6 @@ +{ + "images": { + "svg": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGRhdGEtbmFtZT0iTGF5ZXIgMSIgd2lkdGg9IjgwIiBoZWlnaHQ9IjgwIiB2aWV3Qm94PSIwIDAgODAgODAiIHN0eWxlPSJ6b29tOiAxOyI+PHBhdGggZD0iTTYzLjI5IDE5LjFhMTUuMzkgMTUuMzkgMCAwIDAtMjIuNTkgMi4zNCAxIDEgMCAwIDEtMS41OSAwIDE1LjE0IDE1LjE0IDAgMCAwLTIyLjM4LTIuMkMxMSAyNC40MyAxMSAzMy42NCAxNS4yNiA0MC4wN2M1LjU4IDguMzYgMjIuMTkgMjMuNyAyMi4xOSAyMy43YTQuMTQgNC4xNCAwIDAgMCA1LjY2LS4xOHMxNS0xNC42NiAyMS40My0yMy41MkM2OS4wOSAzMy44MiA2OSAyNC4zIDYzLjI5IDE5LjF6IiBmaWxsPSJwaW5rIiBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIyIi8+PC9zdmc+", + "table": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAE69AABOvQFzamgUAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAABIFJREFUeJzt3T2IVFcYxvG/IRgYNoiaRnZTiX2wMrVumpDC2kYJpBVFbRexESztUkSwUQgpslWakD4EUkVFCxMxrPgJCqkWtbh7cdnMvvfOveece95znh9MN3c5O8+j7zsf7ICIiIiIiNRlz4hrl4EjwGfAS+A+8DjEoSRfS8BF4A7wbs7tHnAJ2DfVASWeVeBf5ge/8/YKWENFKMYZYJN+4asIhfmKYeGrCAX4lGaxGxP+9ttr4CqwP+UvIcNdIlz4+h/BoXvEKYCK4MDndAf4BlgHnva4r4rgzHHs0J4Bh7buOwPOARsd16gIjpzEDmt9zjVtEZ50XKsiOHACO6T7xrUqQgFW6A5oueNnqAjO/YMdzKmeP+cT4Dv6v5S8202vIyR2EzuQ7xf8eSqCM98yfA+wqAhOHGb8HmBRERwItQdYVISMhd4DLCpChmLtARYVISOx9wCLipCJFHuARUWYWMo9wKIiTGSKPcCiIiQ25R5gURESmnoPsKgICeSyB1hUhIhy2wMsKkIEue4BFhUhsJz3AIuKEIiHPcCiIozkaQ+wqAgDedwDLCrCAF73AIuKsADve4BFReihlD3AoiIYStsDLCrCLkrcAywqwg4l7wEWFWFLDXuApfoi1LQHWKouQm17gKXKItS6B1iqKkLte4CliiJoD+hWfBG0B/RTbBG0ByymuCJoDximmCJoDxiniCI86jic9oBuM+A8Yf6Y1jng45SH1x4QTqgi/AYcTHVo7QHhhSjCHzTf8BKd9oB4xhbhRqqD6vWAuMYsi1+mOGDXHuDt9jdwheaBz8mQItxOcbCuPcDr7aeQD1JAi4yG/4C9sQ/UZw/wevsm4OMU2gy4RvfvcDTFYbr2AK+36yEfpEj+xP4dvt7two8CHmLMt5DKREIV4DDNV8yU6JepD2BoR8AXHffbiH2QUpfAH0M+SAFltwSW9DTwLfAQuEyCB25BQ54G3kpxML0QFNeYF4KOxT6cXgqOZ+xLwT+kOKTeDAovxJtBv5PozSC9HRxOqLeDfwUOpDq0PhAyXsgPhJwl4QdCNP/Hcf+RMM3/YdwH39L8X0wxwbf0/L+f4oIHzf8+igy+pfm/u6KDb2n+/18Vwbc0/z+oKnjQ/G9VF3yr9vlfbfCtWud/9cG3apv/Cn6bmua/gp+jhvmv4A0lz38F30OJ81/B91Ta/FfwCypl/iv4gbzPfwU/ktf5r+AD8Dj/FXxAnua/go/Aw/xX8BHlPP8VfGS5zn8Fn0hu81/BJ5bL/FfwE5l6/iv4CU05/xV8BqaY/wo+Iynnv4LPUIr5r+AzFXv+K/jMxZr/Ct6J0PNfwTsTav4reIdWGD//ZzTfYhXijx+tAftC/XLSbZXh81/BF+Akdjjrc65R8AU5jh3Sc+DQ1n3b4Dc6rlHwjvTZAV4APwNPe9xXwTt0l3HBKnjnLqLgqzaj+Q69UMHrebxDq8Am+hdftdMMK4GCL8gq8BgFX7Ul4ALwF/ODv0OzOCp4J8Z82eMycAQ4SPN6wAOaN3tEREREREQkW+8BvCnA1qaj+zAAAAAASUVORK5CYII=", + }, +} diff --git a/Sources/ExampleApp/ExampleApp/SVGImageLoader.swift b/Sources/ExampleApp/ExampleApp/SVGImageLoader.swift new file mode 100644 index 0000000..f2650b7 --- /dev/null +++ b/Sources/ExampleApp/ExampleApp/SVGImageLoader.swift @@ -0,0 +1,52 @@ +// +// SVGImageLoader.swift +// ExampleApp +// +// Created by Oleksii Kolomiiets on 11/4/25. +// + +import Combine +import SnappTheming +import SnappThemingSVGSupport +import SwiftUI + +class SVGImageLoader: ObservableObject { + @Published var currentTheme: Theme + + init(_ currentTheme: Theme = .light) { + self.currentTheme = currentTheme + } + + func toggleImage() throws -> Image { + return try loadImage(from: currentTheme.next()) + } + + func loadImage( + from theme: Theme, + withExtension ext: String = "json" + ) throws -> Image { + #if SWIFT_PACKAGE + let bundle = Bundle.module + #else + let bundle = Bundle.main + #endif + + // TODO: iOS simulator executable target fails with Bundle.module: + // "failure in void __BKSHIDEvent__BUNDLE_IDENTIFIER_FOR_CURRENT_PROCESS_IS_NIL__" + // Works correctly when package is built as library target (macOS) + + guard let url = bundle.url(forResource: theme.rawValue, withExtension: ext) else { + throw SVGImageLoaderError.noJson("\(theme).\(ext)") + } + + print(url) + currentTheme = theme + + let jsonData = try Data(contentsOf: url) + let json = String(data: jsonData, encoding: .utf8) ?? "" + let declaration = try SnappThemingParser.parse(from: json) + + let icon = declaration.images.svg + return icon + } +} diff --git a/Sources/ExampleApp/ExampleApp/SVGImageLoaderError.swift b/Sources/ExampleApp/ExampleApp/SVGImageLoaderError.swift new file mode 100644 index 0000000..0b2d315 --- /dev/null +++ b/Sources/ExampleApp/ExampleApp/SVGImageLoaderError.swift @@ -0,0 +1,19 @@ +// +// SVGImageLoaderError.swift +// SnappThemingSVGSupport +// +// Created by Oleksii Kolomiiets on 11/6/25. +// + +import Foundation + +enum SVGImageLoaderError: Error, LocalizedError { + case noJson(String) + + var errorDescription: String? { + switch self { + case .noJson(let string): + return "Could not find \(string)" + } + } +} diff --git a/Sources/ExampleApp/ExampleApp/Theme.swift b/Sources/ExampleApp/ExampleApp/Theme.swift new file mode 100644 index 0000000..d6886f3 --- /dev/null +++ b/Sources/ExampleApp/ExampleApp/Theme.swift @@ -0,0 +1,20 @@ +// +// Theme.swift +// SnappThemingSVGSupport +// +// Created by Oleksii Kolomiiets on 11/6/25. +// + +import Foundation + +enum Theme: String { + case light, dark, pink + + func next() -> Self { + switch self { + case .light: .dark + case .dark: .pink + case .pink: .light + } + } +} diff --git a/Sources/SnappThemingSVGSupport/SnappThemingSVGSupport.docc/Resources/logo.png b/Sources/SnappThemingSVGSupport/SnappThemingSVGSupport.docc/Resources/logo.png index 301c940..10501b8 100644 Binary files a/Sources/SnappThemingSVGSupport/SnappThemingSVGSupport.docc/Resources/logo.png and b/Sources/SnappThemingSVGSupport/SnappThemingSVGSupport.docc/Resources/logo.png differ diff --git a/Sources/SnappThemingSVGSupport/SnappThemingSVGSupport.docc/Resources/logo@2x.png b/Sources/SnappThemingSVGSupport/SnappThemingSVGSupport.docc/Resources/logo@2x.png index d9b17ba..ec53544 100644 Binary files a/Sources/SnappThemingSVGSupport/SnappThemingSVGSupport.docc/Resources/logo@2x.png and b/Sources/SnappThemingSVGSupport/SnappThemingSVGSupport.docc/Resources/logo@2x.png differ diff --git a/Sources/SnappThemingSVGSupport/SnappThemingSVGSupport.docc/Resources/logo@3x.png b/Sources/SnappThemingSVGSupport/SnappThemingSVGSupport.docc/Resources/logo@3x.png index 2e69dd6..e2e1ab7 100644 Binary files a/Sources/SnappThemingSVGSupport/SnappThemingSVGSupport.docc/Resources/logo@3x.png and b/Sources/SnappThemingSVGSupport/SnappThemingSVGSupport.docc/Resources/logo@3x.png differ diff --git a/Sources/SnappThemingSVGSupport/SnappThemingSVGSupport.docc/SnappThemingSVGSupport.md b/Sources/SnappThemingSVGSupport/SnappThemingSVGSupport.docc/SnappThemingSVGSupport.md index d658398..624b1f1 100644 --- a/Sources/SnappThemingSVGSupport/SnappThemingSVGSupport.docc/SnappThemingSVGSupport.md +++ b/Sources/SnappThemingSVGSupport/SnappThemingSVGSupport.docc/SnappThemingSVGSupport.md @@ -1,21 +1,28 @@ # ``SnappThemingSVGSupport`` -A library that enables support for SVG asset handling in `SnappTheming` +@Metadata { + @PageImage(purpose: icon, source:"logo") + @PageColor(gray) +} -## Overview +Enables support for SVG asset handling in `SnappTheming` -![SnappThemingSVGSupport logo](logo.png) +## Overview -Enabling the SVG support is as easy as adding one line to your codebase. +Enabling the SVG support is as easy as adding one line to your codebase. It extends [SnppTheming](https://github.com/Snapp-Mobile/SnappTheming) functionality. -See the example below +## Usage ```swift import SnappTheming import SnappThemingSVGSupport // in the app delegate or SwiftUI Application init +let svg: SnappThemingSVGSupportSVGProcessor = .svg SnappThemingImageProcessorsRegistry.shared.register(.svg) + +// in case it needed processor can be unregistered +SnappThemingImageProcessorsRegistry.shared.unregister(SnappThemingSVGSupportSVGProcessor.self) ``` [Clone on GitHub](https://github.com/Snapp-Mobile/SnappThemingSVGSupport) diff --git a/Sources/SnappThemingSVGSupport/SnappThemingSVGSupportImageConverter.swift b/Sources/SnappThemingSVGSupport/SnappThemingSVGSupportImageConverter.swift index fa88306..855f4a0 100644 --- a/Sources/SnappThemingSVGSupport/SnappThemingSVGSupportImageConverter.swift +++ b/Sources/SnappThemingSVGSupport/SnappThemingSVGSupportImageConverter.swift @@ -10,44 +10,40 @@ import OSLog import SVGKit import SnappTheming -/// A utility class for converts SVG data into a `UIImage`. -final class SnappThemingSVGSupportImageConverter { - /// The rendered `UIImage` representation of the SVG data. - private(set) var image: SnappThemingImage - +/// A utility class for converts SVG data into a `SnappThemingImage`. +final class SnappThemingSVGSupportImageConverter: Sendable { /// Initializes the `SnappThemingSVGSupportImageConverter` with the provided SVG data. /// /// - Parameters: - /// - data: The SVG data to be rendered into a `SnappThemingImage`. + /// - object: The object of `SnappThemingImageObject` type to contain SVG image data and file url if available. /// - svgImageType: The type used for SVG rendering, defaulting to `SVGKImage.self`. /// - fallbackImageName: The system image name used as a fallback if the SVG data is invalid or cannot be rendered. /// /// - Note: If the SVG data is invalid or rendering fails, a system image with the name specified in `fallbackImageName` /// (default: `"exclamationmark.triangle"`) will be used instead. - init( - data: Data, - svgImageType: SVGKImage.Type = SVGKImage.self, - fallbackImageName: String = "exclamationmark.triangle" - ) { - var svgImage: SVGKImage? = svgImageType.init(data: data) + func convert( + _ object: SnappThemingImageObject, + ofType svgImageType: SVGKImage.Type = SVGKImage.self, + withFallback fallbackImageName: String = "exclamationmark.triangle" + ) -> SnappThemingImage { + let svgImage: SVGKImage? - // Retry logic for simulator or potential parsing issues - // error: `*** Assertion failure in +[SVGLength pixelsPerInchForCurrentDevice], SVGLength.m:238` - if svgImage == nil { - os_log(.info, "Initial SVG parsing failed. Retrying...") - svgImage = svgImageType.init(data: data) + if let url = object.url { + svgImage = prepareSVGImage(using: url, svgImageType: svgImageType) + } else { + svgImage = prepareSVGImage(using: object.data, svgImageType: svgImageType) } if let validSVGImage = svgImage { if let renderedImage = validSVGImage.themeImage { - self.image = renderedImage + return renderedImage } else { - self.image = Self.defaultFallbackImage(fallbackImageName) os_log(.error, "Failed to render SVG to UIImage. Using fallback image.") + return Self.defaultFallbackImage(fallbackImageName) } } else { - self.image = Self.defaultFallbackImage(fallbackImageName) os_log(.error, "Failed to parse SVG data after retry. Using fallback image.") + return Self.defaultFallbackImage(fallbackImageName) } } @@ -55,4 +51,18 @@ final class SnappThemingSVGSupportImageConverter { private static func defaultFallbackImage(_ name: String) -> SnappThemingImage { .system(name) ?? SnappThemingImage() } + + private func prepareSVGImage(using url: URL, svgImageType: SVGKImage.Type) -> SVGKImage? { + // Retry logic for simulator or potential parsing issues + // error: `*** Assertion failure in +[SVGLength pixelsPerInchForCurrentDevice], SVGLength.m:238` + // First attempt may fail with PPI assertion, but retry succeeds due to UIScreen initialization + svgImageType.init(contentsOfFile: url.path()) ?? svgImageType.init(contentsOfFile: url.path()) + } + + private func prepareSVGImage(using data: Data, svgImageType: SVGKImage.Type) -> SVGKImage? { + // Retry logic for simulator or potential parsing issues + // error: `*** Assertion failure in +[SVGLength pixelsPerInchForCurrentDevice], SVGLength.m:238` + // First attempt may fail with PPI assertion, but retry succeeds due to UIScreen initialization + svgImageType.init(data: data) ?? svgImageType.init(data: data) + } } diff --git a/Sources/SnappThemingSVGSupport/SnappThemingSVGSupportSVGProcessor.swift b/Sources/SnappThemingSVGSupport/SnappThemingSVGSupportSVGProcessor.swift index 69617ab..3f23968 100644 --- a/Sources/SnappThemingSVGSupport/SnappThemingSVGSupportSVGProcessor.swift +++ b/Sources/SnappThemingSVGSupport/SnappThemingSVGSupportSVGProcessor.swift @@ -20,28 +20,48 @@ extension SnappThemingExternalImageProcessorProtocol where Self == SnappThemingS /// A processor for handling SVG image data, conforming to `SnappThemingExternalImageProcessorProtocol`. public struct SnappThemingSVGSupportSVGProcessor: SnappThemingExternalImageProcessorProtocol { - /// Processes the provided image data and type and converts it into a `SnappThemingImage` if the type is `.svg`. + private let converter: SnappThemingSVGSupportImageConverter + + /// Processes image data and converts it to a `SnappThemingImage` for SVG types. + /// + /// This method handles SVG image conversion by delegating to the internal converter. + /// For non-SVG types, the method returns `nil` and logs an error. + /// + /// - Parameters: + /// - object: The image object containing the SVG data to process. + /// - type: The uniform type identifier for the image. Only `.svg` is supported. /// - /// - Parameter data: Image `Data`. - /// - Parameter type: Image `UTType`. - /// - Returns: A `SnappThemingImage` if the processing and conversion are successful; otherwise, `nil`. + /// - Returns: A `SnappThemingImage` if the input is valid SVG data; otherwise, `nil`. /// - /// - On **iOS, iPadOS, tvOS, watchOS**, and **visionOS**, `SnappThemingImage` is an alias for `UIImage`. - /// - On **macOS**, `SnappThemingImage` is an alias for `NSImage`. + /// ## Platform Availability + /// - On **iOS**, **iPadOS**, **tvOS**, **watchOS**, and **visionOS**: `SnappThemingImage` is a type alias for `UIImage`. + /// - On **macOS**: `SnappThemingImage` is a type alias for `NSImage`. /// - /// - Note: This method specifically handles `.svg` type. If the type does not match, the method returns `nil`. - /// - Warning: Ensure that the `data` is valid SVG data to avoid potential rendering issues. - public func process(_ data: Data, of type: UTType) -> SnappThemingImage? { + /// ## Supported Types + /// Only `.svg` type is supported. Providing any other type will result in `nil` and an error log. + /// + /// - Warning: Ensure the provided data is valid SVG to prevent rendering issues. + /// + /// ## Example + /// ```swift + /// let svgObject = SnappThemingImageObject(data: svgData) + /// if let image = processor.process(svgObject, of: .svg) { + /// imageView.image = image + /// } + /// ``` + public func process(_ object: SnappThemingImageObject, of type: UTType) -> SnappThemingImage? { guard type == .svg else { os_log(.error, "Invalid type provided: %{public}@. Only .svg type is supported.", "\(type)") return nil } - return SnappThemingSVGSupportImageConverter(data: data).image + return converter.convert(object) } /// Initializes the `SnappThemingSVGSupportSVGProcessor`. /// /// This default initializer is used to create instances of the processor for processing SVG data. - public init() {} + public init() { + converter = SnappThemingSVGSupportImageConverter() + } } diff --git a/Tests/SnappThemingSVGSupportTests/ImageConverterTests.swift b/Tests/SnappThemingSVGSupportTests/ImageConverterTests.swift index 0f59bef..b3f3f32 100644 --- a/Tests/SnappThemingSVGSupportTests/ImageConverterTests.swift +++ b/Tests/SnappThemingSVGSupportTests/ImageConverterTests.swift @@ -13,48 +13,54 @@ import Testing @Suite struct ImageConverterTests { + private let converter = SnappThemingSVGSupportImageConverter() + @Test func testConverterSuccessfulConversionExpectedImage() throws { let expectedImageData = try #require(svgIconString.data(using: .utf8)) - let converter = SnappThemingSVGSupportImageConverter(data: expectedImageData) + let object = SnappThemingImageObject(data: expectedImageData) + let image = converter.convert(object) - #expect(converter.image.size == CGSize(width: 24, height: 24)) + #expect(image.size == CGSize(width: 24, height: 24)) } @Test func testConverterSVGImageFailedFallbackImage() throws { let expectedImageData = try #require(svgIconString.data(using: .utf8)) - let converter = SnappThemingSVGSupportImageConverter(data: expectedImageData, svgImageType: MockSVGKImage.self) + let object = SnappThemingImageObject(data: expectedImageData) + let image = converter.convert(object, ofType: MockSVGKImage.self) let fallbackImage: SnappThemingImage = try #require(.system("exclamationmark.triangle")) - #expect(converter.image.size != CGSize(width: 24, height: 24)) + #expect(image.size != CGSize(width: 24, height: 24)) #if canImport(UIKit) - #expect(converter.image == fallbackImage) + #expect(image == fallbackImage) #elseif canImport(AppKit) - #expect(converter.image.tiffRepresentation == fallbackImage.tiffRepresentation) + #expect(image.tiffRepresentation == fallbackImage.tiffRepresentation) #endif } @Test func testConverterEmptyDataFallbackImage() async throws { - let converter = SnappThemingSVGSupportImageConverter(data: Data()) + let object = SnappThemingImageObject(data: Data()) + let image = converter.convert(object) let fallbackImage: SnappThemingImage = try #require(.system("exclamationmark.triangle")) #if canImport(UIKit) - #expect(converter.image == fallbackImage) + #expect(image == fallbackImage) #elseif canImport(AppKit) - #expect(converter.image.tiffRepresentation == fallbackImage.tiffRepresentation) + #expect(image.tiffRepresentation == fallbackImage.tiffRepresentation) #endif } @Test func testConverterEmptyDataAndBrokenFallbackImage() async throws { - let converter = SnappThemingSVGSupportImageConverter(data: Data(), fallbackImageName: "") + let object = SnappThemingImageObject(data: Data()) + let image = converter.convert(object, withFallback: "") let fallbackImage: SnappThemingImage = try #require(.system("exclamationmark.triangle")) #if canImport(UIKit) - #expect(converter.image == SnappThemingImage()) + #expect(image == SnappThemingImage()) #elseif canImport(AppKit) - #expect(converter.image.tiffRepresentation != fallbackImage.tiffRepresentation) - #expect(converter.image.tiffRepresentation == nil) + #expect(image.tiffRepresentation != fallbackImage.tiffRepresentation) + #expect(image.tiffRepresentation == nil) #endif } } diff --git a/Tests/SnappThemingSVGSupportTests/ParserTests.swift b/Tests/SnappThemingSVGSupportTests/ParserTests.swift index 1bdab55..2a5e028 100644 --- a/Tests/SnappThemingSVGSupportTests/ParserTests.swift +++ b/Tests/SnappThemingSVGSupportTests/ParserTests.swift @@ -25,7 +25,6 @@ struct ParserTests { let declaration = try SnappThemingParser.parse(from: json) #expect(declaration.images.cache.count == 1) - let representation = try #require(declaration.images.cache["svgImage"]?.value) - #expect(representation.data != nil) + let _ = try #require(declaration.images.cache["svgImage"]?.value) } } diff --git a/Tests/SnappThemingSVGSupportTests/SVGProcessorTests.swift b/Tests/SnappThemingSVGSupportTests/SVGProcessorTests.swift index 7d5be5b..4fd4245 100644 --- a/Tests/SnappThemingSVGSupportTests/SVGProcessorTests.swift +++ b/Tests/SnappThemingSVGSupportTests/SVGProcessorTests.swift @@ -17,19 +17,22 @@ struct SVGProcessorTests { @Test func testSVGProcessorFail() { - #expect(sut.process(Data(), of: .png) == nil) - #expect(sut.process(Data(), of: .jpeg) == nil) - #expect(sut.process(Data(), of: .pdf) == nil) - #expect(sut.process(Data(), of: .gif) == nil) + let object = SnappThemingImageObject(data: Data()) + #expect(sut.process(object, of: .png) == nil) + #expect(sut.process(object, of: .jpeg) == nil) + #expect(sut.process(object, of: .pdf) == nil) + #expect(sut.process(object, of: .gif) == nil) } @Test func testSVGProcessorFallback() throws { let emptyStringData = try #require("".data(using: .utf8)) let fallbackImage: SnappThemingImage = try #require(.system("exclamationmark.triangle")) + let emptyStringDataObject = SnappThemingImageObject(data: emptyStringData) - let emptyStringDataImage = try #require(sut.process(emptyStringData, of: .svg)) - let emptyDataImage = try #require(sut.process(Data(), of: .svg)) + let emptyStringDataImage = try #require(sut.process(emptyStringDataObject, of: .svg)) + let emptyDataObject = SnappThemingImageObject(data: Data()) + let emptyDataImage = try #require(sut.process(emptyDataObject, of: .svg)) #if canImport(UIKit) #expect(emptyStringDataImage == fallbackImage) @@ -44,7 +47,8 @@ struct SVGProcessorTests { @Test func testSVGProcessorExpectedImage() throws { let svgData = try #require(svgIconString.data(using: .utf8)) - let processedIcon = try #require(sut.process(svgData, of: .svg)) + let svgDataObject = SnappThemingImageObject(data: svgData) + let processedIcon = try #require(sut.process(svgDataObject, of: .svg)) #expect(processedIcon.size.width == 24) #expect(processedIcon.size.height == 24)