lottie-ios 4.2.0 → 4.3.1

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 (193) 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 +1546 -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 +374 -116
  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/CALayer+addAnimation.swift +3 -4
  20. package/Sources/Private/CoreAnimation/Animations/DropShadowAnimation.swift +160 -0
  21. package/Sources/Private/CoreAnimation/Animations/LayerProperty.swift +83 -10
  22. package/Sources/Private/CoreAnimation/Animations/TransformAnimations.swift +85 -79
  23. package/Sources/Private/CoreAnimation/CoreAnimationLayer.swift +11 -7
  24. package/Sources/Private/CoreAnimation/Extensions/Keyframes+combined.swift +67 -6
  25. package/Sources/Private/CoreAnimation/Layers/AnimationLayer.swift +21 -2
  26. package/Sources/Private/CoreAnimation/Layers/BaseCompositionLayer.swift +9 -0
  27. package/Sources/Private/CoreAnimation/Layers/ImageLayer.swift +1 -0
  28. package/Sources/Private/CoreAnimation/Layers/LayerModel+makeAnimationLayer.swift +1 -1
  29. package/Sources/Private/CoreAnimation/Layers/RepeaterLayer.swift +2 -0
  30. package/Sources/Private/CoreAnimation/Layers/ShapeLayer.swift +11 -13
  31. package/Sources/Private/CoreAnimation/Layers/SolidLayer.swift +20 -5
  32. package/Sources/Private/CoreAnimation/Layers/TextLayer.swift +13 -3
  33. package/Sources/Private/CoreAnimation/ValueProviderStore.swift +6 -4
  34. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/Collection+Diff.swift +263 -0
  35. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/Diffable.swift +18 -0
  36. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/DiffableSection.swift +16 -0
  37. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/IndexChangeset.swift +187 -0
  38. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/SectionedChangeset.swift +32 -0
  39. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Logging/EpoxyLogger.swift +99 -0
  40. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/CallbackContextEpoxyModeled.swift +8 -0
  41. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModelArrayBuilder.swift +48 -0
  42. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModelProperty.swift +158 -0
  43. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModelStorage.swift +88 -0
  44. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModeled.swift +54 -0
  45. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Internal/AnyEpoxyModelProperty.swift +29 -0
  46. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Internal/ClassReference.swift +39 -0
  47. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/AnimatedProviding.swift +10 -0
  48. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DataIDProviding.swift +57 -0
  49. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DidDisplayProviding.swift +41 -0
  50. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DidEndDisplayingProviding.swift +41 -0
  51. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DidSelectProviding.swift +36 -0
  52. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/ErasedContentProviding.swift +49 -0
  53. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/MakeViewProviding.swift +60 -0
  54. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/SetBehaviorsProviding.swift +38 -0
  55. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/SetContentProviding.swift +38 -0
  56. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/StyleIDProviding.swift +37 -0
  57. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/TraitCollectionProviding.swift +14 -0
  58. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/ViewDifferentiatorProviding.swift +34 -0
  59. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/ViewProviding.swift +13 -0
  60. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/WillDisplayProviding.swift +41 -0
  61. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/ViewEpoxyModeled.swift +10 -0
  62. package/Sources/Private/EmbeddedLibraries/EpoxyCore/README.md +31 -0
  63. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIHostingController.swift +46 -0
  64. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIHostingView.swift +391 -0
  65. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIIntrinsicContentSizeInvalidator.swift +44 -0
  66. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUILayoutMargins.swift +51 -0
  67. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxyableView+SwiftUIView.swift +172 -0
  68. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/LayoutUtilities/MeasuringViewRepresentable.swift +128 -0
  69. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/LayoutUtilities/SwiftUIMeasurementContainer.swift +452 -0
  70. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/SwiftUIView.swift +148 -0
  71. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/UIView+SwiftUIView.swift +40 -0
  72. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/UIViewConfiguringSwiftUIView.swift +43 -0
  73. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/BehaviorsConfigurableView.swift +45 -0
  74. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/ContentConfigurableView.swift +36 -0
  75. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/EpoxyableView.swift +5 -0
  76. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/StyledView.swift +42 -0
  77. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/ViewType.swift +51 -0
  78. package/Sources/Private/EmbeddedLibraries/README.md +27 -0
  79. package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+BackingConfiguration.swift +4 -4
  80. package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+MemoryFile.swift +2 -2
  81. package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+Writing.swift +4 -4
  82. package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive.swift +8 -7
  83. package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Data+Compression.swift +5 -5
  84. package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/FileManager+ZIP.swift +4 -4
  85. package/Sources/Private/MainThread/LayerContainers/CompLayers/ImageCompositionLayer.swift +6 -0
  86. package/Sources/Private/MainThread/LayerContainers/CompLayers/MaskContainerLayer.swift +3 -1
  87. package/Sources/Private/MainThread/LayerContainers/CompLayers/PreCompositionLayer.swift +18 -5
  88. package/Sources/Private/MainThread/LayerContainers/CompLayers/TextCompositionLayer.swift +31 -3
  89. package/Sources/Private/MainThread/LayerContainers/MainThreadAnimationLayer.swift +33 -8
  90. package/Sources/Private/MainThread/LayerContainers/Utility/CachedImageProvider.swift +8 -1
  91. package/Sources/Private/MainThread/LayerContainers/Utility/CompositionLayersInitializer.swift +13 -4
  92. package/Sources/Private/MainThread/LayerContainers/Utility/LayerFontProvider.swift +2 -2
  93. package/Sources/Private/MainThread/LayerContainers/Utility/LayerImageProvider.swift +1 -0
  94. package/Sources/Private/MainThread/LayerContainers/Utility/LayerTextProvider.swift +4 -4
  95. package/Sources/Private/MainThread/NodeRenderSystem/NodeProperties/Protocols/NodePropertyMap.swift +1 -1
  96. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/ModifierNodes/TrimPathNode.swift +3 -1
  97. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientFillRenderer.swift +2 -1
  98. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/LegacyGradientFillRenderer.swift +2 -1
  99. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientFillNode.swift +1 -1
  100. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientStrokeNode.swift +2 -2
  101. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift +1 -1
  102. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/Text/TextAnimatorNode.swift +1 -1
  103. package/Sources/Private/MainThread/NodeRenderSystem/RenderLayers/ShapeContainerLayer.swift +6 -2
  104. package/Sources/Private/Model/Assets/Asset.swift +8 -0
  105. package/Sources/Private/Model/Assets/AssetLibrary.swift +2 -2
  106. package/Sources/Private/Model/Assets/ImageAsset.swift +3 -3
  107. package/Sources/Private/Model/DotLottie/DotLottieAnimation.swift +16 -2
  108. package/Sources/Private/Model/DotLottie/DotLottieImageProvider.swift +20 -7
  109. package/Sources/Private/Model/Extensions/KeyedDecodingContainerExtensions.swift +26 -0
  110. package/Sources/Private/Model/Keyframes/KeyframeGroup.swift +8 -5
  111. package/Sources/Private/Model/LayerEffects/DropShadowEffect.swift +45 -0
  112. package/Sources/Private/Model/LayerEffects/EffectValues/ColorEffectValue.swift +38 -0
  113. package/Sources/Private/Model/LayerEffects/EffectValues/EffectValue.swift +98 -0
  114. package/Sources/Private/Model/LayerEffects/EffectValues/Vector1DEffectValue.swift +38 -0
  115. package/Sources/Private/Model/LayerEffects/LayerEffect.swift +103 -0
  116. package/Sources/Private/Model/LayerStyles/DropShadowStyle.swift +72 -0
  117. package/Sources/Private/Model/LayerStyles/LayerStyle.swift +85 -0
  118. package/Sources/Private/Model/Layers/LayerModel.swift +27 -0
  119. package/Sources/Private/Model/Objects/Marker.swift +1 -1
  120. package/Sources/Private/Model/Objects/Transform.swift +1 -2
  121. package/Sources/Private/Model/ShapeItems/GradientFill.swift +1 -1
  122. package/Sources/Private/Model/ShapeItems/GradientStroke.swift +2 -2
  123. package/Sources/Private/Model/ShapeItems/Merge.swift +1 -1
  124. package/Sources/Private/Model/ShapeItems/ShapeItem.swift +31 -26
  125. package/Sources/Private/Model/ShapeItems/ShapeTransform.swift +0 -9
  126. package/Sources/Private/Model/ShapeItems/Star.swift +1 -1
  127. package/Sources/Private/Model/Text/Font.swift +2 -2
  128. package/Sources/Private/Model/Text/Glyph.swift +1 -1
  129. package/Sources/Private/RootAnimationLayer.swift +2 -2
  130. package/Sources/Private/Utility/Debugging/LayerDebugging.swift +3 -1
  131. package/Sources/Private/Utility/Extensions/AnimationKeypathExtension.swift +32 -2
  132. package/Sources/Private/Utility/Helpers/AnimationContext.swift +4 -2
  133. package/Sources/Private/Utility/Helpers/AnyEquatable.swift +24 -0
  134. package/Sources/Private/Utility/Helpers/Binding+Map.swift +18 -0
  135. package/Sources/Private/Utility/Helpers/View+ValueChanged.swift +20 -0
  136. package/Sources/Private/Utility/LottieAnimationSource.swift +41 -0
  137. package/Sources/Private/Utility/Primitives/BezierPath.swift +2 -2
  138. package/Sources/Private/Utility/Primitives/BezierPathRoundExtension.swift +2 -2
  139. package/Sources/Private/Utility/Primitives/VectorsExtensions.swift +1 -1
  140. package/Sources/Public/Animation/LottieAnimation.swift +21 -2
  141. package/Sources/Public/Animation/LottieAnimationLayer.swift +1464 -0
  142. package/Sources/Public/Animation/LottieAnimationView.swift +259 -736
  143. package/Sources/Public/Animation/LottiePlaybackMode.swift +117 -0
  144. package/Sources/Public/Animation/LottieView.swift +462 -0
  145. package/Sources/Public/AnimationCache/AnimationCacheProvider.swift +2 -1
  146. package/Sources/Public/AnimationCache/DefaultAnimationCache.swift +5 -6
  147. package/Sources/Public/Configuration/DecodingStrategy.swift +15 -0
  148. package/Sources/Public/Configuration/LottieConfiguration.swift +47 -0
  149. package/Sources/Public/Configuration/ReducedMotionOption.swift +114 -0
  150. package/Sources/Public/{LottieConfiguration.swift → Configuration/RenderingEngineOption.swift} +2 -57
  151. package/Sources/Public/{iOS → Controls}/AnimatedButton.swift +56 -13
  152. package/Sources/Public/{iOS → Controls}/AnimatedControl.swift +80 -8
  153. package/Sources/Public/{iOS → Controls}/AnimatedSwitch.swift +71 -31
  154. package/Sources/Public/Controls/LottieButton.swift +122 -0
  155. package/Sources/Public/Controls/LottieSwitch.swift +144 -0
  156. package/Sources/Public/Controls/LottieViewType.swift +79 -0
  157. package/Sources/Public/DotLottie/DotLottieConfiguration.swift +24 -0
  158. package/Sources/Public/DotLottie/DotLottieFile.swift +21 -6
  159. package/Sources/Public/DotLottie/DotLottieFileHelpers.swift +79 -0
  160. package/Sources/Public/DynamicProperties/AnimationKeypath.swift +11 -1
  161. package/Sources/Public/DynamicProperties/ValueProviders/ColorValueProvider.swift +14 -0
  162. package/Sources/Public/DynamicProperties/ValueProviders/FloatValueProvider.swift +13 -0
  163. package/Sources/Public/DynamicProperties/ValueProviders/GradientValueProvider.swift +22 -5
  164. package/Sources/Public/DynamicProperties/ValueProviders/PointValueProvider.swift +14 -0
  165. package/Sources/Public/DynamicProperties/ValueProviders/SizeValueProvider.swift +13 -0
  166. package/Sources/Public/FontProvider/AnimationFontProvider.swift +8 -0
  167. package/Sources/Public/ImageProvider/AnimationImageProvider.swift +24 -0
  168. package/Sources/Public/Keyframes/Keyframe.swift +6 -0
  169. package/Sources/Public/Primitives/Vectors.swift +2 -2
  170. package/Sources/Public/TextProvider/AnimationTextProvider.swift +79 -6
  171. package/Sources/Public/iOS/AnimationSubview.swift +1 -1
  172. package/Sources/Public/iOS/BundleImageProvider.swift +16 -2
  173. package/Sources/Public/iOS/Compatibility/CompatibleAnimationView.swift +6 -4
  174. package/Sources/Public/iOS/FilepathImageProvider.swift +22 -3
  175. package/Sources/Public/iOS/LottieAnimationViewBase.swift +7 -1
  176. package/Sources/Public/macOS/BundleImageProvider.macOS.swift +15 -1
  177. package/Sources/Public/macOS/FilepathImageProvider.macOS.swift +21 -2
  178. package/lottie-ios.podspec +2 -1
  179. package/package.json +1 -1
  180. package/Sources/Private/Model/DotLottie/DotLottieConfiguration.swift +0 -15
  181. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+Helpers.swift +0 -0
  182. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+Progress.swift +0 -0
  183. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+Reading.swift +0 -0
  184. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+ReadingDeprecated.swift +0 -0
  185. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+WritingDeprecated.swift +0 -0
  186. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+ZIP64.swift +0 -0
  187. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Data+CompressionDeprecated.swift +0 -0
  188. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Data+Serialization.swift +0 -0
  189. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Entry+Serialization.swift +0 -0
  190. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Entry+ZIP64.swift +0 -0
  191. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Entry.swift +0 -0
  192. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/README.md +0 -0
  193. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/URL+ZIP.swift +0 -0
@@ -102,9 +102,6 @@
102
102
  <ContextState
103
103
  contextName = "closure #4 in CALayer.makeGroupLayers(from:parentGroup:parentGroupPath:context:):ShapeLayer.swift">
104
104
  </ContextState>
105
- <ContextState
106
- contextName = "GroupLayer.init(group:items:groupPath:context:):ShapeLayer.swift">
107
- </ContextState>
108
105
  <ContextState
109
106
  contextName = "RoundedCornersNode.rebuildOutputs(frame:):RoundedCornersNode.swift">
110
107
  <PersistentStrings>
@@ -116,6 +113,20 @@
116
113
  </PersistentString>
117
114
  </PersistentStrings>
118
115
  </ContextState>
116
+ <ContextState
117
+ contextName = "GroupLayer.init(group:items:groupPath:context:):ShapeLayer.swift">
118
+ </ContextState>
119
+ <ContextState
120
+ contextName = "ParsingTests.testParsingIsTheSameForBothImplementations():ParsingTests.swift">
121
+ <PersistentStrings>
122
+ <PersistentString
123
+ value = "codableAnimation.layers.flatMap { $0.effects }.filter { $0.effects.isEmpty }">
124
+ </PersistentString>
125
+ <PersistentString
126
+ value = "dictAnimation.layers.flatMap { $0.effects }.filter { $0.name == &quot;Rotation Offset&quot; }">
127
+ </PersistentString>
128
+ </PersistentStrings>
129
+ </ContextState>
119
130
  <ContextState
120
131
  contextName = "CALayer.addTransformAnimations(for:context:):TransformAnimations.swift">
121
132
  </ContextState>
@@ -129,10 +140,10 @@
129
140
  contextName = "CoreAnimationLayer.display():CoreAnimationLayer.swift">
130
141
  <PersistentStrings>
131
142
  <PersistentString
132
- value = "(superlayer!.delegate! as! UIView).window">
143
+ value = "(superlayer!.delegate! as! UIView).superview!">
133
144
  </PersistentString>
134
145
  <PersistentString
135
- value = "(superlayer!.delegate! as! UIView).superview!">
146
+ value = "(superlayer!.delegate! as! UIView).window">
136
147
  </PersistentString>
137
148
  </PersistentStrings>
138
149
  </ContextState>
package/Package.resolved CHANGED
@@ -6,8 +6,8 @@
6
6
  "repositoryURL": "https://github.com/airbnb/swift",
7
7
  "state": {
8
8
  "branch": null,
9
- "revision": "07bb2e0822ca6e464bf3610ed452568931fdbf65",
10
- "version": "1.0.3"
9
+ "revision": "b408d36b4f5e73ea75441fb9791b849b0a40f58b",
10
+ "version": "1.0.5"
11
11
  }
12
12
  },
13
13
  {
package/Package.swift CHANGED
@@ -1,14 +1,21 @@
1
- // swift-tools-version:5.5
1
+ // swift-tools-version:5.6
2
2
  import PackageDescription
3
3
 
4
4
  let package = Package(
5
5
  name: "Lottie",
6
- // Minimum platform versions should be kept in sync with the per-platform targets in Lottie.xcodeproj and lottie-ios.podspec
7
- platforms: [.iOS("11.0"), .macOS("10.11"), .tvOS("11.0")],
6
+ // Minimum platform versions should be kept in sync with the per-platform targets in Lottie.xcodeproj, lottie-ios.podspec, and lottie-spm's Package.swift
7
+ platforms: [.iOS("11.0"), .macOS("10.11"), .tvOS("11.0"), .custom("visionOS", versionString: "1.0")],
8
8
  products: [.library(name: "Lottie", targets: ["Lottie"])],
9
- targets: [.target(name: "Lottie", path: "Sources")])
10
-
11
- #if swift(>=5.6)
12
- // Add the Airbnb Swift formatting plugin if possible
13
- package.dependencies.append(.package(url: "https://github.com/airbnb/swift", .upToNextMajor(from: "1.0.1")))
14
- #endif
9
+ dependencies: [
10
+ .package(url: "https://github.com/airbnb/swift", .upToNextMajor(from: "1.0.1")),
11
+ ],
12
+ targets: [
13
+ .target(
14
+ name: "Lottie",
15
+ path: "Sources",
16
+ exclude: [
17
+ "Private/EmbeddedLibraries/README.md",
18
+ "Private/EmbeddedLibraries/ZipFoundation/README.md",
19
+ "Private/EmbeddedLibraries/EpoxyCore/README.md",
20
+ ]),
21
+ ])
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.1")
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
@@ -50,15 +50,14 @@ extension CALayer {
50
50
  guard !keyframes.isEmpty else { return nil }
51
51
 
52
52
  // Check if this set of keyframes uses After Effects expressions, which aren't supported.
53
- if let unsupportedAfterEffectsExpression = keyframeGroup.unsupportedAfterEffectsExpression {
53
+ // - We only log this once per `CoreAnimationLayer` instance.
54
+ if keyframeGroup.unsupportedAfterEffectsExpression != nil, !context.loggingState.hasLoggedAfterEffectsExpressionsWarning {
55
+ context.loggingState.hasLoggedAfterEffectsExpressionsWarning = true
54
56
  context.logger.info("""
55
57
  `\(property.caLayerKeypath)` animation for "\(context.currentKeypath.fullPath)" \
56
58
  includes an After Effects expression (https://helpx.adobe.com/after-effects/using/expression-language.html), \
57
59
  which is not supported by lottie-ios (expressions are only supported by lottie-web). \
58
60
  This animation may not play correctly.
59
-
60
- \(unsupportedAfterEffectsExpression.replacingOccurrences(of: "\n", with: "\n "))
61
-
62
61
  """)
63
62
  }
64
63
 
@@ -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
+ }