diff --git a/Sources/Atoms/Effect/AtomEffectBuilder.swift b/Sources/Atoms/Effect/AtomEffectBuilder.swift index b6200284..ab050961 100644 --- a/Sources/Atoms/Effect/AtomEffectBuilder.swift +++ b/Sources/Atoms/Effect/AtomEffectBuilder.swift @@ -19,17 +19,19 @@ @MainActor @resultBuilder public enum AtomEffectBuilder { + public static func buildBlock() -> some AtomEffect { + EmptyEffect() + } + public static func buildBlock(_ effect: Effect) -> Effect { effect } - public static func buildBlock(_ effect: repeat each Effect) -> some AtomEffect { - if #available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) { - return BlockEffect(repeat each effect) - } - else { - return BlockBCEffect(repeat each effect) - } + // NB: The combination of variadic type and opaque return type in iOS 16 fails to demangle + // the witness for the return type, which causes a runtime crash. + // Once iOS 16 support is dropped, this function will be changed to return an opaque type. + public static func buildBlock(_ effect: repeat each Effect) -> BlockEffect { + BlockEffect(repeat each effect) } public static func buildIf(_ effect: Effect?) -> some AtomEffect { @@ -54,6 +56,46 @@ public enum AtomEffectBuilder { } public extension AtomEffectBuilder { + // Use type pack once it is available in iOS 17 or newer. + // struct BlockEffect: AtomEffect + struct BlockEffect: AtomEffect { + private let _initializing: @MainActor (Context) -> Void + private let _initialized: @MainActor (Context) -> Void + private let _updated: @MainActor (Context) -> Void + private let _released: @MainActor (Context) -> Void + + internal init(_ effect: repeat each Effect) { + _initializing = { context in + repeat (each effect).initializing(context: context) + } + _initialized = { context in + repeat (each effect).initialized(context: context) + } + _updated = { context in + repeat (each effect).updated(context: context) + } + _released = { context in + repeat (each effect).released(context: context) + } + } + + public func initializing(context: Context) { + _initializing(context) + } + + public func initialized(context: Context) { + _initialized(context) + } + + public func updated(context: Context) { + _updated(context) + } + + public func released(context: Context) { + _released(context) + } + } + struct ConditionalEffect: AtomEffect { internal enum Storage { case trueEffect(TrueEffect) @@ -108,68 +150,7 @@ public extension AtomEffectBuilder { } private extension AtomEffectBuilder { - @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) - struct BlockEffect: AtomEffect { - private let effect: (repeat each Effect) - - init(_ effect: repeat each Effect) { - self.effect = (repeat each effect) - } - - func initializing(context: Context) { - repeat (each effect).initializing(context: context) - } - - func initialized(context: Context) { - repeat (each effect).initialized(context: context) - } - - func updated(context: Context) { - repeat (each effect).updated(context: context) - } - - func released(context: Context) { - repeat (each effect).released(context: context) - } - } - - struct BlockBCEffect: AtomEffect { - private let _initializing: @MainActor (Context) -> Void - private let _initialized: @MainActor (Context) -> Void - private let _updated: @MainActor (Context) -> Void - private let _released: @MainActor (Context) -> Void - - init(_ effect: repeat each Effect) { - _initializing = { context in - repeat (each effect).initializing(context: context) - } - _initialized = { context in - repeat (each effect).initialized(context: context) - } - _updated = { context in - repeat (each effect).updated(context: context) - } - _released = { context in - repeat (each effect).released(context: context) - } - } - - func initializing(context: Context) { - _initializing(context) - } - - func initialized(context: Context) { - _initialized(context) - } - - func updated(context: Context) { - _updated(context) - } - - func released(context: Context) { - _released(context) - } - } + struct EmptyEffect: AtomEffect {} struct IfEffect: AtomEffect { private let effect: Effect?