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

@FormBuilder #2242

Merged
merged 11 commits into from
Oct 4, 2023
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,22 @@ form +++ {
}
```

#### @FormBuilder
```swift
@FormBuilder
var form: Form {
Section("Section A") { section in
section.tag = "Section_A"
}
if true {
Section("Section B") { section in
section.tag = "Section_B"
}
}
NameRow("NameRow_f1") { $0.title = "Name" }
}
```

### Using the callbacks

Eureka includes callbacks to change the appearance and behavior of a row.
Expand Down
47 changes: 45 additions & 2 deletions Source/Core/ResultBuilders.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,22 @@ extension Array: RowsProvider where Element == BaseRow {
public var rows: [BaseRow] { self }
}

public protocol SectionsProvider {
var sections: [Section] { get }
}

extension Section: SectionsProvider {
public var sections: [Section] { [self] }
}

extension Array: SectionsProvider where Element == Section {
public var sections: [Section] { self }
}

extension BaseRow: SectionsProvider {
public var sections: [Section] { [.init([self])] }
}

@resultBuilder
public struct SectionBuilder {
public static func buildBlock(_ components: RowsProvider...) -> [BaseRow] {
Expand All @@ -57,8 +73,35 @@ public struct SectionBuilder {
components?.flatMap { $0.rows } ?? []
}

public static func buildExpression(_ expression: BaseRow?) -> [BaseRow] {
expression.flatMap { [$0] } ?? []
public static func buildExpression(_ expression: RowsProvider?) -> [BaseRow] {
expression.flatMap { $0.rows } ?? []
}
}

@resultBuilder
public struct FormBuilder {
public static func buildBlock(_ components: SectionsProvider...) -> [Section] {
components.flatMap { $0.sections }
}

public static func buildFinalResult(_ components: [Section]) -> Form {
.init(components)
}

public static func buildEither(first components: [SectionsProvider]) -> [Section] {
components.flatMap { $0.sections }
}

public static func buildEither(second components: [SectionsProvider]) -> [Section] {
components.flatMap { $0.sections }
}

public static func buildOptional(_ components: [SectionsProvider]?) -> [Section] {
components?.flatMap { $0.sections } ?? []
}

public static func buildExpression(_ expression: SectionsProvider?) -> [Section] {
expression.flatMap { $0.sections } ?? []
}
}
#endif
71 changes: 59 additions & 12 deletions Tests/ResultBuildersTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,40 +35,87 @@ class ResultBuildersTests: BaseEurekaTests {
}
DecimalRow("DecimalRow_f1") { $0.title = "Decimal" }
}

@FormBuilder
var form: Form {
Section("Section A") { section in
section.tag = "Section_A"
}
if true {
Section("Section B") { section in
section.tag = "Section_B"
}
}
NameRow("NameRow_f1") { $0.title = "Name" }
}
#endif

private var checkBuildEither = false
private var checkBuildExpressionBaseRowOptional = false

func testSectionBuilder() {
#if swift(>=5.4)
var checkBuildEither = false
setupManySectionsForm()
addMoreItemsToManySectionsForm()

XCTAssertNotNil(manySectionsForm.rowBy(tag: "NameRow_f1"))
XCTAssertNotNil(manySectionsForm.rowBy(tag: "IntRow_f1"))
XCTAssertNotNil(manySectionsForm.rowBy(tag: "DecimalRow_f1"))
XCTAssertNotNil(manySectionsForm.rowBy(tag: "UrlRow_f1"))
XCTAssertNotNil(manySectionsForm.rowBy(tag: "TwitterRow_f1"))
XCTAssertNil(manySectionsForm.rowBy(tag: "TwitterRow_f2"))
XCTAssertNil(manySectionsForm.rowBy(tag: "EmailRow_f1"))
XCTAssertNotNil(manySectionsForm.rowBy(tag: "AccountRow_f1"))
XCTAssertNotNil(manySectionsForm.rowBy(tag: "PhoneRow_f1"))
XCTAssertNil(manySectionsForm.rowBy(tag: "PhoneRow_f2"))
XCTAssertNotNil(manySectionsForm.rowBy(tag: "ZipCodeRow_f1"))
XCTAssertNotNil(manySectionsForm.rowBy(tag: "PasswordRow_f1"))
#endif
}

private func setupManySectionsForm() {
checkBuildEither = false
checkBuildExpressionBaseRowOptional = true
manySectionsForm = (section1 +++ {
URLRow("UrlRow_f1") { $0.title = "Url" }
if checkBuildEither {
TwitterRow("TwitterRow_f2") { $0.title = "Twitter" }
} else {
TwitterRow("TwitterRow_f1") { $0.title = "Twitter" }
}
if checkBuildExpressionBaseRowOptional {
nil
} else {
EmailRow("EmailRow_f1") { $0.title = "Email" }
}
AccountRow("AccountRow_f1") { $0.title = "Account" }
})
checkBuildEither = true
}

private func addMoreItemsToManySectionsForm() {
checkBuildEither.toggle()
checkBuildExpressionBaseRowOptional.toggle()
manySectionsForm +++ {
if checkBuildEither {
PhoneRow("PhoneRow_f1") { $0.title = "Phone" }
} else {
PhoneRow("PhoneRow_f2") { $0.title = "Phone" }
}
if checkBuildExpressionBaseRowOptional {
nil
} else {
ZipCodeRow("ZipCodeRow_f1") { $0.title = "Zip Code" }
}
PasswordRow("PasswordRow_f1") { $0.title = "Password" }
}

}

func testFormBuilder() {
#if swift(>=5.4)
manySectionsForm = form
XCTAssertNotNil(manySectionsForm.sectionBy(tag: "Section_A"))
XCTAssertNotNil(manySectionsForm.sectionBy(tag: "Section_B"))
XCTAssertNotNil(manySectionsForm.rowBy(tag: "NameRow_f1"))
XCTAssertNotNil(manySectionsForm.rowBy(tag: "IntRow_f1"))
XCTAssertNotNil(manySectionsForm.rowBy(tag: "DecimalRow_f1"))
XCTAssertNotNil(manySectionsForm.rowBy(tag: "UrlRow_f1"))
XCTAssertNotNil(manySectionsForm.rowBy(tag: "TwitterRow_f1"))
XCTAssertNil(manySectionsForm.rowBy(tag: "TwitterRow_f2"))
XCTAssertNotNil(manySectionsForm.rowBy(tag: "AccountRow_f1"))
XCTAssertNotNil(manySectionsForm.rowBy(tag: "PhoneRow_f1"))
XCTAssertNil(manySectionsForm.rowBy(tag: "PhoneRow_f2"))
XCTAssertNotNil(manySectionsForm.rowBy(tag: "PasswordRow_f1"))
#endif
}
}