lottie-ios 4.2.0 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/main.yml +117 -11
- package/Gemfile +1 -0
- package/Gemfile.lock +5 -0
- package/Lottie.xcodeproj/project.pbxproj +1542 -194
- package/Lottie.xcodeproj/project.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/Lottie.xcodeproj/xcshareddata/xcschemes/Lottie (iOS).xcscheme +1 -1
- package/Lottie.xcodeproj/xcshareddata/xcschemes/Lottie (macOS).xcscheme +1 -1
- package/Lottie.xcodeproj/xcshareddata/xcschemes/Lottie (tvOS).xcscheme +4 -5
- package/Lottie.xcodeproj/xcshareddata/xcschemes/Lottie (visionOS).xcscheme +66 -0
- package/Lottie.xcodeproj/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +18 -0
- package/Lottie.xcworkspace/xcshareddata/swiftpm/Package.resolved +2 -11
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +346 -102
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Expressions.xcexplist +16 -5
- package/Package.resolved +2 -2
- package/Package.swift +16 -9
- package/README.md +2 -2
- package/Rakefile +122 -25
- package/Sources/Private/CoreAnimation/Animations/DropShadowAnimation.swift +160 -0
- package/Sources/Private/CoreAnimation/Animations/LayerProperty.swift +83 -10
- package/Sources/Private/CoreAnimation/Animations/TransformAnimations.swift +85 -79
- package/Sources/Private/CoreAnimation/CoreAnimationLayer.swift +7 -7
- package/Sources/Private/CoreAnimation/Extensions/Keyframes+combined.swift +67 -6
- package/Sources/Private/CoreAnimation/Layers/AnimationLayer.swift +2 -2
- package/Sources/Private/CoreAnimation/Layers/BaseCompositionLayer.swift +9 -0
- package/Sources/Private/CoreAnimation/Layers/ImageLayer.swift +1 -0
- package/Sources/Private/CoreAnimation/Layers/LayerModel+makeAnimationLayer.swift +1 -1
- package/Sources/Private/CoreAnimation/Layers/RepeaterLayer.swift +2 -0
- package/Sources/Private/CoreAnimation/Layers/ShapeLayer.swift +11 -13
- package/Sources/Private/CoreAnimation/Layers/SolidLayer.swift +20 -5
- package/Sources/Private/CoreAnimation/Layers/TextLayer.swift +13 -3
- package/Sources/Private/CoreAnimation/ValueProviderStore.swift +6 -4
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/Collection+Diff.swift +263 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/Diffable.swift +18 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/DiffableSection.swift +16 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/IndexChangeset.swift +187 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/SectionedChangeset.swift +32 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Logging/EpoxyLogger.swift +99 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/CallbackContextEpoxyModeled.swift +8 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModelArrayBuilder.swift +48 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModelProperty.swift +158 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModelStorage.swift +88 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModeled.swift +54 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Internal/AnyEpoxyModelProperty.swift +29 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Internal/ClassReference.swift +39 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/AnimatedProviding.swift +10 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DataIDProviding.swift +57 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DidDisplayProviding.swift +41 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DidEndDisplayingProviding.swift +41 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DidSelectProviding.swift +36 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/ErasedContentProviding.swift +49 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/MakeViewProviding.swift +60 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/SetBehaviorsProviding.swift +38 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/SetContentProviding.swift +38 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/StyleIDProviding.swift +37 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/TraitCollectionProviding.swift +14 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/ViewDifferentiatorProviding.swift +34 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/ViewProviding.swift +13 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/WillDisplayProviding.swift +41 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/ViewEpoxyModeled.swift +10 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/README.md +31 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIHostingController.swift +46 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIHostingView.swift +391 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIIntrinsicContentSizeInvalidator.swift +44 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUILayoutMargins.swift +51 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxyableView+SwiftUIView.swift +172 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/LayoutUtilities/MeasuringViewRepresentable.swift +128 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/LayoutUtilities/SwiftUIMeasurementContainer.swift +452 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/SwiftUIView.swift +148 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/UIView+SwiftUIView.swift +40 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/UIViewConfiguringSwiftUIView.swift +43 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/BehaviorsConfigurableView.swift +45 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/ContentConfigurableView.swift +36 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/EpoxyableView.swift +5 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/StyledView.swift +42 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/ViewType.swift +51 -0
- package/Sources/Private/EmbeddedLibraries/README.md +27 -0
- package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+BackingConfiguration.swift +4 -4
- package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+MemoryFile.swift +2 -2
- package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+Writing.swift +4 -4
- package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive.swift +8 -7
- package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Data+Compression.swift +5 -5
- package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/FileManager+ZIP.swift +4 -4
- package/Sources/Private/MainThread/LayerContainers/CompLayers/ImageCompositionLayer.swift +6 -0
- package/Sources/Private/MainThread/LayerContainers/CompLayers/MaskContainerLayer.swift +3 -1
- package/Sources/Private/MainThread/LayerContainers/CompLayers/PreCompositionLayer.swift +18 -5
- package/Sources/Private/MainThread/LayerContainers/CompLayers/TextCompositionLayer.swift +31 -3
- package/Sources/Private/MainThread/LayerContainers/MainThreadAnimationLayer.swift +33 -8
- package/Sources/Private/MainThread/LayerContainers/Utility/CachedImageProvider.swift +8 -1
- package/Sources/Private/MainThread/LayerContainers/Utility/CompositionLayersInitializer.swift +13 -4
- package/Sources/Private/MainThread/LayerContainers/Utility/LayerFontProvider.swift +2 -2
- package/Sources/Private/MainThread/LayerContainers/Utility/LayerImageProvider.swift +1 -0
- package/Sources/Private/MainThread/LayerContainers/Utility/LayerTextProvider.swift +4 -4
- package/Sources/Private/MainThread/NodeRenderSystem/NodeProperties/Protocols/NodePropertyMap.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/ModifierNodes/TrimPathNode.swift +3 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientFillRenderer.swift +2 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/LegacyGradientFillRenderer.swift +2 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientFillNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientStrokeNode.swift +2 -2
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/Text/TextAnimatorNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/RenderLayers/ShapeContainerLayer.swift +6 -2
- package/Sources/Private/Model/Assets/Asset.swift +8 -0
- package/Sources/Private/Model/Assets/AssetLibrary.swift +2 -2
- package/Sources/Private/Model/Assets/ImageAsset.swift +3 -3
- package/Sources/Private/Model/DotLottie/DotLottieAnimation.swift +16 -2
- package/Sources/Private/Model/DotLottie/DotLottieImageProvider.swift +20 -7
- package/Sources/Private/Model/Extensions/KeyedDecodingContainerExtensions.swift +26 -0
- package/Sources/Private/Model/Keyframes/KeyframeGroup.swift +8 -5
- package/Sources/Private/Model/LayerEffects/DropShadowEffect.swift +45 -0
- package/Sources/Private/Model/LayerEffects/EffectValues/ColorEffectValue.swift +38 -0
- package/Sources/Private/Model/LayerEffects/EffectValues/EffectValue.swift +98 -0
- package/Sources/Private/Model/LayerEffects/EffectValues/Vector1DEffectValue.swift +38 -0
- package/Sources/Private/Model/LayerEffects/LayerEffect.swift +103 -0
- package/Sources/Private/Model/LayerStyles/DropShadowStyle.swift +72 -0
- package/Sources/Private/Model/LayerStyles/LayerStyle.swift +85 -0
- package/Sources/Private/Model/Layers/LayerModel.swift +27 -0
- package/Sources/Private/Model/Objects/Marker.swift +1 -1
- package/Sources/Private/Model/Objects/Transform.swift +1 -2
- package/Sources/Private/Model/ShapeItems/GradientFill.swift +1 -1
- package/Sources/Private/Model/ShapeItems/GradientStroke.swift +2 -2
- package/Sources/Private/Model/ShapeItems/Merge.swift +1 -1
- package/Sources/Private/Model/ShapeItems/ShapeItem.swift +31 -26
- package/Sources/Private/Model/ShapeItems/ShapeTransform.swift +0 -9
- package/Sources/Private/Model/ShapeItems/Star.swift +1 -1
- package/Sources/Private/Model/Text/Font.swift +2 -2
- package/Sources/Private/Model/Text/Glyph.swift +1 -1
- package/Sources/Private/RootAnimationLayer.swift +2 -2
- package/Sources/Private/Utility/Debugging/LayerDebugging.swift +3 -1
- package/Sources/Private/Utility/Extensions/AnimationKeypathExtension.swift +32 -2
- package/Sources/Private/Utility/Helpers/AnimationContext.swift +4 -2
- package/Sources/Private/Utility/Helpers/AnyEquatable.swift +24 -0
- package/Sources/Private/Utility/Helpers/Binding+Map.swift +18 -0
- package/Sources/Private/Utility/Helpers/View+ValueChanged.swift +20 -0
- package/Sources/Private/Utility/LottieAnimationSource.swift +41 -0
- package/Sources/Private/Utility/Primitives/BezierPath.swift +2 -2
- package/Sources/Private/Utility/Primitives/BezierPathRoundExtension.swift +2 -2
- package/Sources/Private/Utility/Primitives/VectorsExtensions.swift +1 -1
- package/Sources/Public/Animation/LottieAnimation.swift +21 -2
- package/Sources/Public/Animation/LottieAnimationLayer.swift +1464 -0
- package/Sources/Public/Animation/LottieAnimationView.swift +258 -735
- package/Sources/Public/Animation/LottiePlaybackMode.swift +117 -0
- package/Sources/Public/Animation/LottieView.swift +462 -0
- package/Sources/Public/AnimationCache/AnimationCacheProvider.swift +2 -1
- package/Sources/Public/AnimationCache/DefaultAnimationCache.swift +5 -6
- package/Sources/Public/Configuration/DecodingStrategy.swift +15 -0
- package/Sources/Public/Configuration/LottieConfiguration.swift +47 -0
- package/Sources/Public/Configuration/ReducedMotionOption.swift +114 -0
- package/Sources/Public/{LottieConfiguration.swift → Configuration/RenderingEngineOption.swift} +2 -57
- package/Sources/Public/{iOS → Controls}/AnimatedButton.swift +56 -13
- package/Sources/Public/{iOS → Controls}/AnimatedControl.swift +80 -8
- package/Sources/Public/{iOS → Controls}/AnimatedSwitch.swift +71 -31
- package/Sources/Public/Controls/LottieButton.swift +122 -0
- package/Sources/Public/Controls/LottieSwitch.swift +144 -0
- package/Sources/Public/Controls/LottieViewType.swift +79 -0
- package/Sources/Public/DotLottie/DotLottieConfiguration.swift +24 -0
- package/Sources/Public/DotLottie/DotLottieFile.swift +21 -6
- package/Sources/Public/DotLottie/DotLottieFileHelpers.swift +79 -0
- package/Sources/Public/DynamicProperties/AnimationKeypath.swift +11 -1
- package/Sources/Public/DynamicProperties/ValueProviders/ColorValueProvider.swift +14 -0
- package/Sources/Public/DynamicProperties/ValueProviders/FloatValueProvider.swift +13 -0
- package/Sources/Public/DynamicProperties/ValueProviders/GradientValueProvider.swift +22 -5
- package/Sources/Public/DynamicProperties/ValueProviders/PointValueProvider.swift +14 -0
- package/Sources/Public/DynamicProperties/ValueProviders/SizeValueProvider.swift +13 -0
- package/Sources/Public/FontProvider/AnimationFontProvider.swift +8 -0
- package/Sources/Public/ImageProvider/AnimationImageProvider.swift +24 -0
- package/Sources/Public/Keyframes/Keyframe.swift +6 -0
- package/Sources/Public/Primitives/Vectors.swift +2 -2
- package/Sources/Public/TextProvider/AnimationTextProvider.swift +79 -6
- package/Sources/Public/iOS/AnimationSubview.swift +1 -1
- package/Sources/Public/iOS/BundleImageProvider.swift +16 -2
- package/Sources/Public/iOS/Compatibility/CompatibleAnimationView.swift +2 -2
- package/Sources/Public/iOS/FilepathImageProvider.swift +22 -3
- package/Sources/Public/iOS/LottieAnimationViewBase.swift +7 -1
- package/Sources/Public/macOS/BundleImageProvider.macOS.swift +15 -1
- package/Sources/Public/macOS/FilepathImageProvider.macOS.swift +21 -2
- package/lottie-ios.podspec +2 -1
- package/package.json +1 -1
- package/Sources/Private/Model/DotLottie/DotLottieConfiguration.swift +0 -15
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+Helpers.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+Progress.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+Reading.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+ReadingDeprecated.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+WritingDeprecated.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+ZIP64.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Data+CompressionDeprecated.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Data+Serialization.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Entry+Serialization.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Entry+ZIP64.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Entry.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/README.md +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/URL+ZIP.swift +0 -0
|
@@ -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
|
+
}
|
package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Internal/AnyEpoxyModelProperty.swift
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Created by eric_horacek on 12/1/20.
|
|
2
|
+
// Copyright © 2020 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
// MARK: - AnyEpoxyModelProperty
|
|
5
|
+
|
|
6
|
+
/// An erased `EpoxyModelProperty`, with the ability to call the `UpdateStrategy` even when the type
|
|
7
|
+
/// has been erased.
|
|
8
|
+
protocol AnyEpoxyModelProperty {
|
|
9
|
+
/// Returns the updated property from updating from given old to new property.
|
|
10
|
+
func update(old: Any, new: Any) -> Any
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// MARK: - EpoxyModelProperty + AnyEpoxyModelProperty
|
|
14
|
+
|
|
15
|
+
extension EpoxyModelProperty: AnyEpoxyModelProperty {
|
|
16
|
+
func update(old: Any, new: Any) -> Any {
|
|
17
|
+
guard let typedOld = old as? Value else {
|
|
18
|
+
EpoxyLogger.shared.assertionFailure(
|
|
19
|
+
"Expected old to be of type \(Value.self), instead found \(old). This is programmer error.")
|
|
20
|
+
return defaultValue()
|
|
21
|
+
}
|
|
22
|
+
guard let typedNew = new as? Value else {
|
|
23
|
+
EpoxyLogger.shared.assertionFailure(
|
|
24
|
+
"Expected new to be of type \(Value.self), instead found \(old). This is programmer error.")
|
|
25
|
+
return defaultValue()
|
|
26
|
+
}
|
|
27
|
+
return updateStrategy.update(typedOld, typedNew)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Created by Cal Stephens on 10/15/21.
|
|
2
|
+
// Copyright © 2021 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
// MARK: - ClassReference
|
|
5
|
+
|
|
6
|
+
/// A `Hashable` value wrapper around an `AnyClass` value
|
|
7
|
+
/// - Unlike `ObjectIdentifier(class)`, `ClassReference(class)`
|
|
8
|
+
/// preserves the `AnyClass` value and is more human-readable.
|
|
9
|
+
struct ClassReference {
|
|
10
|
+
init(_ class: AnyClass) {
|
|
11
|
+
self.class = `class`
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let `class`: AnyClass
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// MARK: Equatable
|
|
18
|
+
|
|
19
|
+
extension ClassReference: Equatable {
|
|
20
|
+
static func ==(_ lhs: Self, _ rhs: Self) -> Bool {
|
|
21
|
+
ObjectIdentifier(lhs.class) == ObjectIdentifier(rhs.class)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// MARK: Hashable
|
|
26
|
+
|
|
27
|
+
extension ClassReference: Hashable {
|
|
28
|
+
func hash(into hasher: inout Hasher) {
|
|
29
|
+
hasher.combine(ObjectIdentifier(`class`))
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// MARK: CustomStringConvertible
|
|
34
|
+
|
|
35
|
+
extension ClassReference: CustomStringConvertible {
|
|
36
|
+
var description: String {
|
|
37
|
+
String(describing: `class`)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Created by eric_horacek on 12/16/20.
|
|
2
|
+
// Copyright © 2020 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
/// The capability of providing a flag indicating whether an operation should be animated.
|
|
5
|
+
///
|
|
6
|
+
/// Typically conformed to by the `CallbackContext` of a `CallbackContextEpoxyModeled`.
|
|
7
|
+
protocol AnimatedProviding {
|
|
8
|
+
/// Whether this operation should be animated.
|
|
9
|
+
var animated: Bool { get }
|
|
10
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// Created by eric_horacek on 12/1/20.
|
|
2
|
+
// Copyright © 2020 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
// MARK: - DataIDProviding
|
|
5
|
+
|
|
6
|
+
/// The capability of providing a stable data identifier with an erased type.
|
|
7
|
+
///
|
|
8
|
+
/// While it has similar semantics, this type cannot inherit from `Identifiable` as this would give
|
|
9
|
+
/// it an associated type, which would cause the `keyPath` used in its `EpoxyModelProperty` to not
|
|
10
|
+
/// be stable across types if written as `\Self.dataID` since the `KeyPath` `Root` would be
|
|
11
|
+
/// different for each type.
|
|
12
|
+
///
|
|
13
|
+
/// - SeeAlso: `Identifiable`.
|
|
14
|
+
protocol DataIDProviding {
|
|
15
|
+
/// A stable identifier that uniquely identifies this instance, with its typed erased.
|
|
16
|
+
///
|
|
17
|
+
/// Defaults to `DefaultDataID.noneProvided` if no data ID is provided.
|
|
18
|
+
var dataID: AnyHashable { get }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// MARK: - EpoxyModeled
|
|
22
|
+
|
|
23
|
+
extension EpoxyModeled where Self: DataIDProviding {
|
|
24
|
+
|
|
25
|
+
// MARK: Internal
|
|
26
|
+
|
|
27
|
+
/// A stable identifier that uniquely identifies this model, with its typed erased.
|
|
28
|
+
var dataID: AnyHashable {
|
|
29
|
+
get { self[dataIDProperty] }
|
|
30
|
+
set { self[dataIDProperty] = newValue }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/// Returns a copy of this model with the ID replaced with the provided ID.
|
|
34
|
+
func dataID(_ value: AnyHashable) -> Self {
|
|
35
|
+
copy(updating: dataIDProperty, to: value)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// MARK: Private
|
|
39
|
+
|
|
40
|
+
private var dataIDProperty: EpoxyModelProperty<AnyHashable> {
|
|
41
|
+
EpoxyModelProperty(
|
|
42
|
+
keyPath: \DataIDProviding.dataID,
|
|
43
|
+
defaultValue: DefaultDataID.noneProvided,
|
|
44
|
+
updateStrategy: .replace)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// MARK: - DefaultDataID
|
|
49
|
+
|
|
50
|
+
/// The default data ID when none is provided.
|
|
51
|
+
enum DefaultDataID: Hashable, CustomDebugStringConvertible {
|
|
52
|
+
case noneProvided
|
|
53
|
+
|
|
54
|
+
var debugDescription: String {
|
|
55
|
+
"DefaultDataID.noneProvided"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Created by eric_horacek on 1/6/21.
|
|
2
|
+
// Copyright © 2021 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
// MARK: - DidDisplayProviding
|
|
5
|
+
|
|
6
|
+
/// A sentinel protocol for enabling an `CallbackContextEpoxyModeled` to provide a `didDisplay`
|
|
7
|
+
/// closure property.
|
|
8
|
+
///
|
|
9
|
+
/// - SeeAlso: `WillDisplayProviding`
|
|
10
|
+
/// - SeeAlso: `DidEndDisplayingProviding`
|
|
11
|
+
protocol DidDisplayProviding { }
|
|
12
|
+
|
|
13
|
+
// MARK: - CallbackContextEpoxyModeled
|
|
14
|
+
|
|
15
|
+
extension CallbackContextEpoxyModeled where Self: DidDisplayProviding {
|
|
16
|
+
|
|
17
|
+
// MARK: Internal
|
|
18
|
+
|
|
19
|
+
/// A closure that's called after a view has been added to the view hierarchy following any
|
|
20
|
+
/// appearance animations.
|
|
21
|
+
typealias DidDisplay = (_ context: CallbackContext) -> Void
|
|
22
|
+
|
|
23
|
+
/// A closure that's called after the view has been added to the view hierarchy following any
|
|
24
|
+
/// appearance animations.
|
|
25
|
+
var didDisplay: DidDisplay? {
|
|
26
|
+
get { self[didDisplayProperty] }
|
|
27
|
+
set { self[didDisplayProperty] = newValue }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/// Returns a copy of this model with the given did display closure called after the current did
|
|
31
|
+
/// display closure of this model, if there is one.
|
|
32
|
+
func didDisplay(_ value: DidDisplay?) -> Self {
|
|
33
|
+
copy(updating: didDisplayProperty, to: value)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// MARK: Private
|
|
37
|
+
|
|
38
|
+
private var didDisplayProperty: EpoxyModelProperty<DidDisplay?> {
|
|
39
|
+
.init(keyPath: \Self.didDisplay, defaultValue: nil, updateStrategy: .chain())
|
|
40
|
+
}
|
|
41
|
+
}
|
package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DidEndDisplayingProviding.swift
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Created by eric_horacek on 12/15/20.
|
|
2
|
+
// Copyright © 2020 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
// MARK: - DidEndDisplayingProviding
|
|
5
|
+
|
|
6
|
+
/// A sentinel protocol for enabling an `CallbackContextEpoxyModeled` to provide a
|
|
7
|
+
/// `didEndDisplaying` closure property.
|
|
8
|
+
protocol DidEndDisplayingProviding { }
|
|
9
|
+
|
|
10
|
+
// MARK: - CallbackContextEpoxyModeled
|
|
11
|
+
|
|
12
|
+
extension CallbackContextEpoxyModeled where Self: DidEndDisplayingProviding {
|
|
13
|
+
|
|
14
|
+
// MARK: Internal
|
|
15
|
+
|
|
16
|
+
/// A closure that's called when a view is no longer displayed following any disappearance
|
|
17
|
+
/// animations and when it has been removed from the view hierarchy.
|
|
18
|
+
typealias DidEndDisplaying = (_ context: CallbackContext) -> Void
|
|
19
|
+
|
|
20
|
+
/// A closure that's called when the view is no longer displayed following any disappearance
|
|
21
|
+
/// animations and when it has been removed from the view hierarchy.
|
|
22
|
+
var didEndDisplaying: DidEndDisplaying? {
|
|
23
|
+
get { self[didEndDisplayingProperty] }
|
|
24
|
+
set { self[didEndDisplayingProperty] = newValue }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/// Returns a copy of this model with the given did end displaying closure called after the
|
|
28
|
+
/// current did end displaying closure of this model, if there is one.
|
|
29
|
+
func didEndDisplaying(_ value: DidEndDisplaying?) -> Self {
|
|
30
|
+
copy(updating: didEndDisplayingProperty, to: value)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// MARK: Private
|
|
34
|
+
|
|
35
|
+
private var didEndDisplayingProperty: EpoxyModelProperty<DidEndDisplaying?> {
|
|
36
|
+
.init(
|
|
37
|
+
keyPath: \Self.didEndDisplaying,
|
|
38
|
+
defaultValue: nil,
|
|
39
|
+
updateStrategy: .chain())
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Created by eric_horacek on 12/2/20.
|
|
2
|
+
// Copyright © 2020 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
// MARK: - DidSelectProviding
|
|
5
|
+
|
|
6
|
+
/// A sentinel protocol for enabling an `CallbackContextEpoxyModeled` to provide a `didSelect`
|
|
7
|
+
/// closure property.
|
|
8
|
+
protocol DidSelectProviding { }
|
|
9
|
+
|
|
10
|
+
// MARK: - CallbackContextEpoxyModeled
|
|
11
|
+
|
|
12
|
+
extension CallbackContextEpoxyModeled where Self: DidSelectProviding {
|
|
13
|
+
|
|
14
|
+
// MARK: Internal
|
|
15
|
+
|
|
16
|
+
/// A closure that's called to handle this model's view being selected.
|
|
17
|
+
typealias DidSelect = (CallbackContext) -> Void
|
|
18
|
+
|
|
19
|
+
/// A closure that's called to handle this model's view being selected.
|
|
20
|
+
var didSelect: DidSelect? {
|
|
21
|
+
get { self[didSelectProperty] }
|
|
22
|
+
set { self[didSelectProperty] = newValue }
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/// Returns a copy of this model with the given did select closure called after the current did
|
|
26
|
+
/// select closure of this model, if there is one.
|
|
27
|
+
func didSelect(_ value: DidSelect?) -> Self {
|
|
28
|
+
copy(updating: didSelectProperty, to: value)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// MARK: Private
|
|
32
|
+
|
|
33
|
+
private var didSelectProperty: EpoxyModelProperty<DidSelect?> {
|
|
34
|
+
.init(keyPath: \Self.didSelect, defaultValue: nil, updateStrategy: .chain())
|
|
35
|
+
}
|
|
36
|
+
}
|
package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/ErasedContentProviding.swift
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Created by eric_horacek on 12/2/20.
|
|
2
|
+
// Copyright © 2020 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
// MARK: - ErasedContentProviding
|
|
5
|
+
|
|
6
|
+
/// The capability of providing an type-erased `Equatable` content instance.
|
|
7
|
+
protocol ErasedContentProviding {
|
|
8
|
+
/// The type-erased content instance of this model, else `nil` if there is no content.
|
|
9
|
+
///
|
|
10
|
+
/// If there was an `AnyEquatable` type, we could store this property using it. Instead we need
|
|
11
|
+
/// need to store `isErasedContentEqual` to determine equality.
|
|
12
|
+
var erasedContent: Any? { get }
|
|
13
|
+
|
|
14
|
+
/// A closure that can be called to determine whether the given `model`'s `erasedContent` is equal
|
|
15
|
+
/// to this model's `erasedContent`, else `nil` if there is no content or the content is always
|
|
16
|
+
/// equal.
|
|
17
|
+
var isErasedContentEqual: ((Self) -> Bool)? { get }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// MARK: - EpoxyModeled
|
|
21
|
+
|
|
22
|
+
extension EpoxyModeled where Self: ErasedContentProviding {
|
|
23
|
+
|
|
24
|
+
// MARK: Internal
|
|
25
|
+
|
|
26
|
+
/// The type-erased content instance of this model, else `nil` if there is no content.
|
|
27
|
+
var erasedContent: Any? {
|
|
28
|
+
get { self[contentProperty] }
|
|
29
|
+
set { self[contentProperty] = newValue }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/// A closure that can be called to determine whether the given `model`'s `erasedContent` is equal
|
|
33
|
+
/// to this model's `erasedContent`, else `nil` if there is no content or the content is always
|
|
34
|
+
/// equal.
|
|
35
|
+
var isErasedContentEqual: ((Self) -> Bool)? {
|
|
36
|
+
get { self[isContentEqualProperty] }
|
|
37
|
+
set { self[isContentEqualProperty] = newValue }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// MARK: Private
|
|
41
|
+
|
|
42
|
+
private var contentProperty: EpoxyModelProperty<Any?> {
|
|
43
|
+
.init(keyPath: \Self.erasedContent, defaultValue: nil, updateStrategy: .replace)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private var isContentEqualProperty: EpoxyModelProperty<((Self) -> Bool)?> {
|
|
47
|
+
.init(keyPath: \Self.isErasedContentEqual, defaultValue: nil, updateStrategy: .replace)
|
|
48
|
+
}
|
|
49
|
+
}
|