Quellcode durchsuchen

Fix format-xx.sh files wrt logging, add proper Swift formatting configuration.

Mario Zechner vor 1 Monat
Ursprung
Commit
d409ff23ff
49 geänderte Dateien mit 892 neuen und 690 gelöschten Zeilen
  1. 4 4
      Package.swift
  2. 52 0
      formatters/.swift-format
  3. 26 27
      formatters/format-cpp.sh
  4. 35 17
      formatters/format-csharp.sh
  5. 29 7
      formatters/format-dart.sh
  6. 32 8
      formatters/format-haxe.sh
  7. 29 10
      formatters/format-java.sh
  8. 30 7
      formatters/format-swift.sh
  9. 32 8
      formatters/format-ts.sh
  10. 3 23
      formatters/format.sh
  11. 14 15
      formatters/logging/logging.sh
  12. 89 54
      formatters/logging/terminal-logging-guide.md
  13. 16 22
      spine-c/build.sh
  14. 9 12
      spine-cpp/build.sh
  15. 4 7
      spine-cpp/tests/test.sh
  16. 8 8
      spine-flutter/example/ios/Runner/AppDelegate.swift
  17. 0 1
      spine-flutter/example/macos/Flutter/GeneratedPluginRegistrant.swift
  18. 3 3
      spine-flutter/example/macos/Runner/AppDelegate.swift
  19. 8 8
      spine-flutter/example/macos/Runner/MainFlutterWindow.swift
  20. 12 6
      spine-haxe/build.sh
  21. 5 5
      spine-ios/Example - Cocoapods/Spine iOS Example/SimpleAnimation.swift
  22. 1 1
      spine-ios/Example - Cocoapods/Spine iOS Example/Spine_iOS_ExampleApp.swift
  23. 8 6
      spine-ios/Example/Spine iOS Example/AnimationStateEvents.swift
  24. 9 8
      spine-ios/Example/Spine iOS Example/DebugRendering.swift
  25. 8 8
      spine-ios/Example/Spine iOS Example/DisableRendering.swift
  26. 20 20
      spine-ios/Example/Spine iOS Example/DressUp.swift
  27. 10 9
      spine-ios/Example/Spine iOS Example/IKFollowing.swift
  28. 1 1
      spine-ios/Example/Spine iOS Example/MainView.swift
  29. 24 23
      spine-ios/Example/Spine iOS Example/Physics.swift
  30. 5 5
      spine-ios/Example/Spine iOS Example/PlayPauseAnimation.swift
  31. 5 5
      spine-ios/Example/Spine iOS Example/SimpleAnimation.swift
  32. 2 2
      spine-ios/Example/Spine iOS Example/SimpleAnimationViewControllerRepresentable.swift
  33. 2 2
      spine-ios/Example/Spine iOS Example/SpineExampleApp.swift
  34. 10 10
      spine-ios/Sources/Spine/AnimationStateWrapper.swift
  35. 15 14
      spine-ios/Sources/Spine/BoundsProvider.swift
  36. 3 3
      spine-ios/Sources/Spine/Extensions/MTLClearColor+UIColor.swift
  37. 2 2
      spine-ios/Sources/Spine/Extensions/RenderCommand+Vertices.swift
  38. 18 14
      spine-ios/Sources/Spine/Extensions/SkeletonDrawableWrapper+CGImage.swift
  39. 3 3
      spine-ios/Sources/Spine/Metal/SpineObjects.swift
  40. 131 129
      spine-ios/Sources/Spine/Metal/SpineRenderer.swift
  41. 18 18
      spine-ios/Sources/Spine/SkeletonDrawableWrapper.swift
  42. 58 57
      spine-ios/Sources/Spine/Spine.Generated+Extensions.swift
  43. 36 36
      spine-ios/Sources/Spine/SpineController.swift
  44. 28 20
      spine-ios/Sources/Spine/SpineUIView.swift
  45. 6 6
      spine-ios/Sources/Spine/SpineView.swift
  46. 4 4
      spine-ios/Sources/SpineModule/SpineModule.swift
  47. 15 10
      spine-ts/build.sh
  48. 8 18
      tests/generate-serializers.sh
  49. 2 4
      tests/test.sh

+ 4 - 4
Package.swift

@@ -27,7 +27,7 @@ let package = Package(
                 .byName(
                 .byName(
                     name: "Spine",
                     name: "Spine",
                     condition: .when(platforms: [
                     condition: .when(platforms: [
-                        .iOS,
+                        .iOS
                     ])
                     ])
                 ),
                 ),
                 "SpineCppLite",
                 "SpineCppLite",
@@ -38,7 +38,7 @@ let package = Package(
         .target(
         .target(
             name: "Spine",
             name: "Spine",
             dependencies: [
             dependencies: [
-                "SpineCppLite", "SpineShadersStructs"
+                "SpineCppLite", "SpineShadersStructs",
             ],
             ],
             path: "spine-ios/Sources/Spine"
             path: "spine-ios/Sources/Spine"
         ),
         ),
@@ -46,13 +46,13 @@ let package = Package(
             name: "SpineCppLite",
             name: "SpineCppLite",
             path: "spine-ios/Sources/SpineCppLite",
             path: "spine-ios/Sources/SpineCppLite",
             linkerSettings: [
             linkerSettings: [
-                .linkedLibrary("c++"),
+                .linkedLibrary("c++")
             ]
             ]
         ),
         ),
         .systemLibrary(
         .systemLibrary(
             name: "SpineShadersStructs",
             name: "SpineShadersStructs",
             path: "spine-ios/Sources/SpineShadersStructs"
             path: "spine-ios/Sources/SpineShadersStructs"
-        )
+        ),
     ],
     ],
     cxxLanguageStandard: .cxx11
     cxxLanguageStandard: .cxx11
 )
 )

+ 52 - 0
formatters/.swift-format

@@ -0,0 +1,52 @@
+{
+  "version": 1,
+  "lineLength": 150,
+  "indentation": {
+    "spaces": 4
+  },
+  "tabWidth": 4,
+  "maximumBlankLines": 2,
+  "respectsExistingLineBreaks": true,
+  "lineBreakBeforeControlFlowKeywords": false,
+  "lineBreakBeforeEachArgument": false,
+  "lineBreakBeforeEachGenericRequirement": false,
+  "prioritizeKeepingFunctionOutputTogether": true,
+  "indentConditionalCompilationBlocks": true,
+  "lineBreakAroundMultilineExpressionChainComponents": false,
+  "rules": {
+    "AllPublicDeclarationsHaveDocumentation": false,
+    "AlwaysUseLowerCamelCase": true,
+    "AmbiguousTrailingClosureOverload": true,
+    "BeginDocumentationCommentWithOneLineSummary": false,
+    "DoNotUseSemicolons": true,
+    "DontRepeatTypeInStaticProperties": true,
+    "FileScopedDeclarationPrivacy": true,
+    "FullyIndirectEnum": true,
+    "GroupNumericLiterals": true,
+    "IdentifiersMustBeASCII": true,
+    "NeverForceUnwrap": false,
+    "NeverUseForceTry": false,
+    "NeverUseImplicitlyUnwrappedOptionals": false,
+    "NoAccessLevelOnExtensionDeclaration": true,
+    "NoBlockComments": true,
+    "NoCasesWithOnlyFallthrough": true,
+    "NoEmptyTrailingClosureParentheses": true,
+    "NoLabelsInCasePatterns": true,
+    "NoLeadingUnderscores": false,
+    "NoParensAroundConditions": true,
+    "NoVoidReturnOnFunctionSignature": true,
+    "OneCasePerLine": true,
+    "OneVariableDeclarationPerLine": true,
+    "OnlyOneTrailingClosureArgument": false,
+    "OrderedImports": true,
+    "ReturnVoidInsteadOfEmptyTuple": true,
+    "UseEarlyExits": false,
+    "UseLetInEveryBoundCaseVariable": true,
+    "UseShorthandTypeNames": true,
+    "UseSingleLinePropertyGetter": true,
+    "UseSynthesizedInitializer": true,
+    "UseTripleSlashForDocumentationComments": true,
+    "UseWhereClausesInForLoops": false,
+    "ValidateDocumentationComments": false
+  }
+}

+ 26 - 27
formatters/format-cpp.sh

@@ -1,16 +1,21 @@
 #!/bin/bash
 #!/bin/bash
 set -e
 set -e
 
 
-# Format C/C++ files with clang-format
-echo "Formatting C/C++ files..."
-
 dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
 dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
 
 
+# Source logging utilities
+source "$dir/logging/logging.sh"
+
+log_title "C/C++ Formatting"
+
 # Store original directory
 # Store original directory
 pushd "$dir" > /dev/null
 pushd "$dir" > /dev/null
 
 
-if [ ! -f ".clang-format" ]; then
-    echo "Error: .clang-format not found in formatters directory"
+log_action "Checking for formatters/.clang-format"
+if [ -f ".clang-format" ]; then
+    log_ok
+else
+    log_fail
     popd > /dev/null
     popd > /dev/null
     exit 1
     exit 1
 fi
 fi
@@ -22,47 +27,47 @@ cpp_dirs=(
     "../spine-cpp/src/spine"
     "../spine-cpp/src/spine"
     "../spine-cpp/spine-cpp-lite"
     "../spine-cpp/spine-cpp-lite"
     "../spine-cpp/tests"
     "../spine-cpp/tests"
-    
+
     # spine-c
     # spine-c
     "../spine-c/include"
     "../spine-c/include"
     "../spine-c/src"
     "../spine-c/src"
     "../spine-c/src/generated"
     "../spine-c/src/generated"
     "../spine-c/tests"
     "../spine-c/tests"
-    
+
     # spine-godot
     # spine-godot
     "../spine-godot/spine_godot"
     "../spine-godot/spine_godot"
-    
+
     # spine-ue
     # spine-ue
     "../spine-ue/Source/SpineUE"
     "../spine-ue/Source/SpineUE"
     "../spine-ue/Plugins/SpinePlugin/Source/SpinePlugin/Public"
     "../spine-ue/Plugins/SpinePlugin/Source/SpinePlugin/Public"
     "../spine-ue/Plugins/SpinePlugin/Source/SpinePlugin/Private"
     "../spine-ue/Plugins/SpinePlugin/Source/SpinePlugin/Private"
     "../spine-ue/Plugins/SpinePlugin/Source/SpineEditorPlugin/Public"
     "../spine-ue/Plugins/SpinePlugin/Source/SpineEditorPlugin/Public"
     "../spine-ue/Plugins/SpinePlugin/Source/SpineEditorPlugin/Private"
     "../spine-ue/Plugins/SpinePlugin/Source/SpineEditorPlugin/Private"
-    
+
     # spine-glfw
     # spine-glfw
     "../spine-glfw/src"
     "../spine-glfw/src"
     "../spine-glfw/example"
     "../spine-glfw/example"
-    
+
     # spine-sdl
     # spine-sdl
     "../spine-sdl/src"
     "../spine-sdl/src"
     "../spine-sdl/example"
     "../spine-sdl/example"
-    
+
     # spine-sfml
     # spine-sfml
     "../spine-sfml/c/src/spine"
     "../spine-sfml/c/src/spine"
     "../spine-sfml/c/example"
     "../spine-sfml/c/example"
     "../spine-sfml/cpp/src/spine"
     "../spine-sfml/cpp/src/spine"
     "../spine-sfml/cpp/example"
     "../spine-sfml/cpp/example"
-    
+
     # spine-cocos2dx
     # spine-cocos2dx
     "../spine-cocos2dx/spine-cocos2dx/src/spine"
     "../spine-cocos2dx/spine-cocos2dx/src/spine"
     "../spine-cocos2dx/example/Classes"
     "../spine-cocos2dx/example/Classes"
-    
+
     # spine-ios
     # spine-ios
     "../spine-ios/Sources/SpineCppLite"
     "../spine-ios/Sources/SpineCppLite"
     "../spine-ios/Sources/SpineCppLite/include"
     "../spine-ios/Sources/SpineCppLite/include"
     "../spine-ios/Sources/SpineShadersStructs"
     "../spine-ios/Sources/SpineShadersStructs"
     "../spine-ios/Example/Spine iOS Example"
     "../spine-ios/Example/Spine iOS Example"
-    
+
     # spine-flutter
     # spine-flutter
     "../spine-flutter/ios/Classes"
     "../spine-flutter/ios/Classes"
     "../spine-flutter/macos/Classes"
     "../spine-flutter/macos/Classes"
@@ -86,22 +91,16 @@ for cpp_dir in "${cpp_dirs[@]}"; do
     fi
     fi
 done
 done
 
 
-echo "Found ${#files[@]} C/C++ files to format"
 
 
-# Format all files in one call - works for both Docker and native
-echo "Formatting ${#files[@]} files..."
-if ! clang-format -i -style=file:".clang-format" "${files[@]}" 2>&1; then
-    echo "Error: clang-format failed"
-    errors=1
+log_action "Formatting ${#files[@]} C/C++ files"
+if FORMAT_OUTPUT=$(clang-format -i -style=file:".clang-format" "${files[@]}" 2>&1); then
+    log_ok
 else
 else
-    errors=0
-fi
-
-if [ $errors -gt 0 ]; then
-    echo "Completed with $errors errors"
+    log_fail
+    log_error_output "$FORMAT_OUTPUT"
+    popd > /dev/null
+    exit 1
 fi
 fi
 
 
-echo "C/C++ formatting complete"
-
 # Return to original directory
 # Return to original directory
 popd > /dev/null
 popd > /dev/null

+ 35 - 17
formatters/format-csharp.sh

@@ -1,48 +1,66 @@
 #!/bin/bash
 #!/bin/bash
 set -e
 set -e
 
 
-# Format C# files with dotnet-format
-echo "Formatting C# files..."
-
 dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
 dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
 
 
+# Source logging utilities
+source "$dir/logging/logging.sh"
+
+log_title "C# Formatting"
+
 if command -v dotnet &> /dev/null; then
 if command -v dotnet &> /dev/null; then
     # Store original directory
     # Store original directory
     pushd "$dir" > /dev/null
     pushd "$dir" > /dev/null
-    
-    # Copy .editorconfig to C# directories
+
     cp .editorconfig ../spine-csharp/ 2>/dev/null || true
     cp .editorconfig ../spine-csharp/ 2>/dev/null || true
     cp .editorconfig ../spine-monogame/ 2>/dev/null || true
     cp .editorconfig ../spine-monogame/ 2>/dev/null || true
     cp .editorconfig ../spine-unity/ 2>/dev/null || true
     cp .editorconfig ../spine-unity/ 2>/dev/null || true
 
 
     # Format spine-csharp
     # Format spine-csharp
+    log_action "Formatting spine-csharp"
     pushd ../spine-csharp > /dev/null
     pushd ../spine-csharp > /dev/null
-    dotnet format spine-csharp.csproj --no-restore --verbosity quiet 2>/dev/null || echo "Warning: Some issues with spine-csharp formatting"
+    if DOTNET_OUTPUT=$(dotnet format spine-csharp.csproj --no-restore --verbosity quiet 2>&1); then
+        log_ok
+    else
+        log_warn
+        log_detail "$DOTNET_OUTPUT"
+    fi
     popd > /dev/null
     popd > /dev/null
-    
+
     # Format spine-monogame
     # Format spine-monogame
+    log_action "Formatting spine-monogame"
     pushd ../spine-monogame > /dev/null
     pushd ../spine-monogame > /dev/null
-    dotnet format --no-restore --verbosity quiet 2>/dev/null || echo "Warning: Some issues with spine-monogame formatting"
+    if DOTNET_OUTPUT=$(dotnet format --no-restore --verbosity quiet 2>&1); then
+        log_ok
+    else
+        log_warn
+        log_detail "$DOTNET_OUTPUT"
+    fi
     popd > /dev/null
     popd > /dev/null
-    
+
     # Format spine-unity - look for .cs files directly
     # Format spine-unity - look for .cs files directly
-    if [ -d ../spine-unity ]; then
-        echo "Formatting spine-unity C# files directly..."
-        pushd ../spine-unity > /dev/null
-        # Find all .cs files and format them using dotnet format whitespace
+    log_action "Formatting spine-unity C# files"
+    pushd ../spine-unity > /dev/null
+    # Find all .cs files and format them using dotnet format whitespace
+    cs_files=$(find . -name "*.cs" -type f -not -path "./Library/*" -not -path "./Temp/*" -not -path "./obj/*" -not -path "./bin/*" | wc -l | tr -d ' ')
+    if [ "$cs_files" -gt 0 ]; then
         find . -name "*.cs" -type f -not -path "./Library/*" -not -path "./Temp/*" -not -path "./obj/*" -not -path "./bin/*" | while read -r file; do
         find . -name "*.cs" -type f -not -path "./Library/*" -not -path "./Temp/*" -not -path "./obj/*" -not -path "./bin/*" | while read -r file; do
             dotnet format whitespace --include "$file" --no-restore 2>/dev/null || true
             dotnet format whitespace --include "$file" --no-restore 2>/dev/null || true
         done
         done
-        popd > /dev/null
+        log_ok
+    else
+        log_skip
     fi
     fi
+    popd > /dev/null
 
 
-    # Clean up .editorconfig files
     rm -f ../spine-csharp/.editorconfig
     rm -f ../spine-csharp/.editorconfig
     rm -f ../spine-monogame/.editorconfig
     rm -f ../spine-monogame/.editorconfig
     rm -f ../spine-unity/.editorconfig
     rm -f ../spine-unity/.editorconfig
-    
+
     # Return to original directory
     # Return to original directory
     popd > /dev/null
     popd > /dev/null
 else
 else
-    echo "Warning: dotnet not found. Skipping C# formatting."
+    log_fail
+    log_error_output "dotnet not found. Please install .NET SDK to format C# files."
+    exit 1
 fi
 fi

+ 29 - 7
formatters/format-dart.sh

@@ -1,22 +1,44 @@
 #!/bin/bash
 #!/bin/bash
 set -e
 set -e
 
 
-# Format Dart files
-echo "Formatting Dart files..."
-
 dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
 dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
 
 
+# Source logging utilities
+source "$dir/logging/logging.sh"
+
+log_title "Dart Formatting"
+
 # Store original directory
 # Store original directory
 pushd "$dir" > /dev/null
 pushd "$dir" > /dev/null
 
 
 if command -v dart &> /dev/null; then
 if command -v dart &> /dev/null; then
-    find .. -name "*.dart" \
+    dart_files=$(find .. -name "*.dart" \
         -not -path "*/.*" \
         -not -path "*/.*" \
         -not -path "*/node_modules/*" \
         -not -path "*/node_modules/*" \
-        -not -path "*/build/*" \
-        -exec dart format --page-width 120 {} +
+        -not -path "*/build/*" | wc -l | tr -d ' ')
+
+    if [ "$dart_files" -gt 0 ]; then
+        log_action "Formatting $dart_files Dart files"
+        if DART_OUTPUT=$(find .. -name "*.dart" \
+            -not -path "*/.*" \
+            -not -path "*/node_modules/*" \
+            -not -path "*/build/*" \
+            -exec dart format --page-width 120 {} + 2>&1); then
+            log_ok
+        else
+            log_fail
+            log_error_output "$DART_OUTPUT"
+            popd > /dev/null
+            exit 1
+        fi
+    else
+        log_action "Formatting Dart files"
+        log_skip
+    fi
 else
 else
-    echo "Warning: dart not found. Skipping Dart formatting."
+    log_fail
+    log_error_output "dart not found. Please install Dart SDK to format Dart files."
+    exit 1
 fi
 fi
 
 
 # Return to original directory
 # Return to original directory

+ 32 - 8
formatters/format-haxe.sh

@@ -1,21 +1,45 @@
 #!/bin/bash
 #!/bin/bash
 set -e
 set -e
 
 
-# Format Haxe files
-echo "Formatting Haxe files..."
-
 dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
 dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
 
 
+# Source logging utilities
+source "$dir/logging/logging.sh"
+
+log_title "Haxe Formatting"
+
 # Store original directory
 # Store original directory
 pushd "$dir" > /dev/null
 pushd "$dir" > /dev/null
 
 
-if command -v haxelib &> /dev/null && haxelib list formatter &> /dev/null; then
-    # Format spine-haxe directory
-    if [ -d ../spine-haxe ]; then
-        haxelib run formatter -s ../spine-haxe
+if command -v haxelib &> /dev/null; then
+    log_action "Checking Haxe formatter availability"
+    if HAXELIB_OUTPUT=$(haxelib list formatter 2>&1); then
+        log_ok
+
+        # Format spine-haxe directory
+        if [ -d ../spine-haxe ]; then
+            log_action "Formatting spine-haxe directory"
+            if FORMATTER_OUTPUT=$(haxelib run formatter -s ../spine-haxe 2>&1); then
+                log_ok
+            else
+                log_fail
+                log_error_output "$FORMATTER_OUTPUT"
+                popd > /dev/null
+                exit 1
+            fi
+        else
+            log_action "Formatting spine-haxe directory"
+            log_skip
+        fi
+    else
+        log_fail
+        log_error_output "Haxe formatter not found. Install with: haxelib install formatter"
+        exit 1
     fi
     fi
 else
 else
-    echo "Warning: haxe formatter not found. Install with: haxelib install formatter"
+    log_fail
+    log_error_output "haxelib not found. Please install Haxe to format Haxe files."
+    exit 1
 fi
 fi
 
 
 # Return to original directory
 # Return to original directory

+ 29 - 10
formatters/format-java.sh

@@ -1,11 +1,13 @@
 #!/bin/bash
 #!/bin/bash
 set -e
 set -e
 
 
-# Format Java files with Eclipse formatter
-echo "Formatting Java files..."
-
 dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
 dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
 
 
+# Source logging utilities
+source "$dir/logging/logging.sh"
+
+log_title "Java Formatting"
+
 # Store original directory
 # Store original directory
 pushd "$dir" > /dev/null
 pushd "$dir" > /dev/null
 
 
@@ -14,9 +16,17 @@ jar_file="eclipse-formatter/target/eclipse-formatter-1.0.0-jar-with-dependencies
 src_file="eclipse-formatter/src/main/java/com/esotericsoftware/spine/formatter/EclipseFormatter.java"
 src_file="eclipse-formatter/src/main/java/com/esotericsoftware/spine/formatter/EclipseFormatter.java"
 
 
 if [ ! -f "$jar_file" ] || [ "$src_file" -nt "$jar_file" ]; then
 if [ ! -f "$jar_file" ] || [ "$src_file" -nt "$jar_file" ]; then
-    echo "Building Eclipse formatter..."
+    log_action "Building Eclipse formatter"
     pushd eclipse-formatter > /dev/null
     pushd eclipse-formatter > /dev/null
-    mvn -q clean package
+    if MVN_OUTPUT=$(mvn -q clean package 2>&1); then
+        log_ok
+    else
+        log_fail
+        log_error_output "$MVN_OUTPUT"
+        popd > /dev/null
+        popd > /dev/null
+        exit 1
+    fi
     popd > /dev/null
     popd > /dev/null
 fi
 fi
 
 
@@ -30,13 +40,22 @@ java_files=$(find ../spine-libgdx ../spine-android -name "*.java" -type f \
 
 
 # Run the formatter
 # Run the formatter
 if [ -n "$java_files" ]; then
 if [ -n "$java_files" ]; then
-    echo "Running Eclipse formatter on Java files..."
-    java -jar eclipse-formatter/target/eclipse-formatter-1.0.0-jar-with-dependencies.jar \
+    java_count=$(echo "$java_files" | wc -l | tr -d ' ')
+    log_action "Formatting $java_count Java files"
+    if FORMATTER_OUTPUT=$(java -jar eclipse-formatter/target/eclipse-formatter-1.0.0-jar-with-dependencies.jar \
         eclipse-formatter.xml \
         eclipse-formatter.xml \
-        $java_files
+        $java_files 2>&1); then
+        log_ok
+    else
+        log_fail
+        log_error_output "$FORMATTER_OUTPUT"
+        popd > /dev/null
+        exit 1
+    fi
+else
+    log_action "Formatting Java files"
+    log_skip
 fi
 fi
 
 
-echo "Java formatting complete"
-
 # Return to original directory
 # Return to original directory
 popd > /dev/null
 popd > /dev/null

+ 30 - 7
formatters/format-swift.sh

@@ -1,22 +1,45 @@
 #!/bin/bash
 #!/bin/bash
 set -e
 set -e
 
 
-# Format Swift files
-echo "Formatting Swift files..."
-
 dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
 dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
 
 
+# Source logging utilities
+source "$dir/logging/logging.sh"
+
+log_title "Swift Formatting"
+
 # Store original directory
 # Store original directory
 pushd "$dir" > /dev/null
 pushd "$dir" > /dev/null
 
 
 if command -v swift-format &> /dev/null; then
 if command -v swift-format &> /dev/null; then
-    find .. -name "*.swift" \
+
+    swift_files=$(find .. -name "*.swift" -type f \
         -not -path "*/.*" \
         -not -path "*/.*" \
         -not -path "*/build/*" \
         -not -path "*/build/*" \
-        -not -path "*/DerivedData/*" \
-        | xargs swift-format -i
+        -not -path "*/DerivedData/*" | wc -l | tr -d ' ')
+
+    if [ "$swift_files" -gt 0 ]; then
+        log_action "Formatting $swift_files Swift files"
+        if SWIFT_OUTPUT=$(find .. -name "*.swift" -type f \
+            -not -path "*/.*" \
+            -not -path "*/build/*" \
+            -not -path "*/DerivedData/*" \
+            -print0 | xargs -0 swift-format --in-place --configuration .swift-format 2>&1); then
+            log_ok
+        else
+            log_fail
+            log_error_output "$SWIFT_OUTPUT"
+            popd > /dev/null
+            exit 1
+        fi
+    else
+        log_action "Formatting Swift files"
+        log_skip
+    fi
 else
 else
-    echo "Warning: swift-format not found. Install from https://github.com/apple/swift-format"
+    log_fail
+    log_error_output "swift-format not found. Install via brew install swift-format"
+    exit 1
 fi
 fi
 
 
 # Return to original directory
 # Return to original directory

+ 32 - 8
formatters/format-ts.sh

@@ -1,29 +1,53 @@
 #!/bin/bash
 #!/bin/bash
 set -e
 set -e
 
 
-# Format TypeScript files with tsfmt
-echo "Formatting TypeScript files..."
-
 dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
 dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
 
 
+# Source logging utilities
+source "$dir/logging/logging.sh"
+
+log_title "TypeScript Formatting"
+
 # Store original directory
 # Store original directory
 pushd "$dir" > /dev/null
 pushd "$dir" > /dev/null
 
 
 # Check if tsfmt.json files match
 # Check if tsfmt.json files match
-if ! cmp -s ../spine-ts/tsfmt.json ../tests/tsfmt.json; then
-    echo -e "\033[1;31mERROR: spine-ts/tsfmt.json and tests/tsfmt.json differ!\033[0m"
-    echo -e "\033[1;31mPlease sync them to ensure consistent formatting.\033[0m"
+log_action "Checking tsfmt.json consistency"
+if CMP_OUTPUT=$(cmp -s ../spine-ts/tsfmt.json ../tests/tsfmt.json 2>&1); then
+    log_ok
+else
+    log_fail
+    log_error_output "spine-ts/tsfmt.json and tests/tsfmt.json differ!"
+    log_detail "Please sync them to ensure consistent formatting."
     popd > /dev/null
     popd > /dev/null
     exit 1
     exit 1
 fi
 fi
 
 
 # Format TypeScript files
 # Format TypeScript files
+log_action "Formatting spine-ts TypeScript files"
 pushd ../spine-ts > /dev/null
 pushd ../spine-ts > /dev/null
-npm run format
+if NPM_OUTPUT=$(npm run format 2>&1); then
+    log_ok
+else
+    log_fail
+    log_error_output "$NPM_OUTPUT"
+    popd > /dev/null
+    popd > /dev/null
+    exit 1
+fi
 popd > /dev/null
 popd > /dev/null
 
 
+log_action "Formatting tests TypeScript files"
 pushd ../tests > /dev/null
 pushd ../tests > /dev/null
-npm run format
+if NPM_OUTPUT=$(npm run format 2>&1); then
+    log_ok
+else
+    log_fail
+    log_error_output "$NPM_OUTPUT"
+    popd > /dev/null
+    popd > /dev/null
+    exit 1
+fi
 popd > /dev/null
 popd > /dev/null
 
 
 # Return to original directory
 # Return to original directory

+ 3 - 23
formatters/format.sh

@@ -89,7 +89,8 @@ while [[ $# -gt 0 ]]; do
             shift
             shift
             ;;
             ;;
         *)
         *)
-            log_fail "Unknown option: $1"
+            log_fail
+            log_error_output "Unknown option: $1"
             log_detail "Use --help for usage information"
             log_detail "Use --help for usage information"
             exit 1
             exit 1
             ;;
             ;;
@@ -98,54 +99,33 @@ done
 
 
 log_title "Code Formatting"
 log_title "Code Formatting"
 
 
-# Call individual formatter scripts
+# Call individual formatter scripts (they handle their own logging)
 if [ "$FORMAT_CPP" = true ]; then
 if [ "$FORMAT_CPP" = true ]; then
-    log_section "C/C++"
-    log_action "Formatting C/C++ files"
     "$dir/format-cpp.sh"
     "$dir/format-cpp.sh"
-    log_ok "C/C++ formatting completed"
 fi
 fi
 
 
 if [ "$FORMAT_JAVA" = true ]; then
 if [ "$FORMAT_JAVA" = true ]; then
-    log_section "Java"
-    log_action "Formatting Java files"
     "$dir/format-java.sh"
     "$dir/format-java.sh"
-    log_ok "Java formatting completed"
 fi
 fi
 
 
 if [ "$FORMAT_CSHARP" = true ]; then
 if [ "$FORMAT_CSHARP" = true ]; then
-    log_section "C#"
-    log_action "Formatting C# files"
     "$dir/format-csharp.sh"
     "$dir/format-csharp.sh"
-    log_ok "C# formatting completed"
 fi
 fi
 
 
 if [ "$FORMAT_TS" = true ]; then
 if [ "$FORMAT_TS" = true ]; then
-    log_section "TypeScript"
-    log_action "Formatting TypeScript files"
     "$dir/format-ts.sh"
     "$dir/format-ts.sh"
-    log_ok "TypeScript formatting completed"
 fi
 fi
 
 
 if [ "$FORMAT_DART" = true ]; then
 if [ "$FORMAT_DART" = true ]; then
-    log_section "Dart"
-    log_action "Formatting Dart files"
     "$dir/format-dart.sh"
     "$dir/format-dart.sh"
-    log_ok "Dart formatting completed"
 fi
 fi
 
 
 if [ "$FORMAT_HAXE" = true ]; then
 if [ "$FORMAT_HAXE" = true ]; then
-    log_section "Haxe"
-    log_action "Formatting Haxe files"
     "$dir/format-haxe.sh"
     "$dir/format-haxe.sh"
-    log_ok "Haxe formatting completed"
 fi
 fi
 
 
 if [ "$FORMAT_SWIFT" = true ]; then
 if [ "$FORMAT_SWIFT" = true ]; then
-    log_section "Swift"
-    log_action "Formatting Swift files"
     "$dir/format-swift.sh"
     "$dir/format-swift.sh"
-    log_ok "Swift formatting completed"
 fi
 fi
 
 
 log_summary "✓ All formatting completed"
 log_summary "✓ All formatting completed"

+ 14 - 15
formatters/logging/logging.sh

@@ -43,42 +43,41 @@ NC='\033[0m' # No Color
 
 
 # Design principles:
 # Design principles:
 # 1. Minimal visual noise - use color sparingly for emphasis
 # 1. Minimal visual noise - use color sparingly for emphasis
-# 2. Clear hierarchy - different levels of information have different treatments  
+# 2. Clear hierarchy - different levels of information have different treatments
 # 3. Consistent spacing - clean vertical rhythm
 # 3. Consistent spacing - clean vertical rhythm
 # 4. Accessible - readable without colors
 # 4. Accessible - readable without colors
 
 
 # Main header for script/tool name
 # Main header for script/tool name
 log_title() {
 log_title() {
-    echo ""
-    echo -e "${BOLD}$1${NC}"
-    echo ""
+    echo -e "${GREEN}${BOLD}$1${NC}"
 }
 }
 
 
-# Section headers for major phases
-log_section() {
-    echo -e "${BOLD}${BLUE}$1${NC}"
-}
 
 
-# Individual actions/steps
+# Individual actions/steps - inline result format
 log_action() {
 log_action() {
-    echo -e "  $1..."
+    echo -n "  $1... "
 }
 }
 
 
-# Results - success/failure/info
+# Results - success/failure/info (on same line)
 log_ok() {
 log_ok() {
-    echo -e "  ${GREEN}✓${NC} $1"
+    echo -e "${GREEN}✓${NC}"
 }
 }
 
 
 log_fail() {
 log_fail() {
-    echo -e "  ${RED}✗${NC} $1"
+    echo -e "${RED}✗${NC}"
 }
 }
 
 
 log_warn() {
 log_warn() {
-    echo -e "  ${YELLOW}!${NC} $1"
+    echo -e "${YELLOW}!${NC}"
 }
 }
 
 
 log_skip() {
 log_skip() {
-    echo -e "  ${GRAY}-${NC} $1"
+    echo -e "${GRAY}-${NC}"
+}
+
+# For errors, show full output prominently (not grayed)
+log_error_output() {
+    echo "$1"
 }
 }
 
 
 # Final summary
 # Final summary

+ 89 - 54
formatters/logging/terminal-logging-guide.md

@@ -18,48 +18,51 @@ This guide defines the terminal output style for all bash scripts in the Spine R
 - **Usage**: Once at the beginning of script execution
 - **Usage**: Once at the beginning of script execution
 
 
 ```bash
 ```bash
-log_title "Spine-C++ Test"
+log_title "Spine-C++ Build"
 ```
 ```
 
 
-### 2. Section (`log_section`)
-- **Purpose**: Major phases or groups of operations
-- **Style**: Bold blue text, no extra spacing
-- **Usage**: Build, Test, Deploy, etc.
-
-```bash
-log_section "Build"
-log_section "Test"
-```
-
-### 3. Action (`log_action`)
-- **Purpose**: Individual operations in progress
-- **Style**: Indented, followed by "..."
-- **Usage**: Before starting an operation
+### 2. Action + Result (inline format)
+- **Purpose**: Individual operations with immediate result
+- **Style**: Action on same line as result for density
+- **Usage**: `log_action` followed immediately by `log_ok/fail/warn/skip`
 
 
 ```bash
 ```bash
 log_action "Building all variants"
 log_action "Building all variants"
-log_action "Testing headless-test"
+log_ok                           # Green ✓ on same line
+
+log_action "Testing headless-test"  
+log_fail                         # Red ✗ on same line
 ```
 ```
 
 
-### 4. Results
-- **Purpose**: Outcome of operations
-- **Style**: Indented with colored symbols
+**Results**:
+- `log_ok` - Green ✓ (success)
+- `log_fail` - Red ✗ (failure) 
+- `log_warn` - Yellow ! (warning)
+- `log_skip` - Gray - (skipped)
+
+### 4. Error Output (`log_error_output`)
+- **Purpose**: Full error output when operations fail
+- **Style**: Normal text (not grayed), prominent
+- **Usage**: Show command output after failures
 
 
 ```bash
 ```bash
-log_ok "Build completed"          # Green ✓
-log_fail "Build failed"           # Red ✗  
-log_warn "Deprecated feature"     # Yellow !
-log_skip "Not supported on macOS" # Gray -
+log_action "Building"
+if BUILD_OUTPUT=$(command 2>&1); then
+    log_ok
+else
+    log_fail
+    log_error_output "$BUILD_OUTPUT"
+fi
 ```
 ```
 
 
 ### 5. Detail (`log_detail`)
 ### 5. Detail (`log_detail`)
-- **Purpose**: Secondary information, error output, debug info
+- **Purpose**: Secondary information, debug info (not errors)
 - **Style**: Gray text, indented
 - **Style**: Gray text, indented
-- **Usage**: Additional context, error messages
+- **Usage**: Additional context, platform info
 
 
 ```bash
 ```bash
 log_detail "Platform: Darwin"
 log_detail "Platform: Darwin"
-log_detail "$ERROR_OUTPUT"
+log_detail "Branch: main"
 ```
 ```
 
 
 ### 6. Summary (`log_summary`)
 ### 6. Summary (`log_summary`)
@@ -78,58 +81,90 @@ log_summary "✗ Tests failed (3/5)"
 #!/bin/bash
 #!/bin/bash
 source ../formatters/logging/logging.sh
 source ../formatters/logging/logging.sh
 
 
-log_title "Spine-C++ Test"
+log_title "Spine-C++ Build"
 log_detail "Platform: $(uname)"
 log_detail "Platform: $(uname)"
 
 
-log_section "Build"
-log_action "Building all variants"
-if BUILD_OUTPUT=$(./build.sh clean release 2>&1); then
-    log_ok "Build completed"
+log_action "Configuring debug build"
+if CMAKE_OUTPUT=$(cmake --preset=debug . 2>&1); then
+    log_ok
 else
 else
-    log_fail "Build failed"
-    log_detail "$BUILD_OUTPUT"
+    log_fail
+    log_error_output "$CMAKE_OUTPUT"
     exit 1
     exit 1
 fi
 fi
 
 
-log_section "Test"
-log_action "Testing headless-test"
-if test_result; then
-    log_ok "headless-test"
+log_action "Building"
+if BUILD_OUTPUT=$(cmake --build --preset=debug 2>&1); then
+    log_ok
 else
 else
-    log_fail "headless-test - execution failed"
-    log_detail "$error_output"
+    log_fail
+    log_error_output "$BUILD_OUTPUT"
+    exit 1
 fi
 fi
 
 
-log_summary "✓ All tests passed (2/2)"
+log_summary "✓ Build successful"
 ```
 ```
 
 
 ## Output Preview
 ## Output Preview
 
 
+**Success case:**
 ```
 ```
-Spine-C++ Test
-
+Spine-C++ Build
 Platform: Darwin
 Platform: Darwin
 
 
-Build
-  Building all variants...
-  ✓ Build completed
+  Configuring debug build... ✓
+  Building... ✓
 
 
-Test
-  Testing headless-test...
-  ✓ headless-test
-  Testing headless-test-nostdcpp...
-  ✓ headless-test-nostdcpp
+✓ Build successful
+```
 
 
-✓ All tests passed (2/2)
+**Failure case:**
+```
+Spine-C++ Build
+Platform: Darwin
+
+  Configuring debug build... ✗
+CMake Error: Could not find cmake file...
+(full error output shown prominently)
 ```
 ```
 
 
 ## Error Handling Best Practices
 ## Error Handling Best Practices
 
 
 1. **Capture output**: Use `OUTPUT=$(command 2>&1)` to capture both stdout and stderr
 1. **Capture output**: Use `OUTPUT=$(command 2>&1)` to capture both stdout and stderr
-2. **Check exit codes**: Always check if critical operations succeeded
-3. **Show details on failure**: Use `log_detail` to show error output
+2. **Check exit codes**: Always check if critical operations succeeded  
+3. **Show errors prominently**: Use `log_error_output` for command failures (not grayed)
 4. **Fail fast**: Exit immediately on critical failures
 4. **Fail fast**: Exit immediately on critical failures
-5. **Clear error messages**: Make failure reasons obvious
+5. **Use inline results**: `log_action` + `log_ok/fail` for dense, scannable output
+
+## Calling Other Scripts
+
+When calling other bash scripts that have their own logging:
+
+1. **Trust their logging**: Don't wrap calls in redundant log actions
+2. **Capture output for errors**: Use `OUTPUT=$(script.sh 2>&1)` to capture output and only show on failure
+3. **Let them handle success**: Avoid "script completed" messages when the script shows its own status
+
+**Good**:
+```bash
+# Let the script handle its own logging
+../formatters/format.sh cpp
+
+# Or capture output and only show on error
+if output=$(./setup.sh 2>&1); then
+    log_ok "Setup completed"
+else
+    log_fail "Setup failed"
+    log_detail "$output"
+fi
+```
+
+**Avoid**:
+```bash
+# This creates duplicate logging
+log_action "Formatting C++ files"
+../formatters/format.sh cpp
+log_ok "C++ formatting completed"
+```
 
 
 ```bash
 ```bash
 if BUILD_OUTPUT=$(./build.sh clean release 2>&1); then
 if BUILD_OUTPUT=$(./build.sh clean release 2>&1); then

+ 16 - 22
spine-c/build.sh

@@ -34,34 +34,30 @@ fi
 
 
 # Run codegen if requested
 # Run codegen if requested
 if [ "$1" = "codegen" ]; then
 if [ "$1" = "codegen" ]; then
-    log_title "Spine-C Code Generation"
-    
-    log_section "Generate"
+    log_title "spine-c code generation"
+
     log_action "Generating C bindings"
     log_action "Generating C bindings"
     if CODEGEN_OUTPUT=$(npx -y tsx codegen/src/index.ts 2>&1); then
     if CODEGEN_OUTPUT=$(npx -y tsx codegen/src/index.ts 2>&1); then
-        log_ok "Code generation completed"
+        log_ok
     else
     else
-        log_fail "Code generation failed"
-        log_detail "$CODEGEN_OUTPUT"
+        log_fail
+        log_error_output "$CODEGEN_OUTPUT"
         exit 1
         exit 1
     fi
     fi
-    
-    log_section "Format"
-    log_action "Formatting generated C++ files"
+
     ../formatters/format.sh cpp
     ../formatters/format.sh cpp
-    
+
     log_summary "✓ Code generation successful"
     log_summary "✓ Code generation successful"
     exit 0
     exit 0
 fi
 fi
 
 
-log_title "Spine-C Build"
+log_title "spine-c build"
 
 
 # Clean only if explicitly requested
 # Clean only if explicitly requested
 if [ "$1" = "clean" ]; then
 if [ "$1" = "clean" ]; then
-    log_section "Clean"
-    log_action "Removing build directory"
+    log_action "Cleaning build directory"
     rm -rf build
     rm -rf build
-    log_ok "Cleaned"
+    log_ok
 fi
 fi
 
 
 # Determine build type
 # Determine build type
@@ -71,23 +67,21 @@ if [ "$1" = "release" ]; then
 fi
 fi
 
 
 # Configure and build
 # Configure and build
-log_section "Configure"
 log_action "Configuring $BUILD_TYPE build"
 log_action "Configuring $BUILD_TYPE build"
 if CMAKE_OUTPUT=$(cmake --preset=$BUILD_TYPE . 2>&1); then
 if CMAKE_OUTPUT=$(cmake --preset=$BUILD_TYPE . 2>&1); then
-    log_ok "Configured"
+    log_ok
 else
 else
-    log_fail "Configuration failed"
-    log_detail "$CMAKE_OUTPUT"
+    log_fail
+    log_error_output "$CMAKE_OUTPUT"
     exit 1
     exit 1
 fi
 fi
 
 
-log_section "Build"
 log_action "Building"
 log_action "Building"
 if BUILD_OUTPUT=$(cmake --build --preset=$BUILD_TYPE 2>&1); then
 if BUILD_OUTPUT=$(cmake --build --preset=$BUILD_TYPE 2>&1); then
-    log_ok "Build completed"
+    log_ok
 else
 else
-    log_fail "Build failed"
-    log_detail "$BUILD_OUTPUT"
+    log_fail
+    log_error_output "$BUILD_OUTPUT"
     exit 1
     exit 1
 fi
 fi
 
 

+ 9 - 12
spine-cpp/build.sh

@@ -33,34 +33,31 @@ for arg in "$@"; do
     esac
     esac
 done
 done
 
 
-log_title "Spine-C++ Build"
+log_title "spine-cpp build"
 
 
 # Clean if requested
 # Clean if requested
 if [ "$CLEAN" = "true" ]; then
 if [ "$CLEAN" = "true" ]; then
-    log_section "Clean"
-    log_action "Removing build directory"
+    log_action "Cleaning build directory"
     rm -rf build
     rm -rf build
-    log_ok "Cleaned"
+    log_ok
 fi
 fi
 
 
 # Configure and build
 # Configure and build
-log_section "Configure"
 log_action "Configuring $BUILD_TYPE build"
 log_action "Configuring $BUILD_TYPE build"
 if CMAKE_OUTPUT=$(cmake --preset=$BUILD_TYPE $NOFILEIO . 2>&1); then
 if CMAKE_OUTPUT=$(cmake --preset=$BUILD_TYPE $NOFILEIO . 2>&1); then
-    log_ok "Configured"
+    log_ok
 else
 else
-    log_fail "Configuration failed"
-    log_detail "$CMAKE_OUTPUT"
+    log_fail
+    log_error_output "$CMAKE_OUTPUT"
     exit 1
     exit 1
 fi
 fi
 
 
-log_section "Build"
 log_action "Building"
 log_action "Building"
 if BUILD_OUTPUT=$(cmake --build --preset=$BUILD_TYPE 2>&1); then
 if BUILD_OUTPUT=$(cmake --build --preset=$BUILD_TYPE 2>&1); then
-    log_ok "Build completed"
+    log_ok
 else
 else
-    log_fail "Build failed"
-    log_detail "$BUILD_OUTPUT"
+    log_fail
+    log_error_output "$BUILD_OUTPUT"
     exit 1
     exit 1
 fi
 fi
 
 

+ 4 - 7
spine-cpp/tests/test.sh

@@ -3,7 +3,7 @@
 #
 #
 # Tests all spine-cpp build variants with spineboy example data:
 # Tests all spine-cpp build variants with spineboy example data:
 # - headless-test (regular dynamic)
 # - headless-test (regular dynamic)
-# - headless-test-nostdcpp (nostdcpp dynamic)  
+# - headless-test-nostdcpp (nostdcpp dynamic)
 # - headless-test-static (regular static, Linux only)
 # - headless-test-static (regular static, Linux only)
 # - headless-test-nostdcpp-static (nostdcpp static, Linux only)
 # - headless-test-nostdcpp-static (nostdcpp static, Linux only)
 
 
@@ -35,7 +35,6 @@ EXPECTED_OUTPUT="=== SKELETON DATA ===
 log_title "Spine-C++ Test"
 log_title "Spine-C++ Test"
 log_detail "Platform: $(uname)"
 log_detail "Platform: $(uname)"
 
 
-log_section "Build"
 log_action "Building all variants"
 log_action "Building all variants"
 if BUILD_OUTPUT=$(./build.sh clean release 2>&1); then
 if BUILD_OUTPUT=$(./build.sh clean release 2>&1); then
     log_ok "Build completed"
     log_ok "Build completed"
@@ -45,8 +44,6 @@ else
     exit 1
     exit 1
 fi
 fi
 
 
-log_section "Test"
-
 test_count=0
 test_count=0
 pass_count=0
 pass_count=0
 
 
@@ -54,12 +51,12 @@ for exe in build/headless-test*; do
     if [ -f "$exe" ] && [ -x "$exe" ]; then
     if [ -f "$exe" ] && [ -x "$exe" ]; then
         exe_name=$(basename "$exe")
         exe_name=$(basename "$exe")
         log_action "Testing $exe_name"
         log_action "Testing $exe_name"
-        
+
         test_count=$((test_count + 1))
         test_count=$((test_count + 1))
-        
+
         if OUTPUT=$("$exe" $SPINEBOY_SKEL $SPINEBOY_ATLAS $SPINEBOY_ANIM 2>&1); then
         if OUTPUT=$("$exe" $SPINEBOY_SKEL $SPINEBOY_ATLAS $SPINEBOY_ANIM 2>&1); then
             actual_output=$(echo "$OUTPUT" | head -10)
             actual_output=$(echo "$OUTPUT" | head -10)
-            
+
             if [ "$actual_output" = "$EXPECTED_OUTPUT" ]; then
             if [ "$actual_output" = "$EXPECTED_OUTPUT" ]; then
                 log_ok "$exe_name"
                 log_ok "$exe_name"
                 pass_count=$((pass_count + 1))
                 pass_count=$((pass_count + 1))

+ 8 - 8
spine-flutter/example/ios/Runner/AppDelegate.swift

@@ -1,13 +1,13 @@
-import UIKit
 import Flutter
 import Flutter
+import UIKit
 
 
 @UIApplicationMain
 @UIApplicationMain
 @objc class AppDelegate: FlutterAppDelegate {
 @objc class AppDelegate: FlutterAppDelegate {
-  override func application(
-    _ application: UIApplication,
-    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
-  ) -> Bool {
-    GeneratedPluginRegistrant.register(with: self)
-    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
-  }
+    override func application(
+        _ application: UIApplication,
+        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
+    ) -> Bool {
+        GeneratedPluginRegistrant.register(with: self)
+        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
+    }
 }
 }

+ 0 - 1
spine-flutter/example/macos/Flutter/GeneratedPluginRegistrant.swift

@@ -5,6 +5,5 @@
 import FlutterMacOS
 import FlutterMacOS
 import Foundation
 import Foundation
 
 
-
 func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
 func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
 }
 }

+ 3 - 3
spine-flutter/example/macos/Runner/AppDelegate.swift

@@ -3,7 +3,7 @@ import FlutterMacOS
 
 
 @NSApplicationMain
 @NSApplicationMain
 class AppDelegate: FlutterAppDelegate {
 class AppDelegate: FlutterAppDelegate {
-  override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
-    return true
-  }
+    override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
+        return true
+    }
 }
 }

+ 8 - 8
spine-flutter/example/macos/Runner/MainFlutterWindow.swift

@@ -2,14 +2,14 @@ import Cocoa
 import FlutterMacOS
 import FlutterMacOS
 
 
 class MainFlutterWindow: NSWindow {
 class MainFlutterWindow: NSWindow {
-  override func awakeFromNib() {
-    let flutterViewController = FlutterViewController.init()
-    let windowFrame = self.frame
-    self.contentViewController = flutterViewController
-    self.setFrame(windowFrame, display: true)
+    override func awakeFromNib() {
+        let flutterViewController = FlutterViewController.init()
+        let windowFrame = self.frame
+        self.contentViewController = flutterViewController
+        self.setFrame(windowFrame, display: true)
 
 
-    RegisterGeneratedPlugins(registry: flutterViewController)
+        RegisterGeneratedPlugins(registry: flutterViewController)
 
 
-    super.awakeFromNib()
-  }
+        super.awakeFromNib()
+    }
 }
 }

+ 12 - 6
spine-haxe/build.sh

@@ -26,18 +26,24 @@ if echo "$COMMIT_MSG" | grep -qE '^\[haxe\] Release [0-9]+\.[0-9]+\.[0-9]+$'; th
     if [ ! -z "$HAXE_UPDATE_URL" ] && [ ! -z "$BRANCH" ]; then
     if [ ! -z "$HAXE_UPDATE_URL" ] && [ ! -z "$BRANCH" ]; then
         log_section "Deploy"
         log_section "Deploy"
         log_action "Creating release package"
         log_action "Creating release package"
-        zip -r "spine-haxe-$VERSION.zip" \
+        if ZIP_OUTPUT=$(zip -r "spine-haxe-$VERSION.zip" \
             haxelib.json \
             haxelib.json \
             LICENSE \
             LICENSE \
             README.md \
             README.md \
-            spine-haxe > /dev/null 2>&1
-        log_ok "Package created"
+            spine-haxe 2>&1); then
+            log_ok
+        else
+            log_fail
+            log_error_output "$ZIP_OUTPUT"
+            exit 1
+        fi
         
         
         log_action "Uploading to $HAXE_UPDATE_URL$BRANCH"
         log_action "Uploading to $HAXE_UPDATE_URL$BRANCH"
-        if curl -f -F "file=@spine-haxe-$VERSION.zip" "$HAXE_UPDATE_URL$BRANCH" > /dev/null 2>&1; then
-            log_ok "Package deployed"
+        if CURL_OUTPUT=$(curl -f -F "file=@spine-haxe-$VERSION.zip" "$HAXE_UPDATE_URL$BRANCH" 2>&1); then
+            log_ok
         else
         else
-            log_fail "Upload failed"
+            log_fail
+            log_error_output "$CURL_OUTPUT"
             exit 1
             exit 1
         fi
         fi
         
         

+ 5 - 5
spine-ios/Example - Cocoapods/Spine iOS Example/SimpleAnimation.swift

@@ -27,8 +27,8 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
  *****************************************************************************/
 
 
-import SwiftUI
 import Spine
 import Spine
+import SwiftUI
 
 
 struct SimpleAnimation: View {
 struct SimpleAnimation: View {
 
 
@@ -46,10 +46,10 @@ struct SimpleAnimation: View {
     var body: some View {
     var body: some View {
         SpineView(
         SpineView(
             from: .bundle(atlasFileName: "spineboy-pma.atlas", skeletonFileName: "spineboy-pro.skel"),
             from: .bundle(atlasFileName: "spineboy-pma.atlas", skeletonFileName: "spineboy-pro.skel"),
-//            from: .http(
-//                atlasURL: URL(string: "https://github.com/esotericsoftware/spine-runtimes/raw/spine-ios/spine-ios/Example/Spine%20iOS%20Example/Assets/spineboy/spineboy.atlas")!,
-//                skeletonURL:  URL(string: "https://github.com/esotericsoftware/spine-runtimes/raw/spine-ios/spine-ios/Example/Spine%20iOS%20Example/Assets/spineboy/spineboy-pro.skel")!
-//            ),
+            //            from: .http(
+            //                atlasURL: URL(string: "https://github.com/esotericsoftware/spine-runtimes/raw/spine-ios/spine-ios/Example/Spine%20iOS%20Example/Assets/spineboy/spineboy.atlas")!,
+            //                skeletonURL:  URL(string: "https://github.com/esotericsoftware/spine-runtimes/raw/spine-ios/spine-ios/Example/Spine%20iOS%20Example/Assets/spineboy/spineboy-pro.skel")!
+            //            ),
             controller: controller,
             controller: controller,
             mode: .fit,
             mode: .fit,
             alignment: .center
             alignment: .center

+ 1 - 1
spine-ios/Example - Cocoapods/Spine iOS Example/Spine_iOS_ExampleApp.swift

@@ -5,8 +5,8 @@
 //  Created by Denis Andrašec on 29.05.24.
 //  Created by Denis Andrašec on 29.05.24.
 //
 //
 
 
-import SwiftUI
 import SpineCppLite
 import SpineCppLite
+import SwiftUI
 
 
 @main
 @main
 struct Spine_iOS_ExampleApp: App {
 struct Spine_iOS_ExampleApp: App {

+ 8 - 6
spine-ios/Example/Spine iOS Example/AnimationStateEvents.swift

@@ -27,12 +27,12 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
  *****************************************************************************/
 
 
-import SwiftUI
 import Spine
 import Spine
 import SpineCppLite
 import SpineCppLite
+import SwiftUI
 
 
 struct AnimationStateEvents: View {
 struct AnimationStateEvents: View {
-    
+
     @StateObject
     @StateObject
     var controller = SpineController(
     var controller = SpineController(
         onInitialized: { controller in
         onInitialized: { controller in
@@ -42,23 +42,25 @@ struct AnimationStateEvents: View {
             controller.animationStateData.defaultMix = 0.2
             controller.animationStateData.defaultMix = 0.2
             let walk = controller.animationState.setAnimationByName(trackIndex: 0, animationName: "walk", loop: true)
             let walk = controller.animationState.setAnimationByName(trackIndex: 0, animationName: "walk", loop: true)
             controller.animationStateWrapper.setTrackEntryListener(entry: walk) { type, entry, event in
             controller.animationStateWrapper.setTrackEntryListener(entry: walk) { type, entry, event in
-                print("Walk animation event \(type)");
+                print("Walk animation event \(type)")
             }
             }
             controller.animationState.addAnimationByName(trackIndex: 0, animationName: "jump", loop: false, delay: 2)
             controller.animationState.addAnimationByName(trackIndex: 0, animationName: "jump", loop: false, delay: 2)
             let run = controller.animationState.addAnimationByName(trackIndex: 0, animationName: "run", loop: true, delay: 0)
             let run = controller.animationState.addAnimationByName(trackIndex: 0, animationName: "run", loop: true, delay: 0)
             controller.animationStateWrapper.setTrackEntryListener(entry: run) { type, entry, event in
             controller.animationStateWrapper.setTrackEntryListener(entry: run) { type, entry, event in
-                print("Run animation event \(type)");
+                print("Run animation event \(type)")
             }
             }
             controller.animationStateWrapper.setStateListener { type, entry, event in
             controller.animationStateWrapper.setStateListener { type, entry, event in
                 if type == SPINE_EVENT_TYPE_EVENT, let event {
                 if type == SPINE_EVENT_TYPE_EVENT, let event {
-                    print("User event: { name: \(event.data.name ?? "--"), intValue: \(event.intValue), floatValue: \(event.floatValue), stringValue: \(event.stringValue ?? "--") }")
+                    print(
+                        "User event: { name: \(event.data.name ?? "--"), intValue: \(event.intValue), floatValue: \(event.floatValue), stringValue: \(event.stringValue ?? "--") }"
+                    )
                 }
                 }
             }
             }
             let current = controller.animationState.getCurrent(trackIndex: 0)?.animation.name ?? "--"
             let current = controller.animationState.getCurrent(trackIndex: 0)?.animation.name ?? "--"
             print("Current: \(current)")
             print("Current: \(current)")
         }
         }
     )
     )
-    
+
     var body: some View {
     var body: some View {
         VStack {
         VStack {
             Text("See output in console!")
             Text("See output in console!")

+ 9 - 8
spine-ios/Example/Spine iOS Example/DebugRendering.swift

@@ -27,14 +27,14 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
  *****************************************************************************/
 
 
-import SwiftUI
 import Spine
 import Spine
+import SwiftUI
 
 
 struct DebugRendering: View {
 struct DebugRendering: View {
-    
+
     @StateObject
     @StateObject
     var model = DebugRenderingModel()
     var model = DebugRenderingModel()
-    
+
     var body: some View {
     var body: some View {
         ZStack {
         ZStack {
             Color.red.ignoresSafeArea()
             Color.red.ignoresSafeArea()
@@ -61,13 +61,13 @@ struct DebugRendering: View {
 }
 }
 
 
 final class DebugRenderingModel: ObservableObject {
 final class DebugRenderingModel: ObservableObject {
-    
+
     @Published
     @Published
     var controller: SpineController!
     var controller: SpineController!
-    
+
     @Published
     @Published
     var boneRects = [BoneRect]()
     var boneRects = [BoneRect]()
-    
+
     init() {
     init() {
         controller = SpineController(
         controller = SpineController(
             onInitialized: { controller in
             onInitialized: { controller in
@@ -77,8 +77,9 @@ final class DebugRenderingModel: ObservableObject {
                     loop: true
                     loop: true
                 )
                 )
             },
             },
-            onAfterPaint: { 
-                [weak self] controller in guard let self else { return }
+            onAfterPaint: {
+                [weak self] controller in
+                guard let self else { return }
                 boneRects = controller.drawable.skeleton.bones.map { bone in
                 boneRects = controller.drawable.skeleton.bones.map { bone in
                     let position = controller.fromSkeletonCoordinates(
                     let position = controller.fromSkeletonCoordinates(
                         position: CGPointMake(CGFloat(bone.worldX), CGFloat(bone.worldY))
                         position: CGPointMake(CGFloat(bone.worldX), CGFloat(bone.worldY))

+ 8 - 8
spine-ios/Example/Spine iOS Example/DisableRendering.swift

@@ -27,11 +27,11 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
  *****************************************************************************/
 
 
-import SwiftUI
 import Spine
 import Spine
+import SwiftUI
 
 
 struct DisableRendering: View {
 struct DisableRendering: View {
-    
+
     @StateObject
     @StateObject
     var controller = SpineController(
     var controller = SpineController(
         onInitialized: { controller in
         onInitialized: { controller in
@@ -42,10 +42,10 @@ struct DisableRendering: View {
             )
             )
         }
         }
     )
     )
-    
+
     @State
     @State
     var isRendering: Bool?
     var isRendering: Bool?
-    
+
     var body: some View {
     var body: some View {
         VStack {
         VStack {
             List {
             List {
@@ -54,7 +54,7 @@ struct DisableRendering: View {
                     Text("Rendering is disabled when the spine view moves out of the viewport, preserving CPU/GPU resources.")
                     Text("Rendering is disabled when the spine view moves out of the viewport, preserving CPU/GPU resources.")
                         .foregroundColor(.secondary)
                         .foregroundColor(.secondary)
                 }
                 }
-                
+
                 SpineView(
                 SpineView(
                     from: .bundle(atlasFileName: "spineboy-pma.atlas", skeletonFileName: "spineboy-pro.skel"),
                     from: .bundle(atlasFileName: "spineboy-pma.atlas", skeletonFileName: "spineboy-pro.skel"),
                     controller: controller,
                     controller: controller,
@@ -69,13 +69,13 @@ struct DisableRendering: View {
                     isRendering = false
                     isRendering = false
                     print("rendering disabled")
                     print("rendering disabled")
                 }
                 }
-                
+
                 Text("Foo")
                 Text("Foo")
                     .frame(minHeight: 400)
                     .frame(minHeight: 400)
-                
+
                 Text("Bar")
                 Text("Bar")
                     .frame(minHeight: 400)
                     .frame(minHeight: 400)
-                
+
                 Text("Baz")
                 Text("Baz")
                     .frame(minHeight: 400)
                     .frame(minHeight: 400)
             }
             }

+ 20 - 20
spine-ios/Example/Spine iOS Example/DressUp.swift

@@ -27,15 +27,15 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
  *****************************************************************************/
 
 
-import SwiftUI
 import Spine
 import Spine
 import SpineCppLite
 import SpineCppLite
+import SwiftUI
 
 
 struct DressUp: View {
 struct DressUp: View {
-    
+
     @StateObject
     @StateObject
     var model = DressUpModel()
     var model = DressUpModel()
-    
+
     var body: some View {
     var body: some View {
         HStack(spacing: 0) {
         HStack(spacing: 0) {
             List {
             List {
@@ -51,9 +51,9 @@ struct DressUp: View {
                 }
                 }
             }
             }
             .listStyle(.plain)
             .listStyle(.plain)
-            
+
             Divider()
             Divider()
-            
+
             if let drawable = model.drawable {
             if let drawable = model.drawable {
                 SpineView(
                 SpineView(
                     from: .drawable(drawable),
                     from: .drawable(drawable),
@@ -74,23 +74,23 @@ struct DressUp: View {
 }
 }
 
 
 final class DressUpModel: ObservableObject {
 final class DressUpModel: ObservableObject {
-    
+
     let thumbnailSize = CGSize(width: 200, height: 200)
     let thumbnailSize = CGSize(width: 200, height: 200)
-    
+
     @Published
     @Published
     var controller: SpineController
     var controller: SpineController
-    
+
     @Published
     @Published
     var drawable: SkeletonDrawableWrapper?
     var drawable: SkeletonDrawableWrapper?
-    
+
     @Published
     @Published
     var skinImages = [String: CGImage]()
     var skinImages = [String: CGImage]()
-    
+
     @Published
     @Published
     var selectedSkins = [String: Bool]()
     var selectedSkins = [String: Bool]()
-    
+
     private var customSkin: Skin?
     private var customSkin: Skin?
-    
+
     init() {
     init() {
         controller = SpineController(
         controller = SpineController(
             onInitialized: { controller in
             onInitialized: { controller in
@@ -129,28 +129,28 @@ final class DressUpModel: ObservableObject {
             }
             }
         }
         }
     }
     }
-    
+
     deinit {
     deinit {
         drawable?.dispose()
         drawable?.dispose()
         customSkin?.dispose()
         customSkin?.dispose()
     }
     }
-    
+
     func toggleSkin(skinName: String) {
     func toggleSkin(skinName: String) {
         if let drawable {
         if let drawable {
             toggleSkin(skinName: skinName, drawable: drawable)
             toggleSkin(skinName: skinName, drawable: drawable)
         }
         }
     }
     }
-    
+
     func toggleSkin(skinName: String, drawable: SkeletonDrawableWrapper) {
     func toggleSkin(skinName: String, drawable: SkeletonDrawableWrapper) {
         selectedSkins[skinName] = !(selectedSkins[skinName] ?? false)
         selectedSkins[skinName] = !(selectedSkins[skinName] ?? false)
         customSkin?.dispose()
         customSkin?.dispose()
         customSkin = Skin.create(name: "custom-skin")
         customSkin = Skin.create(name: "custom-skin")
         for skinName in selectedSkins.keys {
         for skinName in selectedSkins.keys {
-          if selectedSkins[skinName] == true {
-              if let skin = drawable.skeletonData.findSkin(name: skinName) {
-                  customSkin?.addSkin(other: skin)
-              }
-          }
+            if selectedSkins[skinName] == true {
+                if let skin = drawable.skeletonData.findSkin(name: skinName) {
+                    customSkin?.addSkin(other: skin)
+                }
+            }
         }
         }
         drawable.skeleton.skin = customSkin
         drawable.skeleton.skin = customSkin
         drawable.skeleton.setToSetupPose()
         drawable.skeleton.setToSetupPose()

+ 10 - 9
spine-ios/Example/Spine iOS Example/IKFollowing.swift

@@ -27,14 +27,14 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
  *****************************************************************************/
 
 
-import SwiftUI
 import Spine
 import Spine
+import SwiftUI
 
 
 struct IKFollowing: View {
 struct IKFollowing: View {
-    
+
     @StateObject
     @StateObject
     var model = IKFollowingModel()
     var model = IKFollowingModel()
-        
+
     var body: some View {
     var body: some View {
         SpineView(
         SpineView(
             from: .bundle(atlasFileName: "spineboy-pma.atlas", skeletonFileName: "spineboy-pro.skel"),
             from: .bundle(atlasFileName: "spineboy-pma.atlas", skeletonFileName: "spineboy-pro.skel"),
@@ -64,13 +64,13 @@ struct IKFollowing: View {
 }
 }
 
 
 final class IKFollowingModel: ObservableObject {
 final class IKFollowingModel: ObservableObject {
-    
+
     @Published
     @Published
     var controller: SpineController!
     var controller: SpineController!
-    
+
     @Published
     @Published
     var crossHairPosition: CGPoint?
     var crossHairPosition: CGPoint?
-    
+
     init() {
     init() {
         controller = SpineController(
         controller = SpineController(
             onInitialized: { controller in
             onInitialized: { controller in
@@ -85,14 +85,15 @@ final class IKFollowingModel: ObservableObject {
                     loop: true
                     loop: true
                 )
                 )
             },
             },
-            onAfterUpdateWorldTransforms: { 
-                [weak self] controller in guard let self else { return }
+            onAfterUpdateWorldTransforms: {
+                [weak self] controller in
+                guard let self else { return }
                 guard let worldPosition = self.crossHairPosition else {
                 guard let worldPosition = self.crossHairPosition else {
                     return
                     return
                 }
                 }
                 let bone = controller.skeleton.findBone(boneName: "crosshair")!
                 let bone = controller.skeleton.findBone(boneName: "crosshair")!
                 if let parent = bone.parent {
                 if let parent = bone.parent {
-                    let position = parent.worldToLocal(worldX: Float(worldPosition.x), worldY:  Float(worldPosition.y))
+                    let position = parent.worldToLocal(worldX: Float(worldPosition.x), worldY: Float(worldPosition.y))
                     bone.x = position.x
                     bone.x = position.x
                     bone.y = position.y
                     bone.y = position.y
                 }
                 }

+ 1 - 1
spine-ios/Example/Spine iOS Example/MainView.swift

@@ -27,8 +27,8 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
  *****************************************************************************/
 
 
-import SwiftUI
 import Spine
 import Spine
+import SwiftUI
 
 
 struct MainView: View {
 struct MainView: View {
     var body: some View {
     var body: some View {

+ 24 - 23
spine-ios/Example/Spine iOS Example/Physics.swift

@@ -27,28 +27,28 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
  *****************************************************************************/
 
 
-import SwiftUI
 import Spine
 import Spine
+import SwiftUI
 
 
 struct Physics: View {
 struct Physics: View {
-    
+
     @StateObject
     @StateObject
     var model = PhysicsModel()
     var model = PhysicsModel()
-    
+
     var body: some View {
     var body: some View {
-		ZStack {
-			Color(red: 51 / 255, green: 51 / 255, blue: 51 / 255).ignoresSafeArea()
-			SpineView(
-				from: .bundle(atlasFileName: "celestial-circus-pma.atlas", skeletonFileName: "celestial-circus-pro.skel"),
-				controller: model.controller
-			)
-			.gesture(
-				DragGesture(minimumDistance: 0)
-					.onChanged { gesture in
-						model.updateBonePosition(position: gesture.location)
-					}
-			)
-		}
+        ZStack {
+            Color(red: 51 / 255, green: 51 / 255, blue: 51 / 255).ignoresSafeArea()
+            SpineView(
+                from: .bundle(atlasFileName: "celestial-circus-pma.atlas", skeletonFileName: "celestial-circus-pro.skel"),
+                controller: model.controller
+            )
+            .gesture(
+                DragGesture(minimumDistance: 0)
+                    .onChanged { gesture in
+                        model.updateBonePosition(position: gesture.location)
+                    }
+            )
+        }
         .navigationTitle("Physics (drag anywhere)")
         .navigationTitle("Physics (drag anywhere)")
         .navigationBarTitleDisplayMode(.inline)
         .navigationBarTitleDisplayMode(.inline)
     }
     }
@@ -59,16 +59,16 @@ struct Physics: View {
 }
 }
 
 
 final class PhysicsModel: ObservableObject {
 final class PhysicsModel: ObservableObject {
-    
+
     @Published
     @Published
     var controller: SpineController!
     var controller: SpineController!
-    
+
     @Published
     @Published
     var mousePosition: CGPoint?
     var mousePosition: CGPoint?
-    
+
     @Published
     @Published
     var lastMousePosition: CGPoint?
     var lastMousePosition: CGPoint?
-    
+
     init() {
     init() {
         controller = SpineController(
         controller = SpineController(
             onInitialized: { controller in
             onInitialized: { controller in
@@ -84,8 +84,9 @@ final class PhysicsModel: ObservableObject {
                 )
                 )
             },
             },
             onAfterUpdateWorldTransforms: {
             onAfterUpdateWorldTransforms: {
-                [weak self] controller in guard let self else { return }
-                
+                [weak self] controller in
+                guard let self else { return }
+
                 guard let lastMousePosition else {
                 guard let lastMousePosition else {
                     self.lastMousePosition = mousePosition
                     self.lastMousePosition = mousePosition
                     return
                     return
@@ -102,7 +103,7 @@ final class PhysicsModel: ObservableObject {
             }
             }
         )
         )
     }
     }
-    
+
     func updateBonePosition(position: CGPoint) {
     func updateBonePosition(position: CGPoint) {
         mousePosition = controller.toSkeletonCoordinates(position: position)
         mousePosition = controller.toSkeletonCoordinates(position: position)
     }
     }

+ 5 - 5
spine-ios/Example/Spine iOS Example/PlayPauseAnimation.swift

@@ -27,8 +27,8 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
  *****************************************************************************/
 
 
-import SwiftUI
 import Spine
 import Spine
+import SwiftUI
 
 
 struct PlayPauseAnimation: View {
 struct PlayPauseAnimation: View {
 
 
@@ -46,10 +46,10 @@ struct PlayPauseAnimation: View {
     var body: some View {
     var body: some View {
         SpineView(
         SpineView(
             from: .bundle(atlasFileName: "dragon.atlas", skeletonFileName: "dragon-ess.skel"),
             from: .bundle(atlasFileName: "dragon.atlas", skeletonFileName: "dragon-ess.skel"),
-//            from: .http(
-//                atlasURL: URL(string: "https://github.com/esotericsoftware/spine-runtimes/raw/spine-ios/spine-ios/Example/Spine%20iOS%20Example/Assets/dragon/dragon.atlas")!,
-//                skeletonURL:  URL(string: "https://github.com/esotericsoftware/spine-runtimes/raw/spine-ios/spine-ios/Example/Spine%20iOS%20Example/Assets/dragon/dragon-ess.skel")!
-//            ),
+            //            from: .http(
+            //                atlasURL: URL(string: "https://github.com/esotericsoftware/spine-runtimes/raw/spine-ios/spine-ios/Example/Spine%20iOS%20Example/Assets/dragon/dragon.atlas")!,
+            //                skeletonURL:  URL(string: "https://github.com/esotericsoftware/spine-runtimes/raw/spine-ios/spine-ios/Example/Spine%20iOS%20Example/Assets/dragon/dragon-ess.skel")!
+            //            ),
             controller: controller,
             controller: controller,
             boundsProvider: SkinAndAnimationBounds(animation: "flying")
             boundsProvider: SkinAndAnimationBounds(animation: "flying")
         )
         )

+ 5 - 5
spine-ios/Example/Spine iOS Example/SimpleAnimation.swift

@@ -27,8 +27,8 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
  *****************************************************************************/
 
 
-import SwiftUI
 import Spine
 import Spine
+import SwiftUI
 
 
 struct SimpleAnimation: View {
 struct SimpleAnimation: View {
 
 
@@ -46,10 +46,10 @@ struct SimpleAnimation: View {
     var body: some View {
     var body: some View {
         SpineView(
         SpineView(
             from: .bundle(atlasFileName: "spineboy-pma.atlas", skeletonFileName: "spineboy-pro.skel"),
             from: .bundle(atlasFileName: "spineboy-pma.atlas", skeletonFileName: "spineboy-pro.skel"),
-//            from: .http(
-//                atlasURL: URL(string: "https://github.com/esotericsoftware/spine-runtimes/raw/spine-ios/spine-ios/Example/Spine%20iOS%20Example/Assets/spineboy/spineboy.atlas")!,
-//                skeletonURL:  URL(string: "https://github.com/esotericsoftware/spine-runtimes/raw/spine-ios/spine-ios/Example/Spine%20iOS%20Example/Assets/spineboy/spineboy-pro.skel")!
-//            ),
+            //            from: .http(
+            //                atlasURL: URL(string: "https://github.com/esotericsoftware/spine-runtimes/raw/spine-ios/spine-ios/Example/Spine%20iOS%20Example/Assets/spineboy/spineboy.atlas")!,
+            //                skeletonURL:  URL(string: "https://github.com/esotericsoftware/spine-runtimes/raw/spine-ios/spine-ios/Example/Spine%20iOS%20Example/Assets/spineboy/spineboy-pro.skel")!
+            //            ),
             controller: controller,
             controller: controller,
             mode: .fit,
             mode: .fit,
             alignment: .center
             alignment: .center

+ 2 - 2
spine-ios/Example/Spine iOS Example/SimpleAnimationViewControllerRepresentable.swift

@@ -31,11 +31,11 @@ import SwiftUI
 
 
 struct SimpleAnimationViewControllerRepresentable: UIViewControllerRepresentable {
 struct SimpleAnimationViewControllerRepresentable: UIViewControllerRepresentable {
     typealias UIViewControllerType = SimpleAnimationViewController
     typealias UIViewControllerType = SimpleAnimationViewController
-    
+
     func makeUIViewController(context: Context) -> SimpleAnimationViewController {
     func makeUIViewController(context: Context) -> SimpleAnimationViewController {
         return SimpleAnimationViewController()
         return SimpleAnimationViewController()
     }
     }
-    
+
     func updateUIViewController(_ uiViewController: SimpleAnimationViewController, context: Context) {
     func updateUIViewController(_ uiViewController: SimpleAnimationViewController, context: Context) {
         //
         //
     }
     }

+ 2 - 2
spine-ios/Example/Spine iOS Example/SpineExampleApp.swift

@@ -27,12 +27,12 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
  *****************************************************************************/
 
 
-import SwiftUI
 import Spine
 import Spine
+import SwiftUI
 
 
 @main
 @main
 struct SpineExampleApp: App {
 struct SpineExampleApp: App {
-    
+
     var body: some Scene {
     var body: some Scene {
         WindowGroup {
         WindowGroup {
             NavigationView {
             NavigationView {

+ 10 - 10
spine-ios/Sources/Spine/AnimationStateWrapper.swift

@@ -39,20 +39,20 @@ public typealias AnimationStateListener = (_ type: EventType, _ entry: TrackEntr
 @objc(SpineAnimationStateWrapper)
 @objc(SpineAnimationStateWrapper)
 @objcMembers
 @objcMembers
 public final class AnimationStateWrapper: NSObject {
 public final class AnimationStateWrapper: NSObject {
-    
+
     public let animationState: AnimationState
     public let animationState: AnimationState
     public let aninationStateEvents: AnimationStateEvents
     public let aninationStateEvents: AnimationStateEvents
-    
+
     private var trackEntryListeners = [spine_track_entry: AnimationStateListener]()
     private var trackEntryListeners = [spine_track_entry: AnimationStateListener]()
-    
+
     private var stateListener: AnimationStateListener?
     private var stateListener: AnimationStateListener?
-    
+
     public init(animationState: AnimationState, aninationStateEvents: AnimationStateEvents) {
     public init(animationState: AnimationState, aninationStateEvents: AnimationStateEvents) {
         self.animationState = animationState
         self.animationState = animationState
         self.aninationStateEvents = aninationStateEvents
         self.aninationStateEvents = aninationStateEvents
         super.init()
         super.init()
     }
     }
-    
+
     /// The listener for events generated by the provided ``TrackEntry``, or nil.
     /// The listener for events generated by the provided ``TrackEntry``, or nil.
     ///
     ///
     /// A track entry returned from ``AnimationState/setAnimation(trackIndex:animation:loop:)`` is already the current animation
     /// A track entry returned from ``AnimationState/setAnimation(trackIndex:animation:loop:)`` is already the current animation
@@ -64,18 +64,18 @@ public final class AnimationStateWrapper: NSObject {
             trackEntryListeners.removeValue(forKey: entry.wrappee)
             trackEntryListeners.removeValue(forKey: entry.wrappee)
         }
         }
     }
     }
-    
+
     /// Increments each track entry ``TrackEntry/trackTime``, setting queued animations as current if needed.
     /// Increments each track entry ``TrackEntry/trackTime``, setting queued animations as current if needed.
     public func update(delta: Float) {
     public func update(delta: Float) {
         animationState.update(delta: delta)
         animationState.update(delta: delta)
-        
+
         let numEvents = spine_animation_state_events_get_num_events(aninationStateEvents.wrappee)
         let numEvents = spine_animation_state_events_get_num_events(aninationStateEvents.wrappee)
         for i in 0..<numEvents {
         for i in 0..<numEvents {
             let type = aninationStateEvents.getEventType(index: i)
             let type = aninationStateEvents.getEventType(index: i)
-            
+
             let entry = aninationStateEvents.getTrackEntry(index: i)
             let entry = aninationStateEvents.getTrackEntry(index: i)
             let event = aninationStateEvents.getEvent(index: i)
             let event = aninationStateEvents.getEvent(index: i)
-            
+
             if let trackEntryListener = trackEntryListeners[entry.wrappee] {
             if let trackEntryListener = trackEntryListeners[entry.wrappee] {
                 trackEntryListener(type, entry, event)
                 trackEntryListener(type, entry, event)
             }
             }
@@ -88,7 +88,7 @@ public final class AnimationStateWrapper: NSObject {
         }
         }
         aninationStateEvents.reset()
         aninationStateEvents.reset()
     }
     }
-    
+
     /// The listener for events generated for all tracks managed by the ``AnimationState``, or nil.
     /// The listener for events generated for all tracks managed by the ``AnimationState``, or nil.
     ///
     ///
     /// A track entry returned from ``AnimationState/setAnimation(trackIndex:animation:loop:)`` is already the current animation
     /// A track entry returned from ``AnimationState/setAnimation(trackIndex:animation:loop:)`` is already the current animation

+ 15 - 14
spine-ios/Sources/Spine/BoundsProvider.swift

@@ -27,8 +27,8 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
  *****************************************************************************/
 
 
-import Foundation
 import CoreGraphics
 import CoreGraphics
+import Foundation
 
 
 /// Base class for bounds providers. A bounds provider calculates the axis aligned bounding box
 /// Base class for bounds providers. A bounds provider calculates the axis aligned bounding box
 /// used to scale and fit a skeleton inside the bounds of a ``SpineUIView``.
 /// used to scale and fit a skeleton inside the bounds of a ``SpineUIView``.
@@ -42,7 +42,7 @@ public protocol BoundsProvider {
 @objc(SpineSetupPoseBounds)
 @objc(SpineSetupPoseBounds)
 @objcMembers
 @objcMembers
 public final class SetupPoseBounds: NSObject, BoundsProvider {
 public final class SetupPoseBounds: NSObject, BoundsProvider {
-    
+
     public override init() {
     public override init() {
         super.init()
         super.init()
     }
     }
@@ -79,10 +79,10 @@ public final class RawBounds: NSObject, BoundsProvider {
 @objc(SpineSkinAndAnimationBounds)
 @objc(SpineSkinAndAnimationBounds)
 @objcMembers
 @objcMembers
 public final class SkinAndAnimationBounds: NSObject, BoundsProvider {
 public final class SkinAndAnimationBounds: NSObject, BoundsProvider {
-    
+
     private let animation: String?
     private let animation: String?
     private let skins: [String]
     private let skins: [String]
-    private let stepTime: TimeInterval;
+    private let stepTime: TimeInterval
 
 
     /// Constructs a new provider that will use the given `skins` and `animation` to calculate
     /// Constructs a new provider that will use the given `skins` and `animation` to calculate
     /// the bounding box of the skeleton. If no skins are given, the default skin is used.
     /// the bounding box of the skeleton. If no skins are given, the default skin is used.
@@ -98,7 +98,7 @@ public final class SkinAndAnimationBounds: NSObject, BoundsProvider {
         self.stepTime = stepTime
         self.stepTime = stepTime
         super.init()
         super.init()
     }
     }
-    
+
     public func computeBounds(for drawable: SkeletonDrawableWrapper) -> CGRect {
     public func computeBounds(for drawable: SkeletonDrawableWrapper) -> CGRect {
         let data = drawable.skeletonData
         let data = drawable.skeletonData
         let oldSkin: Skin? = drawable.skeleton.skin
         let oldSkin: Skin? = drawable.skeleton.skin
@@ -110,7 +110,7 @@ public final class SkinAndAnimationBounds: NSObject, BoundsProvider {
             }
             }
         }
         }
         drawable.skeleton.skin = customSkin
         drawable.skeleton.skin = customSkin
-        drawable.skeleton.setToSetupPose();
+        drawable.skeleton.setToSetupPose()
 
 
         let animation = animation.flatMap { data.findAnimation(name: $0) }
         let animation = animation.flatMap { data.findAnimation(name: $0) }
         var minX = Float.Magnitude.greatestFiniteMagnitude
         var minX = Float.Magnitude.greatestFiniteMagnitude
@@ -122,14 +122,14 @@ public final class SkinAndAnimationBounds: NSObject, BoundsProvider {
             let steps = Int(max(Double(animation.duration) / stepTime, 1.0))
             let steps = Int(max(Double(animation.duration) / stepTime, 1.0))
             for i in 0..<steps {
             for i in 0..<steps {
                 drawable.update(delta: i > 0 ? Float(stepTime) : 0.0)
                 drawable.update(delta: i > 0 ? Float(stepTime) : 0.0)
-                let bounds = drawable.skeleton.bounds;
+                let bounds = drawable.skeleton.bounds
                 minX = min(minX, bounds.x)
                 minX = min(minX, bounds.x)
                 minY = min(minY, bounds.y)
                 minY = min(minY, bounds.y)
                 maxX = max(maxX, minX + bounds.width)
                 maxX = max(maxX, minX + bounds.width)
                 maxY = max(maxY, minY + bounds.height)
                 maxY = max(maxY, minY + bounds.height)
             }
             }
         } else {
         } else {
-            let bounds = drawable.skeleton.bounds;
+            let bounds = drawable.skeleton.bounds
             minX = bounds.x
             minX = bounds.x
             minY = bounds.y
             minY = bounds.y
             maxX = minX + bounds.width
             maxX = minX + bounds.width
@@ -137,7 +137,7 @@ public final class SkinAndAnimationBounds: NSObject, BoundsProvider {
         }
         }
         drawable.skeleton.setSkinByName(skinName: "default")
         drawable.skeleton.setSkinByName(skinName: "default")
         drawable.animationState.clearTracks()
         drawable.animationState.clearTracks()
-        
+
         if let oldSkin {
         if let oldSkin {
             drawable.skeleton.skin = oldSkin
             drawable.skeleton.skin = oldSkin
         }
         }
@@ -145,14 +145,15 @@ public final class SkinAndAnimationBounds: NSObject, BoundsProvider {
         drawable.update(delta: 0)
         drawable.update(delta: 0)
         customSkin.dispose()
         customSkin.dispose()
         return CGRectMake(CGFloat(minX), CGFloat(minY), CGFloat(maxX - minX), CGFloat(maxY - minY))
         return CGRectMake(CGFloat(minX), CGFloat(minY), CGFloat(maxX - minX), CGFloat(maxY - minY))
-      }
+    }
 }
 }
 
 
 /// How a view should be inscribed into another view.
 /// How a view should be inscribed into another view.
 @objc
 @objc
 public enum ContentMode: Int {
 public enum ContentMode: Int {
-    case fit /// As large as possible while still containing the source view entirely within the target view.
-    case fill /// Fill the target view by distorting the source's aspect ratio.
+    case fit
+    /// As large as possible while still containing the source view entirely within the target view.
+    case fill/// Fill the target view by distorting the source's aspect ratio.
 }
 }
 
 
 /// How a view should aligned withing another view.
 /// How a view should aligned withing another view.
@@ -167,7 +168,7 @@ public enum Alignment: Int {
     case bottomLeft
     case bottomLeft
     case bottomCenter
     case bottomCenter
     case bottomRight
     case bottomRight
-    
+
     internal var x: CGFloat {
     internal var x: CGFloat {
         switch self {
         switch self {
         case .topLeft, .centerLeft, .bottomLeft: return -1.0
         case .topLeft, .centerLeft, .bottomLeft: return -1.0
@@ -175,7 +176,7 @@ public enum Alignment: Int {
         case .topRight, .centerRight, .bottomRight: return 1.0
         case .topRight, .centerRight, .bottomRight: return 1.0
         }
         }
     }
     }
-    
+
     internal var y: CGFloat {
     internal var y: CGFloat {
         switch self {
         switch self {
         case .topLeft, .topCenter, .topRight: return -1.0
         case .topLeft, .topCenter, .topRight: return -1.0

+ 3 - 3
spine-ios/Sources/Spine/Extensions/MTLClearColor+UIColor.swift

@@ -27,8 +27,8 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
  *****************************************************************************/
 
 
-import UIKit
 import MetalKit
 import MetalKit
+import UIKit
 
 
 extension MTLClearColor {
 extension MTLClearColor {
     init(_ color: UIColor) {
     init(_ color: UIColor) {
@@ -36,9 +36,9 @@ extension MTLClearColor {
         var green: CGFloat = 0
         var green: CGFloat = 0
         var blue: CGFloat = 0
         var blue: CGFloat = 0
         var alpha: CGFloat = 0
         var alpha: CGFloat = 0
-        
+
         color.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
         color.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
-        
+
         self.init(red: Double(red), green: Double(green), blue: Double(blue), alpha: Double(alpha))
         self.init(red: Double(red), green: Double(green), blue: Double(blue), alpha: Double(alpha))
     }
     }
 }
 }

+ 2 - 2
spine-ios/Sources/Spine/Extensions/RenderCommand+Vertices.swift

@@ -27,8 +27,8 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
  *****************************************************************************/
 
 
-import SpineShadersStructs
 import Foundation
 import Foundation
+import SpineShadersStructs
 import simd
 import simd
 
 
 extension RenderCommand {
 extension RenderCommand {
@@ -55,7 +55,7 @@ extension RenderCommand {
             )
             )
             vertices.append(vertex)
             vertices.append(vertex)
         }
         }
-        
+
         return vertices
         return vertices
     }
     }
 
 

+ 18 - 14
spine-ios/Sources/Spine/Extensions/SkeletonDrawableWrapper+CGImage.swift

@@ -27,21 +27,21 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
  *****************************************************************************/
 
 
+import CoreGraphics
 import Foundation
 import Foundation
 import UIKit
 import UIKit
-import CoreGraphics
 
 
-public extension SkeletonDrawableWrapper {
-    
+extension SkeletonDrawableWrapper {
+
     /// Render the ``Skeleton`` to a `CGImage`
     /// Render the ``Skeleton`` to a `CGImage`
     ///
     ///
     /// Parameters:
     /// Parameters:
     ///     - size: The size of the `CGImage` that should be rendered.
     ///     - size: The size of the `CGImage` that should be rendered.
     ///     - backgroundColor: the background color of the image
     ///     - backgroundColor: the background color of the image
     ///     - scaleFactor: The scale factor. Set this to `UIScreen.main.scale` if you want to show the image in a view
     ///     - scaleFactor: The scale factor. Set this to `UIScreen.main.scale` if you want to show the image in a view
-    func renderToImage(size: CGSize, backgroundColor: UIColor, scaleFactor: CGFloat = 1) throws -> CGImage? {
+    public func renderToImage(size: CGSize, backgroundColor: UIColor, scaleFactor: CGFloat = 1) throws -> CGImage? {
         let spineView = SpineUIView(
         let spineView = SpineUIView(
-            controller: SpineController(disposeDrawableOnDeInit: false), // Doesn't own the drawable
+            controller: SpineController(disposeDrawableOnDeInit: false),  // Doesn't own the drawable
             backgroundColor: backgroundColor
             backgroundColor: backgroundColor
         )
         )
         spineView.frame = CGRect(origin: .zero, size: size)
         spineView.frame = CGRect(origin: .zero, size: size)
@@ -49,12 +49,12 @@ public extension SkeletonDrawableWrapper {
         spineView.enableSetNeedsDisplay = false
         spineView.enableSetNeedsDisplay = false
         spineView.framebufferOnly = false
         spineView.framebufferOnly = false
         spineView.contentScaleFactor = scaleFactor
         spineView.contentScaleFactor = scaleFactor
-        
+
         try spineView.load(drawable: self)
         try spineView.load(drawable: self)
         spineView.renderer?.waitUntilCompleted = true
         spineView.renderer?.waitUntilCompleted = true
-        
+
         spineView.delegate?.draw(in: spineView)
         spineView.delegate?.draw(in: spineView)
-        
+
         guard let texture = spineView.currentDrawable?.texture else {
         guard let texture = spineView.currentDrawable?.texture else {
             throw SpineError("Could not read texture.")
             throw SpineError("Could not read texture.")
         }
         }
@@ -65,18 +65,22 @@ public extension SkeletonDrawableWrapper {
         defer {
         defer {
             data.deallocate()
             data.deallocate()
         }
         }
-        
+
         let region = MTLRegionMake2D(0, 0, width, height)
         let region = MTLRegionMake2D(0, 0, width, height)
         texture.getBytes(data, bytesPerRow: rowBytes, from: region, mipmapLevel: 0)
         texture.getBytes(data, bytesPerRow: rowBytes, from: region, mipmapLevel: 0)
-        
+
         let bitmapInfo = CGBitmapInfo(
         let bitmapInfo = CGBitmapInfo(
             rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue
             rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue
         ).union(.byteOrder32Little)
         ).union(.byteOrder32Little)
-        
+
         let colorSpace = CGColorSpaceCreateDeviceRGB()
         let colorSpace = CGColorSpaceCreateDeviceRGB()
-        guard let context = CGContext(data: data, width: width, height: height, bitsPerComponent: 8, bytesPerRow: rowBytes, space: colorSpace, bitmapInfo: bitmapInfo.rawValue),
-              let cgImage = context.makeImage() else {
-                throw SpineError("Could not create image.")
+        guard
+            let context = CGContext(
+                data: data, width: width, height: height, bitsPerComponent: 8, bytesPerRow: rowBytes, space: colorSpace,
+                bitmapInfo: bitmapInfo.rawValue),
+            let cgImage = context.makeImage()
+        else {
+            throw SpineError("Could not create image.")
         }
         }
         return cgImage
         return cgImage
     }
     }

+ 3 - 3
spine-ios/Sources/Spine/Metal/SpineObjects.swift

@@ -35,13 +35,13 @@ import MetalKit
 /// Persistent Objects
 /// Persistent Objects
 /// https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/MTLBestPracticesGuide/PersistentObjects.html#//apple_ref/doc/uid/TP40016642-CH3-SW1
 /// https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/MTLBestPracticesGuide/PersistentObjects.html#//apple_ref/doc/uid/TP40016642-CH3-SW1
 internal final class SpineObjects {
 internal final class SpineObjects {
-    
+
     static let shared = SpineObjects()
     static let shared = SpineObjects()
-    
+
     internal lazy var device: MTLDevice = {
     internal lazy var device: MTLDevice = {
         MTLCreateSystemDefaultDevice()!
         MTLCreateSystemDefaultDevice()!
     }()
     }()
-    
+
     internal lazy var commandQueue: MTLCommandQueue = {
     internal lazy var commandQueue: MTLCommandQueue = {
         device.makeCommandQueue()!
         device.makeCommandQueue()!
     }()
     }()

+ 131 - 129
spine-ios/Sources/Spine/Metal/SpineRenderer.swift

@@ -29,18 +29,18 @@
 
 
 import Foundation
 import Foundation
 import MetalKit
 import MetalKit
-import SpineShadersStructs
 import Spine
 import Spine
 import SpineCppLite
 import SpineCppLite
+import SpineShadersStructs
 
 
 protocol SpineRendererDelegate: AnyObject {
 protocol SpineRendererDelegate: AnyObject {
     func spineRendererWillUpdate(_ spineRenderer: SpineRenderer)
     func spineRendererWillUpdate(_ spineRenderer: SpineRenderer)
     func spineRenderer(_ spineRenderer: SpineRenderer, needsUpdate delta: TimeInterval)
     func spineRenderer(_ spineRenderer: SpineRenderer, needsUpdate delta: TimeInterval)
     func spineRendererDidUpdate(_ spineRenderer: SpineRenderer)
     func spineRendererDidUpdate(_ spineRenderer: SpineRenderer)
-    
+
     func spineRendererWillDraw(_ spineRenderer: SpineRenderer)
     func spineRendererWillDraw(_ spineRenderer: SpineRenderer)
     func spineRendererDidDraw(_ spineRenderer: SpineRenderer)
     func spineRendererDidDraw(_ spineRenderer: SpineRenderer)
-    
+
     func spineRendererDidUpdate(_ spineRenderer: SpineRenderer, scaleX: CGFloat, scaleY: CGFloat, offsetX: CGFloat, offsetY: CGFloat, size: CGSize)
     func spineRendererDidUpdate(_ spineRenderer: SpineRenderer, scaleX: CGFloat, scaleY: CGFloat, offsetX: CGFloat, offsetY: CGFloat, size: CGSize)
 }
 }
 
 
@@ -50,11 +50,11 @@ protocol SpineRendererDataSource: AnyObject {
 }
 }
 
 
 internal final class SpineRenderer: NSObject, MTKViewDelegate {
 internal final class SpineRenderer: NSObject, MTKViewDelegate {
-    
+
     private let device: MTLDevice
     private let device: MTLDevice
     private let textures: [MTLTexture]
     private let textures: [MTLTexture]
     private let commandQueue: MTLCommandQueue
     private let commandQueue: MTLCommandQueue
-    
+
     private var sizeInPoints: CGSize = .zero
     private var sizeInPoints: CGSize = .zero
     private var viewPortSize = vector_uint2(0, 0)
     private var viewPortSize = vector_uint2(0, 0)
     private var transform = SpineTransform(
     private var transform = SpineTransform(
@@ -65,17 +65,17 @@ internal final class SpineRenderer: NSObject, MTKViewDelegate {
     internal var lastDraw: CFTimeInterval = 0
     internal var lastDraw: CFTimeInterval = 0
     internal var waitUntilCompleted = false
     internal var waitUntilCompleted = false
     private var pipelineStatesByBlendMode = [Int: MTLRenderPipelineState]()
     private var pipelineStatesByBlendMode = [Int: MTLRenderPipelineState]()
-    
+
     private static let numberOfBuffers = 3
     private static let numberOfBuffers = 3
-    private static let defaultBufferSize = 32 * 1024 // 32KB
-    
+    private static let defaultBufferSize = 32 * 1024  // 32KB
+
     private var buffers = [MTLBuffer]()
     private var buffers = [MTLBuffer]()
     private let bufferingSemaphore = DispatchSemaphore(value: SpineRenderer.numberOfBuffers)
     private let bufferingSemaphore = DispatchSemaphore(value: SpineRenderer.numberOfBuffers)
     private var currentBufferIndex: Int = 0
     private var currentBufferIndex: Int = 0
-    
+
     weak var dataSource: SpineRendererDataSource?
     weak var dataSource: SpineRendererDataSource?
     weak var delegate: SpineRendererDelegate?
     weak var delegate: SpineRendererDelegate?
-    
+
     internal init(
     internal init(
         device: MTLDevice,
         device: MTLDevice,
         commandQueue: MTLCommandQueue,
         commandQueue: MTLCommandQueue,
@@ -85,18 +85,19 @@ internal final class SpineRenderer: NSObject, MTKViewDelegate {
     ) throws {
     ) throws {
         self.device = device
         self.device = device
         self.commandQueue = commandQueue
         self.commandQueue = commandQueue
-        
+
         let bundle: Bundle
         let bundle: Bundle
-        #if SWIFT_PACKAGE // SPM
-        bundle = .module
-        #else // CocoaPods
-        let bundleURL = Bundle(for: SpineRenderer.self).url(forResource: "SpineBundle", withExtension: "bundle")
-        bundle = Bundle(url: bundleURL!)!
+        #if SWIFT_PACKAGE  // SPM
+            bundle = .module
+        #else  // CocoaPods
+            let bundleURL = Bundle(for: SpineRenderer.self).url(forResource: "SpineBundle", withExtension: "bundle")
+            bundle = Bundle(url: bundleURL!)!
         #endif
         #endif
-        
+
         let defaultLibrary = try device.makeDefaultLibrary(bundle: bundle)
         let defaultLibrary = try device.makeDefaultLibrary(bundle: bundle)
         let textureLoader = MTKTextureLoader(device: device)
         let textureLoader = MTKTextureLoader(device: device)
-        textures = try atlasPages
+        textures =
+            try atlasPages
             .compactMap { $0.cgImage }
             .compactMap { $0.cgImage }
             .map {
             .map {
                 try textureLoader.newTexture(
                 try textureLoader.newTexture(
@@ -107,12 +108,12 @@ internal final class SpineRenderer: NSObject, MTKViewDelegate {
                     ]
                     ]
                 )
                 )
             }
             }
-        
+
         let blendModes = [
         let blendModes = [
             SPINE_BLEND_MODE_NORMAL,
             SPINE_BLEND_MODE_NORMAL,
             SPINE_BLEND_MODE_ADDITIVE,
             SPINE_BLEND_MODE_ADDITIVE,
             SPINE_BLEND_MODE_MULTIPLY,
             SPINE_BLEND_MODE_MULTIPLY,
-            SPINE_BLEND_MODE_SCREEN
+            SPINE_BLEND_MODE_SCREEN,
         ]
         ]
         for blendMode in blendModes {
         for blendMode in blendModes {
             let descriptor = MTLRenderPipelineDescriptor()
             let descriptor = MTLRenderPipelineDescriptor()
@@ -125,15 +126,15 @@ internal final class SpineRenderer: NSObject, MTKViewDelegate {
             )
             )
             pipelineStatesByBlendMode[Int(blendMode.rawValue)] = try device.makeRenderPipelineState(descriptor: descriptor)
             pipelineStatesByBlendMode[Int(blendMode.rawValue)] = try device.makeRenderPipelineState(descriptor: descriptor)
         }
         }
-        
+
         super.init()
         super.init()
-                
+
         increaseBuffersSize(to: SpineRenderer.defaultBufferSize)
         increaseBuffersSize(to: SpineRenderer.defaultBufferSize)
     }
     }
-    
+
     func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
     func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
         guard let spineView = view as? SpineUIView else { return }
         guard let spineView = view as? SpineUIView else { return }
-        
+
         sizeInPoints = CGSize(width: size.width / UIScreen.main.scale, height: size.height / UIScreen.main.scale)
         sizeInPoints = CGSize(width: size.width / UIScreen.main.scale, height: size.height / UIScreen.main.scale)
         viewPortSize = vector_uint2(UInt32(size.width), UInt32(size.height))
         viewPortSize = vector_uint2(UInt32(size.width), UInt32(size.height))
         setTransform(
         setTransform(
@@ -142,35 +143,36 @@ internal final class SpineRenderer: NSObject, MTKViewDelegate {
             alignment: spineView.alignment
             alignment: spineView.alignment
         )
         )
     }
     }
-    
+
     func draw(in view: MTKView) {
     func draw(in view: MTKView) {
         guard dataSource?.isPlaying(self) ?? false else {
         guard dataSource?.isPlaying(self) ?? false else {
             lastDraw = CACurrentMediaTime()
             lastDraw = CACurrentMediaTime()
             return
             return
         }
         }
-        
+
         callNeedsUpdate()
         callNeedsUpdate()
-        
+
         // Tripple Buffering
         // Tripple Buffering
         // Source: https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/MTLBestPracticesGuide/TripleBuffering.html#//apple_ref/doc/uid/TP40016642-CH5-SW1
         // Source: https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/MTLBestPracticesGuide/TripleBuffering.html#//apple_ref/doc/uid/TP40016642-CH5-SW1
         bufferingSemaphore.wait()
         bufferingSemaphore.wait()
         currentBufferIndex = (currentBufferIndex + 1) % SpineRenderer.numberOfBuffers
         currentBufferIndex = (currentBufferIndex + 1) % SpineRenderer.numberOfBuffers
-        
+
         guard let renderCommands = dataSource?.renderCommands(self),
         guard let renderCommands = dataSource?.renderCommands(self),
-              let commandBuffer = commandQueue.makeCommandBuffer(),
-              let renderPassDescriptor = view.currentRenderPassDescriptor,
-              let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor) else {
-	// this can happen if, 
-	// - CAMetalLayer is configured with drawable timeout, and CAMetalLayer is run out of Drawable 
-	// - CAMetalLayer is added to the window with frame size of zero or incorrect layout constraint -> currentRenderPassDescriptor is null
+            let commandBuffer = commandQueue.makeCommandBuffer(),
+            let renderPassDescriptor = view.currentRenderPassDescriptor,
+            let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor)
+        else {
+            // this can happen if,
+            // - CAMetalLayer is configured with drawable timeout, and CAMetalLayer is run out of Drawable
+            // - CAMetalLayer is added to the window with frame size of zero or incorrect layout constraint -> currentRenderPassDescriptor is null
             bufferingSemaphore.signal()
             bufferingSemaphore.signal()
             return
             return
         }
         }
-        
+
         delegate?.spineRendererWillDraw(self)
         delegate?.spineRendererWillDraw(self)
         draw(renderCommands: renderCommands, renderEncoder: renderEncoder, in: view)
         draw(renderCommands: renderCommands, renderEncoder: renderEncoder, in: view)
         delegate?.spineRendererDidDraw(self)
         delegate?.spineRendererDidDraw(self)
-        
+
         renderEncoder.endEncoding()
         renderEncoder.endEncoding()
         view.currentDrawable.flatMap {
         view.currentDrawable.flatMap {
             commandBuffer.present($0)
             commandBuffer.present($0)
@@ -183,14 +185,14 @@ internal final class SpineRenderer: NSObject, MTKViewDelegate {
             commandBuffer.waitUntilCompleted()
             commandBuffer.waitUntilCompleted()
         }
         }
     }
     }
-    
+
     private func setTransform(bounds: CGRect, mode: Spine.ContentMode, alignment: Spine.Alignment) {
     private func setTransform(bounds: CGRect, mode: Spine.ContentMode, alignment: Spine.Alignment) {
         let x = -bounds.minX - bounds.width / 2.0
         let x = -bounds.minX - bounds.width / 2.0
         let y = -bounds.minY - bounds.height / 2.0
         let y = -bounds.minY - bounds.height / 2.0
-        
+
         var scaleX: CGFloat = 1.0
         var scaleX: CGFloat = 1.0
         var scaleY: CGFloat = 1.0
         var scaleY: CGFloat = 1.0
-        
+
         switch mode {
         switch mode {
         case .fit:
         case .fit:
             scaleX = min(sizeInPoints.width / bounds.width, sizeInPoints.height / bounds.height)
             scaleX = min(sizeInPoints.width / bounds.width, sizeInPoints.height / bounds.height)
@@ -199,16 +201,16 @@ internal final class SpineRenderer: NSObject, MTKViewDelegate {
             scaleX = max(sizeInPoints.width / bounds.width, sizeInPoints.height / bounds.height)
             scaleX = max(sizeInPoints.width / bounds.width, sizeInPoints.height / bounds.height)
             scaleY = scaleX
             scaleY = scaleX
         }
         }
-        
+
         let offsetX = abs(sizeInPoints.width - bounds.width * scaleX) / 2 * alignment.x
         let offsetX = abs(sizeInPoints.width - bounds.width * scaleX) / 2 * alignment.x
         let offsetY = abs(sizeInPoints.height - bounds.height * scaleY) / 2 * alignment.y
         let offsetY = abs(sizeInPoints.height - bounds.height * scaleY) / 2 * alignment.y
-        
+
         transform = SpineTransform(
         transform = SpineTransform(
             translation: vector_float2(Float(x), Float(y)),
             translation: vector_float2(Float(x), Float(y)),
             scale: vector_float2(Float(scaleX * UIScreen.main.scale), Float(scaleY * UIScreen.main.scale)),
             scale: vector_float2(Float(scaleX * UIScreen.main.scale), Float(scaleY * UIScreen.main.scale)),
             offset: vector_float2(Float(offsetX * UIScreen.main.scale), Float(offsetY * UIScreen.main.scale))
             offset: vector_float2(Float(offsetX * UIScreen.main.scale), Float(offsetY * UIScreen.main.scale))
         )
         )
-        
+
         delegate?.spineRendererDidUpdate(
         delegate?.spineRendererDidUpdate(
             self,
             self,
             scaleX: scaleX,
             scaleX: scaleX,
@@ -218,37 +220,37 @@ internal final class SpineRenderer: NSObject, MTKViewDelegate {
             size: sizeInPoints
             size: sizeInPoints
         )
         )
     }
     }
-    
+
     private func callNeedsUpdate() {
     private func callNeedsUpdate() {
         if lastDraw == 0 {
         if lastDraw == 0 {
             lastDraw = CACurrentMediaTime()
             lastDraw = CACurrentMediaTime()
         }
         }
         let delta = CACurrentMediaTime() - lastDraw
         let delta = CACurrentMediaTime() - lastDraw
-         delegate?.spineRendererWillUpdate(self)
+        delegate?.spineRendererWillUpdate(self)
         delegate?.spineRenderer(self, needsUpdate: delta)
         delegate?.spineRenderer(self, needsUpdate: delta)
         lastDraw = CACurrentMediaTime()
         lastDraw = CACurrentMediaTime()
         delegate?.spineRendererDidUpdate(self)
         delegate?.spineRendererDidUpdate(self)
     }
     }
-        
+
     private func draw(renderCommands: [RenderCommand], renderEncoder: MTLRenderCommandEncoder, in view: MTKView) {
     private func draw(renderCommands: [RenderCommand], renderEncoder: MTLRenderCommandEncoder, in view: MTKView) {
         let allVertices = renderCommands.map { renderCommand in
         let allVertices = renderCommands.map { renderCommand in
             Array(renderCommand.getVertices())
             Array(renderCommand.getVertices())
         }
         }
         let vertices = allVertices.flatMap { $0 }
         let vertices = allVertices.flatMap { $0 }
         let verticesSize = MemoryLayout<SpineVertex>.stride * vertices.count
         let verticesSize = MemoryLayout<SpineVertex>.stride * vertices.count
-        
+
         guard verticesSize > 0 else {
         guard verticesSize > 0 else {
             return
             return
         }
         }
-        
+
         var vertexBuffer = buffers[currentBufferIndex]
         var vertexBuffer = buffers[currentBufferIndex]
         var vertexBufferSize = vertexBuffer.length
         var vertexBufferSize = vertexBuffer.length
-        
+
         if vertexBufferSize < verticesSize {
         if vertexBufferSize < verticesSize {
             increaseBuffersSize(to: verticesSize)
             increaseBuffersSize(to: verticesSize)
             vertexBuffer = buffers[currentBufferIndex]
             vertexBuffer = buffers[currentBufferIndex]
         }
         }
-        
+
         renderEncoder.setViewport(
         renderEncoder.setViewport(
             MTLViewport(
             MTLViewport(
                 originX: 0.0,
                 originX: 0.0,
@@ -259,9 +261,9 @@ internal final class SpineRenderer: NSObject, MTKViewDelegate {
                 zfar: 1.0
                 zfar: 1.0
             )
             )
         )
         )
-        
+
         memcpy(vertexBuffer.contents(), vertices, verticesSize)
         memcpy(vertexBuffer.contents(), vertices, verticesSize)
-        
+
         renderEncoder.setVertexBuffer(
         renderEncoder.setVertexBuffer(
             vertexBuffer,
             vertexBuffer,
             offset: 0,
             offset: 0,
@@ -277,7 +279,7 @@ internal final class SpineRenderer: NSObject, MTKViewDelegate {
             length: MemoryLayout.size(ofValue: viewPortSize),
             length: MemoryLayout.size(ofValue: viewPortSize),
             index: Int(SpineVertexInputIndexViewportSize.rawValue)
             index: Int(SpineVertexInputIndexViewportSize.rawValue)
         )
         )
-        
+
         // Buffer Bindings
         // Buffer Bindings
         // https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/MTLBestPracticesGuide/BufferBindings.html#//apple_ref/doc/uid/TP40016642-CH28-SW3
         // https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/MTLBestPracticesGuide/BufferBindings.html#//apple_ref/doc/uid/TP40016642-CH28-SW3
         var vertexStart = 0
         var vertexStart = 0
@@ -286,9 +288,9 @@ internal final class SpineRenderer: NSObject, MTKViewDelegate {
                 continue
                 continue
             }
             }
             renderEncoder.setRenderPipelineState(pipelineState)
             renderEncoder.setRenderPipelineState(pipelineState)
-            
+
             let vertices = allVertices[index]
             let vertices = allVertices[index]
-            
+
             let textureIndex = Int(renderCommand.atlasPage)
             let textureIndex = Int(renderCommand.atlasPage)
             if textures.indices.contains(textureIndex) {
             if textures.indices.contains(textureIndex) {
                 renderEncoder.setFragmentTexture(
                 renderEncoder.setFragmentTexture(
@@ -296,7 +298,7 @@ internal final class SpineRenderer: NSObject, MTKViewDelegate {
                     index: Int(SpineTextureIndexBaseColor.rawValue)
                     index: Int(SpineTextureIndexBaseColor.rawValue)
                 )
                 )
             }
             }
-            
+
             renderEncoder.drawPrimitives(
             renderEncoder.drawPrimitives(
                 type: .triangle,
                 type: .triangle,
                 vertexStart: vertexStart,
                 vertexStart: vertexStart,
@@ -305,89 +307,89 @@ internal final class SpineRenderer: NSObject, MTKViewDelegate {
             vertexStart += vertices.count
             vertexStart += vertices.count
         }
         }
     }
     }
-    
+
     private func getPipelineState(blendMode: BlendMode) -> MTLRenderPipelineState? {
     private func getPipelineState(blendMode: BlendMode) -> MTLRenderPipelineState? {
         pipelineStatesByBlendMode[Int(blendMode.rawValue)]
         pipelineStatesByBlendMode[Int(blendMode.rawValue)]
     }
     }
-    
+
     private func increaseBuffersSize(to size: Int) {
     private func increaseBuffersSize(to size: Int) {
-        buffers = (0 ..< SpineRenderer.numberOfBuffers).map { _ in
+        buffers = (0..<SpineRenderer.numberOfBuffers).map { _ in
             device.makeBuffer(length: size, options: .storageModeShared)!
             device.makeBuffer(length: size, options: .storageModeShared)!
         }
         }
     }
     }
 }
 }
 
 
-fileprivate extension BlendMode {
-	func sourceRGBBlendFactor(premultipliedAlpha: Bool) -> MTLBlendFactor {
-		switch self {
-		case SPINE_BLEND_MODE_NORMAL:
-			return premultipliedAlpha ? .one : .sourceAlpha
-		case SPINE_BLEND_MODE_ADDITIVE:
-			// additvie only needs sourceAlpha multiply if it is not pma
-			return premultipliedAlpha ? .one : .sourceAlpha
-		case SPINE_BLEND_MODE_MULTIPLY:
-			return .destinationColor
-		case SPINE_BLEND_MODE_SCREEN:
-			return .one
-		default:
-			return .one // Should never be called
-		}
-	}
-	
-	var sourceAlphaBlendFactor: MTLBlendFactor {
-		// pma and non-pma has no-relation ship with alpha blending
-		switch self {
-		case SPINE_BLEND_MODE_NORMAL:
-			return .one
-		case SPINE_BLEND_MODE_ADDITIVE:
-			return .one
-		case SPINE_BLEND_MODE_MULTIPLY:
-			return .oneMinusSourceAlpha
-		case SPINE_BLEND_MODE_SCREEN:
-			return .oneMinusSourceColor
-		default:
-			return .one // Should never be called
-		}
-	}
-
-	var destinationRGBBlendFactor: MTLBlendFactor {
-		switch self {
-		case SPINE_BLEND_MODE_NORMAL:
-			return .oneMinusSourceAlpha
-		case SPINE_BLEND_MODE_ADDITIVE:
-			return .one
-		case SPINE_BLEND_MODE_MULTIPLY:
-			return .oneMinusSourceAlpha
-		case SPINE_BLEND_MODE_SCREEN:
-			return .oneMinusSourceColor
-		default:
-			return .one // Should never be called
-		}
-	}
-
-	var destinationAlphaBlendFactor: MTLBlendFactor {
-		switch self {
-		case SPINE_BLEND_MODE_NORMAL:
-			return .oneMinusSourceAlpha
-		case SPINE_BLEND_MODE_ADDITIVE:
-			return .one
-		case SPINE_BLEND_MODE_MULTIPLY:
-			return .oneMinusSourceAlpha
-		case SPINE_BLEND_MODE_SCREEN:
-			return .oneMinusSourceColor
-		default:
-			return .one // Should never be called
-		}
-	}
+extension BlendMode {
+    fileprivate func sourceRGBBlendFactor(premultipliedAlpha: Bool) -> MTLBlendFactor {
+        switch self {
+        case SPINE_BLEND_MODE_NORMAL:
+            return premultipliedAlpha ? .one : .sourceAlpha
+        case SPINE_BLEND_MODE_ADDITIVE:
+            // additvie only needs sourceAlpha multiply if it is not pma
+            return premultipliedAlpha ? .one : .sourceAlpha
+        case SPINE_BLEND_MODE_MULTIPLY:
+            return .destinationColor
+        case SPINE_BLEND_MODE_SCREEN:
+            return .one
+        default:
+            return .one  // Should never be called
+        }
+    }
+
+    fileprivate var sourceAlphaBlendFactor: MTLBlendFactor {
+        // pma and non-pma has no-relation ship with alpha blending
+        switch self {
+        case SPINE_BLEND_MODE_NORMAL:
+            return .one
+        case SPINE_BLEND_MODE_ADDITIVE:
+            return .one
+        case SPINE_BLEND_MODE_MULTIPLY:
+            return .oneMinusSourceAlpha
+        case SPINE_BLEND_MODE_SCREEN:
+            return .oneMinusSourceColor
+        default:
+            return .one  // Should never be called
+        }
+    }
+
+    fileprivate var destinationRGBBlendFactor: MTLBlendFactor {
+        switch self {
+        case SPINE_BLEND_MODE_NORMAL:
+            return .oneMinusSourceAlpha
+        case SPINE_BLEND_MODE_ADDITIVE:
+            return .one
+        case SPINE_BLEND_MODE_MULTIPLY:
+            return .oneMinusSourceAlpha
+        case SPINE_BLEND_MODE_SCREEN:
+            return .oneMinusSourceColor
+        default:
+            return .one  // Should never be called
+        }
+    }
+
+    fileprivate var destinationAlphaBlendFactor: MTLBlendFactor {
+        switch self {
+        case SPINE_BLEND_MODE_NORMAL:
+            return .oneMinusSourceAlpha
+        case SPINE_BLEND_MODE_ADDITIVE:
+            return .one
+        case SPINE_BLEND_MODE_MULTIPLY:
+            return .oneMinusSourceAlpha
+        case SPINE_BLEND_MODE_SCREEN:
+            return .oneMinusSourceColor
+        default:
+            return .one  // Should never be called
+        }
+    }
 }
 }
 
 
-fileprivate extension MTLRenderPipelineColorAttachmentDescriptor {
-	
-	func apply(blendMode: BlendMode, with premultipliedAlpha: Bool) {
-		isBlendingEnabled = true
-		sourceRGBBlendFactor = blendMode.sourceRGBBlendFactor(premultipliedAlpha: premultipliedAlpha)
-		sourceAlphaBlendFactor = blendMode.sourceAlphaBlendFactor
-		destinationRGBBlendFactor = blendMode.destinationRGBBlendFactor
-		destinationAlphaBlendFactor = blendMode.destinationAlphaBlendFactor
-	}
+extension MTLRenderPipelineColorAttachmentDescriptor {
+
+    fileprivate func apply(blendMode: BlendMode, with premultipliedAlpha: Bool) {
+        isBlendingEnabled = true
+        sourceRGBBlendFactor = blendMode.sourceRGBBlendFactor(premultipliedAlpha: premultipliedAlpha)
+        sourceAlphaBlendFactor = blendMode.sourceAlphaBlendFactor
+        destinationRGBBlendFactor = blendMode.destinationRGBBlendFactor
+        destinationAlphaBlendFactor = blendMode.destinationAlphaBlendFactor
+    }
 }
 }

+ 18 - 18
spine-ios/Sources/Spine/SkeletonDrawableWrapper.swift

@@ -27,10 +27,10 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
  *****************************************************************************/
 
 
+import CoreGraphics
 import Foundation
 import Foundation
 import Spine
 import Spine
 import SpineCppLite
 import SpineCppLite
-import CoreGraphics
 import UIKit
 import UIKit
 
 
 /// A ``SkeletonDrawableWrapper`` with ``SkeletonDrawable`` bundle loading, updating, and rendering an ``Atlas``, ``Skeleton``, and ``AnimationState``
 /// A ``SkeletonDrawableWrapper`` with ``SkeletonDrawable`` bundle loading, updating, and rendering an ``Atlas``, ``Skeleton``, and ``AnimationState``
@@ -54,19 +54,19 @@ import UIKit
 @objc(SpineSkeletonDrawableWrapper)
 @objc(SpineSkeletonDrawableWrapper)
 @objcMembers
 @objcMembers
 public final class SkeletonDrawableWrapper: NSObject {
 public final class SkeletonDrawableWrapper: NSObject {
-    
+
     public let atlas: Atlas
     public let atlas: Atlas
     public let atlasPages: [UIImage]
     public let atlasPages: [UIImage]
     public let skeletonData: SkeletonData
     public let skeletonData: SkeletonData
-    
+
     public let skeletonDrawable: SkeletonDrawable
     public let skeletonDrawable: SkeletonDrawable
     public let skeleton: Skeleton
     public let skeleton: Skeleton
     public let animationStateData: AnimationStateData
     public let animationStateData: AnimationStateData
     public let animationState: AnimationState
     public let animationState: AnimationState
     public let animationStateWrapper: AnimationStateWrapper
     public let animationStateWrapper: AnimationStateWrapper
-    
+
     internal var disposed = false
     internal var disposed = false
-    
+
     /// Constructs a new skeleton drawable from the `atlasFileName` and `skeletonFileName` from the `main` bundle
     /// Constructs a new skeleton drawable from the `atlasFileName` and `skeletonFileName` from the `main` bundle
     /// or the optionally provided `bundle`.
     /// or the optionally provided `bundle`.
     ///
     ///
@@ -84,7 +84,7 @@ public final class SkeletonDrawableWrapper: NSObject {
             skeletonData: skeletonData
             skeletonData: skeletonData
         )
         )
     }
     }
-    
+
     /// Constructs a new skeleton drawable from the `atlasFile` and `skeletonFile`.
     /// Constructs a new skeleton drawable from the `atlasFile` and `skeletonFile`.
     ///
     ///
     /// Throws an `Error` in case the data could not be loaded.
     /// Throws an `Error` in case the data could not be loaded.
@@ -100,7 +100,7 @@ public final class SkeletonDrawableWrapper: NSObject {
             skeletonData: skeletonData
             skeletonData: skeletonData
         )
         )
     }
     }
-    
+
     /// Constructs a new skeleton drawable wrapper from the http `atlasUrl` and `skeletonUrl`.
     /// Constructs a new skeleton drawable wrapper from the http `atlasUrl` and `skeletonUrl`.
     ///
     ///
     /// Throws an `Error` in case the data could not be loaded.
     /// Throws an `Error` in case the data could not be loaded.
@@ -116,27 +116,27 @@ public final class SkeletonDrawableWrapper: NSObject {
             skeletonData: skeletonData
             skeletonData: skeletonData
         )
         )
     }
     }
-    
+
     public init(atlas: Atlas, atlasPages: [UIImage], skeletonData: SkeletonData) throws {
     public init(atlas: Atlas, atlasPages: [UIImage], skeletonData: SkeletonData) throws {
         self.atlas = atlas
         self.atlas = atlas
         self.atlasPages = atlasPages
         self.atlasPages = atlasPages
         self.skeletonData = skeletonData
         self.skeletonData = skeletonData
-            
+
         guard let nativeSkeletonDrawable = spine_skeleton_drawable_create(skeletonData.wrappee) else {
         guard let nativeSkeletonDrawable = spine_skeleton_drawable_create(skeletonData.wrappee) else {
             throw SpineError("Could not load native skeleton drawable")
             throw SpineError("Could not load native skeleton drawable")
         }
         }
         skeletonDrawable = SkeletonDrawable(nativeSkeletonDrawable)
         skeletonDrawable = SkeletonDrawable(nativeSkeletonDrawable)
-        
+
         guard let nativeSkeleton = spine_skeleton_drawable_get_skeleton(skeletonDrawable.wrappee) else {
         guard let nativeSkeleton = spine_skeleton_drawable_get_skeleton(skeletonDrawable.wrappee) else {
             throw SpineError("Could not load native skeleton")
             throw SpineError("Could not load native skeleton")
         }
         }
         skeleton = Skeleton(nativeSkeleton)
         skeleton = Skeleton(nativeSkeleton)
-        
+
         guard let nativeAnimationStateData = spine_skeleton_drawable_get_animation_state_data(skeletonDrawable.wrappee) else {
         guard let nativeAnimationStateData = spine_skeleton_drawable_get_animation_state_data(skeletonDrawable.wrappee) else {
             throw SpineError("Could not load native animation state data")
             throw SpineError("Could not load native animation state data")
         }
         }
         animationStateData = AnimationStateData(nativeAnimationStateData)
         animationStateData = AnimationStateData(nativeAnimationStateData)
-        
+
         guard let nativeAnimationState = spine_skeleton_drawable_get_animation_state(skeletonDrawable.wrappee) else {
         guard let nativeAnimationState = spine_skeleton_drawable_get_animation_state(skeletonDrawable.wrappee) else {
             throw SpineError("Could not load native animation state")
             throw SpineError("Could not load native animation state")
         }
         }
@@ -148,27 +148,27 @@ public final class SkeletonDrawableWrapper: NSObject {
         skeleton.updateWorldTransform(physics: SPINE_PHYSICS_NONE)
         skeleton.updateWorldTransform(physics: SPINE_PHYSICS_NONE)
         super.init()
         super.init()
     }
     }
-    
+
     /// Updates the ``AnimationState`` using the `delta` time given in seconds, applies the
     /// Updates the ``AnimationState`` using the `delta` time given in seconds, applies the
     /// animation state to the ``Skeleton`` and updates the world transforms of the skeleton
     /// animation state to the ``Skeleton`` and updates the world transforms of the skeleton
     /// to calculate its current pose.
     /// to calculate its current pose.
     public func update(delta: Float) {
     public func update(delta: Float) {
         if disposed { return }
         if disposed { return }
-        
+
         animationStateWrapper.update(delta: delta)
         animationStateWrapper.update(delta: delta)
         animationState.apply(skeleton: skeleton)
         animationState.apply(skeleton: skeleton)
-        
+
         skeleton.update(delta: delta)
         skeleton.update(delta: delta)
         skeleton.updateWorldTransform(physics: SPINE_PHYSICS_UPDATE)
         skeleton.updateWorldTransform(physics: SPINE_PHYSICS_UPDATE)
     }
     }
-    
+
     public func dispose() {
     public func dispose() {
         if disposed { return }
         if disposed { return }
         disposed = true
         disposed = true
-        
+
         atlas.dispose()
         atlas.dispose()
         skeletonData.dispose()
         skeletonData.dispose()
-        
+
         skeletonDrawable.dispose()
         skeletonDrawable.dispose()
     }
     }
 }
 }

+ 58 - 57
spine-ios/Sources/Spine/Spine.Generated+Extensions.swift

@@ -28,8 +28,8 @@
  *****************************************************************************/
  *****************************************************************************/
 
 
 import Foundation
 import Foundation
-import SwiftUI
 import SpineCppLite
 import SpineCppLite
+import SwiftUI
 
 
 public var version: String {
 public var version: String {
     return "\(majorVersion).\(minorVersion)"
     return "\(majorVersion).\(minorVersion)"
@@ -49,22 +49,22 @@ public var minorVersion: Int {
 ///
 ///
 /// Use the static methods ``Atlas/fromBundle(_:bundle:)``, ``Atlas/fromFile(_:)``, and ``Atlas/fromHttp(_:)`` to load an atlas. Call ``Atlas/dispose()`
 /// Use the static methods ``Atlas/fromBundle(_:bundle:)``, ``Atlas/fromFile(_:)``, and ``Atlas/fromHttp(_:)`` to load an atlas. Call ``Atlas/dispose()`
 /// when the atlas is no longer in use to release its resources.
 /// when the atlas is no longer in use to release its resources.
-public extension Atlas {
-    
+extension Atlas {
+
     /// Loads an ``Atlas`` from the file with name `atlasFileName` in the `main` bundle or the optionally provided [bundle].
     /// Loads an ``Atlas`` from the file with name `atlasFileName` in the `main` bundle or the optionally provided [bundle].
     ///
     ///
     /// Throws an `Error` in case the atlas could not be loaded.
     /// Throws an `Error` in case the atlas could not be loaded.
-    static func fromBundle(_ atlasFileName: String, bundle: Bundle = .main) async throws -> (Atlas, [UIImage]) {
+    public static func fromBundle(_ atlasFileName: String, bundle: Bundle = .main) async throws -> (Atlas, [UIImage]) {
         let data = try await FileSource.bundle(fileName: atlasFileName, bundle: bundle).load()
         let data = try await FileSource.bundle(fileName: atlasFileName, bundle: bundle).load()
         return try await Self.fromData(data: data) { name in
         return try await Self.fromData(data: data) { name in
             return try await FileSource.bundle(fileName: name, bundle: bundle).load()
             return try await FileSource.bundle(fileName: name, bundle: bundle).load()
         }
         }
     }
     }
-    
+
     /// Loads an ``Atlas`` from the file URL `atlasFile`.
     /// Loads an ``Atlas`` from the file URL `atlasFile`.
     ///
     ///
     /// Throws an `Error` in case the atlas could not be loaded.
     /// Throws an `Error` in case the atlas could not be loaded.
-    static func fromFile(_ atlasFile: URL) async throws -> (Atlas, [UIImage]) {
+    public static func fromFile(_ atlasFile: URL) async throws -> (Atlas, [UIImage]) {
         let data = try await FileSource.file(atlasFile).load()
         let data = try await FileSource.file(atlasFile).load()
         return try await Self.fromData(data: data) { name in
         return try await Self.fromData(data: data) { name in
             let dir = atlasFile.deletingLastPathComponent()
             let dir = atlasFile.deletingLastPathComponent()
@@ -72,11 +72,11 @@ public extension Atlas {
             return try await FileSource.file(file).load()
             return try await FileSource.file(file).load()
         }
         }
     }
     }
-        
+
     /// Loads an ``Atlas`` from the http URL `atlasURL`.
     /// Loads an ``Atlas`` from the http URL `atlasURL`.
     ///
     ///
     /// Throws an `Error` in case the atlas could not be loaded.
     /// Throws an `Error` in case the atlas could not be loaded.
-    static func fromHttp(_ atlasURL: URL) async throws -> (Atlas, [UIImage]) {
+    public static func fromHttp(_ atlasURL: URL) async throws -> (Atlas, [UIImage]) {
         let data = try await FileSource.http(atlasURL).load()
         let data = try await FileSource.http(atlasURL).load()
         return try await Self.fromData(data: data) { name in
         return try await Self.fromData(data: data) { name in
             let dir = atlasURL.deletingLastPathComponent()
             let dir = atlasURL.deletingLastPathComponent()
@@ -84,7 +84,7 @@ public extension Atlas {
             return try await FileSource.http(file).load()
             return try await FileSource.http(file).load()
         }
         }
     }
     }
-    
+
     private static func fromData(data: Data, loadFile: (_ name: String) async throws -> Data) async throws -> (Atlas, [UIImage]) {
     private static func fromData(data: Data, loadFile: (_ name: String) async throws -> Data) async throws -> (Atlas, [UIImage]) {
         guard let atlasData = String(data: data, encoding: .utf8) else {
         guard let atlasData = String(data: data, encoding: .utf8) else {
             throw SpineError("Couldn't read atlas bytes as utf8 string")
             throw SpineError("Couldn't read atlas bytes as utf8 string")
@@ -100,10 +100,10 @@ public extension Atlas {
             spine_atlas_dispose(atlas)
             spine_atlas_dispose(atlas)
             throw SpineError("Couldn't load atlas: \(message)")
             throw SpineError("Couldn't load atlas: \(message)")
         }
         }
-        
+
         var atlasPages = [UIImage]()
         var atlasPages = [UIImage]()
-        let numImagePaths = spine_atlas_get_num_image_paths(atlas);
-        
+        let numImagePaths = spine_atlas_get_num_image_paths(atlas)
+
         for i in 0..<numImagePaths {
         for i in 0..<numImagePaths {
             guard let atlasPageFilePointer = spine_atlas_get_image_path(atlas, i) else {
             guard let atlasPageFilePointer = spine_atlas_get_image_path(atlas, i) else {
                 continue
                 continue
@@ -115,52 +115,52 @@ public extension Atlas {
             }
             }
             atlasPages.append(image)
             atlasPages.append(image)
         }
         }
-        
+
         return (Atlas(atlas), atlasPages)
         return (Atlas(atlas), atlasPages)
     }
     }
 }
 }
 
 
-public extension SkeletonData {
-    
+extension SkeletonData {
+
     /// Loads a ``SkeletonData`` from the file with name `skeletonFileName` in the main bundle or the optionally provided `bundle`.
     /// Loads a ``SkeletonData`` from the file with name `skeletonFileName` in the main bundle or the optionally provided `bundle`.
     /// Uses the provided ``Atlas`` to resolve attachment images.
     /// Uses the provided ``Atlas`` to resolve attachment images.
     ///
     ///
     /// Throws an `Error` in case the skeleton data could not be loaded.
     /// Throws an `Error` in case the skeleton data could not be loaded.
-    static func fromBundle(atlas: Atlas, skeletonFileName: String, bundle: Bundle = .main) async throws -> SkeletonData {
+    public static func fromBundle(atlas: Atlas, skeletonFileName: String, bundle: Bundle = .main) async throws -> SkeletonData {
         return try fromData(
         return try fromData(
             atlas: atlas,
             atlas: atlas,
             data: try await FileSource.bundle(fileName: skeletonFileName, bundle: bundle).load(),
             data: try await FileSource.bundle(fileName: skeletonFileName, bundle: bundle).load(),
             isJson: skeletonFileName.hasSuffix(".json")
             isJson: skeletonFileName.hasSuffix(".json")
         )
         )
     }
     }
-    
+
     /// Loads a ``SkeletonData`` from the file URL `skeletonFile`. Uses the provided ``Atlas`` to resolve attachment images.
     /// Loads a ``SkeletonData`` from the file URL `skeletonFile`. Uses the provided ``Atlas`` to resolve attachment images.
     ///
     ///
     /// Throws an `Error` in case the skeleton data could not be loaded.
     /// Throws an `Error` in case the skeleton data could not be loaded.
-    static func fromFile(atlas: Atlas, skeletonFile: URL) async throws -> SkeletonData {
+    public static func fromFile(atlas: Atlas, skeletonFile: URL) async throws -> SkeletonData {
         return try fromData(
         return try fromData(
             atlas: atlas,
             atlas: atlas,
             data: try await FileSource.file(skeletonFile).load(),
             data: try await FileSource.file(skeletonFile).load(),
             isJson: skeletonFile.absoluteString.hasSuffix(".json")
             isJson: skeletonFile.absoluteString.hasSuffix(".json")
         )
         )
     }
     }
-    
+
     /// Loads a ``SkeletonData`` from the http URL `skeletonFile`. Uses the provided ``Atlas`` to resolve attachment images.
     /// Loads a ``SkeletonData`` from the http URL `skeletonFile`. Uses the provided ``Atlas`` to resolve attachment images.
     ///
     ///
     /// Throws an `Error` in case the skeleton data could not be loaded.
     /// Throws an `Error` in case the skeleton data could not be loaded.
-    static func fromHttp(atlas: Atlas, skeletonURL: URL) async throws -> SkeletonData {
+    public static func fromHttp(atlas: Atlas, skeletonURL: URL) async throws -> SkeletonData {
         return try fromData(
         return try fromData(
             atlas: atlas,
             atlas: atlas,
             data: try await FileSource.http(skeletonURL).load(),
             data: try await FileSource.http(skeletonURL).load(),
             isJson: skeletonURL.absoluteString.hasSuffix(".json")
             isJson: skeletonURL.absoluteString.hasSuffix(".json")
         )
         )
     }
     }
-    
+
     /// Loads a ``SkeletonData`` from the ``binary`` skeleton `Data`, using the provided ``Atlas`` to resolve attachment images.
     /// Loads a ``SkeletonData`` from the ``binary`` skeleton `Data`, using the provided ``Atlas`` to resolve attachment images.
     ///
     ///
     /// Throws an `Error` in case the skeleton data could not be loaded.
     /// Throws an `Error` in case the skeleton data could not be loaded.
-    static func fromData(atlas: Atlas, data: Data) throws -> SkeletonData {
-        let result = try data.withUnsafeBytes{
+    public static func fromData(atlas: Atlas, data: Data) throws -> SkeletonData {
+        let result = try data.withUnsafeBytes {
             try $0.withMemoryRebound(to: UInt8.self) { buffer in
             try $0.withMemoryRebound(to: UInt8.self) { buffer in
                 guard let ptr = buffer.baseAddress else {
                 guard let ptr = buffer.baseAddress else {
                     throw SpineError("Couldn't read atlas binary")
                     throw SpineError("Couldn't read atlas binary")
@@ -187,16 +187,17 @@ public extension SkeletonData {
         }
         }
         return SkeletonData(data)
         return SkeletonData(data)
     }
     }
-    
+
     /// Loads a ``SkeletonData`` from the `json` string, using the provided ``Atlas`` to resolve attachment
     /// Loads a ``SkeletonData`` from the `json` string, using the provided ``Atlas`` to resolve attachment
     /// images.
     /// images.
     ///
     ///
     /// Throws an `Error` in case the atlas could not be loaded.
     /// Throws an `Error` in case the atlas could not be loaded.
-    static func fromJson(atlas: Atlas, json: String) throws -> SkeletonData {
+    public static func fromJson(atlas: Atlas, json: String) throws -> SkeletonData {
         let result = try json.utf8CString.withUnsafeBufferPointer { buffer in
         let result = try json.utf8CString.withUnsafeBufferPointer { buffer in
             guard
             guard
                 let basePtr = buffer.baseAddress,
                 let basePtr = buffer.baseAddress,
-                let result = spine_skeleton_data_load_json(atlas.wrappee, basePtr) else {
+                let result = spine_skeleton_data_load_json(atlas.wrappee, basePtr)
+            else {
                 throw SpineError("Couldn't load skeleton data json")
                 throw SpineError("Couldn't load skeleton data json")
             }
             }
             return result
             return result
@@ -213,7 +214,7 @@ public extension SkeletonData {
         }
         }
         return SkeletonData(data)
         return SkeletonData(data)
     }
     }
-    
+
     private static func fromData(atlas: Atlas, data: Data, isJson: Bool) throws -> SkeletonData {
     private static func fromData(atlas: Atlas, data: Data, isJson: Bool) throws -> SkeletonData {
         if isJson {
         if isJson {
             guard let json = String(data: data, encoding: .utf8) else {
             guard let json = String(data: data, encoding: .utf8) else {
@@ -226,12 +227,12 @@ public extension SkeletonData {
     }
     }
 }
 }
 
 
-internal extension SkeletonDrawable {
-    
+extension SkeletonDrawable {
+
     func render() -> [RenderCommand] {
     func render() -> [RenderCommand] {
         var commands = [RenderCommand]()
         var commands = [RenderCommand]()
         if disposed { return commands }
         if disposed { return commands }
-        
+
         var nativeCmd = spine_skeleton_drawable_render(wrappee)
         var nativeCmd = spine_skeleton_drawable_render(wrappee)
         repeat {
         repeat {
             if let ncmd = nativeCmd {
             if let ncmd = nativeCmd {
@@ -240,18 +241,18 @@ internal extension SkeletonDrawable {
             } else {
             } else {
                 nativeCmd = nil
                 nativeCmd = nil
             }
             }
-        } while (nativeCmd != nil)
-        
+        } while nativeCmd != nil
+
         return commands
         return commands
     }
     }
 }
 }
 
 
-internal extension RenderCommand {
-    
+extension RenderCommand {
+
     var numVertices: Int {
     var numVertices: Int {
         Int(spine_render_command_get_num_vertices(wrappee))
         Int(spine_render_command_get_num_vertices(wrappee))
     }
     }
-    
+
     func positions(numVertices: Int) -> [Float] {
     func positions(numVertices: Int) -> [Float] {
         let num = numVertices * 2
         let num = numVertices * 2
         let ptr = spine_render_command_get_positions(wrappee)
         let ptr = spine_render_command_get_positions(wrappee)
@@ -259,7 +260,7 @@ internal extension RenderCommand {
         let buffer = UnsafeBufferPointer(start: validPtr, count: num)
         let buffer = UnsafeBufferPointer(start: validPtr, count: num)
         return Array(buffer)
         return Array(buffer)
     }
     }
-    
+
     func uvs(numVertices: Int) -> [Float] {
     func uvs(numVertices: Int) -> [Float] {
         let num = numVertices * 2
         let num = numVertices * 2
         let ptr = spine_render_command_get_uvs(wrappee)
         let ptr = spine_render_command_get_uvs(wrappee)
@@ -267,8 +268,8 @@ internal extension RenderCommand {
         let buffer = UnsafeBufferPointer(start: validPtr, count: num)
         let buffer = UnsafeBufferPointer(start: validPtr, count: num)
         return Array(buffer)
         return Array(buffer)
     }
     }
-    
-    func colors(numVertices: Int) ->[Int32] {
+
+    func colors(numVertices: Int) -> [Int32] {
         let num = numVertices
         let num = numVertices
         let ptr = spine_render_command_get_colors(wrappee)
         let ptr = spine_render_command_get_colors(wrappee)
         guard let validPtr = ptr else { return [] }
         guard let validPtr = ptr else { return [] }
@@ -277,21 +278,21 @@ internal extension RenderCommand {
     }
     }
 }
 }
 
 
-public extension Skin {
-    
+extension Skin {
+
     /// Constructs a new empty ``Skin`` using the given `name`. Skins constructed this way must be manually disposed via the `dispose` method
     /// Constructs a new empty ``Skin`` using the given `name`. Skins constructed this way must be manually disposed via the `dispose` method
     /// if they are no longer used.
     /// if they are no longer used.
-    static func create(name: String) -> Skin {
+    public static func create(name: String) -> Skin {
         return Skin(spine_skin_create(name))
         return Skin(spine_skin_create(name))
     }
     }
 }
 }
 
 
 // Helper
 // Helper
 
 
-public extension CGRect {
-    
+extension CGRect {
+
     /// Construct a `CGRect` from ``Bounds``
     /// Construct a `CGRect` from ``Bounds``
-    init(bounds: Bounds) {
+    public init(bounds: Bounds) {
         self = CGRect(
         self = CGRect(
             x: CGFloat(bounds.x),
             x: CGFloat(bounds.x),
             y: CGFloat(bounds.y),
             y: CGFloat(bounds.y),
@@ -305,7 +306,7 @@ internal enum FileSource {
     case bundle(fileName: String, bundle: Bundle = .main)
     case bundle(fileName: String, bundle: Bundle = .main)
     case file(URL)
     case file(URL)
     case http(URL)
     case http(URL)
-    
+
     internal func load() async throws -> Data {
     internal func load() async throws -> Data {
         switch self {
         switch self {
         case .bundle(let fileName, let bundle):
         case .bundle(let fileName, let bundle):
@@ -314,7 +315,7 @@ internal enum FileSource {
                 throw SpineError("Provide both file name and file extension")
                 throw SpineError("Provide both file name and file extension")
             }
             }
             let name = components.dropLast(1).joined(separator: ".")
             let name = components.dropLast(1).joined(separator: ".")
-            
+
             guard let fileUrl = bundle.url(forResource: name, withExtension: String(ext)) else {
             guard let fileUrl = bundle.url(forResource: name, withExtension: String(ext)) else {
                 throw SpineError("Could not load file with name \(name) from bundle")
                 throw SpineError("Could not load file with name \(name) from bundle")
             }
             }
@@ -331,9 +332,9 @@ internal enum FileSource {
             } else {
             } else {
                 let lock = NSRecursiveLock()
                 let lock = NSRecursiveLock()
                 nonisolated(unsafe)
                 nonisolated(unsafe)
-                var isCancelled = false
+                    var isCancelled = false
                 nonisolated(unsafe)
                 nonisolated(unsafe)
-                var taskHolder:URLSessionDownloadTask? = nil
+                    var taskHolder: URLSessionDownloadTask? = nil
                 return try await withTaskCancellationHandler {
                 return try await withTaskCancellationHandler {
                     try await withCheckedThrowingContinuation { continuation in
                     try await withCheckedThrowingContinuation { continuation in
                         let task = URLSession.shared.downloadTask(with: url) { temp, response, error in
                         let task = URLSession.shared.downloadTask(with: url) { temp, response, error in
@@ -380,25 +381,25 @@ internal enum FileSource {
 }
 }
 
 
 public struct SpineError: Error, CustomStringConvertible {
 public struct SpineError: Error, CustomStringConvertible {
-    
+
     public let description: String
     public let description: String
-    
+
     internal init(_ description: String) {
     internal init(_ description: String) {
         self.description = description
         self.description = description
     }
     }
-    
+
 }
 }
 
 
-public extension SkeletonBounds {
-    static func create() -> SkeletonBounds {
+extension SkeletonBounds {
+    public static func create() -> SkeletonBounds {
         return SkeletonBounds(spine_skeleton_bounds_create())
         return SkeletonBounds(spine_skeleton_bounds_create())
     }
     }
 }
 }
 
 
-@objc public extension Atlas {
-    
-    var imagePathCount:Int32 {
+@objc extension Atlas {
+
+    public var imagePathCount: Int32 {
         spine_atlas_get_num_image_paths(wrappee)
         spine_atlas_get_num_image_paths(wrappee)
     }
     }
-    
+
 }
 }

+ 36 - 36
spine-ios/Sources/Spine/SpineController.swift

@@ -27,8 +27,8 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
  *****************************************************************************/
 
 
-import Foundation
 import CoreGraphics
 import CoreGraphics
+import Foundation
 import QuartzCore
 import QuartzCore
 import UIKit
 import UIKit
 
 
@@ -63,27 +63,27 @@ public typealias SpineControllerCallback = (_ controller: SpineController) -> Vo
 /// Per default, ``SkeletonDrawableWrapper`` is disposed when ``SpineController`` is deinitialized. You can disable this behaviour with the ``disposeDrawableOnDeInit`` contructor parameter.
 /// Per default, ``SkeletonDrawableWrapper`` is disposed when ``SpineController`` is deinitialized. You can disable this behaviour with the ``disposeDrawableOnDeInit`` contructor parameter.
 @objcMembers
 @objcMembers
 public final class SpineController: NSObject, ObservableObject {
 public final class SpineController: NSObject, ObservableObject {
-    
+
     public internal(set) var drawable: SkeletonDrawableWrapper!
     public internal(set) var drawable: SkeletonDrawableWrapper!
-    
+
     private let onInitialized: SpineControllerCallback?
     private let onInitialized: SpineControllerCallback?
     private let onBeforeUpdateWorldTransforms: SpineControllerCallback?
     private let onBeforeUpdateWorldTransforms: SpineControllerCallback?
     private let onAfterUpdateWorldTransforms: SpineControllerCallback?
     private let onAfterUpdateWorldTransforms: SpineControllerCallback?
     private let onBeforePaint: SpineControllerCallback?
     private let onBeforePaint: SpineControllerCallback?
     private let onAfterPaint: SpineControllerCallback?
     private let onAfterPaint: SpineControllerCallback?
     private let disposeDrawableOnDeInit: Bool
     private let disposeDrawableOnDeInit: Bool
-    
+
     private var scaleX: CGFloat = 1
     private var scaleX: CGFloat = 1
     private var scaleY: CGFloat = 1
     private var scaleY: CGFloat = 1
     private var offsetX: CGFloat = 0
     private var offsetX: CGFloat = 0
     private var offsetY: CGFloat = 0
     private var offsetY: CGFloat = 0
-    
+
     @Published
     @Published
     public private(set) var isPlaying: Bool = true
     public private(set) var isPlaying: Bool = true
-    
+
     @Published
     @Published
     public private(set) var viewSize: CGSize = .zero
     public private(set) var viewSize: CGSize = .zero
-    
+
     /// Constructs a new ``SpineUIview`` controller. See the class documentation of ``SpineWidgetController`` for information on
     /// Constructs a new ``SpineUIview`` controller. See the class documentation of ``SpineWidgetController`` for information on
     /// the optional arguments.
     /// the optional arguments.
     public init(
     public init(
@@ -100,80 +100,80 @@ public final class SpineController: NSObject, ObservableObject {
         self.onBeforePaint = onBeforePaint
         self.onBeforePaint = onBeforePaint
         self.onAfterPaint = onAfterPaint
         self.onAfterPaint = onAfterPaint
         self.disposeDrawableOnDeInit = disposeDrawableOnDeInit
         self.disposeDrawableOnDeInit = disposeDrawableOnDeInit
-        
+
         super.init()
         super.init()
     }
     }
-    
+
     deinit {
     deinit {
         if disposeDrawableOnDeInit {
         if disposeDrawableOnDeInit {
-            drawable?.dispose() // TODO move drawable out of view?
+            drawable?.dispose()  // TODO move drawable out of view?
         }
         }
     }
     }
-    
+
     /// The ``Atlas`` from which images to render the skeleton are sourced.
     /// The ``Atlas`` from which images to render the skeleton are sourced.
     public var atlas: Atlas {
     public var atlas: Atlas {
         drawable.atlas
         drawable.atlas
     }
     }
-    
+
     /// The setup-pose data used by the skeleton.
     /// The setup-pose data used by the skeleton.
     public var skeletonData: SkeletonData {
     public var skeletonData: SkeletonData {
         drawable.skeletonData
         drawable.skeletonData
     }
     }
-    
+
     /// The ``Skeleton``
     /// The ``Skeleton``
     public var skeleton: Skeleton {
     public var skeleton: Skeleton {
         drawable.skeleton
         drawable.skeleton
     }
     }
-    
+
     /// The mixing information used by the ``AnimationState``
     /// The mixing information used by the ``AnimationState``
     public var animationStateData: AnimationStateData {
     public var animationStateData: AnimationStateData {
         drawable.animationStateData
         drawable.animationStateData
     }
     }
-    
+
     /// The ``AnimationState`` used to manage animations that are being applied to the
     /// The ``AnimationState`` used to manage animations that are being applied to the
     /// skeleton.
     /// skeleton.
     public var animationState: AnimationState {
     public var animationState: AnimationState {
         drawable.animationState
         drawable.animationState
     }
     }
-    
+
     /// The ``AnimationStateWrapper`` used to hold ``AnimationState``, register ``AnimationStateListener`` and call ``AnimationStateWrapper/update(delta:)``
     /// The ``AnimationStateWrapper`` used to hold ``AnimationState``, register ``AnimationStateListener`` and call ``AnimationStateWrapper/update(delta:)``
     public var animationStateWrapper: AnimationStateWrapper {
     public var animationStateWrapper: AnimationStateWrapper {
         drawable.animationStateWrapper
         drawable.animationStateWrapper
     }
     }
-    
+
     /// Transforms the coordinates given in the ``SpineUIView`` coordinate system in `position` to
     /// Transforms the coordinates given in the ``SpineUIView`` coordinate system in `position` to
     /// the skeleton coordinate system. See the `IKFollowing.swift` example how to use this
     /// the skeleton coordinate system. See the `IKFollowing.swift` example how to use this
     /// to move a bone based on user touch input.
     /// to move a bone based on user touch input.
     public func toSkeletonCoordinates(position: CGPoint) -> CGPoint {
     public func toSkeletonCoordinates(position: CGPoint) -> CGPoint {
-        let x = position.x;
-        let y = position.y;
+        let x = position.x
+        let y = position.y
         return CGPoint(
         return CGPoint(
             x: (x - viewSize.width / 2) / scaleX - offsetX,
             x: (x - viewSize.width / 2) / scaleX - offsetX,
             y: (y - viewSize.height / 2) / scaleY - offsetY
             y: (y - viewSize.height / 2) / scaleY - offsetY
         )
         )
     }
     }
-    
+
     /// Transforms the coordinates given in skeleton coordinate system to
     /// Transforms the coordinates given in skeleton coordinate system to
     /// the the ``SpineUIView`` coordinates. See the `DebugRendering.swift` example hot to use this to draw rectangles over skeleton bones for debugging purposes.
     /// the the ``SpineUIView`` coordinates. See the `DebugRendering.swift` example hot to use this to draw rectangles over skeleton bones for debugging purposes.
     public func fromSkeletonCoordinates(position: CGPoint) -> CGPoint {
     public func fromSkeletonCoordinates(position: CGPoint) -> CGPoint {
-        let x = position.x;
-        let y = position.y;
+        let x = position.x
+        let y = position.y
         return CGPoint(
         return CGPoint(
             x: (x + offsetX) * scaleX,
             x: (x + offsetX) * scaleX,
             y: (y + offsetY) * scaleY
             y: (y + offsetY) * scaleY
         )
         )
     }
     }
-    
+
     /// Pauses updating and rendering the skeleton.
     /// Pauses updating and rendering the skeleton.
     public func pause() {
     public func pause() {
         isPlaying = false
         isPlaying = false
     }
     }
-    
+
     /// Resumes updating and rendering the skeleton.
     /// Resumes updating and rendering the skeleton.
     public func resume() {
     public func resume() {
         isPlaying = true
         isPlaying = true
     }
     }
-    
+
     internal func load(atlasFile: String, skeletonFile: String, bundle: Bundle = .main) async throws {
     internal func load(atlasFile: String, skeletonFile: String, bundle: Bundle = .main) async throws {
         let atlasAndPages = try await Atlas.fromBundle(atlasFile, bundle: bundle)
         let atlasAndPages = try await Atlas.fromBundle(atlasFile, bundle: bundle)
         let skeletonData = try await SkeletonData.fromBundle(
         let skeletonData = try await SkeletonData.fromBundle(
@@ -190,23 +190,23 @@ public final class SpineController: NSObject, ObservableObject {
             self.drawable = skeletonDrawableWrapper
             self.drawable = skeletonDrawableWrapper
         }
         }
     }
     }
-    
+
     internal func initialize() {
     internal func initialize() {
         onInitialized?(self)
         onInitialized?(self)
     }
     }
-    
+
 }
 }
 
 
 extension SpineController: SpineRendererDelegate {
 extension SpineController: SpineRendererDelegate {
-    
+
     func spineRendererWillDraw(_ spineRenderer: SpineRenderer) {
     func spineRendererWillDraw(_ spineRenderer: SpineRenderer) {
         onBeforePaint?(self)
         onBeforePaint?(self)
     }
     }
-    
+
     func spineRendererDidDraw(_ spineRenderer: SpineRenderer) {
     func spineRendererDidDraw(_ spineRenderer: SpineRenderer) {
         onAfterPaint?(self)
         onAfterPaint?(self)
     }
     }
-    
+
     func spineRendererDidUpdate(_ spineRenderer: SpineRenderer, scaleX: CGFloat, scaleY: CGFloat, offsetX: CGFloat, offsetY: CGFloat, size: CGSize) {
     func spineRendererDidUpdate(_ spineRenderer: SpineRenderer, scaleX: CGFloat, scaleY: CGFloat, offsetX: CGFloat, offsetY: CGFloat, size: CGSize) {
         self.scaleX = scaleX
         self.scaleX = scaleX
         self.scaleY = scaleY
         self.scaleY = scaleY
@@ -217,27 +217,27 @@ extension SpineController: SpineRendererDelegate {
 }
 }
 
 
 extension SpineController: SpineRendererDataSource {
 extension SpineController: SpineRendererDataSource {
-    
+
     func spineRendererWillUpdate(_ spineRenderer: SpineRenderer) {
     func spineRendererWillUpdate(_ spineRenderer: SpineRenderer) {
         onBeforeUpdateWorldTransforms?(self)
         onBeforeUpdateWorldTransforms?(self)
     }
     }
-    
+
     func spineRendererDidUpdate(_ spineRenderer: SpineRenderer) {
     func spineRendererDidUpdate(_ spineRenderer: SpineRenderer) {
         onAfterUpdateWorldTransforms?(self)
         onAfterUpdateWorldTransforms?(self)
     }
     }
-    
+
     func spineRenderer(_ spineRenderer: SpineRenderer, needsUpdate delta: TimeInterval) {
     func spineRenderer(_ spineRenderer: SpineRenderer, needsUpdate delta: TimeInterval) {
         drawable?.update(delta: Float(delta))
         drawable?.update(delta: Float(delta))
     }
     }
-    
+
     func isPlaying(_ spineRenderer: SpineRenderer) -> Bool {
     func isPlaying(_ spineRenderer: SpineRenderer) -> Bool {
         return isPlaying
         return isPlaying
     }
     }
-    
+
     func skeletonDrawable(_ spineRenderer: SpineRenderer) -> SkeletonDrawableWrapper {
     func skeletonDrawable(_ spineRenderer: SpineRenderer) -> SkeletonDrawableWrapper {
         return drawable
         return drawable
     }
     }
-    
+
     func renderCommands(_ spineRenderer: SpineRenderer) -> [RenderCommand] {
     func renderCommands(_ spineRenderer: SpineRenderer) -> [RenderCommand] {
         return drawable?.skeletonDrawable.render() ?? []
         return drawable?.skeletonDrawable.render() ?? []
     }
     }

+ 28 - 20
spine-ios/Sources/Spine/SpineUIView.swift

@@ -27,8 +27,8 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
  *****************************************************************************/
 
 
-import UIKit
 import MetalKit
 import MetalKit
+import UIKit
 
 
 /// A ``UIView`` to display a Spine skeleton. The skeleton can be loaded from a bundle, local files, http, or a pre-loaded ``SkeletonDrawableWrapper``.
 /// A ``UIView`` to display a Spine skeleton. The skeleton can be loaded from a bundle, local files, http, or a pre-loaded ``SkeletonDrawableWrapper``.
 ///
 ///
@@ -40,15 +40,15 @@ import MetalKit
 /// This is a direct subclass of ``MTKView`` and is using `Metal` to render the skeleton.
 /// This is a direct subclass of ``MTKView`` and is using `Metal` to render the skeleton.
 @objc
 @objc
 public final class SpineUIView: MTKView {
 public final class SpineUIView: MTKView {
-    
+
     let controller: SpineController
     let controller: SpineController
     let mode: Spine.ContentMode
     let mode: Spine.ContentMode
     let alignment: Spine.Alignment
     let alignment: Spine.Alignment
     let boundsProvider: BoundsProvider
     let boundsProvider: BoundsProvider
-    
+
     internal var computedBounds: CGRect = .zero
     internal var computedBounds: CGRect = .zero
     internal var renderer: SpineRenderer?
     internal var renderer: SpineRenderer?
-    
+
     @objc internal init(
     @objc internal init(
         controller: SpineController = SpineController(),
         controller: SpineController = SpineController(),
         mode: Spine.ContentMode = .fit,
         mode: Spine.ContentMode = .fit,
@@ -60,12 +60,12 @@ public final class SpineUIView: MTKView {
         self.mode = mode
         self.mode = mode
         self.alignment = alignment
         self.alignment = alignment
         self.boundsProvider = boundsProvider
         self.boundsProvider = boundsProvider
-        
+
         super.init(frame: .zero, device: SpineObjects.shared.device)
         super.init(frame: .zero, device: SpineObjects.shared.device)
         clearColor = MTLClearColor(backgroundColor)
         clearColor = MTLClearColor(backgroundColor)
         isOpaque = backgroundColor != .clear
         isOpaque = backgroundColor != .clear
     }
     }
-    
+
     /// An initializer that constructs a new ``SpineUIView`` from a ``SpineViewSource``.
     /// An initializer that constructs a new ``SpineUIView`` from a ``SpineViewSource``.
     ///
     ///
     /// After initialization is complete, the provided `controller` is invoked as per the ``SpineController`` semantics, to allow
     /// After initialization is complete, the provided `controller` is invoked as per the ``SpineController`` semantics, to allow
@@ -98,7 +98,7 @@ public final class SpineUIView: MTKView {
             }
             }
         }
         }
     }
     }
-    
+
     /// A convenience initializer that constructs a new ``SpineUIView`` from bundled files.
     /// A convenience initializer that constructs a new ``SpineUIView`` from bundled files.
     ///
     ///
     /// After initialization is complete, the provided `controller` is invoked as per the ``SpineController`` semantics, to allow
     /// After initialization is complete, the provided `controller` is invoked as per the ``SpineController`` semantics, to allow
@@ -125,9 +125,11 @@ public final class SpineUIView: MTKView {
         boundsProvider: BoundsProvider = SetupPoseBounds(),
         boundsProvider: BoundsProvider = SetupPoseBounds(),
         backgroundColor: UIColor = .clear
         backgroundColor: UIColor = .clear
     ) {
     ) {
-        self.init(from: .bundle(atlasFileName: atlasFileName, skeletonFileName: skeletonFileName, bundle: bundle), controller: controller, mode: mode, alignment: alignment, boundsProvider: boundsProvider, backgroundColor: backgroundColor)
+        self.init(
+            from: .bundle(atlasFileName: atlasFileName, skeletonFileName: skeletonFileName, bundle: bundle), controller: controller, mode: mode,
+            alignment: alignment, boundsProvider: boundsProvider, backgroundColor: backgroundColor)
     }
     }
-    
+
     /// A convenience initializer that constructs a new ``SpineUIView`` from file URLs.
     /// A convenience initializer that constructs a new ``SpineUIView`` from file URLs.
     ///
     ///
     /// After initialization is complete, the provided `controller` is invoked as per the ``SpineController`` semantics, to allow
     /// After initialization is complete, the provided `controller` is invoked as per the ``SpineController`` semantics, to allow
@@ -152,9 +154,11 @@ public final class SpineUIView: MTKView {
         boundsProvider: BoundsProvider = SetupPoseBounds(),
         boundsProvider: BoundsProvider = SetupPoseBounds(),
         backgroundColor: UIColor = .clear
         backgroundColor: UIColor = .clear
     ) {
     ) {
-        self.init(from: .file(atlasFile: atlasFile, skeletonFile: skeletonFile), controller: controller, mode: mode, alignment: alignment, boundsProvider: boundsProvider, backgroundColor: backgroundColor)
+        self.init(
+            from: .file(atlasFile: atlasFile, skeletonFile: skeletonFile), controller: controller, mode: mode, alignment: alignment,
+            boundsProvider: boundsProvider, backgroundColor: backgroundColor)
     }
     }
-    
+
     /// A convenience initializer that constructs a new ``SpineUIView`` from HTTP.
     /// A convenience initializer that constructs a new ``SpineUIView`` from HTTP.
     ///
     ///
     /// After initialization is complete, the provided `controller` is invoked as per the ``SpineController`` semantics, to allow
     /// After initialization is complete, the provided `controller` is invoked as per the ``SpineController`` semantics, to allow
@@ -179,9 +183,11 @@ public final class SpineUIView: MTKView {
         boundsProvider: BoundsProvider = SetupPoseBounds(),
         boundsProvider: BoundsProvider = SetupPoseBounds(),
         backgroundColor: UIColor = .clear
         backgroundColor: UIColor = .clear
     ) {
     ) {
-        self.init(from: .http(atlasURL: atlasURL, skeletonURL: skeletonURL), controller: controller, mode: mode, alignment: alignment, boundsProvider: boundsProvider, backgroundColor: backgroundColor)
+        self.init(
+            from: .http(atlasURL: atlasURL, skeletonURL: skeletonURL), controller: controller, mode: mode, alignment: alignment,
+            boundsProvider: boundsProvider, backgroundColor: backgroundColor)
     }
     }
-    
+
     /// A convenience initializer that constructs a new ``SpineUIView`` with a ``SkeletonDrawableWrapper``.
     /// A convenience initializer that constructs a new ``SpineUIView`` with a ``SkeletonDrawableWrapper``.
     ///
     ///
     /// After initialization is complete, the provided `controller` is invoked as per the ``SpineController`` semantics, to allow
     /// After initialization is complete, the provided `controller` is invoked as per the ``SpineController`` semantics, to allow
@@ -203,17 +209,19 @@ public final class SpineUIView: MTKView {
         boundsProvider: BoundsProvider = SetupPoseBounds(),
         boundsProvider: BoundsProvider = SetupPoseBounds(),
         backgroundColor: UIColor = .clear
         backgroundColor: UIColor = .clear
     ) {
     ) {
-        self.init(from: .drawable(drawable), controller: controller, mode: mode, alignment: alignment, boundsProvider: boundsProvider, backgroundColor: backgroundColor)
+        self.init(
+            from: .drawable(drawable), controller: controller, mode: mode, alignment: alignment, boundsProvider: boundsProvider,
+            backgroundColor: backgroundColor)
     }
     }
-    
+
     internal override init(frame frameRect: CGRect, device: MTLDevice?) {
     internal override init(frame frameRect: CGRect, device: MTLDevice?) {
         fatalError("init(frame: device:) has not been implemented. Use init() instead.")
         fatalError("init(frame: device:) has not been implemented. Use init() instead.")
     }
     }
-    
+
     internal required init(coder: NSCoder) {
     internal required init(coder: NSCoder) {
         fatalError("init(coder:) has not been implemented. Use init() instead.")
         fatalError("init(coder:) has not been implemented. Use init() instead.")
     }
     }
-    
+
     /// Disable or enable rendering. Disable it when the spine view is out of bounds and you want to preserve CPU/GPU resources.
     /// Disable or enable rendering. Disable it when the spine view is out of bounds and you want to preserve CPU/GPU resources.
     public var isRendering: Bool {
     public var isRendering: Bool {
         get { !super.isPaused }
         get { !super.isPaused }
@@ -227,7 +235,7 @@ public final class SpineUIView: MTKView {
 }
 }
 
 
 extension SpineUIView {
 extension SpineUIView {
-    
+
     internal func load(drawable: SkeletonDrawableWrapper) throws {
     internal func load(drawable: SkeletonDrawableWrapper) throws {
         controller.drawable = drawable
         controller.drawable = drawable
         computedBounds = boundsProvider.computeBounds(for: drawable)
         computedBounds = boundsProvider.computeBounds(for: drawable)
@@ -236,7 +244,7 @@ extension SpineUIView {
         )
         )
         controller.initialize()
         controller.initialize()
     }
     }
-    
+
     private func initRenderer(atlasPages: [UIImage]) throws {
     private func initRenderer(atlasPages: [UIImage]) throws {
         renderer = try SpineRenderer(
         renderer = try SpineRenderer(
             device: SpineObjects.shared.device,
             device: SpineObjects.shared.device,
@@ -265,7 +273,7 @@ public enum SpineViewSource {
     case file(atlasFile: URL, skeletonFile: URL)
     case file(atlasFile: URL, skeletonFile: URL)
     case http(atlasURL: URL, skeletonURL: URL)
     case http(atlasURL: URL, skeletonURL: URL)
     case drawable(SkeletonDrawableWrapper)
     case drawable(SkeletonDrawableWrapper)
-    
+
     internal func loadDrawable() async throws -> SkeletonDrawableWrapper {
     internal func loadDrawable() async throws -> SkeletonDrawableWrapper {
         switch self {
         switch self {
         case .bundle(let atlasFileName, let skeletonFileName, let bundle):
         case .bundle(let atlasFileName, let skeletonFileName, let bundle):

+ 6 - 6
spine-ios/Sources/Spine/SpineView.swift

@@ -38,7 +38,7 @@ import SwiftUI
 ///
 ///
 /// This is a ``UIViewRepresentable`` of `SpineUIView`.
 /// This is a ``UIViewRepresentable`` of `SpineUIView`.
 public struct SpineView: UIViewRepresentable {
 public struct SpineView: UIViewRepresentable {
-    
+
     public typealias UIViewType = SpineUIView
     public typealias UIViewType = SpineUIView
 
 
     private let source: SpineViewSource
     private let source: SpineViewSource
@@ -46,11 +46,11 @@ public struct SpineView: UIViewRepresentable {
     private let mode: Spine.ContentMode
     private let mode: Spine.ContentMode
     private let alignment: Spine.Alignment
     private let alignment: Spine.Alignment
     private let boundsProvider: BoundsProvider
     private let boundsProvider: BoundsProvider
-    private let backgroundColor: UIColor // Not using `SwiftUI.Color`, as briging to `UIColor` prior iOS 14 might not always work.
-    
+    private let backgroundColor: UIColor  // Not using `SwiftUI.Color`, as briging to `UIColor` prior iOS 14 might not always work.
+
     @Binding
     @Binding
     private var isRendering: Bool?
     private var isRendering: Bool?
-    
+
     /// An initializer that constructs a new ``SpineView`` from a ``SpineViewSource``.
     /// An initializer that constructs a new ``SpineView`` from a ``SpineViewSource``.
     ///
     ///
     /// After initialization is complete, the provided `controller` is invoked as per the ``SpineController`` semantics, to allow
     /// After initialization is complete, the provided `controller` is invoked as per the ``SpineController`` semantics, to allow
@@ -85,7 +85,7 @@ public struct SpineView: UIViewRepresentable {
         self.backgroundColor = backgroundColor
         self.backgroundColor = backgroundColor
         _isRendering = isRendering
         _isRendering = isRendering
     }
     }
-    
+
     public func makeUIView(context: Context) -> SpineUIView {
     public func makeUIView(context: Context) -> SpineUIView {
         return SpineUIView(
         return SpineUIView(
             from: source,
             from: source,
@@ -96,7 +96,7 @@ public struct SpineView: UIViewRepresentable {
             backgroundColor: backgroundColor
             backgroundColor: backgroundColor
         )
         )
     }
     }
-    
+
     public func updateUIView(_ uiView: SpineUIView, context: Context) {
     public func updateUIView(_ uiView: SpineUIView, context: Context) {
         if let isRendering {
         if let isRendering {
             uiView.isRendering = isRendering
             uiView.isRendering = isRendering

+ 4 - 4
spine-ios/Sources/SpineModule/SpineModule.swift

@@ -6,9 +6,9 @@
 //
 //
 
 
 
 
-#if canImport(Spine)
-@_exported import Spine
-#endif
-
 @_exported import SpineCppLite
 @_exported import SpineCppLite
 @_exported import SpineShadersStructs
 @_exported import SpineShadersStructs
+
+#if canImport(Spine)
+    @_exported import Spine
+#endif

+ 15 - 10
spine-ts/build.sh

@@ -16,21 +16,19 @@ fi
 log_title "Spine-TS Build"
 log_title "Spine-TS Build"
 log_detail "Branch: $BRANCH"
 log_detail "Branch: $BRANCH"
 
 
-log_section "Setup"
 log_action "Installing dependencies"
 log_action "Installing dependencies"
 if npm install > /tmp/npm-install.log 2>&1; then
 if npm install > /tmp/npm-install.log 2>&1; then
-	log_ok "Dependencies installed"
+	log_ok
 else
 else
-	log_fail "npm install failed"
-	log_detail "$(cat /tmp/npm-install.log)"
+	log_fail
+	log_error_output "$(cat /tmp/npm-install.log)"
 	exit 1
 	exit 1
 fi
 fi
 
 
 if ! [ -z "$TS_UPDATE_URL" ] && ! [ -z "$BRANCH" ];
 if ! [ -z "$TS_UPDATE_URL" ] && ! [ -z "$BRANCH" ];
 then
 then
-	log_section "Deploy"
 	log_action "Creating artifacts zip"
 	log_action "Creating artifacts zip"
-	zip -j spine-ts.zip \
+	if ZIP_OUTPUT=$(zip -j spine-ts.zip \
 		spine-core/dist/iife/* \
 		spine-core/dist/iife/* \
 		spine-canvas/dist/iife/* \
 		spine-canvas/dist/iife/* \
 		spine-webgl/dist/iife/* \
 		spine-webgl/dist/iife/* \
@@ -51,13 +49,20 @@ then
 		spine-phaser-v3/dist/esm/* \
 		spine-phaser-v3/dist/esm/* \
 		spine-phaser-v4/dist/esm/* \
 		spine-phaser-v4/dist/esm/* \
 		spine-webcomponents/dist/esm/* \
 		spine-webcomponents/dist/esm/* \
-		spine-player/css/spine-player.css > /dev/null 2>&1
+		spine-player/css/spine-player.css 2>&1); then
+		log_ok
+	else
+		log_fail
+		log_error_output "$ZIP_OUTPUT"
+		exit 1
+	fi
 	
 	
 	log_action "Uploading to $TS_UPDATE_URL$BRANCH"
 	log_action "Uploading to $TS_UPDATE_URL$BRANCH"
-	if curl -f -F "[email protected]" "$TS_UPDATE_URL$BRANCH" > /dev/null 2>&1; then
-		log_ok "Artifacts deployed"
+	if CURL_OUTPUT=$(curl -f -F "[email protected]" "$TS_UPDATE_URL$BRANCH" 2>&1); then
+		log_ok
 	else
 	else
-		log_fail "Upload failed"
+		log_fail
+		log_error_output "$CURL_OUTPUT"
 		exit 1
 		exit 1
 	fi
 	fi
 	
 	

+ 8 - 18
tests/generate-serializers.sh

@@ -17,7 +17,6 @@ fi
 
 
 # Install dependencies if node_modules doesn't exist
 # Install dependencies if node_modules doesn't exist
 if [ ! -d "node_modules" ]; then
 if [ ! -d "node_modules" ]; then
-    log_section "Setup"
     log_action "Installing dependencies"
     log_action "Installing dependencies"
     if npm install > /tmp/npm-install.log 2>&1; then
     if npm install > /tmp/npm-install.log 2>&1; then
         log_ok "Dependencies installed"
         log_ok "Dependencies installed"
@@ -28,51 +27,42 @@ if [ ! -d "node_modules" ]; then
     fi
     fi
 fi
 fi
 
 
-log_section "Analyzing API"
 log_action "Analyzing Java API"
 log_action "Analyzing Java API"
 if output=$(npx -y tsx src/analyze-java-api.ts 2>&1); then
 if output=$(npx -y tsx src/analyze-java-api.ts 2>&1); then
-    log_ok "Java API analysis completed"
+    log_ok
 else
 else
-    log_fail "Failed to analyze Java API"
+    log_fail
     log_detail "$output"
     log_detail "$output"
     exit 1
     exit 1
 fi
 fi
 
 
-log_section "Generating Serializer IR"
 log_action "Generating intermediate representation"
 log_action "Generating intermediate representation"
 if output=$(npx -y tsx src/generate-serializer-ir.ts 2>&1); then
 if output=$(npx -y tsx src/generate-serializer-ir.ts 2>&1); then
-    log_ok "Serializer IR generated successfully"
+    log_ok
 else
 else
-    log_fail "Failed to generate serializer IR"
+    log_fail
     log_detail "$output"
     log_detail "$output"
     exit 1
     exit 1
 fi
 fi
 
 
-log_section "Generating Language-Specific Serializers"
 log_action "Generating Java SkeletonSerializer"
 log_action "Generating Java SkeletonSerializer"
 if output=$(npx -y tsx src/generate-java-serializer.ts 2>&1); then
 if output=$(npx -y tsx src/generate-java-serializer.ts 2>&1); then
-    log_ok "Java serializer generated successfully"
-    
-    log_action "Formatting Java code"
-    ../formatters/format.sh java
-    log_ok "Java code formatted"
+    log_ok
 else
 else
     log_fail "Failed to generate Java serializer"
     log_fail "Failed to generate Java serializer"
     log_detail "$output"
     log_detail "$output"
     exit 1
     exit 1
 fi
 fi
+../formatters/format-java.sh
 
 
 log_action "Generating C++ SkeletonSerializer"
 log_action "Generating C++ SkeletonSerializer"
 if output=$(npx -y tsx src/generate-cpp-serializer.ts 2>&1); then
 if output=$(npx -y tsx src/generate-cpp-serializer.ts 2>&1); then
     log_ok "C++ serializer generated successfully"
     log_ok "C++ serializer generated successfully"
-    
-    log_action "Formatting C++ code"
-    ../formatters/format.sh cpp
-    log_ok "C++ code formatted"
 else
 else
     log_fail "Failed to generate C++ serializer"
     log_fail "Failed to generate C++ serializer"
     log_detail "$output"
     log_detail "$output"
     exit 1
     exit 1
 fi
 fi
+../formatters/format-cpp.sh
 
 
-log_summary "✓ Serializer generation and formatting completed successfully"
+log_summary "✓ Serializer generation completed successfully"

+ 2 - 4
tests/test.sh

@@ -16,18 +16,16 @@ fi
 
 
 # Install dependencies if node_modules doesn't exist
 # Install dependencies if node_modules doesn't exist
 if [ ! -d "node_modules" ]; then
 if [ ! -d "node_modules" ]; then
-    log_section "Setup"
     log_action "Installing dependencies"
     log_action "Installing dependencies"
     if npm install > /tmp/npm-install.log 2>&1; then
     if npm install > /tmp/npm-install.log 2>&1; then
-        log_ok "Dependencies installed"
+        log_ok
     else
     else
-        log_fail "npm install failed"
+        log_fail
         log_detail "$(cat /tmp/npm-install.log)"
         log_detail "$(cat /tmp/npm-install.log)"
         exit 1
         exit 1
     fi
     fi
 fi
 fi
 
 
-log_section "Test"
 log_action "Running TypeScript test runner"
 log_action "Running TypeScript test runner"
 
 
 # Run the TypeScript headless test runner with all arguments
 # Run the TypeScript headless test runner with all arguments