My team delivers a UI SDK implemented with UIKit. I'm investigating the best practices for how to ship a framework with reusable UI controls implemented in SwiftUI.
One feature I really like in SwiftUI is @ViewBuilder. I could see one path for us is to ship a TableViewCell (well, the replacement) which has our classic API with String and UILabel-like parameters, but also provide a @ViewBuilder parameter that lets the developer do more significant customizations than we support in the control.
I have this working nicely, where the @ViewBuilder property is required. But, it does not support an optional parameter, which I think is necessary for the struct init(...) method to behave as expected. Per the Swift 5.1 fix, I should be able to init a struct with only the un-inited parameters; I should be able to ignore the optional or pre-defined parameters.
Desired behavior:
ObjectView(title: "Hello, World")
Current behavior:
ObjectView(title: "Hello, World", main: { EmptyView() })
Working example where @ViewBuilder parameter is required.
// UIKit View
class ObjectTableViewCell: UITableViewCell {
var title: String?
var titleLabel: UILabel
var contentView: UIView // inherited from table view cell, gives developer ability to customize layout
}
// SwiftUI View
class ObjectView: View {
var title: String? = nil
var titleText: Text? = nil
private let mainItems: () -> MainItems // custom @ViewBuilder property, gives developer ability to supply custom 'subviews'
init(title: String? = nil, titleText: Text? = nil, @ViewBuilder main mainItems: @escaping () -> MainItems) {
self.title = title
self.titleText = titleText
self.mainItems = mainItems
}
var body: some View {
HStack(alignment: .firstTextBaseline) {
VStack(alignment: .leading, spacing: 3) {
if titleText != nil {
titleText!
} else if title != nil {
Text(title!)
} else {
mainItems()
}
} // + adjacent views ...
}.padding()
}
}
Unfortunately, if I modify the definition of the mainItems parameter to (() -> MainItems)?, I get the error: "Function builder attribute 'ViewBuilder' can only be applied to a parameter of function type." Fair enough, but it should be able to account for optionality on that closure parameter.