CKSwift/SwiftComponent.swift (95 lines of code) (raw):

/* * Copyright (c) 2014-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * */ import Foundation import ComponentKit public struct SwiftComponentModel { struct LifecycleCallbacks { typealias Callback = @convention(block) () -> Void var didInit: [Callback] = [] var willMount: [Callback] = [] var didUnmount: [Callback] = [] var willDispose: [Callback] = [] var isEmpty: Bool { didUnmount.isEmpty && willMount.isEmpty && didUnmount.isEmpty && willDispose.isEmpty } } struct Animations { var animations: [CAAnimation] = [] var initialMount: [CAAnimation] = [] var finalUnmount: [CAAnimation] = [] var isEmpty: Bool { animations.isEmpty && initialMount.isEmpty && finalUnmount.isEmpty } } func toSwiftBridge() -> SwiftComponentModelSwiftBridge? { guard isEmpty == false else { return nil } return SwiftComponentModelSwiftBridge( animation: animations.animations.groupAnimationOrNil(), initialMountAnimation: animations.initialMount.groupAnimationOrNil(), finalUnmountAnimation: animations.finalUnmount.groupAnimationOrNil(fillMode: .forwards), didInitCallbacks: lifecycleCallbacks.didInit.isEmpty ? nil : lifecycleCallbacks.didInit, willMountCallbacks: lifecycleCallbacks.willMount.isEmpty ? nil : lifecycleCallbacks.willMount, didUnmountCallbacks: lifecycleCallbacks.didUnmount.isEmpty ? nil : lifecycleCallbacks.didUnmount, willDisposeCallbacks: lifecycleCallbacks.willDispose.isEmpty ? nil : lifecycleCallbacks.willDispose ) } var lifecycleCallbacks = LifecycleCallbacks() var animations = Animations() var isEmpty: Bool { lifecycleCallbacks.isEmpty && animations.isEmpty } } class SwiftComponent<View: CKSwift.View> : CKSwiftComponent { let view: View init(_ view: View, body: Component? = nil, viewConfiguration: ViewConfiguration? = nil, size: ComponentSize? = nil, model: SwiftComponentModel?) { self.view = view super.init( swiftView: viewConfiguration?.viewConfiguration, swiftSize: size?.componentSize, child: body, model: model?.toSwiftBridge()) } init(_ shellComponent: SwiftComponent<View>, body: Component? = nil) { self.view = shellComponent.view super.init( fromShellComponent: shellComponent, child: body ) } override var typeName: UnsafePointer<Int8> { // Swift components can either be a `SwiftComponent<View>`, `SwiftReusableComponent<View>` or // `SwiftReusableLeafComponent<View>` based on the capabilities of `View`. // Always use `SwiftComponent<View>` as the `typeName` so that CKSwift's infra doesn't have // to know which concrete subclass is being used. Really what's important is the `View` part // which suffices to identify the component. class_getName(SwiftComponent<View>.self) } } class SwiftReusableBaseComponent<View: ReusableView> : SwiftComponent<View>, ReusableComponentProtocol { var componentIdentifier: Any? { view.id } func didReuseComponent(_ component: ReusableComponentProtocol) { fatalError("Should never be called") } func shouldComponentUpdate(_ untypedComponent: ReusableComponentProtocol) -> Bool { guard let component = untypedComponent as? SwiftReusableBaseComponent<View> else { fatalError("Attempting to reuse a component of a different type: \(type(of: untypedComponent))") } return view != component.view } func clone() -> Self { fatalError("-clone should never be called on `SwiftReusableBaseComponent`.") } } final class SwiftReusableComponent<View: ReusableView> : SwiftReusableBaseComponent<View> where View.Body == Component { override func clone() -> Self { // TODO: Reuse logic view.linkPropertyWrappersWithScopeHandle() defer { CKSwiftPopClass() } return SwiftReusableComponent(self, body: view.body) as! Self } } final class SwiftReusableLeafComponent<View: ReusableView> : SwiftReusableBaseComponent<View> where View.Body == Never { override func clone() -> Self { // TODO: Reuse logic view.linkPropertyWrappersWithScopeHandle() defer { CKSwiftPopClass() } return SwiftReusableLeafComponent(self) as! Self } }