lottie-ios 3.2.2 → 3.4.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.
- package/.github/actions/setup/action.yml +32 -0
- package/.github/issue_template.md +6 -23
- package/.github/workflows/main.yml +98 -0
- package/.spi.yml +6 -0
- package/.swift-version +1 -0
- package/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +7 -0
- package/.swiftpm/xcode/package.xcworkspace/xcuserdata/cal.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/.swiftpm/xcode/xcuserdata/{brandonwithrow.xcuserdatad → cal.xcuserdatad}/xcschemes/xcschememanagement.plist +25 -15
- package/.swiftpm/xcode/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
- package/Gemfile +4 -0
- package/Gemfile.lock +102 -0
- package/Lottie.xcodeproj/project.pbxproj +2003 -1972
- package/Lottie.xcodeproj/project.xcworkspace/contents.xcworkspacedata +1 -1
- package/Lottie.xcodeproj/project.xcworkspace/xcuserdata/cal.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/Lottie.xcodeproj/xcshareddata/xcschemes/{Lottie_iOS.xcscheme → Lottie (iOS).xcscheme } +15 -18
- package/Lottie.xcodeproj/xcshareddata/xcschemes/{Lottie_macOS.xcscheme → Lottie (macOS).xcscheme } +7 -20
- package/Lottie.xcodeproj/xcshareddata/xcschemes/{Lottie_tvOS.xcscheme → Lottie (tvOS).xcscheme } +5 -18
- package/Lottie.xcodeproj/xcuserdata/cal.xcuserdatad/xcschemes/xcschememanagement.plist +37 -0
- package/Lottie.xcodeproj/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +24 -0
- package/Lottie.xcworkspace/contents.xcworkspacedata +10 -0
- package/Lottie.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/Lottie.xcworkspace/xcshareddata/swiftpm/Package.resolved +115 -0
- package/Lottie.xcworkspace/xcuserdata/cal.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/{Lottie.xcodeproj/xcuserdata/brandonwithrow.xcuserdatad → Lottie.xcworkspace/xcuserdata/cal.xcuserdatad}/xcdebugger/Breakpoints_v2.xcbkptlist +2 -2
- package/Lottie.xcworkspace/xcuserdata/cal.xcuserdatad/xcdebugger/Expressions.xcexplist +153 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +39 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Expressions.xcexplist +25 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +30 -0
- package/Package.resolved +88 -0
- package/Package.swift +10 -15
- package/README.md +40 -60
- package/Rakefile +121 -0
- package/Sources/Private/CoreAnimation/Animations/CAAnimation+TimingConfiguration.swift +81 -0
- package/Sources/Private/CoreAnimation/Animations/CALayer+addAnimation.swift +448 -0
- package/Sources/Private/CoreAnimation/Animations/CombinedShapeAnimation.swift +53 -0
- package/Sources/Private/CoreAnimation/Animations/CustomPathAnimation.swift +41 -0
- package/Sources/Private/CoreAnimation/Animations/EllipseAnimation.swift +28 -0
- package/Sources/Private/CoreAnimation/Animations/GradientAnimations.swift +210 -0
- package/Sources/Private/CoreAnimation/Animations/LayerProperty.swift +229 -0
- package/Sources/Private/CoreAnimation/Animations/OpacityAnimation.swift +52 -0
- package/Sources/Private/CoreAnimation/Animations/RectangleAnimation.swift +31 -0
- package/Sources/Private/CoreAnimation/Animations/ShapeAnimation.swift +246 -0
- package/Sources/Private/CoreAnimation/Animations/StarAnimation.swift +95 -0
- package/Sources/Private/CoreAnimation/Animations/StrokeAnimation.swift +70 -0
- package/Sources/Private/CoreAnimation/Animations/TransformAnimations.swift +205 -0
- package/Sources/Private/CoreAnimation/Animations/VisibilityAnimation.swift +37 -0
- package/Sources/Private/CoreAnimation/CompatibilityTracker.swift +130 -0
- package/Sources/Private/CoreAnimation/CoreAnimationLayer.swift +492 -0
- package/Sources/Private/CoreAnimation/Extensions/CALayer+fillBounds.swift +35 -0
- package/Sources/Private/CoreAnimation/Extensions/KeyframeGroup+exactlyOneKeyframe.swift +37 -0
- package/Sources/Private/CoreAnimation/Extensions/Keyframes+combinedIfPossible.swift +73 -0
- package/Sources/Private/CoreAnimation/Layers/AnimationLayer.swift +83 -0
- package/Sources/Private/CoreAnimation/Layers/BaseAnimationLayer.swift +33 -0
- package/Sources/Private/CoreAnimation/Layers/BaseCompositionLayer.swift +87 -0
- package/Sources/Private/CoreAnimation/Layers/CALayer+setupLayerHierarchy.swift +131 -0
- package/Sources/Private/CoreAnimation/Layers/GradientRenderLayer.swift +94 -0
- package/Sources/Private/CoreAnimation/Layers/ImageLayer.swift +79 -0
- package/Sources/Private/CoreAnimation/Layers/LayerModel+makeAnimationLayer.swift +60 -0
- package/Sources/Private/CoreAnimation/Layers/MaskCompositionLayer.swift +138 -0
- package/Sources/Private/CoreAnimation/Layers/PreCompLayer.swift +140 -0
- package/Sources/Private/CoreAnimation/Layers/ShapeItemLayer.swift +302 -0
- package/Sources/Private/CoreAnimation/Layers/ShapeLayer.swift +319 -0
- package/Sources/Private/CoreAnimation/Layers/SolidLayer.swift +47 -0
- package/Sources/Private/CoreAnimation/Layers/TextLayer.swift +91 -0
- package/Sources/Private/CoreAnimation/Layers/TransformLayer.swift +11 -0
- package/Sources/Private/CoreAnimation/ValueProviderStore.swift +139 -0
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/CompLayers/CompositionLayer.swift +93 -85
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/CompLayers/ImageCompositionLayer.swift +24 -21
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/CompLayers/MaskContainerLayer.swift +75 -54
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/CompLayers/NullCompositionLayer.swift +5 -5
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/CompLayers/PreCompositionLayer.swift +61 -44
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/CompLayers/ShapeCompositionLayer.swift +22 -20
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/CompLayers/SolidCompositionLayer.swift +26 -17
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/CompLayers/TextCompositionLayer.swift +42 -36
- package/{lottie-swift/src/Private/LayerContainers/AnimationContainer.swift → Sources/Private/MainThread/LayerContainers/MainThreadAnimationLayer.swift} +200 -140
- package/Sources/Private/MainThread/LayerContainers/Utility/CachedImageProvider.swift +47 -0
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/Utility/CompositionLayersInitializer.swift +33 -24
- package/{lottie-swift/src/Private/LayerContainers/Utility/TextLayer.swift → Sources/Private/MainThread/LayerContainers/Utility/CoreTextRenderLayer.swift} +120 -106
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/Utility/InvertedMatteLayer.swift +25 -24
- package/Sources/Private/MainThread/LayerContainers/Utility/LayerFontProvider.swift +41 -0
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/Utility/LayerImageProvider.swift +20 -16
- package/Sources/Private/MainThread/LayerContainers/Utility/LayerTextProvider.swift +40 -0
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/Utility/LayerTransformNode.swift +82 -67
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Extensions/ItemsExtension.swift +4 -4
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/NodeProperty.swift +29 -21
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/Protocols/AnyNodeProperty.swift +16 -10
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/Protocols/AnyValueContainer.swift +6 -6
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/Protocols/KeypathSearchable.swift +5 -5
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/Protocols/NodePropertyMap.swift +10 -8
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/ValueContainer.swift +25 -21
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/ValueProviders/GroupInterpolator.swift +23 -17
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/ValueProviders/KeyframeInterpolator.swift +125 -108
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/ValueProviders/SingleValueProvider.swift +22 -17
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/ModifierNodes/TrimPathNode.swift +110 -79
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/OutputNodes/GroupOutputNode.swift +22 -16
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/OutputNodes/PassThroughOutputNode.swift +19 -15
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/OutputNodes/PathOutputNode.swift +22 -20
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/OutputNodes/Renderables/FillRenderer.swift +22 -23
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientFillRenderer.swift +242 -0
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientStrokeRenderer.swift +30 -24
- package/{lottie-swift/src/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientFillRenderer.swift → Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/LegacyGradientFillRenderer.swift} +93 -66
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/OutputNodes/Renderables/StrokeRenderer.swift +23 -19
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/EllipseNode.swift +139 -0
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/PolygonNode.swift +170 -0
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/PathNodes/RectNode.swift +95 -58
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/PathNodes/ShapeNode.swift +42 -29
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/PathNodes/StarNode.swift +108 -69
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderContainers/GroupNode.swift +155 -0
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/RenderNodes/FillNode.swift +51 -37
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/RenderNodes/GradientFillNode.swift +54 -42
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/RenderNodes/GradientStrokeNode.swift +65 -57
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift +84 -58
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/Text/TextAnimatorNode.swift +138 -119
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Protocols/AnimatorNode.swift +80 -79
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Protocols/PathNode.swift +5 -3
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Protocols/RenderNode.swift +22 -17
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/RenderLayers/ShapeContainerLayer.swift +27 -24
- package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/RenderLayers/ShapeRenderLayer.swift +34 -25
- package/Sources/Private/Model/Animation.swift +160 -0
- package/Sources/Private/Model/Assets/Asset.swift +43 -0
- package/{lottie-swift/src → Sources}/Private/Model/Assets/AssetLibrary.swift +40 -13
- package/Sources/Private/Model/Assets/ImageAsset.swift +112 -0
- package/{lottie-swift/src → Sources}/Private/Model/Assets/PrecompAsset.swift +20 -10
- package/Sources/Private/Model/DictionaryInitializable.swift +67 -0
- package/Sources/Private/Model/Extensions/Bundle.swift +34 -0
- package/{lottie-swift/src → Sources}/Private/Model/Extensions/KeyedDecodingContainerExtensions.swift +8 -4
- package/Sources/Private/Model/Keyframes/KeyframeData.swift +113 -0
- package/Sources/Private/Model/Keyframes/KeyframeGroup.swift +197 -0
- package/{lottie-swift/src → Sources}/Private/Model/Layers/ImageLayerModel.swift +21 -11
- package/Sources/Private/Model/Layers/LayerModel.swift +228 -0
- package/{lottie-swift/src → Sources}/Private/Model/Layers/PreCompLayerModel.swift +39 -22
- package/{lottie-swift/src → Sources}/Private/Model/Layers/ShapeLayerModel.swift +23 -12
- package/{lottie-swift/src → Sources}/Private/Model/Layers/SolidLayerModel.swift +31 -19
- package/{lottie-swift/src → Sources}/Private/Model/Layers/TextLayerModel.swift +33 -19
- package/Sources/Private/Model/Objects/DashPattern.swift +44 -0
- package/Sources/Private/Model/Objects/Marker.swift +33 -0
- package/Sources/Private/Model/Objects/Mask.swift +80 -0
- package/Sources/Private/Model/Objects/Transform.swift +179 -0
- package/Sources/Private/Model/ShapeItems/Ellipse.swift +75 -0
- package/Sources/Private/Model/ShapeItems/Fill.swift +74 -0
- package/Sources/Private/Model/ShapeItems/GradientFill.swift +124 -0
- package/Sources/Private/Model/ShapeItems/GradientStroke.swift +186 -0
- package/Sources/Private/Model/ShapeItems/Group.swift +48 -0
- package/{lottie-swift/src → Sources}/Private/Model/ShapeItems/Merge.swift +29 -11
- package/Sources/Private/Model/ShapeItems/Rectangle.swift +73 -0
- package/Sources/Private/Model/ShapeItems/Repeater.swift +136 -0
- package/Sources/Private/Model/ShapeItems/Shape.swift +56 -0
- package/Sources/Private/Model/ShapeItems/ShapeItem.swift +163 -0
- package/Sources/Private/Model/ShapeItems/ShapeTransform.swift +136 -0
- package/Sources/Private/Model/ShapeItems/Star.swift +132 -0
- package/Sources/Private/Model/ShapeItems/Stroke.swift +102 -0
- package/Sources/Private/Model/ShapeItems/Trim.swift +78 -0
- package/Sources/Private/Model/Text/Font.swift +61 -0
- package/{lottie-swift/src → Sources}/Private/Model/Text/Glyph.swift +63 -39
- package/Sources/Private/Model/Text/TextAnimator.swift +164 -0
- package/Sources/Private/Model/Text/TextDocument.swift +123 -0
- package/Sources/Private/RootAnimationLayer.swift +51 -0
- package/{lottie-swift/src → Sources}/Private/Utility/Debugging/AnimatorNodeDebugging.swift +7 -7
- package/{lottie-swift/src → Sources}/Private/Utility/Debugging/LayerDebugging.swift +72 -48
- package/Sources/Private/Utility/Debugging/TestHelpers.swift +10 -0
- package/{lottie-swift/src → Sources}/Private/Utility/Extensions/AnimationKeypathExtension.swift +69 -59
- package/Sources/Private/Utility/Extensions/BlendMode+Filter.swift +31 -0
- package/Sources/Private/Utility/Extensions/CGColor+RGB.swift +22 -0
- package/{lottie-swift/src → Sources}/Private/Utility/Extensions/CGFloatExtensions.swift +70 -67
- package/Sources/Private/Utility/Extensions/DataExtension.swift +27 -0
- package/Sources/Private/Utility/Extensions/MathKit.swift +450 -0
- package/Sources/Private/Utility/Extensions/StringExtensions.swift +38 -0
- package/{lottie-swift/src → Sources}/Private/Utility/Helpers/AnimationContext.swift +42 -16
- package/Sources/Private/Utility/Interpolatable/InterpolatableExtensions.swift +134 -0
- package/{lottie-swift/src → Sources}/Private/Utility/Interpolatable/KeyframeExtensions.swift +13 -10
- package/Sources/Private/Utility/Interpolatable/KeyframeGroup+Extensions.swift +59 -0
- package/{lottie-swift/src → Sources}/Private/Utility/Primitives/BezierPath.swift +229 -143
- package/Sources/Private/Utility/Primitives/CGPointExtension.swift +35 -0
- package/{lottie-swift/src → Sources}/Private/Utility/Primitives/ColorExtension.swift +46 -14
- package/{lottie-swift/src → Sources}/Private/Utility/Primitives/CompoundBezierPath.swift +58 -49
- package/Sources/Private/Utility/Primitives/CurveVertex.swift +184 -0
- package/Sources/Private/Utility/Primitives/PathElement.swift +75 -0
- package/Sources/Private/Utility/Primitives/UnitBezier.swift +115 -0
- package/Sources/Private/Utility/Primitives/VectorsExtensions.swift +341 -0
- package/Sources/Public/Animation/AnimationPublic.swift +266 -0
- package/Sources/Public/Animation/AnimationView.swift +1335 -0
- package/Sources/Public/Animation/AnimationViewInitializers.swift +109 -0
- package/Sources/Public/AnimationCache/AnimationCacheProvider.swift +22 -0
- package/{lottie-swift/src → Sources}/Public/AnimationCache/LRUAnimationCache.swift +22 -18
- package/Sources/Public/DynamicProperties/AnimationKeypath.swift +49 -0
- package/Sources/Public/DynamicProperties/AnyValueProvider.swift +132 -0
- package/{lottie-swift/src → Sources}/Public/DynamicProperties/ValueProviders/ColorValueProvider.swift +54 -36
- package/{lottie-swift/src → Sources}/Public/DynamicProperties/ValueProviders/FloatValueProvider.swift +40 -36
- package/Sources/Public/DynamicProperties/ValueProviders/GradientValueProvider.swift +123 -0
- package/{lottie-swift/src → Sources}/Public/DynamicProperties/ValueProviders/PointValueProvider.swift +40 -35
- package/{lottie-swift/src → Sources}/Public/DynamicProperties/ValueProviders/SizeValueProvider.swift +40 -35
- package/{lottie-swift/src → Sources}/Public/FontProvider/AnimationFontProvider.swift +15 -8
- package/Sources/Public/ImageProvider/AnimationImageProvider.swift +21 -0
- package/Sources/Public/Keyframes/Interpolatable.swift +253 -0
- package/Sources/Public/Keyframes/Keyframe.swift +92 -0
- package/Sources/Public/Logging/LottieLogger.swift +137 -0
- package/Sources/Public/LottieConfiguration.swift +143 -0
- package/{lottie-swift/src → Sources}/Public/Primitives/AnimationTime.swift +1 -1
- package/{lottie-swift/src → Sources}/Public/Primitives/Color.swift +10 -6
- package/{lottie-swift/src → Sources}/Public/Primitives/Vectors.swift +13 -12
- package/Sources/Public/TextProvider/AnimationTextProvider.swift +53 -0
- package/{lottie-swift/src → Sources}/Public/iOS/AnimatedButton.swift +42 -26
- package/{lottie-swift/src → Sources}/Public/iOS/AnimatedControl.swift +100 -91
- package/{lottie-swift/src → Sources}/Public/iOS/AnimatedSwitch.swift +128 -94
- package/{lottie-swift/src → Sources}/Public/iOS/AnimationSubview.swift +3 -3
- package/Sources/Public/iOS/AnimationViewBase.swift +78 -0
- package/{lottie-swift/src → Sources}/Public/iOS/BundleImageProvider.swift +36 -34
- package/{lottie-swift/src → Sources}/Public/iOS/Compatibility/CompatibleAnimationKeypath.swift +4 -0
- package/{lottie-swift/src → Sources}/Public/iOS/Compatibility/CompatibleAnimationView.swift +38 -29
- package/{lottie-swift/src → Sources}/Public/iOS/FilepathImageProvider.swift +24 -21
- package/{lottie-swift/src → Sources}/Public/iOS/UIColorExtension.swift +4 -4
- package/{lottie-swift/src/Public/MacOS/AnimationSubview.swift → Sources/Public/macOS/AnimationSubview.macOS.swift} +4 -5
- package/{lottie-swift/src/Public/MacOS/LottieView.swift → Sources/Public/macOS/AnimationViewBase.macOS.swift} +58 -50
- package/{lottie-swift/src/Public/MacOS/BundleImageProvider.swift → Sources/Public/macOS/BundleImageProvider.macOS.swift} +31 -32
- package/Sources/Public/macOS/FilepathImageProvider.macOS.swift +67 -0
- package/Tests/AnimationKeypathTests.swift +94 -0
- package/Tests/AutomaticEngineTests.swift +57 -0
- package/Tests/BundleTests.swift +25 -0
- package/Tests/DataURLTests.swift +64 -0
- package/Tests/ParsingTests.swift +43 -0
- package/Tests/PerformanceTests.swift +215 -0
- package/Tests/SnapshotConfiguration.swift +153 -0
- package/Tests/SnapshotTests.swift +265 -0
- package/Tests/Utils/Bundle+Module.swift +30 -0
- package/Tests/Utils/HardcodedFontProvider.swift +19 -0
- package/Tests/Utils/HardcodedImageProvider.swift +23 -0
- package/Tests/Utils/Snapshotting+presentationLayer.swift +47 -0
- package/Tests/ValueProvidersTests.swift +27 -0
- package/lottie-ios.podspec +11 -12
- package/package.json +1 -1
- package/script/test-carthage/Cartfile +1 -0
- package/script/test-carthage/Cartfile.resolved +1 -0
- package/script/test-carthage/CarthageTest/AppDelegate.swift +26 -0
- package/script/test-carthage/CarthageTest/Assets.xcassets/AccentColor.colorset/Contents.json +11 -0
- package/script/test-carthage/CarthageTest/Assets.xcassets/AppIcon.appiconset/Contents.json +98 -0
- package/script/test-carthage/CarthageTest/Assets.xcassets/Contents.json +6 -0
- package/script/test-carthage/CarthageTest/Base.lproj/LaunchScreen.storyboard +25 -0
- package/script/test-carthage/CarthageTest/Base.lproj/Main.storyboard +24 -0
- package/script/test-carthage/CarthageTest/Info.plist +66 -0
- package/script/test-carthage/CarthageTest/SceneDelegate.swift +10 -0
- package/script/test-carthage/CarthageTest/ViewController.swift +15 -0
- package/script/test-carthage/CarthageTest-macOS/AppDelegate.swift +7 -0
- package/script/test-carthage/CarthageTest-macOS/Assets.xcassets/AccentColor.colorset/Contents.json +11 -0
- package/script/test-carthage/CarthageTest-macOS/Assets.xcassets/AppIcon.appiconset/Contents.json +58 -0
- package/script/test-carthage/CarthageTest-macOS/Assets.xcassets/Contents.json +6 -0
- package/script/test-carthage/CarthageTest-macOS/Base.lproj/Main.storyboard +717 -0
- package/script/test-carthage/CarthageTest-macOS/CarthageTest_macOS.entitlements +10 -0
- package/script/test-carthage/CarthageTest-macOS/ViewController.swift +15 -0
- package/script/test-carthage/CarthageTest.xcodeproj/project.pbxproj +532 -0
- package/script/test-carthage/CarthageTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- package/script/test-carthage/CarthageTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/script/test-carthage/CarthageTest.xcodeproj/project.xcworkspace/xcuserdata/cal.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/script/test-carthage/CarthageTest.xcodeproj/project.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/script/test-carthage/CarthageTest.xcodeproj/xcuserdata/cal.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
- package/script/test-carthage/CarthageTest.xcodeproj/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +19 -0
- package/script/test-carthage/Mintfile +1 -0
- package/script/test-spm/LottieSPM.xcworkspace/contents.xcworkspacedata +7 -0
- package/script/test-spm/LottieSPM.xcworkspace/xcuserdata/cal.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/script/test-spm/Mintfile +1 -0
- package/.swiftpm/xcode/package.xcworkspace/xcuserdata/brandonwithrow.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/Lottie/Info.plist +0 -22
- package/Lottie.xcodeproj/project.xcworkspace/xcuserdata/brandonwithrow.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/Lottie.xcodeproj/xcuserdata/brandonwithrow.xcuserdatad/xcschemes/LottieLibraryMacOS.xcscheme +0 -80
- package/Lottie.xcodeproj/xcuserdata/brandonwithrow.xcuserdatad/xcschemes/xcschememanagement.plist +0 -57
- package/lottie-swift/Assets/.gitkeep +0 -0
- package/lottie-swift/src/Private/LayerContainers/Utility/LayerFontProvider.swift +0 -37
- package/lottie-swift/src/Private/LayerContainers/Utility/LayerTextProvider.swift +0 -36
- package/lottie-swift/src/Private/Model/Animation.swift +0 -107
- package/lottie-swift/src/Private/Model/Assets/Asset.swift +0 -27
- package/lottie-swift/src/Private/Model/Assets/ImageAsset.swift +0 -48
- package/lottie-swift/src/Private/Model/Keyframes/Keyframe.swift +0 -128
- package/lottie-swift/src/Private/Model/Keyframes/KeyframeGroup.swift +0 -108
- package/lottie-swift/src/Private/Model/Layers/LayerModel.swift +0 -150
- package/lottie-swift/src/Private/Model/Objects/DashPattern.swift +0 -24
- package/lottie-swift/src/Private/Model/Objects/Marker.swift +0 -23
- package/lottie-swift/src/Private/Model/Objects/Mask.swift +0 -48
- package/lottie-swift/src/Private/Model/Objects/Transform.swift +0 -105
- package/lottie-swift/src/Private/Model/ShapeItems/Ellipse.swift +0 -50
- package/lottie-swift/src/Private/Model/ShapeItems/FillI.swift +0 -49
- package/lottie-swift/src/Private/Model/ShapeItems/GradientFill.swift +0 -86
- package/lottie-swift/src/Private/Model/ShapeItems/GradientStroke.swift +0 -125
- package/lottie-swift/src/Private/Model/ShapeItems/Group.swift +0 -32
- package/lottie-swift/src/Private/Model/ShapeItems/Rectangle.swift +0 -50
- package/lottie-swift/src/Private/Model/ShapeItems/Repeater.swift +0 -80
- package/lottie-swift/src/Private/Model/ShapeItems/Shape.swift +0 -37
- package/lottie-swift/src/Private/Model/ShapeItems/ShapeItem.swift +0 -95
- package/lottie-swift/src/Private/Model/ShapeItems/ShapeTransform.swift +0 -68
- package/lottie-swift/src/Private/Model/ShapeItems/Star.swift +0 -86
- package/lottie-swift/src/Private/Model/ShapeItems/Stroke.swift +0 -67
- package/lottie-swift/src/Private/Model/ShapeItems/Trim.swift +0 -53
- package/lottie-swift/src/Private/Model/Text/Font.swift +0 -35
- package/lottie-swift/src/Private/Model/Text/TextAnimator.swift +0 -99
- package/lottie-swift/src/Private/Model/Text/TextDocument.swift +0 -70
- package/lottie-swift/src/Private/NodeRenderSystem/Nodes/PathNodes/EllipseNode.swift +0 -109
- package/lottie-swift/src/Private/NodeRenderSystem/Nodes/PathNodes/PolygonNode.swift +0 -132
- package/lottie-swift/src/Private/NodeRenderSystem/Nodes/RenderContainers/GroupNode.swift +0 -141
- package/lottie-swift/src/Private/Utility/Extensions/MathKit.swift +0 -539
- package/lottie-swift/src/Private/Utility/Extensions/StringExtensions.swift +0 -32
- package/lottie-swift/src/Private/Utility/Interpolatable/Interpolatable.swift +0 -18
- package/lottie-swift/src/Private/Utility/Interpolatable/InterpolatableExtensions.swift +0 -170
- package/lottie-swift/src/Private/Utility/Primitives/CurveVertex.swift +0 -177
- package/lottie-swift/src/Private/Utility/Primitives/PathElement.swift +0 -68
- package/lottie-swift/src/Private/Utility/Primitives/VectorsExtensions.swift +0 -218
- package/lottie-swift/src/Public/Animation/AnimationPublic.swift +0 -196
- package/lottie-swift/src/Public/Animation/AnimationView.swift +0 -1006
- package/lottie-swift/src/Public/Animation/AnimationViewInitializers.swift +0 -83
- package/lottie-swift/src/Public/AnimationCache/AnimationCacheProvider.swift +0 -24
- package/lottie-swift/src/Public/DynamicProperties/AnimationKeypath.swift +0 -46
- package/lottie-swift/src/Public/DynamicProperties/AnyValueProvider.swift +0 -29
- package/lottie-swift/src/Public/DynamicProperties/ValueProviders/GradientValueProvider.swift +0 -114
- package/lottie-swift/src/Public/ImageProvider/AnimationImageProvider.swift +0 -23
- package/lottie-swift/src/Public/MacOS/FilepathImageProvider.swift +0 -67
- package/lottie-swift/src/Public/TextProvider/AnimationTextProvider.swift +0 -39
- package/lottie-swift/src/Public/iOS/LottieView.swift +0 -62
- package/lottie-swift-testing.podspec +0 -32
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
//
|
|
2
|
+
// FilepathImageProvider.swift
|
|
3
|
+
// lottie-swift
|
|
4
|
+
//
|
|
5
|
+
// Created by Brandon Withrow on 2/1/19.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#if os(macOS)
|
|
9
|
+
import AppKit
|
|
10
|
+
|
|
11
|
+
/// An `AnimationImageProvider` that provides images by name from a specific filepath.
|
|
12
|
+
public class FilepathImageProvider: AnimationImageProvider {
|
|
13
|
+
|
|
14
|
+
// MARK: Lifecycle
|
|
15
|
+
|
|
16
|
+
/// Initializes an image provider with a specific filepath.
|
|
17
|
+
///
|
|
18
|
+
/// - Parameter filepath: The absolute filepath containing the images.
|
|
19
|
+
///
|
|
20
|
+
public init(filepath: String) {
|
|
21
|
+
self.filepath = URL(fileURLWithPath: filepath)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public init(filepath: URL) {
|
|
25
|
+
self.filepath = filepath
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// MARK: Public
|
|
29
|
+
|
|
30
|
+
public func imageForAsset(asset: ImageAsset) -> CGImage? {
|
|
31
|
+
if
|
|
32
|
+
asset.name.hasPrefix("data:"),
|
|
33
|
+
let url = URL(string: asset.name),
|
|
34
|
+
let data = try? Data(contentsOf: url),
|
|
35
|
+
let image = NSImage(data: data)
|
|
36
|
+
{
|
|
37
|
+
return image.lottie_CGImage
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let directPath = filepath.appendingPathComponent(asset.name).path
|
|
41
|
+
if FileManager.default.fileExists(atPath: directPath) {
|
|
42
|
+
return NSImage(contentsOfFile: directPath)?.lottie_CGImage
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let pathWithDirectory = filepath.appendingPathComponent(asset.directory).appendingPathComponent(asset.name).path
|
|
46
|
+
if FileManager.default.fileExists(atPath: pathWithDirectory) {
|
|
47
|
+
return NSImage(contentsOfFile: pathWithDirectory)?.lottie_CGImage
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
LottieLogger.shared.assertionFailure("Could not find image \"\(asset.name)\" in bundle")
|
|
51
|
+
return nil
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// MARK: Internal
|
|
55
|
+
|
|
56
|
+
let filepath: URL
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
extension NSImage {
|
|
60
|
+
@nonobjc
|
|
61
|
+
var lottie_CGImage: CGImage? {
|
|
62
|
+
guard let imageData = tiffRepresentation else { return nil }
|
|
63
|
+
guard let sourceData = CGImageSourceCreateWithData(imageData as CFData, nil) else { return nil }
|
|
64
|
+
return CGImageSourceCreateImageAtIndex(sourceData, 0, nil)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
#endif
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// Created by Cal Stephens on 1/24/22.
|
|
2
|
+
// Copyright © 2022 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
import SnapshotTesting
|
|
5
|
+
import XCTest
|
|
6
|
+
|
|
7
|
+
@testable import Lottie
|
|
8
|
+
|
|
9
|
+
final class AnimationKeypathTests: XCTestCase {
|
|
10
|
+
|
|
11
|
+
// MARK: Internal
|
|
12
|
+
|
|
13
|
+
func testKeypathMatches() {
|
|
14
|
+
let keypath = AnimationKeypath(keypath: "Layer.Shape Group.Stroke 1.Color")
|
|
15
|
+
|
|
16
|
+
XCTAssertTrue(keypath.matches("Layer.Shape Group.Stroke 1.Color"))
|
|
17
|
+
XCTAssertTrue(keypath.matches("**.Color"))
|
|
18
|
+
XCTAssertTrue(keypath.matches("**.Stroke 1.Color"))
|
|
19
|
+
XCTAssertTrue(keypath.matches("**.Shape Group.Stroke 1.Color"))
|
|
20
|
+
XCTAssertTrue(keypath.matches("Layer.**.Color"))
|
|
21
|
+
XCTAssertTrue(keypath.matches("Layer.Shape Group.*.Color"))
|
|
22
|
+
XCTAssertTrue(keypath.matches("Layer.*.*.Color"))
|
|
23
|
+
|
|
24
|
+
XCTAssertFalse(keypath.matches("Layer.*.Color"))
|
|
25
|
+
XCTAssertFalse(keypath.matches("**.Layer.Shape Group.Stroke 1.Color"))
|
|
26
|
+
XCTAssertFalse(keypath.matches("*.Layer.Shape Group.Stroke 1.Color"))
|
|
27
|
+
XCTAssertFalse(keypath.matches("Layer.Shape Group.Stroke 1.Color.*"))
|
|
28
|
+
XCTAssertFalse(keypath.matches("Layer.Shape Group.Stroke 1.Color.**"))
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
func testLayerForKeypath() {
|
|
32
|
+
let animationView = AnimationView(
|
|
33
|
+
animation: Samples.animation(named: "Boat_Loader"),
|
|
34
|
+
configuration: LottieConfiguration(renderingEngine: .mainThread))
|
|
35
|
+
|
|
36
|
+
XCTAssertNotNil(animationView.animationLayer?.layer(for: "Success.FishComplete.Fish1Tail 7"))
|
|
37
|
+
XCTAssertNotNil(animationView.animationLayer?.layer(for: "Success.FishComplete"))
|
|
38
|
+
XCTAssertNotNil(animationView.animationLayer?.layer(for: "Success"))
|
|
39
|
+
XCTAssertNotNil(animationView.animationLayer?.layer(for: "Success.*.Fish1Tail 7"))
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
func testMainThreadEngineKeypathLogging() {
|
|
43
|
+
snapshotHierarchyKeypaths(
|
|
44
|
+
animationName: "Switch",
|
|
45
|
+
configuration: LottieConfiguration(renderingEngine: .mainThread))
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
func testCoreAnimationEngineKeypathLogging() {
|
|
49
|
+
snapshotHierarchyKeypaths(
|
|
50
|
+
animationName: "Switch",
|
|
51
|
+
configuration: LottieConfiguration(renderingEngine: .coreAnimation))
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/// The Core Animation engine supports a subset of the keypaths supported by the Main Thread engine.
|
|
55
|
+
/// All keypaths that are supported in the Core Animation engine should also be supported by the Main Thread engine.
|
|
56
|
+
func testCoreAnimationEngineKeypathCompatibility() {
|
|
57
|
+
let mainThreadKeypaths = Set(hierarchyKeypaths(animationName: "Switch", configuration: .init(renderingEngine: .mainThread)))
|
|
58
|
+
let coreAnimationKeypaths = hierarchyKeypaths(animationName: "Switch", configuration: .init(renderingEngine: .coreAnimation))
|
|
59
|
+
|
|
60
|
+
for coreAnimationKeypath in coreAnimationKeypaths {
|
|
61
|
+
XCTAssert(mainThreadKeypaths.contains(coreAnimationKeypath))
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// MARK: Private
|
|
66
|
+
|
|
67
|
+
private func snapshotHierarchyKeypaths(
|
|
68
|
+
animationName: String,
|
|
69
|
+
configuration: LottieConfiguration,
|
|
70
|
+
function: String = #function,
|
|
71
|
+
line: UInt = #line)
|
|
72
|
+
{
|
|
73
|
+
let hierarchyKeypaths = hierarchyKeypaths(animationName: animationName, configuration: configuration)
|
|
74
|
+
|
|
75
|
+
assertSnapshot(
|
|
76
|
+
matching: hierarchyKeypaths.sorted().joined(separator: "\n"),
|
|
77
|
+
as: .description,
|
|
78
|
+
named: animationName,
|
|
79
|
+
testName: function,
|
|
80
|
+
line: line)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private func hierarchyKeypaths(animationName: String, configuration: LottieConfiguration) -> [String] {
|
|
84
|
+
var printedMessages = [String]()
|
|
85
|
+
let logger = LottieLogger(info: { message in
|
|
86
|
+
printedMessages.append(message())
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
let animationView = SnapshotConfiguration.makeAnimationView(for: animationName, configuration: configuration, logger: logger)
|
|
90
|
+
animationView?.logHierarchyKeypaths()
|
|
91
|
+
return Array(printedMessages[1...])
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// Created by Cal Stephens on 5/2/22.
|
|
2
|
+
// Copyright © 2022 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
import SnapshotTesting
|
|
5
|
+
import UIKit
|
|
6
|
+
import XCTest
|
|
7
|
+
|
|
8
|
+
@testable import Lottie
|
|
9
|
+
|
|
10
|
+
final class AutomaticEngineTests: XCTestCase {
|
|
11
|
+
|
|
12
|
+
/// Snapshot tests for whether or not each sample animation supports the Core Animation engine
|
|
13
|
+
func testAutomaticEngineDetection() throws {
|
|
14
|
+
for sampleAnimationName in Samples.sampleAnimationNames {
|
|
15
|
+
guard let animation = Samples.animation(named: sampleAnimationName) else { continue }
|
|
16
|
+
|
|
17
|
+
var compatibilityIssues = [CompatibilityIssue]()
|
|
18
|
+
|
|
19
|
+
let animationLayer = try XCTUnwrap(CoreAnimationLayer(
|
|
20
|
+
animation: animation,
|
|
21
|
+
imageProvider: BundleImageProvider(bundle: Bundle.main, searchPath: nil),
|
|
22
|
+
fontProvider: DefaultFontProvider(),
|
|
23
|
+
compatibilityTrackerMode: .track,
|
|
24
|
+
logger: .shared))
|
|
25
|
+
|
|
26
|
+
animationLayer.didSetUpAnimation = { issues in
|
|
27
|
+
compatibilityIssues = issues
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
animationLayer.bounds = CGRect(origin: .zero, size: animation.size)
|
|
31
|
+
animationLayer.layoutIfNeeded()
|
|
32
|
+
animationLayer.display()
|
|
33
|
+
|
|
34
|
+
let compatibilityReport: String
|
|
35
|
+
if compatibilityIssues.isEmpty {
|
|
36
|
+
compatibilityReport = "Supports Core Animation engine"
|
|
37
|
+
} else {
|
|
38
|
+
compatibilityReport = "Does not support Core Animation engine. Encountered compatibility issues:\n"
|
|
39
|
+
+ compatibilityIssues.map { $0.description }.joined(separator: "\n")
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
assertSnapshot(
|
|
43
|
+
matching: compatibilityReport,
|
|
44
|
+
as: .description,
|
|
45
|
+
named: sampleAnimationName)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
override func setUp() {
|
|
50
|
+
LottieLogger.shared = .printToConsole
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
override func tearDown() {
|
|
54
|
+
LottieLogger.shared = LottieLogger()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//
|
|
2
|
+
// BundleTests.swift
|
|
3
|
+
// LottieTests
|
|
4
|
+
//
|
|
5
|
+
// Created by Marcelo Fabri on 5/5/22.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import XCTest
|
|
9
|
+
|
|
10
|
+
@testable import Lottie
|
|
11
|
+
|
|
12
|
+
final class BundleTests: XCTestCase {
|
|
13
|
+
|
|
14
|
+
var bundle: Bundle { .module }
|
|
15
|
+
|
|
16
|
+
func testGetAnimationDataWithSuffix() throws {
|
|
17
|
+
let data = try bundle.getAnimationData("HamburgerArrow.json", subdirectory: "Samples")
|
|
18
|
+
XCTAssertNotNil(data)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
func testGetAnimationDataWithoutSuffix() throws {
|
|
22
|
+
let data = try bundle.getAnimationData("HamburgerArrow", subdirectory: "Samples")
|
|
23
|
+
XCTAssertNotNil(data)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// Created by Nicholas Mata on 2/23/22.
|
|
2
|
+
// Copyright © 2022 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
import Foundation
|
|
5
|
+
import XCTest
|
|
6
|
+
|
|
7
|
+
@testable import Lottie
|
|
8
|
+
|
|
9
|
+
// MARK: - DataURLTests
|
|
10
|
+
|
|
11
|
+
// Tests are based on implementation found here
|
|
12
|
+
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs
|
|
13
|
+
final class DataURLTests: XCTestCase {
|
|
14
|
+
|
|
15
|
+
let red5x5 =
|
|
16
|
+
"%89%50%4e%47%0d%0a%1a%0a%00%00%00%0d%49%48%44%52%00%00%00%05%00%00%00%05%08%06%00%00%00%8d%6f%26%e5%00%00%00%12%49%44%41%54%78%da%63%fc%cf%c0%00%44%a8%80%91%06%82%00%5c%65%09%fc%86%fe%00%b0%00%00%00%00%49%45%4e%44%ae%42%60%82"
|
|
17
|
+
|
|
18
|
+
let red5x5Base64 = "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAEklEQVR42mP8z8AARKiAkQaCAFxlCfyG/gCwAAAAAElFTkSuQmCC"
|
|
19
|
+
|
|
20
|
+
func testValidDataURL() {
|
|
21
|
+
let dataString = "data:image/png;base64,\(red5x5Base64)"
|
|
22
|
+
|
|
23
|
+
let data = Data(dataString: dataString)
|
|
24
|
+
XCTAssertNotNil(data, "Data should not be nil if valid base64 string")
|
|
25
|
+
let image = UIImage(data: data!)
|
|
26
|
+
XCTAssertNotNil(image, "Should be valid image")
|
|
27
|
+
|
|
28
|
+
// Since legacy options will print nil host logs
|
|
29
|
+
let legacyData = Data(dataString: dataString, options: .legacy)
|
|
30
|
+
XCTAssertNotNil(legacyData, "Data should not be nil if valid base64 string")
|
|
31
|
+
let legacyImage = UIImage(data: legacyData!)
|
|
32
|
+
XCTAssertNotNil(legacyImage, "Should be valid image")
|
|
33
|
+
|
|
34
|
+
XCTAssertEqual(data, legacyData)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
func testValidDataURLWithoutBase64() {
|
|
38
|
+
let dataString = "data:image/png,\(red5x5)"
|
|
39
|
+
// Since ;base64 is missing still prints nil host warnings.
|
|
40
|
+
// If we can figure out how to turn red5x5 into Data properly
|
|
41
|
+
// like Data(contentsOf:) does then we can avoid the warning.
|
|
42
|
+
let data = Data(dataString: dataString)
|
|
43
|
+
XCTAssertNotNil(data, "Data should not be nil since format is valid data URL")
|
|
44
|
+
|
|
45
|
+
let image = UIImage(data: data!)
|
|
46
|
+
XCTAssertNotNil(image, "Should be valid image. Since missing ';base64' the data is valid just not base64 encoded")
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
func testInvalidDataURLWithBadBase64() {
|
|
50
|
+
let dataString = "data:image/png;base64,INVALIDBASE64"
|
|
51
|
+
|
|
52
|
+
let data = Data(dataString: dataString)
|
|
53
|
+
let legacyData = Data(dataString: dataString, options: .legacy)
|
|
54
|
+
XCTAssertNil(data, "Data should be nil because 'INVALIDBASE64' is not valid base64 string.")
|
|
55
|
+
XCTAssertNil(legacyData, "Data should be nil because 'INVALIDBASE64' is not valid base64 string.")
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
func testInvalidDataURL() {
|
|
59
|
+
let dataString = "ImageAssetName"
|
|
60
|
+
|
|
61
|
+
let data = Data(dataString: dataString)
|
|
62
|
+
XCTAssertNil(data, "Data should be nil as valid Data URL starts with 'data:'")
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
//
|
|
2
|
+
// ParsingTests.swift
|
|
3
|
+
// Lottie
|
|
4
|
+
//
|
|
5
|
+
// Created by Marcelo Fabri on 5/5/22.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import Difference
|
|
9
|
+
import Foundation
|
|
10
|
+
import Lottie
|
|
11
|
+
import XCTest
|
|
12
|
+
|
|
13
|
+
// MARK: - ParsingTests
|
|
14
|
+
|
|
15
|
+
final class ParsingTests: XCTestCase {
|
|
16
|
+
|
|
17
|
+
func testParsingIsTheSameForBothImplementations() throws {
|
|
18
|
+
for url in Samples.sampleAnimationURLs {
|
|
19
|
+
do {
|
|
20
|
+
let data = try Data(contentsOf: url)
|
|
21
|
+
let codableAnimation = try Animation.from(data: data, strategy: .codable)
|
|
22
|
+
let dictAnimation = try Animation.from(data: data, strategy: .dictionaryBased)
|
|
23
|
+
|
|
24
|
+
XCTAssertNoDiff(codableAnimation, dictAnimation)
|
|
25
|
+
} catch {
|
|
26
|
+
XCTFail("Error for \(url.lastPathComponent): \(error)")
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
func XCTAssertNoDiff<T>(
|
|
33
|
+
_ expected: @autoclosure () throws -> T,
|
|
34
|
+
_ received: @autoclosure () throws -> T,
|
|
35
|
+
file: StaticString = #filePath,
|
|
36
|
+
line: UInt = #line) rethrows
|
|
37
|
+
{
|
|
38
|
+
let expected = try expected()
|
|
39
|
+
let received = try received()
|
|
40
|
+
let diff = diff(expected, received)
|
|
41
|
+
let isEqual = diff.isEmpty || diff.allSatisfy(\.isEmpty)
|
|
42
|
+
XCTAssertTrue(isEqual, "Found difference for \n" + diff.joined(separator: ", "), file: file, line: line)
|
|
43
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
// Created by Cal Stephens on 2/14/22.
|
|
2
|
+
// Copyright © 2022 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
import Foundation
|
|
5
|
+
import XCTest
|
|
6
|
+
|
|
7
|
+
@testable import Lottie
|
|
8
|
+
|
|
9
|
+
// MARK: - PerformanceTests
|
|
10
|
+
|
|
11
|
+
final class PerformanceTests: XCTestCase {
|
|
12
|
+
|
|
13
|
+
// MARK: Internal
|
|
14
|
+
|
|
15
|
+
func testAnimationViewSetup_simpleAnimation() {
|
|
16
|
+
// Compare the performance of displaying this simple animation in the two animation engines
|
|
17
|
+
let ratio = compareEngineSetupPerformance(
|
|
18
|
+
of: .mainThread,
|
|
19
|
+
with: .coreAnimation,
|
|
20
|
+
for: simpleAnimation,
|
|
21
|
+
iterations: 2000)
|
|
22
|
+
|
|
23
|
+
// This is basically a snapshot test for the performance of the Core Animation engine
|
|
24
|
+
// compared to the Main Thread engine. Currently, the Core Animation engine is
|
|
25
|
+
// about the same speed as the Main Thread engine in this example.
|
|
26
|
+
XCTAssertEqual(ratio, 1.0, accuracy: 0.35)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
func testAnimationViewSetup_complexAnimation() {
|
|
30
|
+
// Compare the performance of displaying this simple animation in the two animation engines
|
|
31
|
+
let ratio = compareEngineSetupPerformance(
|
|
32
|
+
of: .mainThread,
|
|
33
|
+
with: .coreAnimation,
|
|
34
|
+
for: complexAnimation,
|
|
35
|
+
iterations: 500)
|
|
36
|
+
|
|
37
|
+
// The Core Animation engine is currently about 1.5x slower than the
|
|
38
|
+
// Main Thread engine in this example.
|
|
39
|
+
XCTAssertEqual(ratio, 1.5, accuracy: 0.6)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
func testAnimationViewSetup_automaticEngine() {
|
|
43
|
+
// Compare the performance of displaying this simple animation with the core animation engine
|
|
44
|
+
// vs with the automatic engine option
|
|
45
|
+
let ratio = compareEngineSetupPerformance(
|
|
46
|
+
of: .coreAnimation,
|
|
47
|
+
with: .automatic,
|
|
48
|
+
for: simpleAnimation,
|
|
49
|
+
iterations: 2000)
|
|
50
|
+
|
|
51
|
+
// The automatic engine option should have the same performance as the core animation engine,
|
|
52
|
+
// when rendering an animation supported by the CA engine.
|
|
53
|
+
XCTAssertEqual(ratio, 1.0, accuracy: 0.2)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
func testAnimationViewScrubbing_simpleAnimation() {
|
|
57
|
+
let ratio = compareEngineScrubbingPerformance(for: simpleAnimation, iterations: 2000)
|
|
58
|
+
XCTAssertEqual(ratio, 0.01, accuracy: 0.01)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
func testAnimationViewScrubbing_complexAnimation() {
|
|
62
|
+
let ratio = compareEngineScrubbingPerformance(for: complexAnimation, iterations: 2000)
|
|
63
|
+
XCTAssertEqual(ratio, 0.01, accuracy: 0.01)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
func testParsing_simpleAnimation() throws {
|
|
67
|
+
let data = try XCTUnwrap(Bundle.module.getAnimationData("loading_dots_1", subdirectory: "Samples/LottieFiles"))
|
|
68
|
+
let ratio = try compareDeserializationPerformance(data: data, iterations: 2000)
|
|
69
|
+
XCTAssertEqual(ratio, 2, accuracy: 0.55)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
func testParsing_complexAnimation() throws {
|
|
73
|
+
let data = try XCTUnwrap(Bundle.module.getAnimationData("LottieLogo2", subdirectory: "Samples"))
|
|
74
|
+
let ratio = try compareDeserializationPerformance(data: data, iterations: 500)
|
|
75
|
+
XCTAssertEqual(ratio, 1.7, accuracy: 0.6)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
override func setUp() {
|
|
79
|
+
TestHelpers.performanceTestsAreRunning = true
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
override func tearDown() {
|
|
83
|
+
TestHelpers.performanceTestsAreRunning = false
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// MARK: Private
|
|
87
|
+
|
|
88
|
+
private let simpleAnimation = Animation.named(
|
|
89
|
+
"loading_dots_1",
|
|
90
|
+
bundle: .module,
|
|
91
|
+
subdirectory: "Samples/LottieFiles")!
|
|
92
|
+
|
|
93
|
+
private let complexAnimation = Animation.named(
|
|
94
|
+
"LottieLogo2",
|
|
95
|
+
bundle: .module,
|
|
96
|
+
subdirectory: "Samples")!
|
|
97
|
+
|
|
98
|
+
/// Compares initializing the given animation with the two given engines,
|
|
99
|
+
/// and returns the ratio of how much slower engine B is than engine A.
|
|
100
|
+
private func compareEngineSetupPerformance(
|
|
101
|
+
of engineA: RenderingEngineOption,
|
|
102
|
+
with engineB: RenderingEngineOption,
|
|
103
|
+
for animation: Animation,
|
|
104
|
+
iterations: Int)
|
|
105
|
+
-> Double
|
|
106
|
+
{
|
|
107
|
+
let engineAPerformance = measurePerformance {
|
|
108
|
+
for _ in 0..<iterations {
|
|
109
|
+
// Each animation setup needs to be wrapped in its own `CATransaction`
|
|
110
|
+
// in order for the layers to be deallocated immediately. Otherwise
|
|
111
|
+
// the layers aren't deallocated until the end of the test run,
|
|
112
|
+
// which causes memory usage to grow unbounded.
|
|
113
|
+
CATransaction.begin()
|
|
114
|
+
let animationView = setupAnimationView(with: animation, configuration: .init(renderingEngine: engineA))
|
|
115
|
+
// Call `display()` on the layer to make sure any pending setup occurs immediately
|
|
116
|
+
animationView.animationLayer!.display()
|
|
117
|
+
CATransaction.commit()
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
LottieLogger.shared.info("\(engineA) engine took \(engineAPerformance) seconds")
|
|
122
|
+
|
|
123
|
+
let engineBPerformance = measurePerformance {
|
|
124
|
+
for _ in 0..<iterations {
|
|
125
|
+
// Each animation setup needs to be wrapped in its own `CATransaction`
|
|
126
|
+
// in order for the layers to be deallocated immediately. Otherwise
|
|
127
|
+
// the layers aren't deallocated until the end of the test run,
|
|
128
|
+
// which causes memory usage to grow unbounded.
|
|
129
|
+
CATransaction.begin()
|
|
130
|
+
let animationView = setupAnimationView(with: animation, configuration: .init(renderingEngine: engineB))
|
|
131
|
+
// Call `display()` on the layer to make sure any pending setup occurs immediately
|
|
132
|
+
animationView.animationLayer!.display()
|
|
133
|
+
CATransaction.commit()
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
LottieLogger.shared.info("\(engineB) engine took \(engineBPerformance) seconds")
|
|
138
|
+
|
|
139
|
+
let ratio = engineBPerformance / engineAPerformance
|
|
140
|
+
LottieLogger.shared.info("\(engineB) engine took \(ratio)x as long as \(engineA) engine")
|
|
141
|
+
return ratio
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/// Compares performance of scrubbing the given animation with both the Main Thread and Core Animation engine,
|
|
145
|
+
/// and returns the ratio of how much slower the Core Animation is than the Main Thread engine
|
|
146
|
+
private func compareEngineScrubbingPerformance(for animation: Animation, iterations: Int) -> Double {
|
|
147
|
+
let mainThreadAnimationView = setupAnimationView(with: animation, configuration: .init(renderingEngine: .mainThread))
|
|
148
|
+
let mainThreadEnginePerformance = measurePerformance {
|
|
149
|
+
for i in 0..<iterations {
|
|
150
|
+
mainThreadAnimationView.currentProgress = Double(i) / Double(iterations)
|
|
151
|
+
|
|
152
|
+
// Since the main thread engine only re-renders in `display()`, which is normally called by Core Animation,
|
|
153
|
+
// we have to invoke it manually. This triggers the same code path that would happen when scrubbing in
|
|
154
|
+
// a real use case.
|
|
155
|
+
mainThreadAnimationView.animationLayer!.display()
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
LottieLogger.shared.info("Main thread engine took \(mainThreadEnginePerformance) seconds")
|
|
160
|
+
|
|
161
|
+
let coreAnimationView = setupAnimationView(with: animation, configuration: .init(renderingEngine: .coreAnimation))
|
|
162
|
+
let coreAnimationEnginePerformance = measurePerformance {
|
|
163
|
+
for i in 0..<iterations {
|
|
164
|
+
coreAnimationView.currentProgress = Double(i) / Double(iterations)
|
|
165
|
+
|
|
166
|
+
// Call `display()` on the layer to make sure any pending setup occurs immediately
|
|
167
|
+
coreAnimationView.animationLayer!.display()
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
LottieLogger.shared.info("Core Animation engine took \(coreAnimationEnginePerformance) seconds")
|
|
172
|
+
|
|
173
|
+
let ratio = coreAnimationEnginePerformance / mainThreadEnginePerformance
|
|
174
|
+
LottieLogger.shared.info("Core Animation engine took \(ratio)x as long as the Main Thread engine")
|
|
175
|
+
return ratio
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private func compareDeserializationPerformance(data: Data, iterations: Int) throws -> Double {
|
|
179
|
+
let codablePerformance = try measurePerformance {
|
|
180
|
+
for _ in 0..<iterations {
|
|
181
|
+
_ = try Animation.from(data: data, strategy: .codable)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
LottieLogger.shared.info("Codable deserialization took \(codablePerformance) seconds")
|
|
186
|
+
|
|
187
|
+
let dictPerformance = try measurePerformance {
|
|
188
|
+
for _ in 0..<iterations {
|
|
189
|
+
_ = try Animation.from(data: data, strategy: .dictionaryBased)
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
LottieLogger.shared.info("DictionaryBased deserialization took \(dictPerformance) seconds")
|
|
194
|
+
let ratio = codablePerformance / dictPerformance
|
|
195
|
+
LottieLogger.shared.info("Codable deserialization took \(ratio)x as long as DictionaryBased")
|
|
196
|
+
return ratio
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
@discardableResult
|
|
200
|
+
private func setupAnimationView(with animation: Animation, configuration: LottieConfiguration) -> AnimationView {
|
|
201
|
+
let animationView = AnimationView(animation: animation, configuration: configuration)
|
|
202
|
+
animationView.frame.size = CGSize(width: animation.width, height: animation.height)
|
|
203
|
+
animationView.layoutIfNeeded()
|
|
204
|
+
return animationView
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
private func measurePerformance(_ block : () throws -> Void) rethrows -> TimeInterval {
|
|
208
|
+
let start = DispatchTime.now()
|
|
209
|
+
try block()
|
|
210
|
+
let end = DispatchTime.now()
|
|
211
|
+
let nanoTime = end.uptimeNanoseconds - start.uptimeNanoseconds
|
|
212
|
+
return Double(nanoTime) / 1_000_000_000
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
}
|