Skip to content

Commit f7c6499

Browse files
author
Kyle Van Essen
committed
oh hell ya
1 parent 0435437 commit f7c6499

6 files changed

Lines changed: 102 additions & 5 deletions

File tree

BlueprintUILists/Sources/Element+HeaderFooter.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,14 @@ extension Element {
4040
configure: configure
4141
)
4242
}
43+
44+
/// Used by internal Listable methods to convert type-erased `Element` instances into `Item` instances.
45+
func toHeaderFooterConvertible() -> AnyHeaderFooterConvertible {
46+
/// We use `type(of:)` to ensure we get the actual type, not just `Element`.
47+
WrappedHeaderFooterContent(
48+
represented: self
49+
)
50+
}
4351
}
4452

4553

BlueprintUILists/Sources/List.swift

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public struct List : Element
5959
//
6060
// MARK: Initialization
6161
//
62-
62+
6363
/// Create a new list, configured with the provided properties,
6464
/// configured with the provided `ListProperties` builder.
6565
public init(
@@ -76,13 +76,26 @@ public struct List : Element
7676
public init(
7777
measurement : List.Measurement = .fillParent,
7878
configure : ListProperties.Configure = { _ in },
79-
@ListableBuilder<Section> sections : () -> [Section]
79+
@ListableBuilder<Section> sections : () -> [Section],
80+
@ListableOptionalBuilder<AnyHeaderFooterConvertible> containerHeader : () -> AnyHeaderFooterConvertible? = { nil },
81+
@ListableOptionalBuilder<AnyHeaderFooterConvertible> header : () -> AnyHeaderFooterConvertible? = { nil },
82+
@ListableOptionalBuilder<AnyHeaderFooterConvertible> footer : () -> AnyHeaderFooterConvertible? = { nil },
83+
@ListableOptionalBuilder<AnyHeaderFooterConvertible> overscrollFooter : () -> AnyHeaderFooterConvertible? = { nil }
8084
) {
8185
self.measurement = measurement
8286

83-
self.properties = .default(with: configure)
87+
var properties = ListProperties.default {
88+
$0.sections = sections()
89+
90+
$0.content.containerHeader = containerHeader()
91+
$0.content.header = header()
92+
$0.content.footer = footer()
93+
$0.content.overscrollFooter = overscrollFooter()
94+
}
95+
96+
configure(&properties)
8497

85-
self.properties.sections += sections()
98+
self.properties = properties
8699
}
87100

88101
//

BlueprintUILists/Sources/ListableBuilder+Element.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,28 @@ public extension ListableBuilder where ContentType == AnyItemConvertible {
7272
}
7373
}
7474

75+
76+
public extension ListableOptionalBuilder where ContentType == AnyHeaderFooterConvertible {
77+
78+
79+
/// Enables support for `if` statements that do not have an `else`.
80+
@_disfavoredOverload static func buildOptional(_ component: Element?) -> Component {
81+
component?.toHeaderFooterConvertible()
82+
}
83+
84+
/// With buildEither(second:), enables support for 'if-else' and 'switch' statements by folding conditional results into a single result.
85+
@_disfavoredOverload static func buildEither(first component: Element) -> Component {
86+
component.toHeaderFooterConvertible()
87+
}
88+
89+
/// With buildEither(first:), enables support for 'if-else' and 'switch' statements by folding conditional results into a single result.
90+
@_disfavoredOverload static func buildEither(second component: Element) -> Component {
91+
component.toHeaderFooterConvertible()
92+
}
93+
94+
/// If declared, this will be called on the partial result of an `if #available` block to allow the result builder to erase type information.
95+
@_disfavoredOverload static func buildLimitedAvailability(_ component: Element) -> Component {
96+
component.toHeaderFooterConvertible()
97+
}
98+
}
99+

ListableUI/Sources/HeaderFooter/HeaderFooter.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public typealias Header<Content:HeaderFooterContent> = HeaderFooter<Content>
1212
public typealias Footer<Content:HeaderFooterContent> = HeaderFooter<Content>
1313

1414

15-
public struct HeaderFooter<Content:HeaderFooterContent> : AnyHeaderFooter
15+
public struct HeaderFooter<Content:HeaderFooterContent> : AnyHeaderFooter, AnyHeaderFooterConvertible
1616
{
1717
public var content : Content
1818

ListableUI/Sources/HeaderFooter/HeaderFooterContent.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,8 @@ public extension HeaderFooterContent {
182182
func asAnyHeaderFooter() -> AnyHeaderFooter {
183183
HeaderFooter(self)
184184
}
185+
186+
185187
}
186188

187189

ListableUI/Sources/ListableBuilder.swift

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,52 @@
8686
component
8787
}
8888
}
89+
90+
91+
///
92+
/// A result builder which can be used to provide a SwiftUI-like DSL for building a single item.
93+
///
94+
/// You provide a result builder in an API by specifying it as a method parameter, like so:
95+
///
96+
/// ```
97+
/// init(@ListableBuilder<SomeContent> thing : () -> SomeContent) {
98+
/// self.thing = thing()
99+
/// }
100+
/// ```
101+
///
102+
/// ## Links & Videos
103+
/// https://github.com/apple/swift-evolution/blob/main/proposals/0289-result-builders.md
104+
/// https://developer.apple.com/videos/play/wwdc2021/10253/
105+
/// https://www.swiftbysundell.com/articles/deep-dive-into-swift-function-builders/
106+
/// https://www.avanderlee.com/swift/result-builders/
107+
///
108+
@resultBuilder public enum ListableOptionalBuilder<ContentType> {
109+
110+
/// The final value from the builder.
111+
public typealias Component = ContentType?
112+
113+
/// If an empty closure is provided, returns an empty array.
114+
public static func buildBlock() -> Component {
115+
nil
116+
}
117+
118+
/// Enables support for `if` statements that do not have an `else`.
119+
public static func buildOptional(_ component: Component?) -> Component {
120+
component ?? nil
121+
}
122+
123+
/// With buildEither(second:), enables support for 'if-else' and 'switch' statements by folding conditional results into a single result.
124+
public static func buildEither(first component: Component) -> Component {
125+
component
126+
}
127+
128+
/// With buildEither(first:), enables support for 'if-else' and 'switch' statements by folding conditional results into a single result.
129+
public static func buildEither(second component: Component) -> Component {
130+
component
131+
}
132+
133+
/// If declared, this will be called on the partial result of an `if #available` block to allow the result builder to erase type information.
134+
public static func buildLimitedAvailability(_ component: Component) -> Component {
135+
component
136+
}
137+
}

0 commit comments

Comments
 (0)