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
@@ -14,7 +14,6 @@ import Foundation
14
14
  /// Keyframe data is wrapped in a dictionary { "k" : KeyframeData }.
15
15
  /// The keyframe data can either be an array of keyframes or, if no animation is present, the raw value.
16
16
  /// This helper object is needed to properly decode the json.
17
-
18
17
  final class KeyframeGroup<T> {
19
18
 
20
19
  // MARK: Lifecycle
@@ -86,8 +85,8 @@ extension KeyframeGroup: Decodable where T: Decodable {
86
85
 
87
86
  guard
88
87
  let value: T = keyframeData.startValue ?? previousKeyframeData?.endValue,
89
- let time = keyframeData.time else
90
- {
88
+ let time = keyframeData.time
89
+ else {
91
90
  /// Missing keyframe data. JSON must be corrupt.
92
91
  throw DecodingError.dataCorruptedError(
93
92
  forKey: KeyframeWrapperKey.keyframeData,
@@ -165,8 +164,8 @@ extension KeyframeGroup: DictionaryInitializable where T: AnyInitializable {
165
164
  let data = try KeyframeData<T>(dictionary: frameDictionary)
166
165
  guard
167
166
  let value: T = data.startValue ?? previousKeyframeData?.endValue,
168
- let time = data.time else
169
- {
167
+ let time = data.time
168
+ else {
170
169
  throw InitializableError.invalidInput
171
170
  }
172
171
  keyframes.append(Keyframe<T>(
@@ -203,6 +202,10 @@ extension KeyframeGroup: Hashable where T: Hashable {
203
202
  }
204
203
  }
205
204
 
205
+ // MARK: Sendable
206
+
207
+ extension KeyframeGroup: Sendable where T: Sendable { }
208
+
206
209
  extension Keyframe {
207
210
  /// Creates a copy of this `Keyframe` with the same timing data, but a different value
208
211
  func withValue<Value>(_ newValue: Value) -> Keyframe<Value> {
@@ -0,0 +1,45 @@
1
+ // Created by Cal Stephens on 8/14/23.
2
+ // Copyright © 2023 Airbnb Inc. All rights reserved.
3
+
4
+ import Foundation
5
+
6
+ final class DropShadowEffect: LayerEffect {
7
+
8
+ // MARK: Lifecycle
9
+
10
+ required init(from decoder: Decoder) throws {
11
+ try super.init(from: decoder)
12
+ }
13
+
14
+ required init(dictionary: [String: Any]) throws {
15
+ try super.init(dictionary: dictionary)
16
+ }
17
+
18
+ // MARK: Internal
19
+
20
+ /// The color of the drop shadow
21
+ var color: ColorEffectValue? {
22
+ value(named: "Shadow Color")
23
+ }
24
+
25
+ /// Opacity between 0 and 255
26
+ var opacity: Vector1DEffectValue? {
27
+ value(named: "Opacity")
28
+ }
29
+
30
+ /// The direction / angle of the drop shadow, in degrees
31
+ var direction: Vector1DEffectValue? {
32
+ value(named: "Direction")
33
+ }
34
+
35
+ /// The distance of the drop shadow
36
+ var distance: Vector1DEffectValue? {
37
+ value(named: "Distance")
38
+ }
39
+
40
+ /// The softness of the drop shadow
41
+ var softness: Vector1DEffectValue? {
42
+ value(named: "Softness")
43
+ }
44
+
45
+ }
@@ -0,0 +1,38 @@
1
+ // Created by Cal Stephens on 8/14/23.
2
+ // Copyright © 2023 Airbnb Inc. All rights reserved.
3
+
4
+ import Foundation
5
+
6
+ final class ColorEffectValue: EffectValue {
7
+
8
+ // MARK: Lifecycle
9
+
10
+ required init(from decoder: Decoder) throws {
11
+ let container = try decoder.container(keyedBy: CodingKeys.self)
12
+ value = try? container.decode(KeyframeGroup<LottieColor>.self, forKey: .value)
13
+ try super.init(from: decoder)
14
+ }
15
+
16
+ required init(dictionary: [String: Any]) throws {
17
+ let valueDictionary: [String: Any] = try dictionary.value(for: CodingKeys.value)
18
+ value = try KeyframeGroup<LottieColor>(dictionary: valueDictionary)
19
+ try super.init(dictionary: dictionary)
20
+ }
21
+
22
+ // MARK: Internal
23
+
24
+ /// The value of the color
25
+ let value: KeyframeGroup<LottieColor>?
26
+
27
+ override func encode(to encoder: Encoder) throws {
28
+ try super.encode(to: encoder)
29
+ var container = encoder.container(keyedBy: CodingKeys.self)
30
+ try container.encode(value, forKey: .value)
31
+ }
32
+
33
+ // MARK: Private
34
+
35
+ private enum CodingKeys: String, CodingKey {
36
+ case value = "v"
37
+ }
38
+ }
@@ -0,0 +1,98 @@
1
+ // Created by Cal Stephens on 8/15/23.
2
+ // Copyright © 2023 Airbnb Inc. All rights reserved.
3
+
4
+ import Foundation
5
+
6
+ // MARK: - EffectValueType
7
+
8
+ /// https://lottiefiles.github.io/lottie-docs/schema/#/$defs/effect-values
9
+ enum EffectValueType: Int, Codable, Sendable {
10
+ case slider = 0
11
+ case angle = 1
12
+ case color = 2
13
+ case unknown = 9999
14
+
15
+ init(from decoder: Decoder) throws {
16
+ self = try EffectValueType(rawValue: decoder.singleValueContainer().decode(RawValue.self)) ?? .unknown
17
+ }
18
+ }
19
+
20
+ // MARK: ClassFamily
21
+
22
+ extension EffectValueType: ClassFamily {
23
+ static var discriminator: Discriminator = .type
24
+
25
+ func getType() -> AnyObject.Type {
26
+ switch self {
27
+ case .slider:
28
+ return Vector1DEffectValue.self
29
+ case .angle:
30
+ return Vector1DEffectValue.self
31
+ case .color:
32
+ return ColorEffectValue.self
33
+ case .unknown:
34
+ // Unsupported
35
+ return LayerEffect.self
36
+ }
37
+ }
38
+ }
39
+
40
+ // MARK: - EffectValue
41
+
42
+ class EffectValue: Codable, DictionaryInitializable {
43
+
44
+ // MARK: Lifecycle
45
+
46
+ required init(from decoder: Decoder) throws {
47
+ let container = try decoder.container(keyedBy: EffectValue.CodingKeys.self)
48
+ type = try container.decode(EffectValueType.self, forKey: .type)
49
+ name = try container.decode(String.self, forKey: .name)
50
+ }
51
+
52
+ required init(dictionary: [String: Any]) throws {
53
+ type = EffectValueType(rawValue: try dictionary.value(for: CodingKeys.type)) ?? .unknown
54
+ name = try dictionary.value(for: CodingKeys.name) ?? "Effect"
55
+ }
56
+
57
+ // MARK: Internal
58
+
59
+ /// The type of effect value
60
+ let type: EffectValueType
61
+
62
+ /// The name of the effect value
63
+ let name: String
64
+
65
+ // MARK: Fileprivate
66
+
67
+ fileprivate enum CodingKeys: String, CodingKey {
68
+ case type = "ty"
69
+ case name = "nm"
70
+ }
71
+ }
72
+
73
+ extension Array where Element == EffectValue {
74
+ static func fromDictionaries(_ dictionaries: [[String: Any]]) throws -> [EffectValue] {
75
+ try dictionaries.compactMap { dictionary in
76
+ let shapeType = dictionary[EffectValue.CodingKeys.type.rawValue] as? Int
77
+ switch EffectValueType(rawValue: shapeType ?? EffectValueType.unknown.rawValue) {
78
+ case .slider:
79
+ return try Vector1DEffectValue(dictionary: dictionary)
80
+ case .angle:
81
+ return try Vector1DEffectValue(dictionary: dictionary)
82
+ case .color:
83
+ return try ColorEffectValue(dictionary: dictionary)
84
+ case .unknown:
85
+ // Unsupported
86
+ return try EffectValue(dictionary: dictionary)
87
+ case nil:
88
+ return nil
89
+ }
90
+ }
91
+ }
92
+ }
93
+
94
+ // MARK: - EffectValue + Sendable
95
+
96
+ /// Since `EffectValue` isn't `final`, we have to use `@unchecked Sendable` instead of `Sendable.`
97
+ /// All `EffectValue` subclasses are immutable `Sendable` values.
98
+ extension EffectValue: @unchecked Sendable { }
@@ -0,0 +1,38 @@
1
+ // Created by Cal Stephens on 8/14/23.
2
+ // Copyright © 2023 Airbnb Inc. All rights reserved.
3
+
4
+ import Foundation
5
+
6
+ final class Vector1DEffectValue: EffectValue {
7
+
8
+ // MARK: Lifecycle
9
+
10
+ required init(from decoder: Decoder) throws {
11
+ let container = try decoder.container(keyedBy: CodingKeys.self)
12
+ value = try? container.decode(KeyframeGroup<LottieVector1D>.self, forKey: .value)
13
+ try super.init(from: decoder)
14
+ }
15
+
16
+ required init(dictionary: [String: Any]) throws {
17
+ let valueDictionary: [String: Any] = try dictionary.value(for: CodingKeys.value)
18
+ value = try KeyframeGroup<LottieVector1D>(dictionary: valueDictionary)
19
+ try super.init(dictionary: dictionary)
20
+ }
21
+
22
+ // MARK: Internal
23
+
24
+ /// The value of the slider
25
+ let value: KeyframeGroup<LottieVector1D>?
26
+
27
+ override func encode(to encoder: Encoder) throws {
28
+ try super.encode(to: encoder)
29
+ var container = encoder.container(keyedBy: CodingKeys.self)
30
+ try container.encode(value, forKey: .value)
31
+ }
32
+
33
+ // MARK: Private
34
+
35
+ private enum CodingKeys: String, CodingKey {
36
+ case value = "v"
37
+ }
38
+ }
@@ -0,0 +1,103 @@
1
+ // Created by Cal Stephens on 8/14/23.
2
+ // Copyright © 2023 Airbnb Inc. All rights reserved.
3
+
4
+ import Foundation
5
+
6
+ // MARK: - LayerEffectType
7
+
8
+ /// https://lottiefiles.github.io/lottie-docs/schema/#/$defs/effects
9
+ enum LayerEffectType: Int, Codable, Sendable {
10
+ case dropShadow = 25
11
+ case unknown = 9999
12
+
13
+ init(from decoder: Decoder) throws {
14
+ self = try LayerEffectType(rawValue: decoder.singleValueContainer().decode(RawValue.self)) ?? .unknown
15
+ }
16
+ }
17
+
18
+ // MARK: ClassFamily
19
+
20
+ extension LayerEffectType: ClassFamily {
21
+ static var discriminator: Discriminator = .type
22
+
23
+ func getType() -> AnyObject.Type {
24
+ switch self {
25
+ case .dropShadow:
26
+ return DropShadowEffect.self
27
+ case .unknown:
28
+ // Unsupported
29
+ return LayerEffect.self
30
+ }
31
+ }
32
+ }
33
+
34
+ // MARK: - LayerEffect
35
+
36
+ class LayerEffect: Codable, DictionaryInitializable {
37
+
38
+ // MARK: Lifecycle
39
+
40
+ required init(from decoder: Decoder) throws {
41
+ let container = try decoder.container(keyedBy: LayerEffect.CodingKeys.self)
42
+ name = try container.decodeIfPresent(String.self, forKey: .name) ?? "Effect"
43
+ type = try container.decode(LayerEffectType.self, forKey: .type)
44
+ effects = try container.decodeIfPresent([EffectValue].self, ofFamily: EffectValueType.self, forKey: .effects) ?? []
45
+ }
46
+
47
+ required init(dictionary: [String: Any]) throws {
48
+ name = (try? dictionary.value(for: CodingKeys.name)) ?? "Layer"
49
+ type = LayerEffectType(rawValue: try dictionary.value(for: CodingKeys.type)) ?? .unknown
50
+ if let valueDictionaries = dictionary[CodingKeys.effects.rawValue] as? [[String: Any]] {
51
+ effects = try [EffectValue].fromDictionaries(valueDictionaries)
52
+ } else {
53
+ effects = []
54
+ }
55
+ }
56
+
57
+ // MARK: Internal
58
+
59
+ /// The name of the effect
60
+ let name: String
61
+
62
+ /// The type of effect
63
+ let type: LayerEffectType
64
+
65
+ /// Values that configure the behavior of the effect
66
+ let effects: [EffectValue]
67
+
68
+ /// Retrieves the `EffectValue` for the given name
69
+ func value<ValueType: EffectValue>(named name: String) -> ValueType? {
70
+ effects.first(where: {
71
+ $0.name == name && $0 is ValueType
72
+ }) as? ValueType
73
+ }
74
+
75
+ // MARK: Fileprivate
76
+
77
+ fileprivate enum CodingKeys: String, CodingKey {
78
+ case name = "nm"
79
+ case type = "ty"
80
+ case effects = "ef"
81
+ }
82
+ }
83
+
84
+ extension Array where Element == LayerEffect {
85
+ static func fromDictionaries(_ dictionaries: [[String: Any]]) throws -> [LayerEffect] {
86
+ try dictionaries.compactMap { dictionary in
87
+ let shapeType = dictionary[LayerEffect.CodingKeys.type.rawValue] as? Int
88
+ switch LayerEffectType(rawValue: shapeType ?? LayerEffectType.unknown.rawValue) {
89
+ case .dropShadow:
90
+ return try DropShadowEffect(dictionary: dictionary)
91
+ case .unknown, nil:
92
+ // Unsupported
93
+ return try LayerEffect(dictionary: dictionary)
94
+ }
95
+ }
96
+ }
97
+ }
98
+
99
+ // MARK: - LayerEffect + Sendable
100
+
101
+ /// Since `LayerEffect` isn't `final`, we have to use `@unchecked Sendable` instead of `Sendable.`
102
+ /// All `LayerEffect` subclasses are immutable `Sendable` values.
103
+ extension LayerEffect: @unchecked Sendable { }
@@ -0,0 +1,72 @@
1
+ // Created by Cal Stephens on 8/14/23.
2
+ // Copyright © 2023 Airbnb Inc. All rights reserved.
3
+
4
+ import Foundation
5
+
6
+ final class DropShadowStyle: LayerStyle {
7
+
8
+ // MARK: Lifecycle
9
+
10
+ required init(from decoder: Decoder) throws {
11
+ let container = try decoder.container(keyedBy: DropShadowStyle.CodingKeys.self)
12
+ opacity = try container.decode(KeyframeGroup<LottieVector1D>.self, forKey: .opacity)
13
+ color = try container.decode(KeyframeGroup<LottieColor>.self, forKey: .color)
14
+ angle = try container.decode(KeyframeGroup<LottieVector1D>.self, forKey: .angle)
15
+ size = try container.decode(KeyframeGroup<LottieVector1D>.self, forKey: .size)
16
+ distance = try container.decode(KeyframeGroup<LottieVector1D>.self, forKey: .distance)
17
+ try super.init(from: decoder)
18
+ }
19
+
20
+ required init(dictionary: [String: Any]) throws {
21
+ let opacityDictionary: [String: Any] = try dictionary.value(for: CodingKeys.opacity)
22
+ opacity = try KeyframeGroup<LottieVector1D>(dictionary: opacityDictionary)
23
+ let colorDictionary: [String: Any] = try dictionary.value(for: CodingKeys.color)
24
+ color = try KeyframeGroup<LottieColor>(dictionary: colorDictionary)
25
+ let angleDictionary: [String: Any] = try dictionary.value(for: CodingKeys.angle)
26
+ angle = try KeyframeGroup<LottieVector1D>(dictionary: angleDictionary)
27
+ let sizeDictionary: [String: Any] = try dictionary.value(for: CodingKeys.size)
28
+ size = try KeyframeGroup<LottieVector1D>(dictionary: sizeDictionary)
29
+ let distanceDictionary: [String: Any] = try dictionary.value(for: CodingKeys.distance)
30
+ distance = try KeyframeGroup<LottieVector1D>(dictionary: distanceDictionary)
31
+ try super.init(dictionary: dictionary)
32
+ }
33
+
34
+ // MARK: Internal
35
+
36
+ /// The opacity of the drop shadow
37
+ let opacity: KeyframeGroup<LottieVector1D>
38
+
39
+ /// The color of the drop shadow
40
+ let color: KeyframeGroup<LottieColor>
41
+
42
+ /// The angle of the drop shadow, in degrees,
43
+ /// with `0` representing a shadow straight-down from the layer
44
+ /// (`offsetY=distance, offsetX=0`).
45
+ let angle: KeyframeGroup<LottieVector1D>
46
+
47
+ /// The size of the drop shadow
48
+ let size: KeyframeGroup<LottieVector1D>
49
+
50
+ /// The distance of the drop shadow
51
+ let distance: KeyframeGroup<LottieVector1D>
52
+
53
+ override func encode(to encoder: Encoder) throws {
54
+ try super.encode(to: encoder)
55
+ var container = encoder.container(keyedBy: CodingKeys.self)
56
+ try container.encode(opacity, forKey: .opacity)
57
+ try container.encode(color, forKey: .color)
58
+ try container.encode(angle, forKey: .angle)
59
+ try container.encode(size, forKey: .size)
60
+ try container.encode(distance, forKey: .distance)
61
+ }
62
+
63
+ // MARK: Private
64
+
65
+ private enum CodingKeys: String, CodingKey {
66
+ case color = "c"
67
+ case opacity = "o"
68
+ case angle = "a"
69
+ case size = "s"
70
+ case distance = "d"
71
+ }
72
+ }
@@ -0,0 +1,85 @@
1
+ // Created by Cal Stephens on 8/14/23.
2
+ // Copyright © 2023 Airbnb Inc. All rights reserved.
3
+
4
+ import Foundation
5
+
6
+ // MARK: - LayerStyleType
7
+
8
+ enum LayerStyleType: Int, Codable, Sendable {
9
+ case dropShadow = 1
10
+ case unknown = 9999
11
+
12
+ init(from decoder: Decoder) throws {
13
+ self = try LayerStyleType(rawValue: decoder.singleValueContainer().decode(RawValue.self)) ?? .unknown
14
+ }
15
+ }
16
+
17
+ // MARK: ClassFamily
18
+
19
+ extension LayerStyleType: ClassFamily {
20
+ static var discriminator: Discriminator = .type
21
+
22
+ func getType() -> AnyObject.Type {
23
+ switch self {
24
+ case .dropShadow:
25
+ return DropShadowStyle.self
26
+ case .unknown:
27
+ // Unsupported
28
+ return LayerStyle.self
29
+ }
30
+ }
31
+ }
32
+
33
+ // MARK: - LayerStyle
34
+
35
+ class LayerStyle: Codable, DictionaryInitializable {
36
+
37
+ // MARK: Lifecycle
38
+
39
+ required init(from decoder: Decoder) throws {
40
+ let container = try decoder.container(keyedBy: LayerStyle.CodingKeys.self)
41
+ name = try container.decodeIfPresent(String.self, forKey: .name) ?? "Style"
42
+ type = try container.decode(LayerStyleType.self, forKey: .type)
43
+ }
44
+
45
+ required init(dictionary: [String: Any]) throws {
46
+ name = (try? dictionary.value(for: CodingKeys.name)) ?? "Layer"
47
+ type = LayerStyleType(rawValue: try dictionary.value(for: CodingKeys.type)) ?? .unknown
48
+ }
49
+
50
+ // MARK: Internal
51
+
52
+ /// The name of the style
53
+ let name: String
54
+
55
+ /// The type of style
56
+ let type: LayerStyleType
57
+
58
+ // MARK: Fileprivate
59
+
60
+ fileprivate enum CodingKeys: String, CodingKey {
61
+ case name = "nm"
62
+ case type = "ty"
63
+ }
64
+ }
65
+
66
+ extension Array where Element == LayerStyle {
67
+ static func fromDictionaries(_ dictionaries: [[String: Any]]) throws -> [LayerStyle] {
68
+ try dictionaries.compactMap { dictionary in
69
+ let shapeType = dictionary[LayerStyle.CodingKeys.type.rawValue] as? Int
70
+ switch LayerStyleType(rawValue: shapeType ?? LayerStyleType.unknown.rawValue) {
71
+ case .dropShadow:
72
+ return try DropShadowStyle(dictionary: dictionary)
73
+ case .unknown, nil:
74
+ // Unsupported
75
+ return try LayerStyle(dictionary: dictionary)
76
+ }
77
+ }
78
+ }
79
+ }
80
+
81
+ // MARK: - LayerStyle + Sendable
82
+
83
+ /// Since `LayerStyle` isn't `final`, we have to use `@unchecked Sendable` instead of `Sendable.`
84
+ /// All `LayerStyle` subclasses are immutable `Sendable` values.
85
+ extension LayerStyle: @unchecked Sendable { }
@@ -99,6 +99,8 @@ class LayerModel: Codable, DictionaryInitializable {
99
99
  timeStretch = try container.decodeIfPresent(Double.self, forKey: .timeStretch) ?? 1
100
100
  matte = try container.decodeIfPresent(MatteType.self, forKey: .matte)
101
101
  hidden = try container.decodeIfPresent(Bool.self, forKey: .hidden) ?? false
102
+ styles = try container.decodeIfPresent([LayerStyle].self, ofFamily: LayerStyleType.self, forKey: .styles) ?? []
103
+ effects = try container.decodeIfPresent([LayerEffect].self, ofFamily: LayerEffectType.self, forKey: .effects) ?? []
102
104
  }
103
105
 
104
106
  required init(dictionary: [String: Any]) throws {
@@ -138,6 +140,16 @@ class LayerModel: Codable, DictionaryInitializable {
138
140
  matte = nil
139
141
  }
140
142
  hidden = (try? dictionary.value(for: CodingKeys.hidden)) ?? false
143
+ if let styleDictionaries = dictionary[CodingKeys.styles.rawValue] as? [[String: Any]] {
144
+ styles = try [LayerStyle].fromDictionaries(styleDictionaries)
145
+ } else {
146
+ styles = []
147
+ }
148
+ if let effectDictionaries = dictionary[CodingKeys.effects.rawValue] as? [[String: Any]] {
149
+ effects = try [LayerEffect].fromDictionaries(effectDictionaries)
150
+ } else {
151
+ effects = []
152
+ }
141
153
  }
142
154
 
143
155
  // MARK: Internal
@@ -180,8 +192,15 @@ class LayerModel: Codable, DictionaryInitializable {
180
192
  /// The type of matte if any.
181
193
  let matte: MatteType?
182
194
 
195
+ /// Whether or not this layer is hidden, in which case it will not be rendered.
183
196
  let hidden: Bool
184
197
 
198
+ /// A list of styles to apply to this layer
199
+ let styles: [LayerStyle]
200
+
201
+ /// A list of effects to apply to this layer
202
+ let effects: [LayerEffect]
203
+
185
204
  // MARK: Fileprivate
186
205
 
187
206
  fileprivate enum CodingKeys: String, CodingKey {
@@ -199,6 +218,8 @@ class LayerModel: Codable, DictionaryInitializable {
199
218
  case timeStretch = "sr"
200
219
  case matte = "tt"
201
220
  case hidden = "hd"
221
+ case styles = "sy"
222
+ case effects = "ef"
202
223
  }
203
224
  }
204
225
 
@@ -226,3 +247,9 @@ extension Array where Element == LayerModel {
226
247
  }
227
248
  }
228
249
  }
250
+
251
+ // MARK: - LayerModel + Sendable
252
+
253
+ /// Since `LayerModel` isn't `final`, we have to use `@unchecked Sendable` instead of `Sendable.`
254
+ /// All `LayerModel` subclasses are immutable `Sendable` values.
255
+ extension LayerModel: @unchecked Sendable { }
@@ -8,7 +8,7 @@
8
8
  import Foundation
9
9
 
10
10
  /// A time marker
11
- final class Marker: Codable, DictionaryInitializable {
11
+ final class Marker: Codable, Sendable, DictionaryInitializable {
12
12
 
13
13
  // MARK: Lifecycle
14
14
 
@@ -154,8 +154,7 @@ final class Transform: Codable, DictionaryInitializable {
154
154
  let rotation = try? KeyframeGroup<LottieVector1D>(dictionary: rotationDictionary)
155
155
  {
156
156
  rotationZ = rotation
157
- }
158
- else {
157
+ } else {
159
158
  rotationZ = KeyframeGroup(LottieVector1D(0))
160
159
  }
161
160
  rotation = nil
@@ -9,7 +9,7 @@ import Foundation
9
9
 
10
10
  // MARK: - GradientType
11
11
 
12
- enum GradientType: Int, Codable {
12
+ enum GradientType: Int, Codable, Sendable {
13
13
  case none
14
14
  case linear
15
15
  case radial
@@ -9,7 +9,7 @@ import Foundation
9
9
 
10
10
  // MARK: - LineCap
11
11
 
12
- enum LineCap: Int, Codable {
12
+ enum LineCap: Int, Codable, Sendable {
13
13
  case none
14
14
  case butt
15
15
  case round
@@ -18,7 +18,7 @@ enum LineCap: Int, Codable {
18
18
 
19
19
  // MARK: - LineJoin
20
20
 
21
- enum LineJoin: Int, Codable {
21
+ enum LineJoin: Int, Codable, Sendable {
22
22
  case none
23
23
  case miter
24
24
  case round
@@ -9,7 +9,7 @@ import Foundation
9
9
 
10
10
  // MARK: - MergeMode
11
11
 
12
- enum MergeMode: Int, Codable {
12
+ enum MergeMode: Int, Codable, Sendable {
13
13
  case none
14
14
  case merge
15
15
  case add