lottie-ios 4.1.3 → 4.2.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.
- package/.github/workflows/main.yml +27 -9
- package/Lottie.xcodeproj/project.pbxproj +158 -70
- package/Lottie.xcodeproj/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +2 -2
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/IDEFindNavigatorScopes.plist +5 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +258 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Expressions.xcexplist +13 -2
- package/Package.swift +2 -1
- package/README.md +1 -1
- package/Rakefile +3 -3
- package/Sources/Private/CoreAnimation/Animations/CALayer+addAnimation.swift +16 -2
- package/Sources/Private/CoreAnimation/Animations/CombinedShapeAnimation.swift +1 -1
- package/Sources/Private/CoreAnimation/Animations/CustomPathAnimation.swift +1 -1
- package/Sources/Private/CoreAnimation/Animations/EllipseAnimation.swift +1 -1
- package/Sources/Private/CoreAnimation/Animations/GradientAnimations.swift +6 -6
- package/Sources/Private/CoreAnimation/Animations/LayerProperty.swift +68 -6
- package/Sources/Private/CoreAnimation/Animations/OpacityAnimation.swift +1 -1
- package/Sources/Private/CoreAnimation/Animations/RectangleAnimation.swift +1 -1
- package/Sources/Private/CoreAnimation/Animations/ShapeAnimation.swift +66 -102
- package/Sources/Private/CoreAnimation/Animations/StarAnimation.swift +2 -2
- package/Sources/Private/CoreAnimation/Animations/StrokeAnimation.swift +3 -3
- package/Sources/Private/CoreAnimation/Animations/TransformAnimations.swift +11 -11
- package/Sources/Private/CoreAnimation/CoreAnimationLayer.swift +55 -32
- package/Sources/Private/CoreAnimation/Layers/AnimationLayer.swift +3 -3
- package/Sources/Private/CoreAnimation/Layers/BaseCompositionLayer.swift +7 -9
- package/Sources/Private/CoreAnimation/Layers/ShapeLayer.swift +9 -1
- package/Sources/Private/CoreAnimation/ValueProviderStore.swift +22 -11
- package/Sources/Private/MainThread/LayerContainers/CompLayers/MaskContainerLayer.swift +1 -1
- package/Sources/Private/MainThread/LayerContainers/MainThreadAnimationLayer.swift +13 -2
- package/Sources/Private/MainThread/LayerContainers/Utility/LayerTransformNode.swift +4 -4
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/EllipseNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/PolygonNode.swift +2 -2
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/RectNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/StarNode.swift +2 -2
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderContainers/GroupNode.swift +4 -4
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/FillNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientFillNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientStrokeNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/Text/TextAnimatorNode.swift +4 -4
- package/Sources/Private/Model/Assets/ImageAsset.swift +4 -3
- package/Sources/Private/Model/DotLottie/DotLottieAnimation.swift +2 -8
- package/Sources/Private/Model/DotLottie/DotLottieManifest.swift +3 -14
- package/Sources/Private/Model/DotLottie/DotLottieUtils.swift +11 -1
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+BackingConfiguration.swift +147 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+Helpers.swift +351 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+MemoryFile.swift +183 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+Progress.swift +66 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+Reading.swift +144 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+ReadingDeprecated.swift +49 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+Writing.swift +385 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+WritingDeprecated.swift +91 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+ZIP64.swift +170 -0
- package/Sources/Private/Model/DotLottie/{Zip/ZipArchive.swift → ZipFoundation/Archive.swift} +150 -227
- package/Sources/Private/Model/DotLottie/ZipFoundation/Data+Compression.swift +403 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Data+CompressionDeprecated.swift +44 -0
- package/Sources/Private/Model/DotLottie/{Zip → ZipFoundation}/Data+Serialization.swift +62 -0
- package/Sources/Private/Model/DotLottie/{Zip/ZipEntry+Serialization.swift → ZipFoundation/Entry+Serialization.swift} +7 -7
- package/Sources/Private/Model/DotLottie/{Zip/ZipEntry+ZIP64.swift → ZipFoundation/Entry+ZIP64.swift} +13 -19
- package/Sources/Private/Model/DotLottie/{Zip/ZipEntry.swift → ZipFoundation/Entry.swift} +141 -10
- package/Sources/Private/Model/DotLottie/ZipFoundation/FileManager+ZIP.swift +368 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/README.md +24 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/URL+ZIP.swift +32 -0
- package/Sources/Private/Model/Extensions/Bundle.swift +5 -14
- package/Sources/Private/Model/Keyframes/KeyframeGroup.swift +31 -8
- package/Sources/Private/RootAnimationLayer.swift +3 -1
- package/Sources/Private/Utility/Extensions/AnimationKeypathExtension.swift +12 -4
- package/Sources/Private/Utility/Extensions/DataExtension.swift +14 -4
- package/Sources/Private/Utility/Primitives/BezierPathRoundExtension.swift +11 -0
- package/Sources/Public/Animation/LottieAnimationHelpers.swift +12 -10
- package/Sources/Public/Animation/LottieAnimationView.swift +52 -27
- package/Sources/Public/DotLottie/DotLottieFile.swift +11 -34
- package/Sources/Public/DotLottie/DotLottieFileHelpers.swift +92 -71
- package/Sources/Public/iOS/Compatibility/CompatibleAnimationView.swift +58 -0
- package/lottie-ios.podspec +1 -1
- package/package.json +1 -1
- package/LottieAnimation/LottieAnimation.xcodeproj/project.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/LottieAnimation/LottieAnimation.xcodeproj/project.xcworkspace/xcuserdata/valentinperignon.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/LottieAnimation/LottieAnimation.xcodeproj/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
- package/LottieAnimation/LottieAnimation.xcodeproj/xcuserdata/valentinperignon.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +0 -6
- package/LottieAnimation/LottieAnimation.xcodeproj/xcuserdata/valentinperignon.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
- package/Sources/Private/Model/DotLottie/Zip/Data+Compression.swift +0 -134
- package/Sources/Private/Model/DotLottie/Zip/FileManager+ZIP.swift +0 -130
- package/Sources/Private/Utility/Interpolatable/KeyframeGroup+Extensions.swift +0 -59
|
@@ -167,7 +167,7 @@ private class MaskNodeProperties: NodePropertyMap {
|
|
|
167
167
|
shape = NodeProperty(provider: KeyframeInterpolator(keyframes: mask.shape.keyframes))
|
|
168
168
|
expansion = NodeProperty(provider: KeyframeInterpolator(keyframes: mask.expansion.keyframes))
|
|
169
169
|
propertyMap = [
|
|
170
|
-
|
|
170
|
+
PropertyName.opacity.rawValue : opacity,
|
|
171
171
|
"Shape" : shape,
|
|
172
172
|
"Expansion" : expansion,
|
|
173
173
|
]
|
|
@@ -23,6 +23,7 @@ final class MainThreadAnimationLayer: CALayer, RootAnimationLayer {
|
|
|
23
23
|
imageProvider: AnimationImageProvider,
|
|
24
24
|
textProvider: AnimationTextProvider,
|
|
25
25
|
fontProvider: AnimationFontProvider,
|
|
26
|
+
maskAnimationToBounds: Bool,
|
|
26
27
|
logger: LottieLogger)
|
|
27
28
|
{
|
|
28
29
|
layerImageProvider = LayerImageProvider(imageProvider: imageProvider, assets: animation.assetLibrary?.imageAssets)
|
|
@@ -31,7 +32,7 @@ final class MainThreadAnimationLayer: CALayer, RootAnimationLayer {
|
|
|
31
32
|
animationLayers = []
|
|
32
33
|
self.logger = logger
|
|
33
34
|
super.init()
|
|
34
|
-
masksToBounds =
|
|
35
|
+
masksToBounds = maskAnimationToBounds
|
|
35
36
|
bounds = animation.bounds
|
|
36
37
|
let layers = animation.layers.initializeCompositionLayers(
|
|
37
38
|
assetLibrary: animation.assetLibrary,
|
|
@@ -146,6 +147,9 @@ final class MainThreadAnimationLayer: CALayer, RootAnimationLayer {
|
|
|
146
147
|
/// The animatable Current Frame Property
|
|
147
148
|
@NSManaged var currentFrame: CGFloat
|
|
148
149
|
|
|
150
|
+
/// The parent `LottieAnimationView` that manages this layer
|
|
151
|
+
weak var animationView: LottieAnimationView?
|
|
152
|
+
|
|
149
153
|
var animationLayers: ContiguousArray<CompositionLayer>
|
|
150
154
|
|
|
151
155
|
var primaryAnimationKey: AnimationKey {
|
|
@@ -200,7 +204,14 @@ final class MainThreadAnimationLayer: CALayer, RootAnimationLayer {
|
|
|
200
204
|
|
|
201
205
|
func logHierarchyKeypaths() {
|
|
202
206
|
logger.info("Lottie: Logging Animation Keypaths")
|
|
203
|
-
|
|
207
|
+
|
|
208
|
+
for keypath in allHierarchyKeypaths() {
|
|
209
|
+
logger.info(keypath)
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
func allHierarchyKeypaths() -> [String] {
|
|
214
|
+
animationLayers.flatMap { $0.allKeypaths() }
|
|
204
215
|
}
|
|
205
216
|
|
|
206
217
|
func setValueProvider(_ valueProvider: AnyValueProvider, keypath: AnimationKeypath) {
|
|
@@ -25,12 +25,12 @@ final class LayerTransformProperties: NodePropertyMap, KeypathSearchable {
|
|
|
25
25
|
|
|
26
26
|
var propertyMap: [String: AnyNodeProperty] = [
|
|
27
27
|
"Anchor Point" : anchor,
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
PropertyName.scale.rawValue : scale,
|
|
29
|
+
PropertyName.rotation.rawValue: rotationZ,
|
|
30
30
|
"Rotation X" : rotationX,
|
|
31
31
|
"Rotation Y" : rotationY,
|
|
32
32
|
"Rotation Z" : rotationZ,
|
|
33
|
-
|
|
33
|
+
PropertyName.opacity.rawValue : opacity,
|
|
34
34
|
]
|
|
35
35
|
|
|
36
36
|
if
|
|
@@ -46,7 +46,7 @@ final class LayerTransformProperties: NodePropertyMap, KeypathSearchable {
|
|
|
46
46
|
position = nil
|
|
47
47
|
} else if let positionKeyframes = transform.position?.keyframes {
|
|
48
48
|
let position: NodeProperty<LottieVector3D> = NodeProperty(provider: KeyframeInterpolator(keyframes: positionKeyframes))
|
|
49
|
-
propertyMap[
|
|
49
|
+
propertyMap[PropertyName.position.rawValue] = position
|
|
50
50
|
self.position = position
|
|
51
51
|
positionX = nil
|
|
52
52
|
positionY = nil
|
|
@@ -20,7 +20,7 @@ final class EllipseNodeProperties: NodePropertyMap, KeypathSearchable {
|
|
|
20
20
|
position = NodeProperty(provider: KeyframeInterpolator(keyframes: ellipse.position.keyframes))
|
|
21
21
|
size = NodeProperty(provider: KeyframeInterpolator(keyframes: ellipse.size.keyframes))
|
|
22
22
|
keypathProperties = [
|
|
23
|
-
|
|
23
|
+
PropertyName.position.rawValue : position,
|
|
24
24
|
"Size" : size,
|
|
25
25
|
]
|
|
26
26
|
properties = Array(keypathProperties.values)
|
|
@@ -23,10 +23,10 @@ final class PolygonNodeProperties: NodePropertyMap, KeypathSearchable {
|
|
|
23
23
|
rotation = NodeProperty(provider: KeyframeInterpolator(keyframes: star.rotation.keyframes))
|
|
24
24
|
points = NodeProperty(provider: KeyframeInterpolator(keyframes: star.points.keyframes))
|
|
25
25
|
keypathProperties = [
|
|
26
|
-
|
|
26
|
+
PropertyName.position.rawValue : position,
|
|
27
27
|
"Outer Radius" : outerRadius,
|
|
28
28
|
"Outer Roundedness" : outerRoundedness,
|
|
29
|
-
|
|
29
|
+
PropertyName.rotation.rawValue : rotation,
|
|
30
30
|
"Points" : points,
|
|
31
31
|
]
|
|
32
32
|
properties = Array(keypathProperties.values)
|
|
@@ -22,7 +22,7 @@ final class RectNodeProperties: NodePropertyMap, KeypathSearchable {
|
|
|
22
22
|
cornerRadius = NodeProperty(provider: KeyframeInterpolator(keyframes: rectangle.cornerRadius.keyframes))
|
|
23
23
|
|
|
24
24
|
keypathProperties = [
|
|
25
|
-
|
|
25
|
+
PropertyName.position.rawValue : position,
|
|
26
26
|
"Size" : size,
|
|
27
27
|
"Roundness" : cornerRadius,
|
|
28
28
|
]
|
|
@@ -33,12 +33,12 @@ final class StarNodeProperties: NodePropertyMap, KeypathSearchable {
|
|
|
33
33
|
rotation = NodeProperty(provider: KeyframeInterpolator(keyframes: star.rotation.keyframes))
|
|
34
34
|
points = NodeProperty(provider: KeyframeInterpolator(keyframes: star.points.keyframes))
|
|
35
35
|
keypathProperties = [
|
|
36
|
-
|
|
36
|
+
PropertyName.position.rawValue : position,
|
|
37
37
|
"Outer Radius" : outerRadius,
|
|
38
38
|
"Outer Roundedness" : outerRoundedness,
|
|
39
39
|
"Inner Radius" : innerRadius,
|
|
40
40
|
"Inner Roundedness" : innerRoundedness,
|
|
41
|
-
|
|
41
|
+
PropertyName.rotation.rawValue : rotation,
|
|
42
42
|
"Points" : points,
|
|
43
43
|
]
|
|
44
44
|
properties = Array(keypathProperties.values)
|
|
@@ -40,13 +40,13 @@ final class GroupNodeProperties: NodePropertyMap, KeypathSearchable {
|
|
|
40
40
|
}
|
|
41
41
|
keypathProperties = [
|
|
42
42
|
"Anchor Point" : anchor,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
PropertyName.position.rawValue : position,
|
|
44
|
+
PropertyName.scale.rawValue : scale,
|
|
45
|
+
PropertyName.rotation.rawValue : rotationZ,
|
|
46
46
|
"Rotation X" : rotationX,
|
|
47
47
|
"Rotation Y" : rotationY,
|
|
48
48
|
"Rotation Z" : rotationZ,
|
|
49
|
-
|
|
49
|
+
PropertyName.opacity.rawValue : opacity,
|
|
50
50
|
"Skew" : skew,
|
|
51
51
|
"Skew Axis" : skewAxis,
|
|
52
52
|
]
|
|
@@ -20,7 +20,7 @@ final class FillNodeProperties: NodePropertyMap, KeypathSearchable {
|
|
|
20
20
|
opacity = NodeProperty(provider: KeyframeInterpolator(keyframes: fill.opacity.keyframes))
|
|
21
21
|
type = fill.fillRule
|
|
22
22
|
keypathProperties = [
|
|
23
|
-
|
|
23
|
+
PropertyName.opacity.rawValue : opacity,
|
|
24
24
|
PropertyName.color.rawValue : color,
|
|
25
25
|
]
|
|
26
26
|
properties = Array(keypathProperties.values)
|
package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientFillNode.swift
CHANGED
|
@@ -24,7 +24,7 @@ final class GradientFillProperties: NodePropertyMap, KeypathSearchable {
|
|
|
24
24
|
numberOfColors = gradientfill.numberOfColors
|
|
25
25
|
fillRule = gradientfill.fillRule
|
|
26
26
|
keypathProperties = [
|
|
27
|
-
|
|
27
|
+
PropertyName.opacity.rawValue : opacity,
|
|
28
28
|
"Start Point" : startPoint,
|
|
29
29
|
"End Point" : endPoint,
|
|
30
30
|
"Colors" : colors,
|
package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientStrokeNode.swift
CHANGED
|
@@ -44,7 +44,7 @@ final class GradientStrokeProperties: NodePropertyMap, KeypathSearchable {
|
|
|
44
44
|
dashPhase = NodeProperty(provider: SingleValueProvider(LottieVector1D(0)))
|
|
45
45
|
}
|
|
46
46
|
keypathProperties = [
|
|
47
|
-
|
|
47
|
+
PropertyName.opacity.rawValue : opacity,
|
|
48
48
|
"Start Point" : startPoint,
|
|
49
49
|
"End Point" : endPoint,
|
|
50
50
|
"Colors" : colors,
|
|
@@ -36,7 +36,7 @@ final class StrokeNodeProperties: NodePropertyMap, KeypathSearchable {
|
|
|
36
36
|
dashPhase = NodeProperty(provider: SingleValueProvider(LottieVector1D(0)))
|
|
37
37
|
}
|
|
38
38
|
keypathProperties = [
|
|
39
|
-
|
|
39
|
+
PropertyName.opacity.rawValue : opacity,
|
|
40
40
|
PropertyName.color.rawValue : color,
|
|
41
41
|
"Stroke Width" : width,
|
|
42
42
|
"Dashes" : dashPattern,
|
|
@@ -28,14 +28,14 @@ final class TextAnimatorNodeProperties: NodePropertyMap, KeypathSearchable {
|
|
|
28
28
|
|
|
29
29
|
if let keyframeGroup = textAnimator.position {
|
|
30
30
|
position = NodeProperty(provider: KeyframeInterpolator(keyframes: keyframeGroup.keyframes))
|
|
31
|
-
properties[
|
|
31
|
+
properties[PropertyName.position.rawValue] = position
|
|
32
32
|
} else {
|
|
33
33
|
position = nil
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
if let keyframeGroup = textAnimator.scale {
|
|
37
37
|
scale = NodeProperty(provider: KeyframeInterpolator(keyframes: keyframeGroup.keyframes))
|
|
38
|
-
properties[
|
|
38
|
+
properties[PropertyName.scale.rawValue] = scale
|
|
39
39
|
} else {
|
|
40
40
|
scale = nil
|
|
41
41
|
}
|
|
@@ -71,14 +71,14 @@ final class TextAnimatorNodeProperties: NodePropertyMap, KeypathSearchable {
|
|
|
71
71
|
if let keyframeGroup = textAnimator.rotationZ {
|
|
72
72
|
rotationZ = NodeProperty(provider: KeyframeInterpolator(keyframes: keyframeGroup.keyframes))
|
|
73
73
|
properties["Rotation Z"] = rotationZ
|
|
74
|
-
properties[
|
|
74
|
+
properties[PropertyName.rotation.rawValue] = rotationZ
|
|
75
75
|
} else {
|
|
76
76
|
rotationZ = nil
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
if let keyframeGroup = textAnimator.opacity {
|
|
80
80
|
opacity = NodeProperty(provider: KeyframeInterpolator(keyframes: keyframeGroup.keyframes))
|
|
81
|
-
properties[
|
|
81
|
+
properties[PropertyName.opacity.rawValue] = opacity
|
|
82
82
|
} else {
|
|
83
83
|
opacity = nil
|
|
84
84
|
}
|
|
@@ -80,9 +80,10 @@ extension Data {
|
|
|
80
80
|
/// - parameter dataString: The data string to parse.
|
|
81
81
|
/// - parameter options: Options for the string parsing. Default value is `[]`.
|
|
82
82
|
internal init?(dataString: String, options: DataURLReadOptions = []) {
|
|
83
|
+
let trimmedDataString = dataString.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
83
84
|
guard
|
|
84
85
|
dataString.hasPrefix("data:"),
|
|
85
|
-
let url = URL(string:
|
|
86
|
+
let url = URL(string: trimmedDataString)
|
|
86
87
|
else {
|
|
87
88
|
return nil
|
|
88
89
|
}
|
|
@@ -90,10 +91,10 @@ extension Data {
|
|
|
90
91
|
// with messages since url doesn't have a host. This only fixes flooding logs
|
|
91
92
|
// when data inside Data URL is base64 encoded.
|
|
92
93
|
if
|
|
93
|
-
let base64Range =
|
|
94
|
+
let base64Range = trimmedDataString.range(of: ";base64,"),
|
|
94
95
|
!options.contains(DataURLReadOptions.legacy)
|
|
95
96
|
{
|
|
96
|
-
let encodedString = String(
|
|
97
|
+
let encodedString = String(trimmedDataString[base64Range.upperBound...])
|
|
97
98
|
self.init(base64Encoded: encodedString)
|
|
98
99
|
} else {
|
|
99
100
|
try? self.init(contentsOf: url)
|
|
@@ -23,9 +23,6 @@ struct DotLottieAnimation: Codable {
|
|
|
23
23
|
/// mode - "bounce" | "normal"
|
|
24
24
|
var mode: String? = "normal"
|
|
25
25
|
|
|
26
|
-
/// URL to animation, to be set internally
|
|
27
|
-
var animationUrl: URL?
|
|
28
|
-
|
|
29
26
|
/// Loop mode for animation
|
|
30
27
|
var loopMode: LottieLoopMode {
|
|
31
28
|
mode == "bounce" ? .autoReverse : ((loop ?? false) ? .loop : .playOnce)
|
|
@@ -38,12 +35,9 @@ struct DotLottieAnimation: Codable {
|
|
|
38
35
|
|
|
39
36
|
/// Loads `LottieAnimation` from `animationUrl`
|
|
40
37
|
/// - Returns: Deserialized `LottieAnimation`. Optional.
|
|
41
|
-
func animation() throws -> LottieAnimation {
|
|
42
|
-
|
|
43
|
-
throw DotLottieError.animationNotAvailable
|
|
44
|
-
}
|
|
38
|
+
func animation(url: URL) throws -> LottieAnimation {
|
|
39
|
+
let animationUrl = url.appendingPathComponent("\(id).json")
|
|
45
40
|
let data = try Data(contentsOf: animationUrl)
|
|
46
41
|
return try LottieAnimation.from(data: data)
|
|
47
42
|
}
|
|
48
|
-
|
|
49
43
|
}
|
|
@@ -10,21 +10,10 @@ import Foundation
|
|
|
10
10
|
/// Manifest model for .lottie File
|
|
11
11
|
struct DotLottieManifest: Codable {
|
|
12
12
|
|
|
13
|
-
// MARK: Lifecycle
|
|
14
|
-
|
|
15
|
-
init(animations: [DotLottieAnimation], version: String, author: String, generator: String) {
|
|
16
|
-
self.animations = animations
|
|
17
|
-
self.version = version
|
|
18
|
-
self.author = author
|
|
19
|
-
self.generator = generator
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// MARK: Internal
|
|
23
|
-
|
|
24
13
|
var animations: [DotLottieAnimation]
|
|
25
|
-
var version: String
|
|
26
|
-
var author: String
|
|
27
|
-
var generator: String
|
|
14
|
+
var version: String?
|
|
15
|
+
var author: String?
|
|
16
|
+
var generator: String?
|
|
28
17
|
|
|
29
18
|
/// Decodes data to Manifest model
|
|
30
19
|
/// - Parameter data: Data to decode
|
|
@@ -15,7 +15,7 @@ struct DotLottieUtils {
|
|
|
15
15
|
|
|
16
16
|
/// Temp folder to app directory
|
|
17
17
|
static var tempDirectoryURL: URL {
|
|
18
|
-
if #available(iOS 10.0, *) {
|
|
18
|
+
if #available(iOS 10.0, macOS 10.12, *) {
|
|
19
19
|
return FileManager.default.temporaryDirectory
|
|
20
20
|
}
|
|
21
21
|
return URL(fileURLWithPath: NSTemporaryDirectory())
|
|
@@ -52,7 +52,17 @@ extension FileManager {
|
|
|
52
52
|
// MARK: - DotLottieError
|
|
53
53
|
|
|
54
54
|
public enum DotLottieError: Error {
|
|
55
|
+
/// URL response has no data.
|
|
56
|
+
case noDataLoaded
|
|
57
|
+
/// Asset with this name was not found in the provided bundle.
|
|
58
|
+
case assetNotFound(name: String, bundle: Bundle?)
|
|
59
|
+
/// Animation loading from asset is not supported on macOS 10.10.
|
|
60
|
+
case loadingFromAssetNotSupported
|
|
61
|
+
|
|
62
|
+
@available(*, deprecated, message: "Unused")
|
|
55
63
|
case invalidFileFormat
|
|
64
|
+
@available(*, deprecated, message: "Unused")
|
|
56
65
|
case invalidData
|
|
66
|
+
@available(*, deprecated, message: "Unused")
|
|
57
67
|
case animationNotAvailable
|
|
58
68
|
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Archive+BackingConfiguration.swift
|
|
3
|
+
// ZIPFoundation
|
|
4
|
+
//
|
|
5
|
+
// Copyright © 2017-2021 Thomas Zoechling, https://www.peakstep.com and the ZIP Foundation project authors.
|
|
6
|
+
// Released under the MIT License.
|
|
7
|
+
//
|
|
8
|
+
// See https://github.com/weichsel/ZIPFoundation/blob/master/LICENSE for license information.
|
|
9
|
+
//
|
|
10
|
+
|
|
11
|
+
import Foundation
|
|
12
|
+
|
|
13
|
+
extension Archive {
|
|
14
|
+
|
|
15
|
+
struct BackingConfiguration {
|
|
16
|
+
let file: FILEPointer
|
|
17
|
+
let endOfCentralDirectoryRecord: EndOfCentralDirectoryRecord
|
|
18
|
+
let zip64EndOfCentralDirectory: ZIP64EndOfCentralDirectory?
|
|
19
|
+
|
|
20
|
+
#if swift(>=5.0)
|
|
21
|
+
let memoryFile: MemoryFile?
|
|
22
|
+
|
|
23
|
+
init(
|
|
24
|
+
file: FILEPointer,
|
|
25
|
+
endOfCentralDirectoryRecord: EndOfCentralDirectoryRecord,
|
|
26
|
+
zip64EndOfCentralDirectory: ZIP64EndOfCentralDirectory? = nil,
|
|
27
|
+
memoryFile: MemoryFile? = nil)
|
|
28
|
+
{
|
|
29
|
+
self.file = file
|
|
30
|
+
self.endOfCentralDirectoryRecord = endOfCentralDirectoryRecord
|
|
31
|
+
self.zip64EndOfCentralDirectory = zip64EndOfCentralDirectory
|
|
32
|
+
self.memoryFile = memoryFile
|
|
33
|
+
}
|
|
34
|
+
#else
|
|
35
|
+
|
|
36
|
+
init(
|
|
37
|
+
file: FILEPointer,
|
|
38
|
+
endOfCentralDirectoryRecord: EndOfCentralDirectoryRecord,
|
|
39
|
+
zip64EndOfCentralDirectory: ZIP64EndOfCentralDirectory?)
|
|
40
|
+
{
|
|
41
|
+
self.file = file
|
|
42
|
+
self.endOfCentralDirectoryRecord = endOfCentralDirectoryRecord
|
|
43
|
+
self.zip64EndOfCentralDirectory = zip64EndOfCentralDirectory
|
|
44
|
+
}
|
|
45
|
+
#endif
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
static func makeBackingConfiguration(for url: URL, mode: AccessMode)
|
|
49
|
+
-> BackingConfiguration?
|
|
50
|
+
{
|
|
51
|
+
let fileManager = FileManager()
|
|
52
|
+
switch mode {
|
|
53
|
+
case .read:
|
|
54
|
+
let fileSystemRepresentation = fileManager.fileSystemRepresentation(withPath: url.path)
|
|
55
|
+
guard
|
|
56
|
+
let archiveFile = fopen(fileSystemRepresentation, "rb"),
|
|
57
|
+
let (eocdRecord, zip64EOCD) = Archive.scanForEndOfCentralDirectoryRecord(in: archiveFile) else
|
|
58
|
+
{
|
|
59
|
+
return nil
|
|
60
|
+
}
|
|
61
|
+
return BackingConfiguration(
|
|
62
|
+
file: archiveFile,
|
|
63
|
+
endOfCentralDirectoryRecord: eocdRecord,
|
|
64
|
+
zip64EndOfCentralDirectory: zip64EOCD)
|
|
65
|
+
case .create:
|
|
66
|
+
let endOfCentralDirectoryRecord = EndOfCentralDirectoryRecord(
|
|
67
|
+
numberOfDisk: 0,
|
|
68
|
+
numberOfDiskStart: 0,
|
|
69
|
+
totalNumberOfEntriesOnDisk: 0,
|
|
70
|
+
totalNumberOfEntriesInCentralDirectory: 0,
|
|
71
|
+
sizeOfCentralDirectory: 0,
|
|
72
|
+
offsetToStartOfCentralDirectory: 0,
|
|
73
|
+
zipFileCommentLength: 0,
|
|
74
|
+
zipFileCommentData: Data())
|
|
75
|
+
do {
|
|
76
|
+
try endOfCentralDirectoryRecord.data.write(to: url, options: .withoutOverwriting)
|
|
77
|
+
} catch { return nil }
|
|
78
|
+
fallthrough
|
|
79
|
+
case .update:
|
|
80
|
+
let fileSystemRepresentation = fileManager.fileSystemRepresentation(withPath: url.path)
|
|
81
|
+
guard
|
|
82
|
+
let archiveFile = fopen(fileSystemRepresentation, "rb+"),
|
|
83
|
+
let (eocdRecord, zip64EOCD) = Archive.scanForEndOfCentralDirectoryRecord(in: archiveFile) else
|
|
84
|
+
{
|
|
85
|
+
return nil
|
|
86
|
+
}
|
|
87
|
+
fseeko(archiveFile, 0, SEEK_SET)
|
|
88
|
+
return BackingConfiguration(
|
|
89
|
+
file: archiveFile,
|
|
90
|
+
endOfCentralDirectoryRecord: eocdRecord,
|
|
91
|
+
zip64EndOfCentralDirectory: zip64EOCD)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
#if swift(>=5.0)
|
|
96
|
+
static func makeBackingConfiguration(for data: Data, mode: AccessMode)
|
|
97
|
+
-> BackingConfiguration?
|
|
98
|
+
{
|
|
99
|
+
let posixMode: String
|
|
100
|
+
switch mode {
|
|
101
|
+
case .read: posixMode = "rb"
|
|
102
|
+
case .create: posixMode = "wb+"
|
|
103
|
+
case .update: posixMode = "rb+"
|
|
104
|
+
}
|
|
105
|
+
let memoryFile = MemoryFile(data: data)
|
|
106
|
+
guard let archiveFile = memoryFile.open(mode: posixMode) else { return nil }
|
|
107
|
+
|
|
108
|
+
switch mode {
|
|
109
|
+
case .read:
|
|
110
|
+
guard let (eocdRecord, zip64EOCD) = Archive.scanForEndOfCentralDirectoryRecord(in: archiveFile) else {
|
|
111
|
+
return nil
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return BackingConfiguration(
|
|
115
|
+
file: archiveFile,
|
|
116
|
+
endOfCentralDirectoryRecord: eocdRecord,
|
|
117
|
+
zip64EndOfCentralDirectory: zip64EOCD,
|
|
118
|
+
memoryFile: memoryFile)
|
|
119
|
+
case .create:
|
|
120
|
+
let endOfCentralDirectoryRecord = EndOfCentralDirectoryRecord(
|
|
121
|
+
numberOfDisk: 0,
|
|
122
|
+
numberOfDiskStart: 0,
|
|
123
|
+
totalNumberOfEntriesOnDisk: 0,
|
|
124
|
+
totalNumberOfEntriesInCentralDirectory: 0,
|
|
125
|
+
sizeOfCentralDirectory: 0,
|
|
126
|
+
offsetToStartOfCentralDirectory: 0,
|
|
127
|
+
zipFileCommentLength: 0,
|
|
128
|
+
zipFileCommentData: Data())
|
|
129
|
+
_ = endOfCentralDirectoryRecord.data.withUnsafeBytes { (buffer: UnsafeRawBufferPointer) in
|
|
130
|
+
fwrite(buffer.baseAddress, buffer.count, 1, archiveFile) // Errors handled during read
|
|
131
|
+
}
|
|
132
|
+
fallthrough
|
|
133
|
+
case .update:
|
|
134
|
+
guard let (eocdRecord, zip64EOCD) = Archive.scanForEndOfCentralDirectoryRecord(in: archiveFile) else {
|
|
135
|
+
return nil
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
fseeko(archiveFile, 0, SEEK_SET)
|
|
139
|
+
return BackingConfiguration(
|
|
140
|
+
file: archiveFile,
|
|
141
|
+
endOfCentralDirectoryRecord: eocdRecord,
|
|
142
|
+
zip64EndOfCentralDirectory: zip64EOCD,
|
|
143
|
+
memoryFile: memoryFile)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
#endif
|
|
147
|
+
}
|