lottie-ios 4.2.0 → 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (192) hide show
  1. package/.github/workflows/main.yml +117 -11
  2. package/Gemfile +1 -0
  3. package/Gemfile.lock +5 -0
  4. package/Lottie.xcodeproj/project.pbxproj +1542 -194
  5. package/Lottie.xcodeproj/project.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  6. package/Lottie.xcodeproj/xcshareddata/xcschemes/Lottie (iOS).xcscheme +1 -1
  7. package/Lottie.xcodeproj/xcshareddata/xcschemes/Lottie (macOS).xcscheme +1 -1
  8. package/Lottie.xcodeproj/xcshareddata/xcschemes/Lottie (tvOS).xcscheme +4 -5
  9. package/Lottie.xcodeproj/xcshareddata/xcschemes/Lottie (visionOS).xcscheme +66 -0
  10. package/Lottie.xcodeproj/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +18 -0
  11. package/Lottie.xcworkspace/xcshareddata/swiftpm/Package.resolved +2 -11
  12. package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  13. package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +346 -102
  14. package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Expressions.xcexplist +16 -5
  15. package/Package.resolved +2 -2
  16. package/Package.swift +16 -9
  17. package/README.md +2 -2
  18. package/Rakefile +122 -25
  19. package/Sources/Private/CoreAnimation/Animations/DropShadowAnimation.swift +160 -0
  20. package/Sources/Private/CoreAnimation/Animations/LayerProperty.swift +83 -10
  21. package/Sources/Private/CoreAnimation/Animations/TransformAnimations.swift +85 -79
  22. package/Sources/Private/CoreAnimation/CoreAnimationLayer.swift +7 -7
  23. package/Sources/Private/CoreAnimation/Extensions/Keyframes+combined.swift +67 -6
  24. package/Sources/Private/CoreAnimation/Layers/AnimationLayer.swift +2 -2
  25. package/Sources/Private/CoreAnimation/Layers/BaseCompositionLayer.swift +9 -0
  26. package/Sources/Private/CoreAnimation/Layers/ImageLayer.swift +1 -0
  27. package/Sources/Private/CoreAnimation/Layers/LayerModel+makeAnimationLayer.swift +1 -1
  28. package/Sources/Private/CoreAnimation/Layers/RepeaterLayer.swift +2 -0
  29. package/Sources/Private/CoreAnimation/Layers/ShapeLayer.swift +11 -13
  30. package/Sources/Private/CoreAnimation/Layers/SolidLayer.swift +20 -5
  31. package/Sources/Private/CoreAnimation/Layers/TextLayer.swift +13 -3
  32. package/Sources/Private/CoreAnimation/ValueProviderStore.swift +6 -4
  33. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/Collection+Diff.swift +263 -0
  34. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/Diffable.swift +18 -0
  35. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/DiffableSection.swift +16 -0
  36. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/IndexChangeset.swift +187 -0
  37. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/SectionedChangeset.swift +32 -0
  38. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Logging/EpoxyLogger.swift +99 -0
  39. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/CallbackContextEpoxyModeled.swift +8 -0
  40. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModelArrayBuilder.swift +48 -0
  41. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModelProperty.swift +158 -0
  42. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModelStorage.swift +88 -0
  43. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModeled.swift +54 -0
  44. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Internal/AnyEpoxyModelProperty.swift +29 -0
  45. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Internal/ClassReference.swift +39 -0
  46. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/AnimatedProviding.swift +10 -0
  47. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DataIDProviding.swift +57 -0
  48. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DidDisplayProviding.swift +41 -0
  49. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DidEndDisplayingProviding.swift +41 -0
  50. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DidSelectProviding.swift +36 -0
  51. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/ErasedContentProviding.swift +49 -0
  52. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/MakeViewProviding.swift +60 -0
  53. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/SetBehaviorsProviding.swift +38 -0
  54. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/SetContentProviding.swift +38 -0
  55. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/StyleIDProviding.swift +37 -0
  56. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/TraitCollectionProviding.swift +14 -0
  57. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/ViewDifferentiatorProviding.swift +34 -0
  58. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/ViewProviding.swift +13 -0
  59. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/WillDisplayProviding.swift +41 -0
  60. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/ViewEpoxyModeled.swift +10 -0
  61. package/Sources/Private/EmbeddedLibraries/EpoxyCore/README.md +31 -0
  62. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIHostingController.swift +46 -0
  63. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIHostingView.swift +391 -0
  64. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIIntrinsicContentSizeInvalidator.swift +44 -0
  65. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUILayoutMargins.swift +51 -0
  66. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxyableView+SwiftUIView.swift +172 -0
  67. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/LayoutUtilities/MeasuringViewRepresentable.swift +128 -0
  68. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/LayoutUtilities/SwiftUIMeasurementContainer.swift +452 -0
  69. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/SwiftUIView.swift +148 -0
  70. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/UIView+SwiftUIView.swift +40 -0
  71. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/UIViewConfiguringSwiftUIView.swift +43 -0
  72. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/BehaviorsConfigurableView.swift +45 -0
  73. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/ContentConfigurableView.swift +36 -0
  74. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/EpoxyableView.swift +5 -0
  75. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/StyledView.swift +42 -0
  76. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/ViewType.swift +51 -0
  77. package/Sources/Private/EmbeddedLibraries/README.md +27 -0
  78. package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+BackingConfiguration.swift +4 -4
  79. package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+MemoryFile.swift +2 -2
  80. package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+Writing.swift +4 -4
  81. package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive.swift +8 -7
  82. package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Data+Compression.swift +5 -5
  83. package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/FileManager+ZIP.swift +4 -4
  84. package/Sources/Private/MainThread/LayerContainers/CompLayers/ImageCompositionLayer.swift +6 -0
  85. package/Sources/Private/MainThread/LayerContainers/CompLayers/MaskContainerLayer.swift +3 -1
  86. package/Sources/Private/MainThread/LayerContainers/CompLayers/PreCompositionLayer.swift +18 -5
  87. package/Sources/Private/MainThread/LayerContainers/CompLayers/TextCompositionLayer.swift +31 -3
  88. package/Sources/Private/MainThread/LayerContainers/MainThreadAnimationLayer.swift +33 -8
  89. package/Sources/Private/MainThread/LayerContainers/Utility/CachedImageProvider.swift +8 -1
  90. package/Sources/Private/MainThread/LayerContainers/Utility/CompositionLayersInitializer.swift +13 -4
  91. package/Sources/Private/MainThread/LayerContainers/Utility/LayerFontProvider.swift +2 -2
  92. package/Sources/Private/MainThread/LayerContainers/Utility/LayerImageProvider.swift +1 -0
  93. package/Sources/Private/MainThread/LayerContainers/Utility/LayerTextProvider.swift +4 -4
  94. package/Sources/Private/MainThread/NodeRenderSystem/NodeProperties/Protocols/NodePropertyMap.swift +1 -1
  95. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/ModifierNodes/TrimPathNode.swift +3 -1
  96. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientFillRenderer.swift +2 -1
  97. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/LegacyGradientFillRenderer.swift +2 -1
  98. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientFillNode.swift +1 -1
  99. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientStrokeNode.swift +2 -2
  100. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift +1 -1
  101. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/Text/TextAnimatorNode.swift +1 -1
  102. package/Sources/Private/MainThread/NodeRenderSystem/RenderLayers/ShapeContainerLayer.swift +6 -2
  103. package/Sources/Private/Model/Assets/Asset.swift +8 -0
  104. package/Sources/Private/Model/Assets/AssetLibrary.swift +2 -2
  105. package/Sources/Private/Model/Assets/ImageAsset.swift +3 -3
  106. package/Sources/Private/Model/DotLottie/DotLottieAnimation.swift +16 -2
  107. package/Sources/Private/Model/DotLottie/DotLottieImageProvider.swift +20 -7
  108. package/Sources/Private/Model/Extensions/KeyedDecodingContainerExtensions.swift +26 -0
  109. package/Sources/Private/Model/Keyframes/KeyframeGroup.swift +8 -5
  110. package/Sources/Private/Model/LayerEffects/DropShadowEffect.swift +45 -0
  111. package/Sources/Private/Model/LayerEffects/EffectValues/ColorEffectValue.swift +38 -0
  112. package/Sources/Private/Model/LayerEffects/EffectValues/EffectValue.swift +98 -0
  113. package/Sources/Private/Model/LayerEffects/EffectValues/Vector1DEffectValue.swift +38 -0
  114. package/Sources/Private/Model/LayerEffects/LayerEffect.swift +103 -0
  115. package/Sources/Private/Model/LayerStyles/DropShadowStyle.swift +72 -0
  116. package/Sources/Private/Model/LayerStyles/LayerStyle.swift +85 -0
  117. package/Sources/Private/Model/Layers/LayerModel.swift +27 -0
  118. package/Sources/Private/Model/Objects/Marker.swift +1 -1
  119. package/Sources/Private/Model/Objects/Transform.swift +1 -2
  120. package/Sources/Private/Model/ShapeItems/GradientFill.swift +1 -1
  121. package/Sources/Private/Model/ShapeItems/GradientStroke.swift +2 -2
  122. package/Sources/Private/Model/ShapeItems/Merge.swift +1 -1
  123. package/Sources/Private/Model/ShapeItems/ShapeItem.swift +31 -26
  124. package/Sources/Private/Model/ShapeItems/ShapeTransform.swift +0 -9
  125. package/Sources/Private/Model/ShapeItems/Star.swift +1 -1
  126. package/Sources/Private/Model/Text/Font.swift +2 -2
  127. package/Sources/Private/Model/Text/Glyph.swift +1 -1
  128. package/Sources/Private/RootAnimationLayer.swift +2 -2
  129. package/Sources/Private/Utility/Debugging/LayerDebugging.swift +3 -1
  130. package/Sources/Private/Utility/Extensions/AnimationKeypathExtension.swift +32 -2
  131. package/Sources/Private/Utility/Helpers/AnimationContext.swift +4 -2
  132. package/Sources/Private/Utility/Helpers/AnyEquatable.swift +24 -0
  133. package/Sources/Private/Utility/Helpers/Binding+Map.swift +18 -0
  134. package/Sources/Private/Utility/Helpers/View+ValueChanged.swift +20 -0
  135. package/Sources/Private/Utility/LottieAnimationSource.swift +41 -0
  136. package/Sources/Private/Utility/Primitives/BezierPath.swift +2 -2
  137. package/Sources/Private/Utility/Primitives/BezierPathRoundExtension.swift +2 -2
  138. package/Sources/Private/Utility/Primitives/VectorsExtensions.swift +1 -1
  139. package/Sources/Public/Animation/LottieAnimation.swift +21 -2
  140. package/Sources/Public/Animation/LottieAnimationLayer.swift +1464 -0
  141. package/Sources/Public/Animation/LottieAnimationView.swift +258 -735
  142. package/Sources/Public/Animation/LottiePlaybackMode.swift +117 -0
  143. package/Sources/Public/Animation/LottieView.swift +462 -0
  144. package/Sources/Public/AnimationCache/AnimationCacheProvider.swift +2 -1
  145. package/Sources/Public/AnimationCache/DefaultAnimationCache.swift +5 -6
  146. package/Sources/Public/Configuration/DecodingStrategy.swift +15 -0
  147. package/Sources/Public/Configuration/LottieConfiguration.swift +47 -0
  148. package/Sources/Public/Configuration/ReducedMotionOption.swift +114 -0
  149. package/Sources/Public/{LottieConfiguration.swift → Configuration/RenderingEngineOption.swift} +2 -57
  150. package/Sources/Public/{iOS → Controls}/AnimatedButton.swift +56 -13
  151. package/Sources/Public/{iOS → Controls}/AnimatedControl.swift +80 -8
  152. package/Sources/Public/{iOS → Controls}/AnimatedSwitch.swift +71 -31
  153. package/Sources/Public/Controls/LottieButton.swift +122 -0
  154. package/Sources/Public/Controls/LottieSwitch.swift +144 -0
  155. package/Sources/Public/Controls/LottieViewType.swift +79 -0
  156. package/Sources/Public/DotLottie/DotLottieConfiguration.swift +24 -0
  157. package/Sources/Public/DotLottie/DotLottieFile.swift +21 -6
  158. package/Sources/Public/DotLottie/DotLottieFileHelpers.swift +79 -0
  159. package/Sources/Public/DynamicProperties/AnimationKeypath.swift +11 -1
  160. package/Sources/Public/DynamicProperties/ValueProviders/ColorValueProvider.swift +14 -0
  161. package/Sources/Public/DynamicProperties/ValueProviders/FloatValueProvider.swift +13 -0
  162. package/Sources/Public/DynamicProperties/ValueProviders/GradientValueProvider.swift +22 -5
  163. package/Sources/Public/DynamicProperties/ValueProviders/PointValueProvider.swift +14 -0
  164. package/Sources/Public/DynamicProperties/ValueProviders/SizeValueProvider.swift +13 -0
  165. package/Sources/Public/FontProvider/AnimationFontProvider.swift +8 -0
  166. package/Sources/Public/ImageProvider/AnimationImageProvider.swift +24 -0
  167. package/Sources/Public/Keyframes/Keyframe.swift +6 -0
  168. package/Sources/Public/Primitives/Vectors.swift +2 -2
  169. package/Sources/Public/TextProvider/AnimationTextProvider.swift +79 -6
  170. package/Sources/Public/iOS/AnimationSubview.swift +1 -1
  171. package/Sources/Public/iOS/BundleImageProvider.swift +16 -2
  172. package/Sources/Public/iOS/Compatibility/CompatibleAnimationView.swift +2 -2
  173. package/Sources/Public/iOS/FilepathImageProvider.swift +22 -3
  174. package/Sources/Public/iOS/LottieAnimationViewBase.swift +7 -1
  175. package/Sources/Public/macOS/BundleImageProvider.macOS.swift +15 -1
  176. package/Sources/Public/macOS/FilepathImageProvider.macOS.swift +21 -2
  177. package/lottie-ios.podspec +2 -1
  178. package/package.json +1 -1
  179. package/Sources/Private/Model/DotLottie/DotLottieConfiguration.swift +0 -15
  180. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+Helpers.swift +0 -0
  181. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+Progress.swift +0 -0
  182. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+Reading.swift +0 -0
  183. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+ReadingDeprecated.swift +0 -0
  184. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+WritingDeprecated.swift +0 -0
  185. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+ZIP64.swift +0 -0
  186. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Data+CompressionDeprecated.swift +0 -0
  187. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Data+Serialization.swift +0 -0
  188. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Entry+Serialization.swift +0 -0
  189. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Entry+ZIP64.swift +0 -0
  190. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Entry.swift +0 -0
  191. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/README.md +0 -0
  192. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/URL+ZIP.swift +0 -0
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  **View documentation, FAQ, help, examples, and more at [airbnb.io/lottie](https://airbnb.io/lottie/)**
5
5
 
6
- Lottie is a cross-platform library for iOS, macOS, tvOS, [Android](https://github.com/airbnb/lottie-android), and [Web](https://github.com/airbnb/lottie-web) that natively renders vector-based animations and art in realtime with minimal code.
6
+ Lottie is a cross-platform library for iOS, macOS, tvOS, visionOS, [Android](https://github.com/airbnb/lottie-android), and [Web](https://github.com/airbnb/lottie-web) that natively renders vector-based animations and art in realtime with minimal code.
7
7
 
8
8
  Lottie loads and renders animations and vectors exported in the bodymovin JSON format. Bodymovin JSON can be created and exported from After Effects with [bodymovin](https://github.com/bodymovin/bodymovin), Sketch with [Lottie Sketch Export](https://github.com/buba447/Lottie-Sketch-Export), and from [Haiku](https://www.haiku.ai).
9
9
 
@@ -41,7 +41,7 @@ To install Lottie using [Swift Package Manager](https://github.com/apple/swift-p
41
41
  or you can add the following dependency to your `Package.swift`:
42
42
 
43
43
  ```swift
44
- .package(url: "https://github.com/airbnb/lottie-spm.git", from: "4.2.0")
44
+ .package(url: "https://github.com/airbnb/lottie-spm.git", from: "4.3.0")
45
45
  ```
46
46
 
47
47
  When using Swift Package Manager we recommend using the [lottie-spm](https://github.com/airbnb/lottie-spm) repo instead of the main lottie-ios repo. The main git repository for [lottie-ios](https://github.com/airbnb/lottie-ios) is somewhat large (300+ MB), and Swift Package Manager always downloads the full repository with all git history. The [lottie-spm](https://github.com/airbnb/lottie-spm) repo is much smaller (less than 500kb), so can be downloaded much more quickly.
package/Rakefile CHANGED
@@ -1,3 +1,6 @@
1
+ require 'json'
2
+ require 'git'
3
+
1
4
  namespace :build do
2
5
  desc 'Builds all packages and executables'
3
6
  task all: ['package:all', 'example:all', 'xcframework']
@@ -5,7 +8,7 @@ namespace :build do
5
8
  desc 'Builds the Lottie package for supported platforms'
6
9
  namespace :package do
7
10
  desc 'Builds the Lottie package for all supported platforms'
8
- task all: ['iOS', 'macOS', 'tvOS']
11
+ task all: ['iOS', 'macOS', 'tvOS', 'visionOS']
9
12
 
10
13
  desc 'Builds the Lottie package for iOS'
11
14
  task :iOS do
@@ -21,56 +24,90 @@ namespace :build do
21
24
  task :tvOS do
22
25
  xcodebuild('build -scheme "Lottie (tvOS)" -destination generic/platform=tvOS -workspace Lottie.xcworkspace')
23
26
  end
27
+
28
+ desc 'Builds the Lottie package for visionOS'
29
+ task :visionOS do
30
+ ifVisionOSEnabled {
31
+ xcodebuild('build -scheme "Lottie (visionOS)" -destination generic/platform=visionOS -workspace Lottie.xcworkspace')
32
+ }
33
+ end
24
34
  end
25
35
 
26
36
  desc 'Builds the Lottie example app for supported platforms'
27
37
  namespace :example do
28
38
  desc 'Builds the Lottie example apps for all supported platforms'
29
- task all: ['iOS', 'macOS', 'tvOS']
39
+ task all: ['iOS', 'macOS', 'tvOS', 'visionOS']
30
40
 
31
41
  desc 'Builds the iOS Lottie Example app'
32
42
  task :iOS do
33
- xcodebuild('build -scheme "Example (iOS)" -destination "platform=iOS Simulator,name=iPhone SE (3rd generation)" -workspace Lottie.xcworkspace')
43
+ xcodebuild('build -scheme "Example (Multiplatform)" -destination "platform=iOS Simulator,name=iPhone SE (3rd generation)" -workspace Lottie.xcworkspace')
34
44
  end
35
45
 
36
46
  desc 'Builds the macOS Lottie Example app'
37
47
  task :macOS do
38
- xcodebuild('build -scheme "Example (macOS)" -workspace Lottie.xcworkspace')
48
+ xcodebuild('build -scheme "Example (Multiplatform)" -workspace Lottie.xcworkspace')
39
49
  end
40
50
 
41
51
  desc 'Builds the tvOS Lottie Example app'
42
52
  task :tvOS do
43
- xcodebuild('build -scheme "Example (tvOS)" -destination "platform=tvOS Simulator,name=Apple TV" -workspace Lottie.xcworkspace')
53
+ xcodebuild('build -scheme "Example (Multiplatform)" -destination "platform=tvOS Simulator,name=Apple TV" -workspace Lottie.xcworkspace')
54
+ end
55
+
56
+ desc 'Builds the visionOS Lottie Example app'
57
+ task :visionOS do
58
+ ifVisionOSEnabled {
59
+ xcodebuild('build -scheme "Example (Multiplatform)" -destination "platform=visionOS Simulator,name=Apple Vision Pro" -workspace Lottie.xcworkspace')
60
+ }
44
61
  end
45
62
  end
46
63
 
47
64
  desc 'Builds an xcframework for all supported platforms'
48
- task :xcframework do
65
+ task :xcframework, [:zip_archive_name] do |_t, args|
66
+ args.with_defaults(:zip_archive_name => 'Lottie')
67
+
49
68
  sh 'rm -rf .build/archives'
50
- xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (iOS)" -destination generic/platform=iOS -archivePath ".build/archives/Lottie_iOS" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES')
51
- xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (iOS)" -destination "generic/platform=iOS Simulator" -archivePath ".build/archives/Lottie_iOS_Simulator" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES')
52
- xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (iOS)" -destination "generic/platform=macOS,variant=Mac Catalyst" -archivePath ".build/archives/Lottie_Mac_Catalyst" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES')
53
- xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (macOS)" -destination generic/platform=macOS -archivePath ".build/archives/Lottie_macOS" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES')
54
- xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (tvOS)" -destination generic/platform=tvOS -archivePath ".build/archives/Lottie_tvOS" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES')
55
- xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (tvOS)" -destination "generic/platform=tvOS Simulator" -archivePath ".build/archives/Lottie_tvOS_Simulator" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES')
56
- xcodebuild(
57
- [
58
- '-create-xcframework',
59
- '-framework .build/archives/Lottie_iOS.xcarchive/Products/Library/Frameworks/Lottie.framework',
60
- '-framework .build/archives/Lottie_iOS_Simulator.xcarchive/Products/Library/Frameworks/Lottie.framework',
61
- '-framework .build/archives/Lottie_Mac_Catalyst.xcarchive/Products/Library/Frameworks/Lottie.framework',
62
- '-framework .build/archives/Lottie_macOS.xcarchive/Products/Library/Frameworks/Lottie.framework',
63
- '-framework .build/archives/Lottie_tvOS.xcarchive/Products/Library/Frameworks/Lottie.framework',
64
- '-framework .build/archives/Lottie_tvOS_Simulator.xcarchive/Products/Library/Frameworks/Lottie.framework',
65
- '-output .build/archives/Lottie.xcframework'
66
- ].join(" "))
69
+
70
+ # Build the framework for each supported platform, including simulators
71
+ xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (iOS)" -destination generic/platform=iOS -archivePath ".build/archives/Lottie_iOS" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES ENABLE_BITCODE=NO')
72
+ xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (iOS)" -destination "generic/platform=iOS Simulator" -archivePath ".build/archives/Lottie_iOS_Simulator" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES ENABLE_BITCODE=NO')
73
+ xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (iOS)" -destination "generic/platform=macOS,variant=Mac Catalyst" -archivePath ".build/archives/Lottie_Mac_Catalyst" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES ENABLE_BITCODE=NO')
74
+ xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (macOS)" -destination generic/platform=macOS -archivePath ".build/archives/Lottie_macOS" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES ENABLE_BITCODE=NO')
75
+ xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (tvOS)" -destination generic/platform=tvOS -archivePath ".build/archives/Lottie_tvOS" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES ENABLE_BITCODE=NO')
76
+ xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (tvOS)" -destination "generic/platform=tvOS Simulator" -archivePath ".build/archives/Lottie_tvOS_Simulator" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES ENABLE_BITCODE=NO')
77
+
78
+ ifVisionOSEnabled {
79
+ xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (visionOS)" -destination generic/platform=visionOS -archivePath ".build/archives/Lottie_visionOS" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES ENABLE_BITCODE=NO')
80
+ xcodebuild('archive -workspace Lottie.xcworkspace -scheme "Lottie (visionOS)" -destination "generic/platform=visionOS Simulator" -archivePath ".build/archives/Lottie_visionOS_Simulator" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES ENABLE_BITCODE=NO')
81
+ }
82
+
83
+ # Combine all of the platforms into a single XCFramework
84
+ xcframeworkInvocation = [
85
+ '-create-xcframework',
86
+ '-framework .build/archives/Lottie_iOS.xcarchive/Products/Library/Frameworks/Lottie.framework',
87
+ '-framework .build/archives/Lottie_iOS_Simulator.xcarchive/Products/Library/Frameworks/Lottie.framework',
88
+ '-framework .build/archives/Lottie_Mac_Catalyst.xcarchive/Products/Library/Frameworks/Lottie.framework',
89
+ '-framework .build/archives/Lottie_tvOS.xcarchive/Products/Library/Frameworks/Lottie.framework',
90
+ '-framework .build/archives/Lottie_tvOS_Simulator.xcarchive/Products/Library/Frameworks/Lottie.framework',
91
+ '-framework .build/archives/Lottie_macOS.xcarchive/Products/Library/Frameworks/Lottie.framework',
92
+ ]
93
+
94
+ ifVisionOSEnabled {
95
+ xcframeworkInvocation.push('-framework .build/archives/Lottie_visionOS.xcarchive/Products/Library/Frameworks/Lottie.framework')
96
+ xcframeworkInvocation.push('-framework .build/archives/Lottie_visionOS_Simulator.xcarchive/Products/Library/Frameworks/Lottie.framework')
97
+ }
98
+
99
+ xcframeworkInvocation.push('-output .build/archives/Lottie.xcframework')
100
+
101
+ xcodebuild(xcframeworkInvocation.join(" "))
102
+
103
+ # Archive the XCFramework into a zip file
67
104
  Dir.chdir('.build/archives') do
68
105
  # Use --symlinks to avoid "Multiple binaries share the same codesign path. This can happen if your build process copies frameworks by following symlinks."
69
106
  # error when validating macOS apps (#1948)
70
- sh 'zip -r --symlinks Lottie.xcframework.zip Lottie.xcframework'
107
+ sh "zip -r --symlinks #{args[:zip_archive_name]}.xcframework.zip Lottie.xcframework"
71
108
  sh 'rm -rf Lottie.xcframework'
72
109
  end
73
- sh 'swift package compute-checksum .build/archives/Lottie.xcframework.zip'
110
+ sh "swift package compute-checksum .build/archives/#{args[:zip_archive_name]}.xcframework.zip"
74
111
  end
75
112
  end
76
113
 
@@ -115,6 +152,10 @@ namespace :test do
115
152
  xcodebuild('build -scheme "Lottie" -destination generic/platform=iOS')
116
153
  xcodebuild('build -scheme "Lottie" -destination generic/platform=macOS')
117
154
  xcodebuild('build -scheme "Lottie" -destination generic/platform=tvOS')
155
+
156
+ ifVisionOSEnabled {
157
+ xcodebuild('build -scheme "Lottie" -destination generic/platform=visionOS')
158
+ }
118
159
  end
119
160
  end
120
161
  end
@@ -129,6 +170,12 @@ namespace :lint do
129
170
  task :podspec do
130
171
  sh 'pod lib lint lottie-ios.podspec'
131
172
  end
173
+
174
+ desc 'Lints the EmbeddedLibraries directory'
175
+ task :EmbeddedLibraries do
176
+ sh 'echo "Linting /Sources/Private/EmbeddedLibraries (should not contain any public symbols)"'
177
+ sh '! grep -r "public" Sources/Private/EmbeddedLibraries --include \*.swift'
178
+ end
132
179
  end
133
180
 
134
181
  namespace :format do
@@ -138,6 +185,41 @@ namespace :format do
138
185
  end
139
186
  end
140
187
 
188
+ namespace :emerge do
189
+ desc 'Uploads to emerge'
190
+ task :upload do
191
+ xcodebuild('build -scheme "SizeTest" -destination generic/platform=iOS -project script/SizeTest/SizeTest.xcodeproj -archivePath test.xcarchive archive CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO')
192
+ sh "zip -r -qq test.zip test.xcarchive"
193
+
194
+ g = Git.open('.')
195
+
196
+ upload_data = {
197
+ repoName: 'airbnb/lottie-ios',
198
+ filename: 'test.zip'
199
+ }
200
+ if ENV["PR_NUMBER"] != "" && ENV["PR_NUMBER"] != "false"
201
+ upload_data[:sha] = g.log[0].parents[1].sha
202
+ upload_data[:baseSha] = g.log[0].parent.sha
203
+ upload_data[:prNumber] = ENV["PR_NUMBER"]
204
+ upload_data[:buildType] = 'pull_request'
205
+ else
206
+ upload_data[:sha] = g.log[0].sha
207
+ upload_data[:buildType] = 'master'
208
+ end
209
+ api_token = ENV['EMERGE_API_TOKEN']
210
+ if api_token.nil? || api_token.empty?
211
+ puts "Skipping Emerge upload because API token was not provided."
212
+ next
213
+ end
214
+ api_token_header = "X-API-Token: #{api_token}"
215
+ url = "https://api.emergetools.com/upload"
216
+ cmd = "curl --fail -s --request POST --url #{url} --header 'Accept: application/json' --header 'Content-Type: application/json' --header '#{api_token_header}' --data '#{upload_data.to_json}'"
217
+ upload_json = %x(#{cmd})
218
+ upload_url = JSON.parse(upload_json)['uploadURL']
219
+ %x(curl --fail -s -H 'Content-Type: application/zip' -T test.zip '#{upload_url}')
220
+ end
221
+ end
222
+
141
223
  def xcodebuild(command)
142
224
  # Check if the mint tool is installed -- if so, pipe the xcodebuild output through xcbeautify
143
225
  `which mint`
@@ -148,3 +230,18 @@ def xcodebuild(command)
148
230
  sh "xcodebuild #{command}"
149
231
  end
150
232
  end
233
+
234
+ # Runs the given code block, unless `SKIP_VISION_OS=true`.
235
+ # This can be removed once CI only uses Xcode 15+.
236
+ def ifVisionOSEnabled
237
+ if ENV["SKIP_VISION_OS"] == "true"
238
+ puts "Skipping visionOS build"
239
+ else
240
+ # As of 9/5/23 the GitHub Actions runner doesn't include the visionOS SDK by default,
241
+ # so we have to download it manually. Following the suggested workaround from
242
+ # https://github.com/actions/runner-images/issues/8144#issuecomment-1702786388
243
+ `brew install xcodesorg/made/xcodes`
244
+ `xcodes runtimes install 'visionOS 1.0-beta3'`
245
+ yield
246
+ end
247
+ end
@@ -0,0 +1,160 @@
1
+ // Created by Cal Stephens on 8/15/23.
2
+ // Copyright © 2023 Airbnb Inc. All rights reserved.
3
+
4
+ import QuartzCore
5
+
6
+ // MARK: - DropShadowModel
7
+
8
+ protocol DropShadowModel {
9
+ /// The opacity of the drop shadow, from 0 to 100.
10
+ var _opacity: KeyframeGroup<LottieVector1D>? { get }
11
+
12
+ /// The shadow radius of the blur
13
+ var _radius: KeyframeGroup<LottieVector1D>? { get }
14
+
15
+ /// The color of the drop shadow
16
+ var _color: KeyframeGroup<LottieColor>? { get }
17
+
18
+ /// The angle of the drop shadow, in degrees,
19
+ /// with "90" resulting in a shadow directly beneath the layer.
20
+ /// Combines with the `distance` to form the `shadowOffset`.
21
+ var _angle: KeyframeGroup<LottieVector1D>? { get }
22
+
23
+ /// The distance of the drop shadow offset.
24
+ /// Combines with the `angle` to form the `shadowOffset`.
25
+ var _distance: KeyframeGroup<LottieVector1D>? { get }
26
+ }
27
+
28
+ // MARK: - DropShadowStyle + DropShadowModel
29
+
30
+ extension DropShadowStyle: DropShadowModel {
31
+ var _opacity: KeyframeGroup<LottieVector1D>? { opacity }
32
+ var _color: KeyframeGroup<LottieColor>? { color }
33
+ var _angle: KeyframeGroup<LottieVector1D>? { angle }
34
+ var _distance: KeyframeGroup<LottieVector1D>? { distance }
35
+
36
+ var _radius: KeyframeGroup<LottieVector1D>? {
37
+ size.map { sizeValue in
38
+ // After Effects shadow softness uses a different range of values than CALayer.shadowRadius,
39
+ // so shadows render too softly if we directly use the value from After Effects. We find that
40
+ // dividing this value from After Effects by 2 produces results that are visually similar.
41
+ LottieVector1D(sizeValue.cgFloatValue / 2)
42
+ }
43
+ }
44
+ }
45
+
46
+ // MARK: - DropShadowEffect + DropShadowModel
47
+
48
+ extension DropShadowEffect: DropShadowModel {
49
+ var _color: KeyframeGroup<LottieColor>? { color?.value }
50
+ var _distance: KeyframeGroup<LottieVector1D>? { distance?.value }
51
+
52
+ var _radius: KeyframeGroup<LottieVector1D>? {
53
+ softness?.value?.map { softnessValue in
54
+ // After Effects shadow softness uses a different range of values than CALayer.shadowRadius,
55
+ // so shadows render too softly if we directly use the value from After Effects. We find that
56
+ // dividing this value from After Effects by 5 produces results that are visually similar.
57
+ LottieVector1D(softnessValue.cgFloatValue / 5)
58
+ }
59
+ }
60
+
61
+ var _opacity: KeyframeGroup<LottieVector1D>? {
62
+ opacity?.value?.map { originalOpacityValue in
63
+ // `DropShadowEffect.opacity` is a value between 0 and 255,
64
+ // but `DropShadowModel._opacity` expects a value between 0 and 100.
65
+ LottieVector1D((originalOpacityValue.value / 255.0) * 100)
66
+ }
67
+ }
68
+
69
+ var _angle: KeyframeGroup<LottieVector1D>? {
70
+ direction?.value?.map { originalAngleValue in
71
+ // `DropShadowEffect.distance` is rotated 90º from the
72
+ // angle value representation expected by `DropShadowModel._angle`
73
+ LottieVector1D(originalAngleValue.value - 90)
74
+ }
75
+ }
76
+ }
77
+
78
+ // MARK: - CALayer + DropShadowModel
79
+
80
+ extension CALayer {
81
+
82
+ // MARK: Internal
83
+
84
+ /// Adds drop shadow animations from the given `DropShadowModel` to this layer
85
+ @nonobjc
86
+ func addDropShadowAnimations(
87
+ for dropShadowModel: DropShadowModel,
88
+ context: LayerAnimationContext)
89
+ throws
90
+ {
91
+ try addShadowOpacityAnimation(from: dropShadowModel, context: context)
92
+ try addShadowColorAnimation(from: dropShadowModel, context: context)
93
+ try addShadowRadiusAnimation(from: dropShadowModel, context: context)
94
+ try addShadowOffsetAnimation(from: dropShadowModel, context: context)
95
+ }
96
+
97
+ // MARK: Private
98
+
99
+ private func addShadowOpacityAnimation(from model: DropShadowModel, context: LayerAnimationContext) throws {
100
+ guard let opacityKeyframes = model._opacity else { return }
101
+
102
+ try addAnimation(
103
+ for: .shadowOpacity,
104
+ keyframes: opacityKeyframes,
105
+ value: {
106
+ // Lottie animation files express opacity as a numerical percentage value
107
+ // (e.g. 0%, 50%, 100%) so we divide by 100 to get the decimal values
108
+ // expected by Core Animation (e.g. 0.0, 0.5, 1.0).
109
+ $0.cgFloatValue / 100
110
+ },
111
+ context: context)
112
+ }
113
+
114
+ private func addShadowColorAnimation(from model: DropShadowModel, context: LayerAnimationContext) throws {
115
+ guard let shadowColorKeyframes = model._color else { return }
116
+
117
+ try addAnimation(
118
+ for: .shadowColor,
119
+ keyframes: shadowColorKeyframes,
120
+ value: \.cgColorValue,
121
+ context: context)
122
+ }
123
+
124
+ private func addShadowRadiusAnimation(from model: DropShadowModel, context: LayerAnimationContext) throws {
125
+ guard let shadowSizeKeyframes = model._radius else { return }
126
+
127
+ try addAnimation(
128
+ for: .shadowRadius,
129
+ keyframes: shadowSizeKeyframes,
130
+ value: \.cgFloatValue,
131
+ context: context)
132
+ }
133
+
134
+ private func addShadowOffsetAnimation(from model: DropShadowModel, context: LayerAnimationContext) throws {
135
+ guard
136
+ let angleKeyframes = model._angle,
137
+ let distanceKeyframes = model._distance
138
+ else { return }
139
+
140
+ let offsetKeyframes = Keyframes.combined(angleKeyframes, distanceKeyframes) { angleDegrees, distance -> CGSize in
141
+ // Lottie animation files express rotation in degrees
142
+ // (e.g. 90º, 180º, 360º) so we convert to radians to get the
143
+ // values expected by Core Animation (e.g. π/2, π, 2π)
144
+ let angleRadians = (angleDegrees.cgFloatValue * .pi) / 180
145
+
146
+ // Lottie animation files express the `shadowOffset` as (angle, distance) pair,
147
+ // which we convert to the expected x / y offset values:
148
+ let offsetX = distance.cgFloatValue * cos(angleRadians)
149
+ let offsetY = distance.cgFloatValue * sin(angleRadians)
150
+ return CGSize(width: offsetX, height: offsetY)
151
+ }
152
+
153
+ try addAnimation(
154
+ for: .shadowOffset,
155
+ keyframes: offsetKeyframes,
156
+ value: { $0 },
157
+ context: context)
158
+ }
159
+
160
+ }
@@ -26,6 +26,16 @@ struct LayerProperty<ValueRepresentation> {
26
26
  }
27
27
 
28
28
  extension LayerProperty where ValueRepresentation: Equatable {
29
+ /// Initializes a `LayerProperty` that corresponds to a property on `CALayer`
30
+ /// or some other `CALayer` subclass like `CAShapeLayer`.
31
+ /// - Parameters:
32
+ /// - caLayerKeypath: The Objective-C `#keyPath` to the `CALayer` property,
33
+ /// e.g. `#keyPath(CALayer.opacity)` or `#keyPath(CAShapeLayer.path)`.
34
+ /// - defaultValue: The default value of the property (e.g. the value of the
35
+ /// property immediately after calling `CALayer.init()`). Knowing this value
36
+ /// lets us perform some optimizations in `CALayer+addAnimation`.
37
+ /// - customizableProperty: A description of how this property can be customized
38
+ /// dynamically at runtime using `AnimationView.setValueProvider(_:keypath:)`.
29
39
  init(
30
40
  caLayerKeypath: String,
31
41
  defaultValue: ValueRepresentation?,
@@ -52,7 +62,10 @@ struct CustomizableProperty<ValueRepresentation> {
52
62
 
53
63
  /// A closure that coverts the type-erased value of an `AnyValueProvider`
54
64
  /// to the strongly-typed representation used by this property, if possible.
55
- let conversion: (Any) -> ValueRepresentation?
65
+ /// - `value` is the value for the current frame that should be converted,
66
+ /// as returned by `AnyValueProvider.typeErasedStorage`.
67
+ /// - `valueProvider` is the `AnyValueProvider` that returned the type-erased value.
68
+ let conversion: (_ value: Any, _ valueProvider: AnyValueProvider) -> ValueRepresentation?
56
69
  }
57
70
 
58
71
  // MARK: - PropertyName
@@ -66,6 +79,8 @@ enum PropertyName: String, CaseIterable {
66
79
  case scale = "Scale"
67
80
  case position = "Position"
68
81
  case rotation = "Rotation"
82
+ case strokeWidth = "Stroke Width"
83
+ case gradientColors = "Colors"
69
84
  }
70
85
 
71
86
  // MARK: CALayer properties
@@ -159,6 +174,34 @@ extension LayerProperty {
159
174
  },
160
175
  customizableProperty: nil /* currently unsupported */ )
161
176
  }
177
+
178
+ static var shadowOpacity: LayerProperty<CGFloat> {
179
+ .init(
180
+ caLayerKeypath: #keyPath(CALayer.shadowOpacity),
181
+ defaultValue: 0,
182
+ customizableProperty: nil /* currently unsupported */ )
183
+ }
184
+
185
+ static var shadowColor: LayerProperty<CGColor> {
186
+ .init(
187
+ caLayerKeypath: #keyPath(CALayer.shadowColor),
188
+ defaultValue: .rgb(0, 0, 0),
189
+ customizableProperty: nil /* currently unsupported */ )
190
+ }
191
+
192
+ static var shadowRadius: LayerProperty<CGFloat> {
193
+ .init(
194
+ caLayerKeypath: #keyPath(CALayer.shadowRadius),
195
+ defaultValue: 3.0,
196
+ customizableProperty: nil /* currently unsupported */ )
197
+ }
198
+
199
+ static var shadowOffset: LayerProperty<CGSize> {
200
+ .init(
201
+ caLayerKeypath: #keyPath(CALayer.shadowOffset),
202
+ defaultValue: CGSize(width: 0, height: -3.0),
203
+ customizableProperty: nil /* currently unsupported */ )
204
+ }
162
205
  }
163
206
 
164
207
  // MARK: CAShapeLayer properties
@@ -182,7 +225,7 @@ extension LayerProperty {
182
225
  .init(
183
226
  caLayerKeypath: #keyPath(CAShapeLayer.lineWidth),
184
227
  defaultValue: 1,
185
- customizableProperty: nil /* currently unsupported */ )
228
+ customizableProperty: .floatValue(.strokeWidth))
186
229
  }
187
230
 
188
231
  static var lineDashPhase: LayerProperty<CGFloat> {
@@ -221,14 +264,14 @@ extension LayerProperty {
221
264
  .init(
222
265
  caLayerKeypath: #keyPath(CAGradientLayer.colors),
223
266
  defaultValue: nil,
224
- customizableProperty: nil /* currently unsupported */ )
267
+ customizableProperty: .gradientColors)
225
268
  }
226
269
 
227
270
  static var locations: LayerProperty<[CGFloat]> {
228
271
  .init(
229
272
  caLayerKeypath: #keyPath(CAGradientLayer.locations),
230
273
  defaultValue: nil,
231
- customizableProperty: nil /* currently unsupported */ )
274
+ customizableProperty: .gradientLocations)
232
275
  }
233
276
 
234
277
  static var startPoint: LayerProperty<CGPoint> {
@@ -252,7 +295,7 @@ extension CustomizableProperty {
252
295
  static var color: CustomizableProperty<CGColor> {
253
296
  .init(
254
297
  name: [.color],
255
- conversion: { typeErasedValue in
298
+ conversion: { typeErasedValue, _ in
256
299
  guard let color = typeErasedValue as? LottieColor else {
257
300
  return nil
258
301
  }
@@ -264,7 +307,7 @@ extension CustomizableProperty {
264
307
  static var opacity: CustomizableProperty<CGFloat> {
265
308
  .init(
266
309
  name: [.opacity],
267
- conversion: { typeErasedValue in
310
+ conversion: { typeErasedValue, _ in
268
311
  guard let vector = typeErasedValue as? LottieVector1D else { return nil }
269
312
 
270
313
  // Lottie animation files express opacity as a numerical percentage value
@@ -277,7 +320,7 @@ extension CustomizableProperty {
277
320
  static var scaleX: CustomizableProperty<CGFloat> {
278
321
  .init(
279
322
  name: [.scale],
280
- conversion: { typeErasedValue in
323
+ conversion: { typeErasedValue, _ in
281
324
  guard let vector = typeErasedValue as? LottieVector3D else { return nil }
282
325
 
283
326
  // Lottie animation files express scale as a numerical percentage value
@@ -290,7 +333,7 @@ extension CustomizableProperty {
290
333
  static var scaleY: CustomizableProperty<CGFloat> {
291
334
  .init(
292
335
  name: [.scale],
293
- conversion: { typeErasedValue in
336
+ conversion: { typeErasedValue, _ in
294
337
  guard let vector = typeErasedValue as? LottieVector3D else { return nil }
295
338
 
296
339
  // Lottie animation files express scale as a numerical percentage value
@@ -303,7 +346,7 @@ extension CustomizableProperty {
303
346
  static var rotation: CustomizableProperty<CGFloat> {
304
347
  .init(
305
348
  name: [.rotation],
306
- conversion: { typeErasedValue in
349
+ conversion: { typeErasedValue, _ in
307
350
  guard let vector = typeErasedValue as? LottieVector1D else { return nil }
308
351
 
309
352
  // Lottie animation files express rotation in degrees
@@ -316,6 +359,36 @@ extension CustomizableProperty {
316
359
  static var position: CustomizableProperty<CGPoint> {
317
360
  .init(
318
361
  name: [.position],
319
- conversion: { ($0 as? LottieVector3D)?.pointValue })
362
+ conversion: { typeErasedValue, _ in
363
+ guard let vector = typeErasedValue as? LottieVector3D else { return nil }
364
+ return vector.pointValue
365
+ })
366
+ }
367
+
368
+ static var gradientColors: CustomizableProperty<[CGColor]> {
369
+ .init(
370
+ name: [.gradientColors],
371
+ conversion: { _, typeErasedValueProvider in
372
+ guard let gradientValueProvider = typeErasedValueProvider as? GradientValueProvider else { return nil }
373
+ return gradientValueProvider.colors.map { $0.cgColorValue }
374
+ })
375
+ }
376
+
377
+ static var gradientLocations: CustomizableProperty<[CGFloat]> {
378
+ .init(
379
+ name: [.gradientColors],
380
+ conversion: { _, typeErasedValueProvider in
381
+ guard let gradientValueProvider = typeErasedValueProvider as? GradientValueProvider else { return nil }
382
+ return gradientValueProvider.locations.map { CGFloat($0) }
383
+ })
384
+ }
385
+
386
+ static func floatValue(_ name: PropertyName...) -> CustomizableProperty<CGFloat> {
387
+ .init(
388
+ name: name,
389
+ conversion: { typeErasedValue, _ in
390
+ guard let vector = typeErasedValue as? LottieVector1D else { return nil }
391
+ return vector.cgFloatValue
392
+ })
320
393
  }
321
394
  }