Some code,
typealias Brio = (String, String, String)
extension Brio {
static var divider: Brio {
return ("_", "_", "_")
}
static var isDivider: Bool {
return (self.0 == "_")
}
}
Tuple extension must be written as extension of '(repeat each Element)',
Tuple extension must declare conformance to exactly one protocol
Tuple extensions are experimental
I really couldn't figure it out from say this chat,
https://forums.swift.org/t/pitch-user-defined-tuple-conformances/67154
I don't understand the following sort of stuff but if relevant:
fattie@m2max203mbp16 ~ % swift --version
swift-driver version: 1.90.11.1 Apple Swift version 5.10 (swiftlang-5.10.0.13 clang-1500.3.9.4)
Target: arm64-apple-macosx14.0
fattie@m2max203mbp16 ~ %
Here's an example of where extending tuples would be sweet
// Setup the screen selector button
screensButton.showsMenuAsPrimaryAction = true
let noms = [
Brio("CA", "Cats", "cat.fill"),
Brio("DO", "Dogs", "fido.circle"),
Brio("DO", "Horsies", "equine.circle"),
.divider,
Brio("DEV", "Dev reload", "link"),
Brio("DX", "Dev, clear", "circle"),
.divider,
Brio("XX", "Example", "circle.dotted"),
Brio("XX", "Example", "link"),
]
var elements = noms.chompchomp(){ k in self.screensButtonProcess(k) }
screensButton.menu = UIMenu(children: elements)
Having to use a class is annoying. It would be more purer with tuples, eg,
let noms = [
("CA", "Cats", "cat.fill"),
("DO", "Dogs", "fido.circle"),
("DO", "Horsies", "equine.circle"),
.divider,
("DEV", "Dev reload", "link"),
Footnote, here's the kodes for chompchomp
, done with tears as a class Brio
rather than tuples:
///A "BUTTON TRIO". Internal code, title, system image.
///
///Use `[Brio]` to make menus. Just put `.divider` where you want dividers.
///
///OR just use `buildBrios { }` if using the matching @resultBuilder
struct Brio {
let c: String
let t: String
let i: String
var isDivider: Bool = false
}
extension Brio {
init(_ x: String, _ y: String, _ z: String) {
self.init(c: x, t: y, i: z)
}
///When you type-out a `Brio` array just put `.divider` where you want dividers.
static var divider: Brio {
return Brio(c: "_", t: "_", i: "_", isDivider: true)
}
}
extension [Brio] {
///Chew on an array of `Brio` and spit out the UIMenuElement array. Recall that to create dividers in UIKit menus, basically each run (other than the first) is marked as inline.
func chompchomp(with: @escaping (_ tapped: String)-> ()) -> [UIMenuElement] {
var brios = self
var result = brios.chomp().elements{ b in with(b) }
var run = brios.chomp().elements(with: { b in with(b) })
while !run.isEmpty {
result.append(UIMenu(options: .displayInline, children: run))
run = brios.chomp().elements(with: { b in with(b) })
}
return result
}
///Simply give the next run of menu items removing that run.
mutating func chomp() -> [Brio] {
let spl = self.split(maxSplits: 1, whereSeparator: { $0.isDivider })
self = (spl.count < 2) ? [] : Array(spl[1])
return (spl.count < 1) ? [] : Array(spl[0])
}
///Alchemize one run of Brio into an array of UIMenuElement.
func elements(with: @escaping (_ tapped: String)-> ()) -> [UIMenuElement] {
return self.map({ brio in
return UIAction(title: brio.t, image: UIImage(systemName: brio.i)) { _ in
with(brio.c)
}
})
}
}
* it's hilarious that cat.fill actually exists!
Extensions on tuples must conform tuples of any number of elements to a protocol. Your use case only makes sense to use 3-tuples, so a tuple extension isn't very suitable.
You should keep your Brio
struct, and create a result builder for Brio
s,
@resultBuilder
enum BrioBuilder {
static func buildExpression(_ expression: (String, String, String)) -> Brio {
let (c, t, i) = expression
return Brio(c, t, i)
}
static func buildExpression(_ expression: Divider) -> Brio {
Brio.divider
}
static func buildBlock(_ components: Brio...) -> [Brio] {
components
}
}
func buildBrios(@BrioBuilder block: () -> [Brio]) -> [Brio] {
block()
}
Then you can achieve a usage like this:
// noms will be of type [Brio]
let noms = buildBrios {
("CA", "Cats", "cat.fill")
("DO", "Dogs", "fido.circle")
("DO", "Horsies", "equine.circle")
Divider()
("DEV", "Dev reload", "link")
}
You cannot use the .divider
syntax in a result builder (See here). You can write Brio.divider
, or use a separate struct (I have borrowed the Divider
from SwiftUI here).
You can extend this further by implementing buildArray
, buildIf
, etc in the result builder. This would allow you to use loops and if statements in the result builder closure. For more information, see the Swift Evolution proposal.