Skip to content

Commit

Permalink
oh hell ya
Browse files Browse the repository at this point in the history
  • Loading branch information
kyleve committed Jul 25, 2022
1 parent 0435437 commit f7c6499
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 5 deletions.
8 changes: 8 additions & 0 deletions BlueprintUILists/Sources/Element+HeaderFooter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ extension Element {
configure: configure
)
}

/// Used by internal Listable methods to convert type-erased `Element` instances into `Item` instances.
func toHeaderFooterConvertible() -> AnyHeaderFooterConvertible {
/// We use `type(of:)` to ensure we get the actual type, not just `Element`.
WrappedHeaderFooterContent(
represented: self
)
}
}


Expand Down
21 changes: 17 additions & 4 deletions BlueprintUILists/Sources/List.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public struct List : Element
//
// MARK: Initialization
//

/// Create a new list, configured with the provided properties,
/// configured with the provided `ListProperties` builder.
public init(
Expand All @@ -76,13 +76,26 @@ public struct List : Element
public init(
measurement : List.Measurement = .fillParent,
configure : ListProperties.Configure = { _ in },
@ListableBuilder<Section> sections : () -> [Section]
@ListableBuilder<Section> sections : () -> [Section],
@ListableOptionalBuilder<AnyHeaderFooterConvertible> containerHeader : () -> AnyHeaderFooterConvertible? = { nil },
@ListableOptionalBuilder<AnyHeaderFooterConvertible> header : () -> AnyHeaderFooterConvertible? = { nil },
@ListableOptionalBuilder<AnyHeaderFooterConvertible> footer : () -> AnyHeaderFooterConvertible? = { nil },
@ListableOptionalBuilder<AnyHeaderFooterConvertible> overscrollFooter : () -> AnyHeaderFooterConvertible? = { nil }
) {
self.measurement = measurement

self.properties = .default(with: configure)
var properties = ListProperties.default {
$0.sections = sections()

$0.content.containerHeader = containerHeader()
$0.content.header = header()
$0.content.footer = footer()
$0.content.overscrollFooter = overscrollFooter()
}

configure(&properties)

self.properties.sections += sections()
self.properties = properties
}

//
Expand Down
25 changes: 25 additions & 0 deletions BlueprintUILists/Sources/ListableBuilder+Element.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,28 @@ public extension ListableBuilder where ContentType == AnyItemConvertible {
}
}


public extension ListableOptionalBuilder where ContentType == AnyHeaderFooterConvertible {


/// Enables support for `if` statements that do not have an `else`.
@_disfavoredOverload static func buildOptional(_ component: Element?) -> Component {
component?.toHeaderFooterConvertible()
}

/// With buildEither(second:), enables support for 'if-else' and 'switch' statements by folding conditional results into a single result.
@_disfavoredOverload static func buildEither(first component: Element) -> Component {
component.toHeaderFooterConvertible()
}

/// With buildEither(first:), enables support for 'if-else' and 'switch' statements by folding conditional results into a single result.
@_disfavoredOverload static func buildEither(second component: Element) -> Component {
component.toHeaderFooterConvertible()
}

/// If declared, this will be called on the partial result of an `if #available` block to allow the result builder to erase type information.
@_disfavoredOverload static func buildLimitedAvailability(_ component: Element) -> Component {
component.toHeaderFooterConvertible()
}
}

2 changes: 1 addition & 1 deletion ListableUI/Sources/HeaderFooter/HeaderFooter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public typealias Header<Content:HeaderFooterContent> = HeaderFooter<Content>
public typealias Footer<Content:HeaderFooterContent> = HeaderFooter<Content>


public struct HeaderFooter<Content:HeaderFooterContent> : AnyHeaderFooter
public struct HeaderFooter<Content:HeaderFooterContent> : AnyHeaderFooter, AnyHeaderFooterConvertible
{
public var content : Content

Expand Down
2 changes: 2 additions & 0 deletions ListableUI/Sources/HeaderFooter/HeaderFooterContent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@ public extension HeaderFooterContent {
func asAnyHeaderFooter() -> AnyHeaderFooter {
HeaderFooter(self)
}


}


Expand Down
49 changes: 49 additions & 0 deletions ListableUI/Sources/ListableBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,52 @@
component
}
}


///
/// A result builder which can be used to provide a SwiftUI-like DSL for building a single item.
///
/// You provide a result builder in an API by specifying it as a method parameter, like so:
///
/// ```
/// init(@ListableBuilder<SomeContent> thing : () -> SomeContent) {
/// self.thing = thing()
/// }
/// ```
///
/// ## Links & Videos
/// https://github.com/apple/swift-evolution/blob/main/proposals/0289-result-builders.md
/// https://developer.apple.com/videos/play/wwdc2021/10253/
/// https://www.swiftbysundell.com/articles/deep-dive-into-swift-function-builders/
/// https://www.avanderlee.com/swift/result-builders/
///
@resultBuilder public enum ListableOptionalBuilder<ContentType> {

/// The final value from the builder.
public typealias Component = ContentType?

/// If an empty closure is provided, returns an empty array.
public static func buildBlock() -> Component {
nil
}

/// Enables support for `if` statements that do not have an `else`.
public static func buildOptional(_ component: Component?) -> Component {
component ?? nil
}

/// With buildEither(second:), enables support for 'if-else' and 'switch' statements by folding conditional results into a single result.
public static func buildEither(first component: Component) -> Component {
component
}

/// With buildEither(first:), enables support for 'if-else' and 'switch' statements by folding conditional results into a single result.
public static func buildEither(second component: Component) -> Component {
component
}

/// If declared, this will be called on the partial result of an `if #available` block to allow the result builder to erase type information.
public static func buildLimitedAvailability(_ component: Component) -> Component {
component
}
}

0 comments on commit f7c6499

Please sign in to comment.