lottie-ios 4.3.2 → 4.3.4

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 (39) hide show
  1. package/Lottie.xcodeproj/project.pbxproj +20 -0
  2. package/Lottie.xcodeproj/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +4 -4
  3. package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  4. package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +204 -81
  5. package/Package.swift +1 -0
  6. package/README.md +1 -1
  7. package/Sources/Private/CoreAnimation/Layers/ShapeLayer.swift +98 -30
  8. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIHostingController.swift +1 -1
  9. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIHostingView.swift +1 -3
  10. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIIntrinsicContentSizeInvalidator.swift +2 -0
  11. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUILayoutMargins.swift +2 -0
  12. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxyableView+SwiftUIView.swift +2 -0
  13. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/LayoutUtilities/MeasuringViewRepresentable.swift +2 -0
  14. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/LayoutUtilities/SwiftUIMeasurementContainer.swift +2 -0
  15. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/SwiftUIView.swift +2 -0
  16. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/UIView+SwiftUIView.swift +2 -0
  17. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/UIViewConfiguringSwiftUIView.swift +2 -0
  18. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/ViewType.swift +2 -1
  19. package/Sources/Private/EmbeddedLibraries/LRUCache/LRUCache.swift +256 -0
  20. package/Sources/Private/EmbeddedLibraries/LRUCache/README.md +24 -0
  21. package/Sources/Private/EmbeddedLibraries/README.md +14 -0
  22. package/Sources/Private/MainThread/LayerContainers/Utility/CachedImageProvider.swift +11 -5
  23. package/Sources/Private/MainThread/LayerContainers/Utility/InvertedMatteLayer.swift +0 -1
  24. package/Sources/Private/Model/Extensions/KeyedDecodingContainerExtensions.swift +5 -2
  25. package/Sources/Private/Model/LayerEffects/EffectValues/EffectValue.swift +2 -2
  26. package/Sources/Private/Model/Layers/LayerModel.swift +5 -0
  27. package/Sources/Private/Utility/Helpers/Binding+Map.swift +2 -0
  28. package/Sources/Private/Utility/Helpers/View+ValueChanged.swift +2 -0
  29. package/Sources/Public/Animation/LottieAnimationLayer.swift +5 -0
  30. package/Sources/Public/Animation/LottieAnimationView.swift +12 -1
  31. package/Sources/Public/Animation/LottieView.swift +2 -0
  32. package/Sources/Public/AnimationCache/DefaultAnimationCache.swift +10 -6
  33. package/Sources/Public/Controls/LottieButton.swift +2 -1
  34. package/Sources/Public/Controls/LottieSwitch.swift +2 -0
  35. package/Sources/Public/DotLottie/Cache/DotLottieCache.swift +8 -4
  36. package/Sources/Public/DotLottie/DotLottieFileHelpers.swift +12 -5
  37. package/Sources/Public/iOS/LottieAnimationViewBase.swift +5 -2
  38. package/lottie-ios.podspec +2 -1
  39. package/package.json +1 -1
@@ -1,6 +1,7 @@
1
1
  // Created by matthew_cheok on 11/19/21.
2
2
  // Copyright © 2021 Airbnb Inc. All rights reserved.
3
3
 
4
+ #if canImport(SwiftUI)
4
5
  import SwiftUI
5
6
 
6
7
  // MARK: - EpoxyIntrinsicContentSizeInvalidator
@@ -42,3 +43,4 @@ extension EnvironmentValues {
42
43
  private struct EpoxyIntrinsicContentSizeInvalidatorKey: EnvironmentKey {
43
44
  static let defaultValue = EpoxyIntrinsicContentSizeInvalidator(invalidate: { })
44
45
  }
46
+ #endif
@@ -1,6 +1,7 @@
1
1
  // Created by eric_horacek on 10/8/21.
2
2
  // Copyright © 2021 Airbnb Inc. All rights reserved.
3
3
 
4
+ #if canImport(SwiftUI)
4
5
  import SwiftUI
5
6
 
6
7
  // MARK: - View
@@ -49,3 +50,4 @@ private struct EpoxyLayoutMarginsPadding: ViewModifier {
49
50
  content.padding(epoxyLayoutMargins)
50
51
  }
51
52
  }
53
+ #endif
@@ -1,6 +1,7 @@
1
1
  // Created by eric_horacek on 9/13/21.
2
2
  // Copyright © 2021 Airbnb Inc. All rights reserved.
3
3
 
4
+ #if canImport(SwiftUI)
4
5
  import SwiftUI
5
6
 
6
7
  // MARK: - StyledView
@@ -170,3 +171,4 @@ extension StyledView
170
171
  }
171
172
  }
172
173
  }
174
+ #endif
@@ -1,6 +1,7 @@
1
1
  // Created by eric_horacek on 6/22/22.
2
2
  // Copyright © 2022 Airbnb Inc. All rights reserved.
3
3
 
4
+ #if canImport(SwiftUI)
4
5
  import SwiftUI
5
6
 
6
7
  // MARK: - MeasuringViewRepresentable
@@ -126,3 +127,4 @@ extension MeasuringViewRepresentable {
126
127
  #endif
127
128
  }
128
129
  #endif
130
+ #endif
@@ -1,6 +1,7 @@
1
1
  // Created by Bryn Bodayle on 1/24/22.
2
2
  // Copyright © 2022 Airbnb Inc. All rights reserved.
3
3
 
4
+ #if canImport(SwiftUI)
4
5
  import SwiftUI
5
6
 
6
7
  // MARK: - SwiftUIMeasurementContainer
@@ -450,3 +451,4 @@ extension CGSize {
450
451
  height: height == ViewType.noIntrinsicMetric ? fallback.height : height)
451
452
  }
452
453
  }
454
+ #endif
@@ -1,6 +1,7 @@
1
1
  // Created by eric_horacek on 9/8/22.
2
2
  // Copyright © 2022 Airbnb Inc. All rights reserved.
3
3
 
4
+ #if canImport(SwiftUI)
4
5
  import SwiftUI
5
6
 
6
7
  // MARK: - SwiftUIView
@@ -146,3 +147,4 @@ extension SwiftUIView {
146
147
  fileprivate(set) var storage: Storage
147
148
  }
148
149
  }
150
+ #endif
@@ -1,6 +1,7 @@
1
1
  // Created by eric_horacek on 3/3/22.
2
2
  // Copyright © 2022 Airbnb Inc. All rights reserved.
3
3
 
4
+ #if canImport(SwiftUI)
4
5
  import SwiftUI
5
6
 
6
7
  // MARK: - ViewTypeProtocol + swiftUIView
@@ -38,3 +39,4 @@ protocol ViewTypeProtocol: ViewType { }
38
39
  // MARK: - ViewType + ViewTypeProtocol
39
40
 
40
41
  extension ViewType: ViewTypeProtocol { }
42
+ #endif
@@ -1,6 +1,7 @@
1
1
  // Created by eric_horacek on 3/4/22.
2
2
  // Copyright © 2022 Airbnb Inc. All rights reserved.
3
3
 
4
+ #if canImport(SwiftUI)
4
5
  import SwiftUI
5
6
 
6
7
  // MARK: - UIViewConfiguringSwiftUIView
@@ -41,3 +42,4 @@ extension UIViewConfiguringSwiftUIView {
41
42
  return copy
42
43
  }
43
44
  }
45
+ #endif
@@ -1,8 +1,8 @@
1
1
  // Created by Cal Stephens on 6/26/23.
2
2
  // Copyright © 2023 Airbnb Inc. All rights reserved.
3
3
 
4
+ #if canImport(SwiftUI)
4
5
  import SwiftUI
5
-
6
6
  #if canImport(UIKit)
7
7
  import UIKit
8
8
 
@@ -49,3 +49,4 @@ extension ViewRepresentableType {
49
49
  typealias RepresentableViewType = NSViewType
50
50
  }
51
51
  #endif
52
+ #endif
@@ -0,0 +1,256 @@
1
+ //
2
+ // LRUCache.swift
3
+ // LRUCache
4
+ //
5
+ // Version 1.0.2
6
+ //
7
+ // Created by Nick Lockwood on 05/08/2021.
8
+ // Copyright © 2021 Nick Lockwood. All rights reserved.
9
+ //
10
+ // Distributed under the permissive MIT license
11
+ // Get the latest version from here:
12
+ //
13
+ // https://github.com/nicklockwood/LRUCache
14
+ //
15
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
16
+ // of this software and associated documentation files (the "Software"), to deal
17
+ // in the Software without restriction, including without limitation the rights
18
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19
+ // copies of the Software, and to permit persons to whom the Software is
20
+ // furnished to do so, subject to the following conditions:
21
+ //
22
+ // The above copyright notice and this permission notice shall be included in all
23
+ // copies or substantial portions of the Software.
24
+ //
25
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31
+ // SOFTWARE.
32
+ //
33
+
34
+ import Foundation
35
+
36
+ #if os(iOS) || os(tvOS)
37
+ import UIKit
38
+
39
+ /// Notification that cache should be cleared
40
+ let LRUCacheMemoryWarningNotification: NSNotification.Name =
41
+ UIApplication.didReceiveMemoryWarningNotification
42
+
43
+ #else
44
+
45
+ /// Notification that cache should be cleared
46
+ let LRUCacheMemoryWarningNotification: NSNotification.Name =
47
+ .init("LRUCacheMemoryWarningNotification")
48
+
49
+ #endif
50
+
51
+ // MARK: - LRUCache
52
+
53
+ final class LRUCache<Key: Hashable, Value> {
54
+
55
+ // MARK: Lifecycle
56
+
57
+ /// Initialize the cache with the specified `totalCostLimit` and `countLimit`
58
+ init(
59
+ totalCostLimit: Int = .max,
60
+ countLimit: Int = .max,
61
+ notificationCenter: NotificationCenter = .default)
62
+ {
63
+ self.totalCostLimit = totalCostLimit
64
+ self.countLimit = countLimit
65
+ self.notificationCenter = notificationCenter
66
+
67
+ token = notificationCenter.addObserver(
68
+ forName: LRUCacheMemoryWarningNotification,
69
+ object: nil,
70
+ queue: nil)
71
+ { [weak self] _ in
72
+ self?.removeAllValues()
73
+ }
74
+ }
75
+
76
+ deinit {
77
+ if let token = token {
78
+ notificationCenter.removeObserver(token)
79
+ }
80
+ }
81
+
82
+ // MARK: Internal
83
+
84
+ /// The current total cost of values in the cache
85
+ private(set) var totalCost = 0
86
+
87
+ /// The maximum total cost permitted
88
+ var totalCostLimit: Int {
89
+ didSet { clean() }
90
+ }
91
+
92
+ /// The maximum number of values permitted
93
+ var countLimit: Int {
94
+ didSet { clean() }
95
+ }
96
+
97
+ // MARK: Private
98
+
99
+ private var values: [Key: Container] = [:]
100
+ private unowned(unsafe) var head: Container?
101
+ private unowned(unsafe) var tail: Container?
102
+ private let lock: NSLock = .init()
103
+ private var token: AnyObject?
104
+ private let notificationCenter: NotificationCenter
105
+
106
+ }
107
+
108
+ extension LRUCache {
109
+ /// The number of values currently stored in the cache
110
+ var count: Int {
111
+ values.count
112
+ }
113
+
114
+ /// Is the cache empty?
115
+ var isEmpty: Bool {
116
+ values.isEmpty
117
+ }
118
+
119
+ /// Returns all values in the cache from oldest to newest
120
+ var allValues: [Value] {
121
+ lock.lock()
122
+ defer { lock.unlock() }
123
+ var values = [Value]()
124
+ var next = head
125
+ while let container = next {
126
+ values.append(container.value)
127
+ next = container.next
128
+ }
129
+ return values
130
+ }
131
+
132
+ /// Insert a value into the cache with optional `cost`
133
+ func setValue(_ value: Value?, forKey key: Key, cost: Int = 0) {
134
+ guard let value = value else {
135
+ removeValue(forKey: key)
136
+ return
137
+ }
138
+ lock.lock()
139
+ if let container = values[key] {
140
+ container.value = value
141
+ totalCost -= container.cost
142
+ container.cost = cost
143
+ remove(container)
144
+ append(container)
145
+ } else {
146
+ let container = Container(
147
+ value: value,
148
+ cost: cost,
149
+ key: key)
150
+ values[key] = container
151
+ append(container)
152
+ }
153
+ totalCost += cost
154
+ lock.unlock()
155
+ clean()
156
+ }
157
+
158
+ /// Remove a value from the cache and return it
159
+ @discardableResult
160
+ func removeValue(forKey key: Key) -> Value? {
161
+ lock.lock()
162
+ defer { lock.unlock() }
163
+ guard let container = values.removeValue(forKey: key) else {
164
+ return nil
165
+ }
166
+ remove(container)
167
+ totalCost -= container.cost
168
+ return container.value
169
+ }
170
+
171
+ /// Fetch a value from the cache
172
+ func value(forKey key: Key) -> Value? {
173
+ lock.lock()
174
+ defer { lock.unlock() }
175
+ if let container = values[key] {
176
+ remove(container)
177
+ append(container)
178
+ return container.value
179
+ }
180
+ return nil
181
+ }
182
+
183
+ /// Remove all values from the cache
184
+ func removeAllValues() {
185
+ lock.lock()
186
+ values.removeAll()
187
+ head = nil
188
+ tail = nil
189
+ lock.unlock()
190
+ }
191
+ }
192
+
193
+ extension LRUCache {
194
+
195
+ // MARK: Fileprivate
196
+
197
+ fileprivate final class Container {
198
+
199
+ // MARK: Lifecycle
200
+
201
+ init(value: Value, cost: Int, key: Key) {
202
+ self.value = value
203
+ self.cost = cost
204
+ self.key = key
205
+ }
206
+
207
+ // MARK: Internal
208
+
209
+ var value: Value
210
+ var cost: Int
211
+ let key: Key
212
+ unowned(unsafe) var prev: Container?
213
+ unowned(unsafe) var next: Container?
214
+
215
+ }
216
+
217
+ // MARK: Private
218
+
219
+ // Remove container from list (must be called inside lock)
220
+ private func remove(_ container: Container) {
221
+ if head === container {
222
+ head = container.next
223
+ }
224
+ if tail === container {
225
+ tail = container.prev
226
+ }
227
+ container.next?.prev = container.prev
228
+ container.prev?.next = container.next
229
+ container.next = nil
230
+ }
231
+
232
+ // Append container to list (must be called inside lock)
233
+ private func append(_ container: Container) {
234
+ assert(container.next == nil)
235
+ if head == nil {
236
+ head = container
237
+ }
238
+ container.prev = tail
239
+ tail?.next = container
240
+ tail = container
241
+ }
242
+
243
+ // Remove expired values (must be called outside lock)
244
+ private func clean() {
245
+ lock.lock()
246
+ defer { lock.unlock() }
247
+ while
248
+ totalCost > totalCostLimit || count > countLimit,
249
+ let container = head
250
+ {
251
+ remove(container)
252
+ values.removeValue(forKey: container.key)
253
+ totalCost -= container.cost
254
+ }
255
+ }
256
+ }
@@ -0,0 +1,24 @@
1
+ ## LRUCache
2
+
3
+ This directory includes the source code of the LRUCache library, from the following release:
4
+ https://github.com/nicklockwood/LRUCache/releases/tag/1.0.4
5
+
6
+ Lottie is distributed via multiple package managers (SPM, Cocoapods, Carthage, and NPM),
7
+ each with different packaging and compilation requirements.
8
+
9
+ Due to limitations of these package managers, we can't depend on / import
10
+ a separate LRUCache module / library. Instead, we include the source
11
+ directly within the Lottie library and compile everything as a single unit.
12
+
13
+ ### Update instructions
14
+
15
+ From time to time we may need to update to a more recent version of LRUCache.
16
+ When doing this, follow these steps:
17
+
18
+ 1. Download the latest release from https://github.com/nicklockwood/LRUCache
19
+ and replace the source code in this directory with the updated code.
20
+
21
+ 2. Update the URL at the top of this file to indicate what release is being used.
22
+
23
+ 3. Change all of the `public` symbols defined in this module to instead be `internal`
24
+ to prevent Lottie from exposing any EpoxyCore APIs.
@@ -5,6 +5,7 @@ This directory includes the source code of libraries that are embedded within lo
5
5
  This includes:
6
6
  - ZipFoundation (https://github.com/weichsel/ZIPFoundation)
7
7
  - EpoxyCore (https://github.com/airbnb/epoxy-ios)
8
+ - LRUCache (https://github.com/nicklockwood/LRUCache)
8
9
 
9
10
  Lottie is distributed via multiple package managers (SPM, Cocoapods, Carthage, and NPM),
10
11
  each with different packaging and compilation requirements.
@@ -25,3 +26,16 @@ When doing this, follow these steps:
25
26
 
26
27
  3. Change all of the `public` symbols defined in the module to instead be `internal`
27
28
  to prevent Lottie from exposing any APIs from other libraries.
29
+
30
+ ### Adding a new dependencies
31
+
32
+ 1. Create a subdirectory in `EmbeddedLibraries` for the new dependency.
33
+
34
+ 2. Add the dependency to the list at the top of this file.
35
+
36
+ 3. Add a `README.md` to the directory for the new library, using the same formatting as the `README.md` file used by other dependencies.
37
+
38
+ 4. Exclude the new `README.md` file from the lottie-ios package by adding it to the `exclude:` list in `Package.swift`.
39
+
40
+ 5. Change all of the `public` symbols defined in the module to instead be `internal`
41
+ to prevent Lottie from exposing any APIs from other libraries.
@@ -22,11 +22,11 @@ private final class CachedImageProvider: AnimationImageProvider {
22
22
  // MARK: Public
23
23
 
24
24
  public func imageForAsset(asset: ImageAsset) -> CGImage? {
25
- if let image = imageCache.object(forKey: asset.id as NSString) {
25
+ if let image = imageCache.value(forKey: asset.id) {
26
26
  return image
27
27
  }
28
28
  if let image = imageProvider.imageForAsset(asset: asset) {
29
- imageCache.setObject(image, forKey: asset.id as NSString)
29
+ imageCache.setValue(image, forKey: asset.id)
30
30
  return image
31
31
  }
32
32
  return nil
@@ -34,13 +34,19 @@ private final class CachedImageProvider: AnimationImageProvider {
34
34
 
35
35
  // MARK: Internal
36
36
 
37
- let imageCache: NSCache<NSString, CGImage> = .init()
38
- let imageProvider: AnimationImageProvider
39
-
40
37
  func contentsGravity(for asset: ImageAsset) -> CALayerContentsGravity {
41
38
  imageProvider.contentsGravity(for: asset)
42
39
  }
43
40
 
41
+ // MARK: Private
42
+
43
+ /// The underlying storage of this cache.
44
+ /// - We use the `LRUCache` library instead of `NSCache`, because `NSCache`
45
+ /// clears all cached values when the app is backgrounded instead of
46
+ /// only when the app receives a memory warning notification.
47
+ private var imageCache = LRUCache<String, CGImage>()
48
+ private let imageProvider: AnimationImageProvider
49
+
44
50
  }
45
51
 
46
52
  extension AnimationImageProvider {
@@ -39,7 +39,6 @@ final class InvertedMatteLayer: CALayer, CompositionLayerDelegate {
39
39
  // MARK: Internal
40
40
 
41
41
  let inputMatte: CompositionLayer?
42
- let wrapperLayer = CALayer()
43
42
 
44
43
  func frameUpdated(frame _: CGFloat) {
45
44
  setNeedsDisplay()
@@ -9,6 +9,9 @@ protocol ClassFamily: Decodable {
9
9
  /// The discriminator key.
10
10
  static var discriminator: Discriminator { get }
11
11
 
12
+ /// The "unknown" fallback case if the type discriminator could not be parsed successfully.
13
+ static var unknown: Self { get }
14
+
12
15
  /// Returns the class type of the object corresponding to the value.
13
16
  func getType() -> AnyObject.Type
14
17
  }
@@ -34,7 +37,7 @@ extension KeyedDecodingContainer {
34
37
  var tmpContainer = container
35
38
  while !container.isAtEnd {
36
39
  let typeContainer = try container.nestedContainer(keyedBy: Discriminator.self)
37
- let family: U = try typeContainer.decode(U.self, forKey: U.discriminator)
40
+ let family: U = (try? typeContainer.decodeIfPresent(U.self, forKey: U.discriminator)) ?? .unknown
38
41
  if let type = family.getType() as? T.Type {
39
42
  list.append(try tmpContainer.decode(type))
40
43
  }
@@ -60,7 +63,7 @@ extension KeyedDecodingContainer {
60
63
  var tmpContainer = container
61
64
  while !container.isAtEnd {
62
65
  let typeContainer = try container.nestedContainer(keyedBy: Discriminator.self)
63
- let family: U = try typeContainer.decode(U.self, forKey: U.discriminator)
66
+ let family: U = (try? typeContainer.decodeIfPresent(U.self, forKey: U.discriminator)) ?? .unknown
64
67
  if let type = family.getType() as? T.Type {
65
68
  list.append(try tmpContainer.decode(type))
66
69
  }
@@ -50,8 +50,8 @@ class EffectValue: Codable, DictionaryInitializable {
50
50
  }
51
51
 
52
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"
53
+ type = (try? dictionary.value(for: CodingKeys.type)).flatMap(EffectValueType.init(rawValue:)) ?? .unknown
54
+ name = (try? dictionary.value(for: CodingKeys.name)) ?? "Effect"
55
55
  }
56
56
 
57
57
  // MARK: Internal
@@ -27,6 +27,8 @@ extension LayerType: ClassFamily {
27
27
  return ShapeLayerModel.self
28
28
  case .text:
29
29
  return TextLayerModel.self
30
+ case .unknown:
31
+ return LayerModel.self
30
32
  }
31
33
  }
32
34
  }
@@ -40,6 +42,7 @@ public enum LayerType: Int, Codable {
40
42
  case null
41
43
  case shape
42
44
  case text
45
+ case unknown
43
46
 
44
47
  public init(from decoder: Decoder) throws {
45
48
  self = try LayerType(rawValue: decoder.singleValueContainer().decode(RawValue.self)) ?? .null
@@ -241,6 +244,8 @@ extension Array where Element == LayerModel {
241
244
  return try ShapeLayerModel(dictionary: dictionary)
242
245
  case .text:
243
246
  return try TextLayerModel(dictionary: dictionary)
247
+ case .unknown:
248
+ return try LayerModel(dictionary: dictionary)
244
249
  case .none:
245
250
  return nil
246
251
  }
@@ -1,6 +1,7 @@
1
1
  // Created by miguel_jimenez on 7/27/23.
2
2
  // Copyright © 2023 Airbnb Inc. All rights reserved.
3
3
 
4
+ #if canImport(SwiftUI)
4
5
  import SwiftUI
5
6
 
6
7
  @available(iOS 13.0, tvOS 13.0, macOS 10.15, *)
@@ -16,3 +17,4 @@ extension Binding {
16
17
  }
17
18
  }
18
19
  }
20
+ #endif
@@ -1,6 +1,7 @@
1
1
  // Created by miguel_jimenez on 7/26/23.
2
2
  // Copyright © 2023 Airbnb Inc. All rights reserved.
3
3
 
4
+ #if canImport(Combine) && canImport(SwiftUI)
4
5
  import Combine
5
6
  import SwiftUI
6
7
 
@@ -18,3 +19,4 @@ extension View {
18
19
  }
19
20
  }
20
21
  }
22
+ #endif
@@ -1134,11 +1134,16 @@ public class LottieAnimationLayer: CALayer {
1134
1134
  }
1135
1135
 
1136
1136
  fileprivate func makeAnimationLayer(usingEngine renderingEngine: RenderingEngineOption) {
1137
+ /// Disable the default implicit crossfade animation Core Animation creates
1138
+ /// when adding or removing sublayers.
1139
+ actions = ["sublayers": NSNull()]
1140
+
1137
1141
  /// Remove current animation if any
1138
1142
  removeCurrentAnimation()
1139
1143
 
1140
1144
  if let oldAnimation = animationLayer {
1141
1145
  oldAnimation.removeFromSuperlayer()
1146
+ rootAnimationLayer = nil
1142
1147
  }
1143
1148
 
1144
1149
  guard let animation = animation else {
@@ -368,6 +368,13 @@ open class LottieAnimationView: LottieAnimationViewBase {
368
368
 
369
369
  // MARK: Public
370
370
 
371
+ /// Whether or not transform and position changes of the view should animate alongside
372
+ /// any existing animation context.
373
+ /// - Defaults to `true` which will grab the current animation context and animate position and
374
+ /// transform changes matching the current context's curve and duration.
375
+ /// `false` will cause transform and position changes to happen unanimated
376
+ public var animateLayoutChangesWithCurrentCoreAnimationContext = true
377
+
371
378
  /// The configuration that this `LottieAnimationView` uses when playing its animation
372
379
  public var configuration: LottieConfiguration {
373
380
  get { lottieAnimationLayer.configuration }
@@ -933,7 +940,11 @@ open class LottieAnimationView: LottieAnimationViewBase {
933
940
  // If layout is changed without animation, explicitly set animation duration to 0.0
934
941
  // inside CATransaction to avoid unwanted artifacts.
935
942
  /// Check if any animation exist on the view's layer, and match it.
936
- if let key = lottieAnimationLayer.animationKeys()?.first, let animation = lottieAnimationLayer.animation(forKey: key) {
943
+ if
944
+ let key = lottieAnimationLayer.animationKeys()?.first,
945
+ let animation = lottieAnimationLayer.animation(forKey: key),
946
+ animateLayoutChangesWithCurrentCoreAnimationContext
947
+ {
937
948
  // The layout is happening within an animation block. Grab the animation data.
938
949
 
939
950
  let positionKey = "LayoutPositionAnimation"
@@ -1,6 +1,7 @@
1
1
  // Created by Bryn Bodayle on 1/20/22.
2
2
  // Copyright © 2022 Airbnb Inc. All rights reserved.
3
3
 
4
+ #if canImport(SwiftUI)
4
5
  import SwiftUI
5
6
 
6
7
  // MARK: - LottieView
@@ -480,3 +481,4 @@ public struct LottieView<Placeholder: View>: UIViewConfiguringSwiftUIView {
480
481
  loadAnimationIfNecessary()
481
482
  }
482
483
  }
484
+ #endif
@@ -12,7 +12,7 @@ import Foundation
12
12
  /// Once `cacheSize` is reached, animations can be ejected.
13
13
  /// The default size of the cache is 100.
14
14
  ///
15
- /// This cache implementation also responds to memory pressure, as it's backed by `NSCache`.
15
+ /// This cache implementation also responds to memory pressure.
16
16
  public class DefaultAnimationCache: AnimationCacheProvider, @unchecked Sendable {
17
17
 
18
18
  // MARK: Lifecycle
@@ -26,7 +26,7 @@ public class DefaultAnimationCache: AnimationCacheProvider, @unchecked Sendable
26
26
  /// The global shared Cache.
27
27
  public static let sharedCache = DefaultAnimationCache()
28
28
 
29
- /// The size of the cache.
29
+ /// The maximum number of animations that can be stored in the cache.
30
30
  public var cacheSize: Int {
31
31
  get { cache.countLimit }
32
32
  set { cache.countLimit = newValue }
@@ -34,20 +34,24 @@ public class DefaultAnimationCache: AnimationCacheProvider, @unchecked Sendable
34
34
 
35
35
  /// Clears the Cache.
36
36
  public func clearCache() {
37
- cache.removeAllObjects()
37
+ cache.removeAllValues()
38
38
  }
39
39
 
40
40
  public func animation(forKey key: String) -> LottieAnimation? {
41
- cache.object(forKey: key as NSString)
41
+ cache.value(forKey: key)
42
42
  }
43
43
 
44
44
  public func setAnimation(_ animation: LottieAnimation, forKey key: String) {
45
- cache.setObject(animation, forKey: key as NSString)
45
+ cache.setValue(animation, forKey: key)
46
46
  }
47
47
 
48
48
  // MARK: Private
49
49
 
50
50
  private static let defaultCacheCountLimit = 100
51
51
 
52
- private let cache = NSCache<NSString, LottieAnimation>()
52
+ /// The underlying storage of this cache.
53
+ /// - We use the `LRUCache` library instead of `NSCache`, because `NSCache`
54
+ /// clears all cached values when the app is backgrounded instead of
55
+ /// only when the app receives a memory warning notification.
56
+ private let cache = LRUCache<String, LottieAnimation>()
53
57
  }