lottie-ios 4.2.0 → 4.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. package/.github/workflows/main.yml +117 -11
  2. package/Gemfile +1 -0
  3. package/Gemfile.lock +5 -0
  4. package/Lottie.xcodeproj/project.pbxproj +1546 -194
  5. package/Lottie.xcodeproj/project.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  6. package/Lottie.xcodeproj/xcshareddata/xcschemes/Lottie (iOS).xcscheme +1 -1
  7. package/Lottie.xcodeproj/xcshareddata/xcschemes/Lottie (macOS).xcscheme +1 -1
  8. package/Lottie.xcodeproj/xcshareddata/xcschemes/Lottie (tvOS).xcscheme +4 -5
  9. package/Lottie.xcodeproj/xcshareddata/xcschemes/Lottie (visionOS).xcscheme +66 -0
  10. package/Lottie.xcodeproj/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +18 -0
  11. package/Lottie.xcworkspace/xcshareddata/swiftpm/Package.resolved +2 -11
  12. package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  13. package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +374 -116
  14. package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Expressions.xcexplist +16 -5
  15. package/Package.resolved +2 -2
  16. package/Package.swift +16 -9
  17. package/README.md +2 -2
  18. package/Rakefile +122 -25
  19. package/Sources/Private/CoreAnimation/Animations/CALayer+addAnimation.swift +3 -4
  20. package/Sources/Private/CoreAnimation/Animations/DropShadowAnimation.swift +160 -0
  21. package/Sources/Private/CoreAnimation/Animations/LayerProperty.swift +83 -10
  22. package/Sources/Private/CoreAnimation/Animations/TransformAnimations.swift +85 -79
  23. package/Sources/Private/CoreAnimation/CoreAnimationLayer.swift +11 -7
  24. package/Sources/Private/CoreAnimation/Extensions/Keyframes+combined.swift +67 -6
  25. package/Sources/Private/CoreAnimation/Layers/AnimationLayer.swift +21 -2
  26. package/Sources/Private/CoreAnimation/Layers/BaseCompositionLayer.swift +9 -0
  27. package/Sources/Private/CoreAnimation/Layers/ImageLayer.swift +1 -0
  28. package/Sources/Private/CoreAnimation/Layers/LayerModel+makeAnimationLayer.swift +1 -1
  29. package/Sources/Private/CoreAnimation/Layers/RepeaterLayer.swift +2 -0
  30. package/Sources/Private/CoreAnimation/Layers/ShapeLayer.swift +11 -13
  31. package/Sources/Private/CoreAnimation/Layers/SolidLayer.swift +20 -5
  32. package/Sources/Private/CoreAnimation/Layers/TextLayer.swift +13 -3
  33. package/Sources/Private/CoreAnimation/ValueProviderStore.swift +6 -4
  34. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/Collection+Diff.swift +263 -0
  35. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/Diffable.swift +18 -0
  36. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/DiffableSection.swift +16 -0
  37. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/IndexChangeset.swift +187 -0
  38. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/SectionedChangeset.swift +32 -0
  39. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Logging/EpoxyLogger.swift +99 -0
  40. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/CallbackContextEpoxyModeled.swift +8 -0
  41. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModelArrayBuilder.swift +48 -0
  42. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModelProperty.swift +158 -0
  43. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModelStorage.swift +88 -0
  44. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModeled.swift +54 -0
  45. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Internal/AnyEpoxyModelProperty.swift +29 -0
  46. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Internal/ClassReference.swift +39 -0
  47. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/AnimatedProviding.swift +10 -0
  48. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DataIDProviding.swift +57 -0
  49. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DidDisplayProviding.swift +41 -0
  50. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DidEndDisplayingProviding.swift +41 -0
  51. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DidSelectProviding.swift +36 -0
  52. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/ErasedContentProviding.swift +49 -0
  53. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/MakeViewProviding.swift +60 -0
  54. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/SetBehaviorsProviding.swift +38 -0
  55. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/SetContentProviding.swift +38 -0
  56. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/StyleIDProviding.swift +37 -0
  57. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/TraitCollectionProviding.swift +14 -0
  58. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/ViewDifferentiatorProviding.swift +34 -0
  59. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/ViewProviding.swift +13 -0
  60. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/WillDisplayProviding.swift +41 -0
  61. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/ViewEpoxyModeled.swift +10 -0
  62. package/Sources/Private/EmbeddedLibraries/EpoxyCore/README.md +31 -0
  63. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIHostingController.swift +46 -0
  64. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIHostingView.swift +391 -0
  65. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIIntrinsicContentSizeInvalidator.swift +44 -0
  66. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUILayoutMargins.swift +51 -0
  67. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxyableView+SwiftUIView.swift +172 -0
  68. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/LayoutUtilities/MeasuringViewRepresentable.swift +128 -0
  69. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/LayoutUtilities/SwiftUIMeasurementContainer.swift +452 -0
  70. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/SwiftUIView.swift +148 -0
  71. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/UIView+SwiftUIView.swift +40 -0
  72. package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/UIViewConfiguringSwiftUIView.swift +43 -0
  73. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/BehaviorsConfigurableView.swift +45 -0
  74. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/ContentConfigurableView.swift +36 -0
  75. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/EpoxyableView.swift +5 -0
  76. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/StyledView.swift +42 -0
  77. package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/ViewType.swift +51 -0
  78. package/Sources/Private/EmbeddedLibraries/README.md +27 -0
  79. package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+BackingConfiguration.swift +4 -4
  80. package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+MemoryFile.swift +2 -2
  81. package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+Writing.swift +4 -4
  82. package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive.swift +8 -7
  83. package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Data+Compression.swift +5 -5
  84. package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/FileManager+ZIP.swift +4 -4
  85. package/Sources/Private/MainThread/LayerContainers/CompLayers/ImageCompositionLayer.swift +6 -0
  86. package/Sources/Private/MainThread/LayerContainers/CompLayers/MaskContainerLayer.swift +3 -1
  87. package/Sources/Private/MainThread/LayerContainers/CompLayers/PreCompositionLayer.swift +18 -5
  88. package/Sources/Private/MainThread/LayerContainers/CompLayers/TextCompositionLayer.swift +31 -3
  89. package/Sources/Private/MainThread/LayerContainers/MainThreadAnimationLayer.swift +33 -8
  90. package/Sources/Private/MainThread/LayerContainers/Utility/CachedImageProvider.swift +8 -1
  91. package/Sources/Private/MainThread/LayerContainers/Utility/CompositionLayersInitializer.swift +13 -4
  92. package/Sources/Private/MainThread/LayerContainers/Utility/LayerFontProvider.swift +2 -2
  93. package/Sources/Private/MainThread/LayerContainers/Utility/LayerImageProvider.swift +1 -0
  94. package/Sources/Private/MainThread/LayerContainers/Utility/LayerTextProvider.swift +4 -4
  95. package/Sources/Private/MainThread/NodeRenderSystem/NodeProperties/Protocols/NodePropertyMap.swift +1 -1
  96. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/ModifierNodes/TrimPathNode.swift +3 -1
  97. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientFillRenderer.swift +2 -1
  98. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/LegacyGradientFillRenderer.swift +2 -1
  99. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientFillNode.swift +1 -1
  100. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientStrokeNode.swift +2 -2
  101. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift +1 -1
  102. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/Text/TextAnimatorNode.swift +1 -1
  103. package/Sources/Private/MainThread/NodeRenderSystem/RenderLayers/ShapeContainerLayer.swift +6 -2
  104. package/Sources/Private/Model/Assets/Asset.swift +8 -0
  105. package/Sources/Private/Model/Assets/AssetLibrary.swift +2 -2
  106. package/Sources/Private/Model/Assets/ImageAsset.swift +3 -3
  107. package/Sources/Private/Model/DotLottie/DotLottieAnimation.swift +16 -2
  108. package/Sources/Private/Model/DotLottie/DotLottieImageProvider.swift +20 -7
  109. package/Sources/Private/Model/Extensions/KeyedDecodingContainerExtensions.swift +26 -0
  110. package/Sources/Private/Model/Keyframes/KeyframeGroup.swift +8 -5
  111. package/Sources/Private/Model/LayerEffects/DropShadowEffect.swift +45 -0
  112. package/Sources/Private/Model/LayerEffects/EffectValues/ColorEffectValue.swift +38 -0
  113. package/Sources/Private/Model/LayerEffects/EffectValues/EffectValue.swift +98 -0
  114. package/Sources/Private/Model/LayerEffects/EffectValues/Vector1DEffectValue.swift +38 -0
  115. package/Sources/Private/Model/LayerEffects/LayerEffect.swift +103 -0
  116. package/Sources/Private/Model/LayerStyles/DropShadowStyle.swift +72 -0
  117. package/Sources/Private/Model/LayerStyles/LayerStyle.swift +85 -0
  118. package/Sources/Private/Model/Layers/LayerModel.swift +27 -0
  119. package/Sources/Private/Model/Objects/Marker.swift +1 -1
  120. package/Sources/Private/Model/Objects/Transform.swift +1 -2
  121. package/Sources/Private/Model/ShapeItems/GradientFill.swift +1 -1
  122. package/Sources/Private/Model/ShapeItems/GradientStroke.swift +2 -2
  123. package/Sources/Private/Model/ShapeItems/Merge.swift +1 -1
  124. package/Sources/Private/Model/ShapeItems/ShapeItem.swift +31 -26
  125. package/Sources/Private/Model/ShapeItems/ShapeTransform.swift +0 -9
  126. package/Sources/Private/Model/ShapeItems/Star.swift +1 -1
  127. package/Sources/Private/Model/Text/Font.swift +2 -2
  128. package/Sources/Private/Model/Text/Glyph.swift +1 -1
  129. package/Sources/Private/RootAnimationLayer.swift +2 -2
  130. package/Sources/Private/Utility/Debugging/LayerDebugging.swift +3 -1
  131. package/Sources/Private/Utility/Extensions/AnimationKeypathExtension.swift +32 -2
  132. package/Sources/Private/Utility/Helpers/AnimationContext.swift +4 -2
  133. package/Sources/Private/Utility/Helpers/AnyEquatable.swift +24 -0
  134. package/Sources/Private/Utility/Helpers/Binding+Map.swift +18 -0
  135. package/Sources/Private/Utility/Helpers/View+ValueChanged.swift +20 -0
  136. package/Sources/Private/Utility/LottieAnimationSource.swift +41 -0
  137. package/Sources/Private/Utility/Primitives/BezierPath.swift +2 -2
  138. package/Sources/Private/Utility/Primitives/BezierPathRoundExtension.swift +2 -2
  139. package/Sources/Private/Utility/Primitives/VectorsExtensions.swift +1 -1
  140. package/Sources/Public/Animation/LottieAnimation.swift +21 -2
  141. package/Sources/Public/Animation/LottieAnimationLayer.swift +1464 -0
  142. package/Sources/Public/Animation/LottieAnimationView.swift +259 -736
  143. package/Sources/Public/Animation/LottiePlaybackMode.swift +117 -0
  144. package/Sources/Public/Animation/LottieView.swift +462 -0
  145. package/Sources/Public/AnimationCache/AnimationCacheProvider.swift +2 -1
  146. package/Sources/Public/AnimationCache/DefaultAnimationCache.swift +5 -6
  147. package/Sources/Public/Configuration/DecodingStrategy.swift +15 -0
  148. package/Sources/Public/Configuration/LottieConfiguration.swift +47 -0
  149. package/Sources/Public/Configuration/ReducedMotionOption.swift +114 -0
  150. package/Sources/Public/{LottieConfiguration.swift → Configuration/RenderingEngineOption.swift} +2 -57
  151. package/Sources/Public/{iOS → Controls}/AnimatedButton.swift +56 -13
  152. package/Sources/Public/{iOS → Controls}/AnimatedControl.swift +80 -8
  153. package/Sources/Public/{iOS → Controls}/AnimatedSwitch.swift +71 -31
  154. package/Sources/Public/Controls/LottieButton.swift +122 -0
  155. package/Sources/Public/Controls/LottieSwitch.swift +144 -0
  156. package/Sources/Public/Controls/LottieViewType.swift +79 -0
  157. package/Sources/Public/DotLottie/DotLottieConfiguration.swift +24 -0
  158. package/Sources/Public/DotLottie/DotLottieFile.swift +21 -6
  159. package/Sources/Public/DotLottie/DotLottieFileHelpers.swift +79 -0
  160. package/Sources/Public/DynamicProperties/AnimationKeypath.swift +11 -1
  161. package/Sources/Public/DynamicProperties/ValueProviders/ColorValueProvider.swift +14 -0
  162. package/Sources/Public/DynamicProperties/ValueProviders/FloatValueProvider.swift +13 -0
  163. package/Sources/Public/DynamicProperties/ValueProviders/GradientValueProvider.swift +22 -5
  164. package/Sources/Public/DynamicProperties/ValueProviders/PointValueProvider.swift +14 -0
  165. package/Sources/Public/DynamicProperties/ValueProviders/SizeValueProvider.swift +13 -0
  166. package/Sources/Public/FontProvider/AnimationFontProvider.swift +8 -0
  167. package/Sources/Public/ImageProvider/AnimationImageProvider.swift +24 -0
  168. package/Sources/Public/Keyframes/Keyframe.swift +6 -0
  169. package/Sources/Public/Primitives/Vectors.swift +2 -2
  170. package/Sources/Public/TextProvider/AnimationTextProvider.swift +79 -6
  171. package/Sources/Public/iOS/AnimationSubview.swift +1 -1
  172. package/Sources/Public/iOS/BundleImageProvider.swift +16 -2
  173. package/Sources/Public/iOS/Compatibility/CompatibleAnimationView.swift +6 -4
  174. package/Sources/Public/iOS/FilepathImageProvider.swift +22 -3
  175. package/Sources/Public/iOS/LottieAnimationViewBase.swift +7 -1
  176. package/Sources/Public/macOS/BundleImageProvider.macOS.swift +15 -1
  177. package/Sources/Public/macOS/FilepathImageProvider.macOS.swift +21 -2
  178. package/lottie-ios.podspec +2 -1
  179. package/package.json +1 -1
  180. package/Sources/Private/Model/DotLottie/DotLottieConfiguration.swift +0 -15
  181. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+Helpers.swift +0 -0
  182. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+Progress.swift +0 -0
  183. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+Reading.swift +0 -0
  184. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+ReadingDeprecated.swift +0 -0
  185. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+WritingDeprecated.swift +0 -0
  186. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+ZIP64.swift +0 -0
  187. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Data+CompressionDeprecated.swift +0 -0
  188. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Data+Serialization.swift +0 -0
  189. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Entry+Serialization.swift +0 -0
  190. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Entry+ZIP64.swift +0 -0
  191. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Entry.swift +0 -0
  192. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/README.md +0 -0
  193. /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/URL+ZIP.swift +0 -0
@@ -0,0 +1,187 @@
1
+ // Created by Laura Skelton on 11/25/16.
2
+ // Copyright © 2016 Airbnb. All rights reserved.
3
+
4
+ import Foundation
5
+
6
+ // MARK: - IndexChangeset
7
+
8
+ /// A set of inserts, deletes, updates, and moves that define the changes between two collections.
9
+ struct IndexChangeset {
10
+
11
+ // MARK: Lifecycle
12
+
13
+ init(
14
+ inserts: [Int] = [],
15
+ deletes: [Int] = [],
16
+ updates: [(old: Int, new: Int)] = [],
17
+ moves: [(old: Int, new: Int)] = [],
18
+ newIndices: [Int?] = [],
19
+ duplicates: [[Int]] = [])
20
+ {
21
+ self.inserts = inserts
22
+ self.deletes = deletes
23
+ self.updates = updates
24
+ self.moves = moves
25
+ self.newIndices = newIndices
26
+ self.duplicates = duplicates
27
+ }
28
+
29
+ // MARK: Internal
30
+
31
+ /// The inserted indices needed to get from the old collection to the new collection.
32
+ var inserts: [Int]
33
+
34
+ /// The deleted indices needed to get from the old collection to the new collection.
35
+ var deletes: [Int]
36
+
37
+ /// The updated indices needed to get from the old collection to the new collection.
38
+ var updates: [(old: Int, new: Int)]
39
+
40
+ /// The moved indices needed to get from the old collection to the new collection.
41
+ var moves: [(old: Int, new: Int)]
42
+
43
+ /// A record for each old collection item to its index (if any) is in the new collection.
44
+ ///
45
+ /// The indexes of this `Array` represent the indexes old collection, with elements of the
46
+ /// corresponding index of the same item in the new collection it exists, else `nil`.
47
+ var newIndices: [Int?]
48
+
49
+ /// A record of each element in the new collection that has an identical `diffIdentifier` with
50
+ /// another element in the same collection.
51
+ ///
52
+ /// Each element in the outer `Array` corresponds to a duplicated identifier, with each inner
53
+ /// `[Int]` containing the indexes that share a duplicate identifier in the new collection.
54
+ ///
55
+ /// While the diffing algorithm makes a best effort to handle duplicates, they can lead to
56
+ /// unexpected behavior since identity of elements cannot be established and should be avoided if
57
+ /// possible.
58
+ var duplicates: [[Int]]
59
+
60
+ /// Whether there are any inserts, deletes, moves, or updates in this changeset.
61
+ var isEmpty: Bool {
62
+ inserts.isEmpty && deletes.isEmpty && updates.isEmpty && moves.isEmpty
63
+ }
64
+ }
65
+
66
+ // MARK: - IndexPathChangeset
67
+
68
+ /// A set of inserts, deletes, updates, and moves that define the changes between two collections
69
+ /// with indexes stored as `IndexPath`s.
70
+ struct IndexPathChangeset {
71
+
72
+ // MARK: Lifecycle
73
+
74
+ init(
75
+ inserts: [IndexPath] = [],
76
+ deletes: [IndexPath] = [],
77
+ updates: [(old: IndexPath, new: IndexPath)] = [],
78
+ moves: [(old: IndexPath, new: IndexPath)] = [],
79
+ duplicates: [[IndexPath]] = [])
80
+ {
81
+ self.inserts = inserts
82
+ self.deletes = deletes
83
+ self.updates = updates
84
+ self.moves = moves
85
+ self.duplicates = duplicates
86
+ }
87
+
88
+ // MARK: Internal
89
+
90
+ /// The inserted `IndexPath`s needed to get from the old collection to the new collection.
91
+ var inserts: [IndexPath]
92
+
93
+ /// The deleted `IndexPath`s needed to get from the old collection to the new collection.
94
+ var deletes: [IndexPath]
95
+
96
+ /// The updated `IndexPath`s needed to get from the old collection to the new collection.
97
+ var updates: [(old: IndexPath, new: IndexPath)]
98
+
99
+ /// The moved `IndexPath`s needed to get from the old collection to the new collection.
100
+ var moves: [(old: IndexPath, new: IndexPath)]
101
+
102
+ /// A record for each element in the new collection that has an identical `diffIdentifier` with
103
+ /// another element in the same collection.
104
+ ///
105
+ /// Each element in the outer `Array` corresponds to a duplicated identifier, with each inner
106
+ /// `[IndexPath]` corresponding to the indexes that share a duplicate identifier in the new
107
+ /// collection.
108
+ ///
109
+ /// While the diffing algorithm makes a best effort to handle duplicates, they can lead to
110
+ /// unexpected behavior since identity of elements cannot be established and should be avoided if
111
+ /// possible.
112
+ var duplicates: [[IndexPath]]
113
+
114
+ /// Whether there are any inserts, deletes, moves, or updates in this changeset.
115
+ var isEmpty: Bool {
116
+ inserts.isEmpty && deletes.isEmpty && updates.isEmpty && moves.isEmpty
117
+ }
118
+
119
+ static func += (left: inout IndexPathChangeset, right: IndexPathChangeset) {
120
+ left.inserts += right.inserts
121
+ left.deletes += right.deletes
122
+ left.updates += right.updates
123
+ left.moves += right.moves
124
+ left.duplicates += right.duplicates
125
+ }
126
+ }
127
+
128
+ // MARK: - IndexSetChangeset
129
+
130
+ /// A set of inserts, deletes, updates, and moves that define the changes between two collections
131
+ /// with indexes stored as `IndexSet`.
132
+ struct IndexSetChangeset {
133
+
134
+ // MARK: Lifecycle
135
+
136
+ init(
137
+ inserts: IndexSet = [],
138
+ deletes: IndexSet = [],
139
+ updates: [(old: Int, new: Int)] = [],
140
+ moves: [(old: Int, new: Int)] = [],
141
+ newIndices: [Int?] = [],
142
+ duplicates: [IndexSet] = [])
143
+ {
144
+ self.inserts = inserts
145
+ self.deletes = deletes
146
+ self.updates = updates
147
+ self.moves = moves
148
+ self.newIndices = newIndices
149
+ self.duplicates = duplicates
150
+ }
151
+
152
+ // MARK: Internal
153
+
154
+ /// An `IndexSet` of inserts needed to get from the old collection to the new collection.
155
+ var inserts: IndexSet
156
+
157
+ /// An `IndexSet` of deletes needed to get from the old collection to the new collection.
158
+ var deletes: IndexSet
159
+
160
+ /// The updated indices needed to get from the old collection to the new collection.
161
+ var updates: [(old: Int, new: Int)]
162
+
163
+ /// The moved indices needed to get from the old collection to the new collection.
164
+ var moves: [(old: Int, new: Int)]
165
+
166
+ /// A record for each old collection item of what its index (if any) is in the new collection.
167
+ ///
168
+ /// The indexes of this `Array` represent the indexes old collection, with elements of the
169
+ /// corresponding index of the same item in the new collection it exists, else `nil`.
170
+ var newIndices: [Int?]
171
+
172
+ /// A record for each element in the new collection that has an identical `diffIdentifier` with
173
+ /// another element in the same collection.
174
+ ///
175
+ /// Each element in the `Array` corresponds to a duplicated identifier, with each `IndexSet`
176
+ /// containing the indexes that share a duplicate identifier in the new collection.
177
+ ///
178
+ /// While the diffing algorithm makes a best effort to handle duplicates, they can lead to
179
+ /// unexpected behavior since identity of elements cannot be established and should be avoided if
180
+ /// possible.
181
+ var duplicates: [IndexSet]
182
+
183
+ /// Whether there are any inserts, deletes, moves, or updates in this changeset.
184
+ var isEmpty: Bool {
185
+ inserts.isEmpty && deletes.isEmpty && updates.isEmpty && moves.isEmpty
186
+ }
187
+ }
@@ -0,0 +1,32 @@
1
+ // Created by Laura Skelton on 5/11/17.
2
+ // Copyright © 2017 Airbnb. All rights reserved.
3
+
4
+ /// A set of the minimum changes to get from one array of `DiffableSection`s to another, used for
5
+ /// diffing.
6
+ struct SectionedChangeset {
7
+
8
+ // MARK: Lifecycle
9
+
10
+ init(
11
+ sectionChangeset: IndexSetChangeset,
12
+ itemChangeset: IndexPathChangeset)
13
+ {
14
+ self.sectionChangeset = sectionChangeset
15
+ self.itemChangeset = itemChangeset
16
+ }
17
+
18
+ // MARK: Internal
19
+
20
+ /// A set of the minimum changes to get from one set of sections to another.
21
+ var sectionChangeset: IndexSetChangeset
22
+
23
+ /// A set of the minimum changes to get from one set of items to another, aggregated across all
24
+ /// sections.
25
+ var itemChangeset: IndexPathChangeset
26
+
27
+ /// Whether there are any inserts, deletes, moves, or updates in this changeset.
28
+ var isEmpty: Bool {
29
+ sectionChangeset.isEmpty && itemChangeset.isEmpty
30
+ }
31
+
32
+ }
@@ -0,0 +1,99 @@
1
+ // Created by eric_horacek on 12/9/20.
2
+ // Copyright © 2020 Airbnb Inc. All rights reserved.
3
+
4
+ /// A shared logger that allows consumers to intercept Epoxy assertions and warning messages to pipe
5
+ /// into their own logging systems.
6
+ final class EpoxyLogger {
7
+
8
+ // MARK: Lifecycle
9
+
10
+ init(
11
+ assert: @escaping Assert = { condition, message, file, line in
12
+ // If we default to `Swift.assert` directly with `assert: Assert = Swift.assert`,
13
+ // the call will unexpectedly not respect the -O flag and will crash in release
14
+ // https://github.com/apple/swift/issues/60249
15
+ Swift.assert(condition(), message(), file: file, line: line)
16
+ },
17
+ assertionFailure: @escaping AssertionFailure = { message, file, line in
18
+ // If we default to `Swift.assertionFailure` directly with
19
+ // `assertionFailure: AssertionFailure = Swift.assertionFailure`,
20
+ // the call will unexpectedly not respect the -O flag and will crash in release
21
+ // https://github.com/apple/swift/issues/60249
22
+ Swift.assertionFailure(message(), file: file, line: line)
23
+ },
24
+ warn: @escaping Warn = { message, _, _ in
25
+ #if DEBUG
26
+ // swiftlint:disable:next no_direct_standard_out_logs
27
+ print(message())
28
+ #endif
29
+ })
30
+ {
31
+ _assert = assert
32
+ _assertionFailure = assertionFailure
33
+ _warn = warn
34
+ }
35
+
36
+ // MARK: Internal
37
+
38
+ /// Logs that an assertion occurred.
39
+ typealias Assert = (
40
+ _ condition: @autoclosure () -> Bool,
41
+ _ message: @autoclosure () -> String,
42
+ _ fileID: StaticString,
43
+ _ line: UInt)
44
+ -> Void
45
+
46
+ /// Logs that an assertion failure occurred.
47
+ typealias AssertionFailure = (
48
+ _ message: @autoclosure () -> String,
49
+ _ fileID: StaticString,
50
+ _ line: UInt)
51
+ -> Void
52
+
53
+ /// Logs a warning message.
54
+ typealias Warn = (
55
+ _ message: @autoclosure () -> String,
56
+ _ fileID: StaticString,
57
+ _ line: UInt)
58
+ -> Void
59
+
60
+ /// The shared instance used to log Epoxy assertions and warnings.
61
+ ///
62
+ /// Set this to a new logger instance to intercept assertions and warnings logged by Epoxy.
63
+ static var shared = EpoxyLogger()
64
+
65
+ /// Logs that an assertion occurred.
66
+ func assert(
67
+ _ condition: @autoclosure () -> Bool,
68
+ _ message: @autoclosure () -> String = String(),
69
+ fileID: StaticString = #fileID,
70
+ line: UInt = #line)
71
+ {
72
+ _assert(condition(), message(), fileID, line)
73
+ }
74
+
75
+ /// Logs that an assertion failure occurred.
76
+ func assertionFailure(
77
+ _ message: @autoclosure () -> String = String(),
78
+ fileID: StaticString = #fileID,
79
+ line: UInt = #line)
80
+ {
81
+ _assertionFailure(message(), fileID, line)
82
+ }
83
+
84
+ /// Logs a warning message.
85
+ func warn(
86
+ _ message: @autoclosure () -> String = String(),
87
+ fileID: StaticString = #fileID,
88
+ line: UInt = #line)
89
+ {
90
+ _warn(message(), fileID, line)
91
+ }
92
+
93
+ // MARK: Private
94
+
95
+ private let _assert: Assert
96
+ private let _assertionFailure: AssertionFailure
97
+ private let _warn: Warn
98
+
99
+ }
@@ -0,0 +1,8 @@
1
+ // Created by eric_horacek on 12/15/20.
2
+ // Copyright © 2020 Airbnb Inc. All rights reserved.
3
+
4
+ /// An Epoxy model with an associated context type that's passed into callback closures.
5
+ protocol CallbackContextEpoxyModeled: EpoxyModeled {
6
+ /// A context type that's passed into callback closures.
7
+ associatedtype CallbackContext
8
+ }
@@ -0,0 +1,48 @@
1
+ // Created by eric_horacek on 3/15/21.
2
+ // Copyright © 2021 Airbnb Inc. All rights reserved.
3
+
4
+ /// A generic result builder that enables a DSL for building arrays of Epoxy models.
5
+ @resultBuilder
6
+ enum EpoxyModelArrayBuilder<Model> {
7
+ typealias Expression = Model
8
+ typealias Component = [Model]
9
+
10
+ static func buildExpression(_ expression: Expression) -> Component {
11
+ [expression]
12
+ }
13
+
14
+ static func buildExpression(_ expression: Component) -> Component {
15
+ expression
16
+ }
17
+
18
+ static func buildExpression(_ expression: Expression?) -> Component {
19
+ if let expression = expression {
20
+ return [expression]
21
+ }
22
+ return []
23
+ }
24
+
25
+ static func buildBlock(_ children: Component...) -> Component {
26
+ children.flatMap { $0 }
27
+ }
28
+
29
+ static func buildBlock(_ component: Component) -> Component {
30
+ component
31
+ }
32
+
33
+ static func buildOptional(_ children: Component?) -> Component {
34
+ children ?? []
35
+ }
36
+
37
+ static func buildEither(first child: Component) -> Component {
38
+ child
39
+ }
40
+
41
+ static func buildEither(second child: Component) -> Component {
42
+ child
43
+ }
44
+
45
+ static func buildArray(_ components: [Component]) -> Component {
46
+ components.flatMap { $0 }
47
+ }
48
+ }
@@ -0,0 +1,158 @@
1
+ // Created by eric_horacek on 11/18/20.
2
+ // Copyright © 2020 Airbnb Inc. All rights reserved.
3
+
4
+ // MARK: - EpoxyModelProperty
5
+
6
+ /// A property that can be stored in any concrete `EpoxyModeled` type.
7
+ ///
8
+ /// Custom model properties can be declared in any module. It's recommended that properties are
9
+ /// declared as `var`s in extensions to `EpoxyModeled` with a `*Property` suffix.
10
+ ///
11
+ /// For example, to declare a `EpoxyModelProperty` that fulfills the `TitleProviding` protocol:
12
+ ///
13
+ /// ````
14
+ /// internal protocol TitleProviding {
15
+ /// var title: String? { get }
16
+ /// }
17
+ ///
18
+ /// extension EpoxyModeled where Self: TitleProviding {
19
+ /// internal var title: String? {
20
+ /// get { self[titleProperty] }
21
+ /// set { self[titleProperty] = newValue }
22
+ /// }
23
+ ///
24
+ /// internal func title(_ value: String?) -> Self {
25
+ /// copy(updating: titleProperty, to: value)
26
+ /// }
27
+ ///
28
+ /// private var titleProperty: EpoxyModelProperty<String?> {
29
+ /// .init(keyPath: \TitleProviding.title, defaultValue: nil, updateStrategy: .replace)
30
+ /// }
31
+ /// }
32
+ /// ````
33
+ struct EpoxyModelProperty<Value> {
34
+
35
+ // MARK: Lifecycle
36
+
37
+ /// Creates a property identified by a `KeyPath` to its provided `value` and with its default
38
+ /// value if not customized in content by consumers.
39
+ ///
40
+ /// The `updateStrategy` is used to update the value when updating from an old value to a new
41
+ /// value.
42
+ init<Model>(
43
+ keyPath: KeyPath<Model, Value>,
44
+ defaultValue: @escaping @autoclosure () -> Value,
45
+ updateStrategy: UpdateStrategy)
46
+ {
47
+ self.keyPath = keyPath
48
+ self.defaultValue = defaultValue
49
+ self.updateStrategy = updateStrategy
50
+ }
51
+
52
+ // MARK: Internal
53
+
54
+ /// The `KeyPath` that uniquely identifies this property.
55
+ let keyPath: AnyKeyPath
56
+
57
+ /// A closure that produces the default property value when called.
58
+ let defaultValue: () -> Value
59
+
60
+ /// A closure used to update an `EpoxyModelProperty` from an old value to a new value.
61
+ let updateStrategy: UpdateStrategy
62
+
63
+ }
64
+
65
+ // MARK: EpoxyModelProperty.UpdateStrategy
66
+
67
+ extension EpoxyModelProperty {
68
+ /// A closure used to update an `EpoxyModelProperty` from an old value to a new value.
69
+ struct UpdateStrategy {
70
+
71
+ // MARK: Lifecycle
72
+
73
+ init(update: @escaping (Value, Value) -> Value) {
74
+ self.update = update
75
+ }
76
+
77
+ // MARK: Public
78
+
79
+ /// A closure used to update an `EpoxyModelProperty` from an old value to a new value.
80
+ var update: (_ old: Value, _ new: Value) -> Value
81
+ }
82
+ }
83
+
84
+ // MARK: Defaults
85
+
86
+ extension EpoxyModelProperty.UpdateStrategy {
87
+ /// Replaces the old value with the new value when an update occurs.
88
+ static var replace: Self {
89
+ .init { _, new in new }
90
+ }
91
+
92
+ /// Chains the new closure value onto the old closure value, returning a new closure that first
93
+ /// calls the old closure and then subsequently calls the new closure.
94
+ static func chain() -> EpoxyModelProperty<(() -> Void)?>.UpdateStrategy {
95
+ .init { old, new in
96
+ guard let new = new else { return old }
97
+ guard let old = old else { return new }
98
+ return {
99
+ old()
100
+ new()
101
+ }
102
+ }
103
+ }
104
+
105
+ /// Chains the new closure value onto the old closure value, returning a new closure that first
106
+ /// calls the old closure and then subsequently calls the new closure.
107
+ static func chain<A>() -> EpoxyModelProperty<((A) -> Void)?>.UpdateStrategy {
108
+ .init { old, new in
109
+ guard let new = new else { return old }
110
+ guard let old = old else { return new }
111
+ return { a in
112
+ old(a)
113
+ new(a)
114
+ }
115
+ }
116
+ }
117
+
118
+ /// Chains the new closure value onto the old closure value, returning a new closure that first
119
+ /// calls the old closure and then subsequently calls the new closure.
120
+ static func chain<A, B>() -> EpoxyModelProperty<((A, B) -> Void)?>.UpdateStrategy {
121
+ .init { old, new in
122
+ guard let new = new else { return old }
123
+ guard let old = old else { return new }
124
+ return { a, b in
125
+ old(a, b)
126
+ new(a, b)
127
+ }
128
+ }
129
+ }
130
+
131
+ /// Chains the new closure value onto the old closure value, returning a new closure that first
132
+ /// calls the old closure and then subsequently calls the new closure.
133
+ static func chain<A, B, C>() -> EpoxyModelProperty<((A, B, C) -> Void)?>.UpdateStrategy {
134
+ .init { old, new in
135
+ guard let new = new else { return old }
136
+ guard let old = old else { return new }
137
+ return { a, b, c in
138
+ old(a, b, c)
139
+ new(a, b, c)
140
+ }
141
+ }
142
+ }
143
+
144
+ /// Chains the new closure value onto the old closure value, returning a new closure that first
145
+ /// calls the old closure and then subsequently calls the new closure.
146
+ static func chain<A, B, C, D>() -> EpoxyModelProperty<((A, B, C, D) -> Void)?>.UpdateStrategy {
147
+ .init { old, new in
148
+ guard let new = new else { return old }
149
+ guard let old = old else { return new }
150
+ return { a, b, c, d in
151
+ old(a, b, c, d)
152
+ new(a, b, c, d)
153
+ }
154
+ }
155
+ }
156
+
157
+ // Add more arities as needed
158
+ }
@@ -0,0 +1,88 @@
1
+ // Created by eric_horacek on 11/18/20.
2
+ // Copyright © 2020 Airbnb Inc. All rights reserved.
3
+
4
+ // MARK: - EpoxyModelStorage
5
+
6
+ /// The underlying storage for an `EpoxyModeled` model that is capable of storing any
7
+ /// `EpoxyModelProperty`.
8
+ ///
9
+ /// Supports being extended with additional storage capabilities in other modules and conditionally
10
+ /// based on the provider capabilities that the content containing this storage conforms to.
11
+ struct EpoxyModelStorage {
12
+
13
+ // MARK: Lifecycle
14
+
15
+ init() { }
16
+
17
+ // MARK: Internal
18
+
19
+ /// Stores or retrieves the value of the specified property.
20
+ subscript<Property>(property: EpoxyModelProperty<Property>) -> Property {
21
+ get {
22
+ guard let propertyStorage = storage[property.keyPath] else {
23
+ return property.defaultValue()
24
+ }
25
+
26
+ // This cast will never fail as the storage is only settable via this subscript and the
27
+ // `KeyPath` key is unique for any provider and value type pair.
28
+ // swiftlint:disable:next force_cast
29
+ return propertyStorage.value as! Property
30
+ }
31
+ set {
32
+ // We first update the value without using the `updateStrategy` since the likely scenario
33
+ // is that there won't be a collision that requires the `updateStrategy`, and we'll be able to
34
+ // return without incurring the cost of another write.
35
+ let propertyStorage = PropertyStorage(value: newValue, property: property)
36
+
37
+ guard var replaced = storage.updateValue(propertyStorage, forKey: property.keyPath) else {
38
+ return
39
+ }
40
+
41
+ // This cast will never fail as the storage is only settable via this subscript and the
42
+ // `KeyPath` key is unique for any provider and value type pair.
43
+ // swiftlint:disable:next force_cast
44
+ replaced.value = property.updateStrategy.update(replaced.value as! Property, newValue)
45
+
46
+ storage[property.keyPath] = replaced
47
+ }
48
+ }
49
+
50
+ /// Merges the given storage into this storage.
51
+ ///
52
+ /// In the case of a collision, the `UpdateStrategy` of the property is used to determine the
53
+ /// resulting value in this storage.
54
+ mutating func merge(_ other: Self) {
55
+ for (key, otherValue) in other.storage {
56
+ // We first update the value without using the `updateStrategy` since the likely scenario
57
+ // is that there won't be a collision that requires the `updateStrategy`, and we'll be able to
58
+ // return without incurring the cost of another write.
59
+ guard var replaced = storage.updateValue(otherValue, forKey: key) else {
60
+ continue
61
+ }
62
+
63
+ replaced.value = replaced.property.update(old: replaced.value, new: otherValue.value)
64
+
65
+ storage[key] = replaced
66
+ }
67
+ }
68
+
69
+ // MARK: Private
70
+
71
+ /// The underlying storage for the properties, with a key of the `EpoxyModelProperty.keyPath` and
72
+ /// a value of the property's `PropertyStorage`.
73
+ ///
74
+ /// Does not include default values.
75
+ private var storage = [AnyKeyPath: PropertyStorage]()
76
+
77
+ }
78
+
79
+ // MARK: - PropertyStorage
80
+
81
+ /// A value stored within an `EpoxyModelStorage`.
82
+ private struct PropertyStorage {
83
+ /// The type-erased value of the `EpoxyModelProperty`.
84
+ var value: Any
85
+
86
+ /// The property's corresponding `EpoxyModelProperty`, erased to an `AnyEpoxyModelProperty`.
87
+ var property: AnyEpoxyModelProperty
88
+ }
@@ -0,0 +1,54 @@
1
+ // Created by eric_horacek on 11/18/20.
2
+ // Copyright © 2020 Airbnb Inc. All rights reserved.
3
+
4
+ // MARK: - EpoxyModeled
5
+
6
+ /// A protocol that all concrete Epoxy declarative UI model types conform to.
7
+ ///
8
+ /// This protocol should be conditionally extended to fulfill provider protocols and with chainable
9
+ /// setters for those providers that concrete model types can receive by declaring conformance to
10
+ /// provider protocols.
11
+ protocol EpoxyModeled {
12
+ /// The underlying storage of this model that stores the current property values.
13
+ var storage: EpoxyModelStorage { get set }
14
+ }
15
+
16
+ // MARK: Extensions
17
+
18
+ extension EpoxyModeled {
19
+ /// Stores or retrieves a value of the specified property in `storage`.
20
+ ///
21
+ /// If the value was set previously for the given `property`, the conflict is resolved using the
22
+ /// `EpoxyModelProperty.UpdateStrategy` of the `property`.
23
+ subscript<Property>(property: EpoxyModelProperty<Property>) -> Property {
24
+ get { storage[property] }
25
+ set { storage[property] = newValue }
26
+ }
27
+
28
+ /// Returns a copy of this model with the given property updated to the provided value.
29
+ ///
30
+ /// Typically called from within the context of a chainable setter to allow fluent setting of a
31
+ /// property, e.g.:
32
+ ///
33
+ /// ````
34
+ /// internal func title(_ value: String?) -> Self {
35
+ /// copy(updating: titleProperty, to: value)
36
+ /// }
37
+ /// ````
38
+ ///
39
+ /// If a `value` was set previously for the given `property`, the conflict is resolved using the
40
+ /// `EpoxyModelProperty.UpdateStrategy` of the `property`.
41
+ func copy<Value>(updating property: EpoxyModelProperty<Value>, to value: Value) -> Self {
42
+ var copy = self
43
+ copy.storage[property] = value
44
+ return copy
45
+ }
46
+
47
+ /// Returns a copy of this model produced by merging the given `other` model's storage into this
48
+ /// model's storage.
49
+ func merging(_ other: EpoxyModeled) -> Self {
50
+ var copy = self
51
+ copy.storage.merge(other.storage)
52
+ return copy
53
+ }
54
+ }