Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Fix Actor #1

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
2 changes: 2 additions & 0 deletions Carbon.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -773,6 +773,7 @@
PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
Expand Down Expand Up @@ -804,6 +805,7 @@
PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
Expand Down
2 changes: 1 addition & 1 deletion Cartfile
Original file line number Diff line number Diff line change
@@ -1 +1 @@
github "ra1028/DifferenceKit" ~> 1.1
github "ra1028/DifferenceKit" ~> 1.3
2 changes: 1 addition & 1 deletion Cartfile.resolved
Original file line number Diff line number Diff line change
@@ -1 +1 @@
github "ra1028/DifferenceKit" "1.1.4"
github "ra1028/DifferenceKit" "1.3.0"
3 changes: 2 additions & 1 deletion Sources/Adapters/Adapter.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import Foundation

/// Represents an adapter that holds data to be rendered.
public protocol Adapter: class {
@MainActor
public protocol Adapter: AnyObject {
/// The data to be rendered.
var data: [Section] { get set }
}
Expand Down
1 change: 1 addition & 0 deletions Sources/Adapters/UICollectionViewAdapter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import UIKit
/// Attention : In UIKit, if inheriting the @objc class which using generics, the delegate and dataSource
/// are don't work properly, so this class doesn't use generics, and also the class inherited
/// this class shouldn't use generics.
@MainActor
open class UICollectionViewAdapter: NSObject, Adapter {
/// The data to be rendered in the list UI.
public var data: [Section]
Expand Down
3 changes: 2 additions & 1 deletion Sources/Adapters/UITableViewAdapter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import UIKit
/// Attention : In UIKit, if inheriting the @objc class which using generics, the delegate and dataSource
/// are don't work properly, so this class doesn't use generics, and also the class inherited
/// this class shouldn't use generics.
@MainActor
open class UITableViewAdapter: NSObject, Adapter {
/// The data to be rendered in the list UI.
public var data: [Section]
Expand All @@ -18,7 +19,7 @@ open class UITableViewAdapter: NSObject, Adapter {
///
/// - Parameters:
/// - data: An initial data to be rendered.
public init(data: [Section] = []) {
public init(data: [Section]) {
self.data = data
}

Expand Down
21 changes: 18 additions & 3 deletions Sources/AnyComponent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public struct AnyComponent: Component {
/// - Returns: The referencing size of content to render on the list UI.
/// If returns nil, the element of list UI falls back to its default size
/// like `UITableView.rowHeight` or `UICollectionViewFlowLayout.itemSize`.
@inlinable
@MainActor @inlinable
public func referenceSize(in bounds: CGRect) -> CGSize? {
return box.referenceSize(in: bounds)
}
Expand Down Expand Up @@ -152,15 +152,30 @@ internal protocol AnyComponentBox {
var base: Any { get }
var reuseIdentifier: String { get }

@MainActor
func renderContent() -> Any

@MainActor
func render(in content: Any)

@MainActor
func referenceSize(in bounds: CGRect) -> CGSize?

@MainActor
func layout(content: Any, in container: UIView)

@MainActor
func intrinsicContentSize(for content: Any) -> CGSize

func shouldContentUpdate(with next: AnyComponentBox) -> Bool

@MainActor
func shouldRender(next: AnyComponentBox, in content: Any) -> Bool

@MainActor
func contentWillDisplay(_ content: Any)

@MainActor
func contentDidEndDisplay(_ content: Any)
}

Expand All @@ -184,12 +199,12 @@ internal struct ComponentBox<Base: Component>: AnyComponentBox {
baseComponent = base
}

@inlinable
@MainActor @inlinable
func renderContent() -> Any {
return baseComponent.renderContent()
}

@inlinable
@MainActor @inlinable
func render(in content: Any) {
guard let content = content as? Base.Content else { return }

Expand Down
13 changes: 13 additions & 0 deletions Sources/Component.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,14 @@ public protocol Component {
/// Returns a new instance of `Content`.
///
/// - Returns: A new `Content` instance.
@MainActor
func renderContent() -> Content

/// Render properties into the content.
///
/// - Parameter:
/// - content: An instance of `Content` laid out on the element of list UI.
@MainActor
func render(in content: Content)

// MARK: - Rendering - optional
Expand All @@ -68,6 +70,7 @@ public protocol Component {
/// - Returns: The referencing size of content to render on the list UI.
/// If returns nil, the element of list UI falls back to its default size
/// like `UITableView.rowHeight` or `UICollectionViewFlowLayout.itemSize`.
@MainActor
func referenceSize(in bounds: CGRect) -> CGSize?

/// Returns a `Bool` value indicating whether the content should be reloaded.
Expand All @@ -89,6 +92,7 @@ public protocol Component {
/// - content: An instance of content laid out on the element.
///
/// - Returns: A `Bool` value indicating whether the component should be render again.
@MainActor
func shouldRender(next: Self, in content: Content) -> Bool

/// Layout the content on top of element of the list UI.
Expand All @@ -98,6 +102,7 @@ public protocol Component {
/// - Parameters:
/// - content: An instance of content to be laid out on top of element.
/// - container: A container view to layout content.
@MainActor
func layout(content: Content, in container: UIView)

/// The natural size for the passed content.
Expand All @@ -106,6 +111,7 @@ public protocol Component {
/// - content: An instance of content.
///
/// - Returns: A `CGSize` value represents a natural size of the passed content.
@MainActor
func intrinsicContentSize(for content: Content) -> CGSize

// MARK: - Lifecycle - optional
Expand All @@ -114,12 +120,14 @@ public protocol Component {
///
/// - Parameter:
/// - content: An instance of content getting into display area.
@MainActor
func contentWillDisplay(_ content: Content)

/// Invoked every time of after a component went out from visible area.
///
/// - Parameter:
/// - content: An instance of content going out from display area.
@MainActor
func contentDidEndDisplay(_ content: Content)
}

Expand Down Expand Up @@ -196,6 +204,7 @@ public extension Component where Content: UIView {
/// - content: An instance of content to be laid out on top of element.
/// - container: A container view to layout content.
/// Default is laid out with edge constraints.
@MainActor
func layout(content: Content, in container: UIView) {
container.addSubviewWithEdgeConstraints(content)
}
Expand All @@ -206,6 +215,7 @@ public extension Component where Content: UIView {
/// - content: An instance of content.
///
/// - Returns: A `CGSize` value represents a natural size of the passed content.
@MainActor
func intrinsicContentSize(for content: Content) -> CGSize {
return content.intrinsicContentSize
}
Expand All @@ -218,6 +228,7 @@ public extension Component where Content: UIViewController {
/// - content: An instance of content to be laid out on top of element.
/// - container: A container view to layout content.
/// Default is laid out with edge constraints.
@MainActor
func layout(content: Content, in container: UIView) {
container.addSubviewWithEdgeConstraints(content.view)
}
Expand All @@ -228,12 +239,14 @@ public extension Component where Content: UIViewController {
/// - content: An instance of content.
///
/// - Returns: A `CGSize` value represents a natural size of the passed content.
@MainActor
func intrinsicContentSize(for content: Content) -> CGSize {
return content.view.intrinsicContentSize
}
}

private extension UIView {
@MainActor
func addSubviewWithEdgeConstraints(_ view: UIView) {
view.translatesAutoresizingMaskIntoConstraints = false
addSubview(view)
Expand Down
15 changes: 8 additions & 7 deletions Sources/ComponentWrapper/ComponentWrapping.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public extension ComponentWrapping {
/// Returns a new instance of `Content`.
///
/// - Returns: A new `Content` instance.
@inlinable
@MainActor @inlinable
func renderContent() -> Wrapped.Content {
return wrapped.renderContent()
}
Expand All @@ -38,7 +38,7 @@ public extension ComponentWrapping {
///
/// - Parameter:
/// - content: An instance of `Content` laid out on the element of list UI.
@inlinable
@MainActor @inlinable
func render(in content: Wrapped.Content) {
wrapped.render(in: content)
}
Expand All @@ -55,7 +55,7 @@ public extension ComponentWrapping {
/// - Returns: The referencing size of content to render on the list UI.
/// If returns nil, the element of list UI falls back to its default size
/// like `UITableView.rowHeight` or `UICollectionViewFlowLayout.itemSize`.
@inlinable
@MainActor @inlinable
func referenceSize(in bounds: CGRect) -> CGSize? {
return wrapped.referenceSize(in: bounds)
}
Expand All @@ -82,7 +82,7 @@ public extension ComponentWrapping {
/// - content: An instance of content laid out on the element.
///
/// - Returns: A `Bool` value indicating whether the component should be render again.
@inlinable
@MainActor @inlinable
func shouldRender(next: Self, in content: Wrapped.Content) -> Bool {
return wrapped.shouldRender(next: next.wrapped, in: content)
}
Expand All @@ -94,7 +94,7 @@ public extension ComponentWrapping {
/// - Parameters:
/// - content: An instance of content to be laid out on top of element.
/// - container: A container view to layout content.
@inlinable
@MainActor @inlinable
func layout(content: Wrapped.Content, in container: UIView) {
wrapped.layout(content: content, in: container)
}
Expand All @@ -105,6 +105,7 @@ public extension ComponentWrapping {
/// - content: An instance of content.
///
/// - Returns: A `CGSize` value represents a natural size of the passed content.
@MainActor
func intrinsicContentSize(for content: Wrapped.Content) -> CGSize {
return wrapped.intrinsicContentSize(for: content)
}
Expand All @@ -113,7 +114,7 @@ public extension ComponentWrapping {
///
/// - Parameter:
/// - content: An instance of content getting into display area.
@inlinable
@MainActor @inlinable
func contentWillDisplay(_ content: Wrapped.Content) {
wrapped.contentWillDisplay(content)
}
Expand All @@ -122,7 +123,7 @@ public extension ComponentWrapping {
///
/// - Parameter:
/// - content: An instance of content going out from display area.
@inlinable
@MainActor @inlinable
func contentDidEndDisplay(_ content: Wrapped.Content) {
wrapped.contentDidEndDisplay(content)
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/FunctionBuilder/CellsBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// swiftlint:disable function_parameter_count

/// The custom parameter attribute that constructs cells from multi-statement closures.
@_functionBuilder
@resultBuilder
public struct CellsBuilder: CellsBuildable {
@usableFromInline
internal var cellNodes: [CellNode]
Expand Down
2 changes: 1 addition & 1 deletion Sources/FunctionBuilder/SectionsBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// swiftlint:disable function_parameter_count

/// The custom parameter attribute that constructs sections from multi-statement closures.
@_functionBuilder
@resultBuilder
public struct SectionsBuilder: SectionsBuildable {
@usableFromInline
internal var sections: [Section]
Expand Down
3 changes: 2 additions & 1 deletion Sources/Interfaces/ComponentRenderable.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import UIKit

/// Represents a container that can render a component.
public protocol ComponentRenderable: class {
@MainActor
public protocol ComponentRenderable: AnyObject {
/// The container view to be render a component.
var componentContainerView: UIView { get }
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/Internal/RuntimeAssociation.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import ObjectiveC

internal final class RuntimeAssociation<Value> {
internal final class RuntimeAssociation<Value>: @unchecked Sendable {
private let key = UnsafeMutablePointer<UInt8>.allocate(capacity: 1)
private let defaultValue: () -> Value

Expand Down
1 change: 1 addition & 0 deletions Sources/Renderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import UIKit
/// Label("Cell 3")
/// .identified(by: \.text)
/// }
@MainActor
open class Renderer<Updater: Carbon.Updater> {
/// An instance of adapter that specified at initialized.
public let adapter: Updater.Adapter
Expand Down
26 changes: 25 additions & 1 deletion Sources/SwiftUISupport/ComponentSwiftUISupport.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#if canImport(SwiftUI) && canImport(Combine)
#if canImport(SwiftUI) && !(os(iOS) && (arch(i386) || arch(arm)))

import SwiftUI

Expand Down Expand Up @@ -38,6 +38,30 @@ private struct ComponentRepresenting<C: Component>: UIViewRepresentable {
uiView.render(component: AnyComponent(component))
proxy.uiView = uiView
}

@available(iOS 16.0, *)
func sizeThatFits(
_ proposal: ProposedViewSize,
uiView: UIComponentView,
context: Context
) -> CGSize? {
if let width = proposal.width, width.isFinite {
uiView.bounds.size.width = width
}
if let height = proposal.height, height.isFinite {
uiView.bounds.size.height = height
}

if uiView.intrinsicContentSize != CGSize(width: UIView.noIntrinsicMetric, height: UIView.noIntrinsicMetric) {
return uiView.intrinsicContentSize
}

return uiView.systemLayoutSizeFitting(
proposal.replacingUnspecifiedDimensions(by: UIView.layoutFittingCompressedSize),
withHorizontalFittingPriority: .required,
verticalFittingPriority: .fittingSizeLevel
)
}
}

private final class UIComponentView: UIView, ComponentRenderable {
Expand Down
2 changes: 2 additions & 0 deletions Sources/Updaters/UICollectionViewUpdater.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import UIKit
import DifferenceKit

/// An updater for managing diffing updates to render data to the `UICollectionView`.
@MainActor
open class UICollectionViewUpdater<Adapter: UICollectionViewAdapter>: Updater {
/// A Bool value indicating whether that enable diffing animation. Default is true.
open var isAnimationEnabled = true
Expand Down
1 change: 1 addition & 0 deletions Sources/Updaters/UITableViewReloadDataUpdater.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import UIKit

/// An updater for managing to perform reload data to render data to the `UITableView`.
@MainActor
open class UITableViewReloadDataUpdater<Adapter: UITableViewAdapter>: Updater {
/// Create a new updater.
public init() {}
Expand Down
2 changes: 2 additions & 0 deletions Sources/Updaters/UITableViewUpdater.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import UIKit
import DifferenceKit

/// An updater for managing diffing updates to render data to the `UITableView`.
@MainActor
open class UITableViewUpdater<Adapter: UITableViewAdapter>: Updater {
/// An animation for section deletions. Default is fade.
open var deleteSectionsAnimation = UITableView.RowAnimation.fade
Expand Down
1 change: 1 addition & 0 deletions Sources/Updaters/Updater.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/// Represents an updater that manages the updation for target.
@MainActor
public protocol Updater {
/// A type that represents a target to be updated for render given data.
associatedtype Target: AnyObject
Expand Down
Loading