How can I define ShapeBuilder for SwiftUI - swift

As we know we have a very useful wrapper called #ViewBuilder for view but I am missing same wrapper for Shape protocol, Since there is no wrapper for this available, I am looking to try build one for learning porous.
Here is working code for view and looking to build the wrapper for Shape:
#ViewBuilder func testForView(value: Bool) -> some View {
if value {
Text("Hello")
}
else {
Button("world!") {}
}
}
// The Goal:
#ShapeBuilder func testForShape(value: Bool) -> some Shape {
if value {
Circle()
}
else {
Rectangle()
}
}
I have experience working with wrappers or even defining a custom wrapper, but all my attempts were wrapping a value, not a function! I never tried to wrapp a function, frankly I do not know that it is thing in Swift or SwiftUI. So how can I make #ShapeBuilder works like #ViewBuilder works, I know that I must use Tuple or more correctly TupleShape, so I think I should define TupleShape as well.

First we should ask whether it's worth writing a ShapeBuilder instead of writing an AnyShape type like this:
public struct AnyShape: Shape {
public let makePath: (CGRect) -> Path
public init(makePath: #escaping (CGRect) -> Path) {
self.makePath = makePath
}
public init<Wrapped: Shape>(_ shape: Wrapped) {
self.makePath = { shape.path(in: $0) }
}
public func path(in rect: CGRect) -> Path {
return makePath(rect)
}
}
We can use AnyShape to write your testForShape function like this:
func testForShape(value: Bool) -> AnyShape {
if value {
return AnyShape(Circle())
}
else {
return AnyShape(Rectangle())
}
}
I've used AnyShape in various projects.
Anyway, if we want to write ShapeBuilder, we can start with the simplest possible implementation, which only supports a body containing a single, unconditional child shape:
#resultBuilder
public struct ShapeBuilder {
public static func buildBlock<C0: Shape>(_ c0: C0) -> C0 { c0 }
}
We can now use it like this:
#ShapeBuilder
func shape1() -> some Shape {
Circle()
}
Hooray!
We can extend it to support a body containing no child shapes by adding a zero-argument buildBlock and an EmptyShape type for it to return:
extension ShapeBuilder {
public static func buildBlock() -> EmptyShape { EmptyShape() }
}
public struct EmptyShape: Shape {
public func path(in rect: CGRect) -> Path { Path() }
}
Now we can write the following function without errors:
#ShapeBuilder
func shape0() -> some Shape {
}
To support an if statement without an else clause, we add a buildOptional method and an OptionalShape type for it to return:
extension ShapeBuilder {
public static func buildOptional<C0: Shape>(_ c0: C0?) -> OptionalShape<C0> {
return OptionalShape(c0)
}
}
public struct OptionalShape<Content: Shape>: Shape {
public let content: Content?
public init(_ content: Content?) {
self.content = content
}
public func path(in rect: CGRect) -> Path {
return content?.path(in: rect) ?? Path()
}
}
Now we can use it like this:
#ShapeBuilder
func shape2(flag: Bool) -> some Shape {
if flag {
Circle()
}
}
To support an if statement with an else clause, and to support a switch statement, we add buildEither and an EitherShape type for it to return:
extension ShapeBuilder {
public static func buildEither<First: Shape, Second: Shape>(first: First) -> EitherShape<First, Second> {
return .first(first)
}
public static func buildEither<First: Shape, Second: Shape>(second: Second) -> EitherShape<First, Second> {
return .second(second)
}
}
public enum EitherShape<First: Shape, Second: Shape>: Shape {
case first(First)
case second(Second)
public func path(in rect: CGRect) -> Path {
switch self {
case .first(let first): return first.path(in: rect)
case .second(let second): return second.path(in: rect)
}
}
}
We can now write this function:
#ShapeBuilder
func shape3(_ n: Int) -> some Shape {
if n < 0 {
Rectangle()
} else {
switch n {
case 0:
EmptyShape()
case 1:
Rectangle()
default:
Capsule()
}
}
}
We can support combining two child shapes by writing a buildBlock method that takes two arguments, and a Tuple2Shape for it to return:
extension ShapeBuilder {
public static func buildBlock<C0: Shape, C1: Shape>(_ c0: C0, _ c1: C1) -> Tuple2Shape<C0, C1> {
return Tuple2Shape(c0, c1)
}
}
public struct Tuple2Shape<C0: Shape, C1: Shape>: Shape {
public let tuple: (C0, C1)
public init(_ c0: C0, _ c1: C1) {
tuple = (c0, c1)
}
public func path(in rect: CGRect) -> Path {
var path = tuple.0.path(in: rect)
path.addPath(tuple.1.path(in: rect))
return path
}
}
Now we can write this function:
#ShapeBuilder
func shape4() -> some Shape {
Circle()
Rectangle()
}
We can support combining two child shapes by writing a buildBlock method that takes three arguments, and a Tuple3Shape for it to return:
extension ShapeBuilder {
public static func buildBlock<C0: Shape, C1: Shape, C2: Shape>(_ c0: C0, _ c1: C1, _ c2: C2) -> Tuple3Shape<C0, C1, C2> {
return Tuple3Shape(c0, c1, c2)
}
}
public struct Tuple3Shape<C0: Shape, C1: Shape, C2: Shape>: Shape {
public let tuple: (C0, C1, C2)
public init(_ c0: C0, _ c1: C1, _ c2: C2) {
tuple = (c0, c1, c2)
}
public func path(in rect: CGRect) -> Path {
var path = tuple.0.path(in: rect)
path.addPath(tuple.1.path(in: rect))
path.addPath(tuple.2.path(in: rect))
return path
}
}
That lets us write this function:
#ShapeBuilder
func shape5() -> some Shape {
Circle()
Rectangle()
Capsule()
}
ViewBuilder has buildBlock methods up to arity 10, but I'm not going to write out any more. You can do it yourself if you need them.
Anyway, here's the ShapeBuilder implementation all together for easy copy'n'paste:
#resultBuilder
public struct ShapeBuilder {
public static func buildBlock<C0: Shape>(_ c0: C0) -> C0 { c0 }
}
extension ShapeBuilder {
public static func buildBlock() -> EmptyShape { EmptyShape() }
}
public struct EmptyShape: Shape {
public func path(in rect: CGRect) -> Path { Path() }
}
extension ShapeBuilder {
public static func buildOptional<C0: Shape>(_ c0: C0?) -> OptionalShape<C0> {
return OptionalShape(c0)
}
}
public struct OptionalShape<Content: Shape>: Shape {
public let content: Content?
public init(_ content: Content?) {
self.content = content
}
public func path(in rect: CGRect) -> Path {
return content?.path(in: rect) ?? Path()
}
}
extension ShapeBuilder {
public static func buildEither<First: Shape, Second: Shape>(first: First) -> EitherShape<First, Second> {
return .first(first)
}
public static func buildEither<First: Shape, Second: Shape>(second: Second) -> EitherShape<First, Second> {
return .second(second)
}
}
public enum EitherShape<First: Shape, Second: Shape>: Shape {
case first(First)
case second(Second)
public func path(in rect: CGRect) -> Path {
switch self {
case .first(let first): return first.path(in: rect)
case .second(let second): return second.path(in: rect)
}
}
}
extension ShapeBuilder {
public static func buildBlock<C0: Shape, C1: Shape>(_ c0: C0, _ c1: C1) -> Tuple2Shape<C0, C1> {
return Tuple2Shape(c0, c1)
}
}
public struct Tuple2Shape<C0: Shape, C1: Shape>: Shape {
public let tuple: (C0, C1)
public init(_ c0: C0, _ c1: C1) {
tuple = (c0, c1)
}
public func path(in rect: CGRect) -> Path {
var path = tuple.0.path(in: rect)
path.addPath(tuple.1.path(in: rect))
return path
}
}
extension ShapeBuilder {
public static func buildBlock<C0: Shape, C1: Shape, C2: Shape>(_ c0: C0, _ c1: C1, _ c2: C2) -> Tuple3Shape<C0, C1, C2> {
return Tuple3Shape(c0, c1, c2)
}
}
public struct Tuple3Shape<C0: Shape, C1: Shape, C2: Shape>: Shape {
public let tuple: (C0, C1, C2)
public init(_ c0: C0, _ c1: C1, _ c2: C2) {
tuple = (c0, c1, c2)
}
public func path(in rect: CGRect) -> Path {
var path = tuple.0.path(in: rect)
path.addPath(tuple.1.path(in: rect))
path.addPath(tuple.2.path(in: rect))
return path
}
}

Related

Make LinkedList Generic in Swift

Consider a Generically constructed Node for a DoublyLinkedList:
public class Node<T> {
var value: T
var next: Node<T>?
weak var previous: Node<T>?
init(value: T) {
self.value = value
}
}
public class DoublyLinkedList<T> {
var head: Node<T>?
private var tail: Node<T>?
public func append(value: T) {
let newNode = Node(value: value)
if let tailNode = tail {
newNode.previous = tailNode
tailNode.next = newNode
} else {
head = newNode
}
tail = newNode
}
....
How do I make the DoublyLinkedList even more Generic?
(i.e. I subclassed Node so that I could implement some specific behaviors via inheritance). I can't seem to synthesize a DoublyLinkedList of my subclass because it is looking for the "Concrete"? type of Node
class TransactionFilterNode: Node<Search> {
let seedTransactions: [Transaction]
init(search: Search, allTransactions: [Transaction]){
self.seedTransactions = allTransactions
super.init(value: search)
}
I can't seem to get this to be inserted or appended to DoublyLinkedList because DoublyLinkedList is looking for a Node, not a subclass of Node.
Edit: Solved
The solution was to pull the call for Node into the function call parameter so that I could pass a subclassed version. See below.
So your append method expects a type T (which in your case is Search), while you are directly subclassing a Node.
So you can
Option 1: create append that accepts the Node, e.g.:
func append(value: T) {
let newNode = Node(value: value)
append(newNode: newNode)
}
func append(newNode: Node<T>) {
if let tailNode = tail {
newNode.previous = tailNode
tailNode.next = newNode
} else {
head = newNode
}
tail = newNode
}
So now you can
let x = DoublyLinkedList<Search>()
let y = TransactionFilterNode(...)
x.append(newNode: y) // No problem
Option 2: subclass Search instead of subclassing Node
I.e. maybe instead of class TransactionFilterNode: Node<Search> you meant to say class TransactionFilter: Search?
My doubly linked list is generic over everything because I conceal Node from the user by embedding it.
He simply asks for a BiLinkedList or a BiLinkedList or whatever he wants to list. Only access via subscripts requires his objects to be Equatable. Note that all references to Node are private.
Here are my headers:
public struct BiLinkedList<Element> {
var head: Node<Element>?
var tail: Node<Element>?
public init(_ array: [Element] = []) {}
private mutating func copyNodes() {}
public var peekFirst: Element? { get }
public var peekLast: Element? { get }
public mutating func push(_ element: Element)
public mutating func append(_ element: Element)
public mutating func pop() -> Element?
public mutating func removeLast() -> Element?
}
extension BiLinkedList {
class Node<Element> {
var element: Element
weak var previous: Node?
var next: Node?
init(_ element: Element, previous: Node? = nil, next: Node? = nil)
}
}
extension BiLinkedList where Element: Equatable {
private func firstNode(containing value: Element) -> Node<Element>?
private func lastNode(containing value: Element) -> Node<Element>?
public mutating func insert(_ element: Element, beside value: Element, before: Bool) -> Bool
}
extension BiLinkedList {
public mutating func removeFirst(_ value: Element) -> Bool
public mutating func removeLast(_ value: Element) -> Bool
public mutating func removeAll(_ value: Element)
public mutating func replaceFirst(_ element: Element, with value: Element) -> Bool
public mutating func replaceLast(_ element: Element, with value: Element) -> Bool
}
extension BiLinkedList: Collection {
public struct Index: Comparable {
var node: Node<Element>?
public static func == (lhs: Index, rhs: Index) -> Bool {}
static public func <(lhs: Index, rhs: Index) -> Bool {}
}
public var startIndex: Index
public var endIndex: Index
public func index(after i: Index) -> Index {}
public subscript(position: Index) -> Element {}
public subscript(position: Int) -> Element? {}
}
extension BiLinkedList: Sequence {
public func makeIterator() -> LinkedListIterator {}
public struct LinkedListIterator: IteratorProtocol {
private var currentNode: Node<Element>?
fileprivate init(node: Node<Element>?) {}
public func next() -> Element? {}
}
}

How dynamically apply a list of modifiers with generics on SwiftUI?

I'm rendering some views that have a dynamic list of modifiers:
public enum Colors:String, CaseIterable {
public func to_color() -> Color {
color_hex(self.rawValue)
}
case black = "#000000"
case white = "#FFFFFF"
case gray_500 = "#F9FAFB"
case blue_200 = "#BFDBFE"
case blue_500 = "#3B82F6"
}
public enum Style {
case bg(Colors)
case text(Colors)
}
struct StyleModifier: ViewModifier {
fileprivate let properties: [Style]
func body(content: _ViewModifier_Content<StyleModifier>) -> some View {
return apply(content: content.toAnyView(), properties)
}
}
public extension View {
func toAnyView() -> AnyView {
return AnyView(self)
}
func style(_ clsName: [Style]) -> some View {
ModifiedContent(content: self, modifier: StyleModifier(properties: clsName))
}
func style2(_ clsName: [Style]) -> some View {
ModifiedContent(content: self, modifier: StyleModifier2(properties: clsName))
}
}
... and try to make this without resorting to AnyView but nothings get applied.
My working code with AnyView:
func apply(content: AnyView, _ properties: [Style]) -> AnyView {
return properties.reduce(content) { (v: AnyView, style: Style) -> AnyView in
switch style {
case .bg(let color):
return v.background(color.to_color()).toAnyView()
case .text(let color):
return v.foregroundColor(color.to_color()).toAnyView()
}
}
I try to translate this with generics:
func apply2<V: View>(of: V, _ properties: [Style]) -> V {
return properties.reduce(of) { (v: V, style: Style) -> V in
switch style {
case .bg(let color):
return v.background(color.to_color()) as! V
case .text(let color):
return v.foregroundColor(color.to_color()) as! V
}
}
But then this not work. Why?

How can I inferring PreferenceKey/PreferenceKey.Type in Swift/SwiftUI?

This below code was my previous question and it is working, since I am not using key parameter directly I want try to remove it from function and possibly infer it,
extension View {
func onPreference<K: PreferenceKey>(key: K.Type, value: K.Value, action: ((K.Value) -> Void)?) -> some View where K.Value: Equatable {
return self
.preference(key: K.self, value: value)
.onPreferenceChange(K.self, perform: { newValue in action?(newValue)})
}
}
For making my goal possible I want build an array that cover all types that i am going use them in function, like Bool, Int and CGSize, I really do not know if it is good idea to make such function or even if it is a thing in swift or swiftUI, these below code are just pseudocode, I am unable code to solve the issues or problems, need help there
Also if you think using an array is not possible may I should work on switch or if let inside function.
I want add that the goal of inferring is stopping making and repeating code for each Keys. Which with given value to function, the function would be understand which key needed to make codes works.
extension View {
func onPreference<K: PreferenceKey, V: Equatable>(value: V, action: #escaping (V) -> Void) -> some View where V == K.Value, K.defaultValue == arrayOfKeys[?].defaultValue {
return self
.preference(key: K.self, value: value)
.onPreferenceChange(K.self, perform: { newValue in action(newValue)})
}
}
let arrayOfKeys: Array<PreferenceKey> = [BoolPreferenceKey, IntPreferenceKey, CGSizePreferenceKey]
struct BoolPreferenceKey: PreferenceKey {
static var defaultValue: Bool { get { return Bool() } }
static func reduce(value: inout Bool, nextValue: () -> Bool) { value = nextValue() }
}
struct IntPreferenceKey: PreferenceKey {
static var defaultValue: Int { get { return Int() } }
static func reduce(value: inout Int, nextValue: () -> Int) { value = nextValue() }
}
struct CGSizePreferenceKey: PreferenceKey {
static var defaultValue: CGSize { get { return CGSize() } }
static func reduce(value: inout CGSize, nextValue: () -> CGSize) { value = nextValue() }
}

swift protocol with associated type

how can I correct errors?!
domain module, contains all abstracts
//MARK: - Domain
protocol TypeB {
associatedtype P
func makeP() -> P
init()
}
protocol TypeA {
associatedtype P
associatedtype B: TypeB
func makeP() -> P
init(objB: B)
}
protocol FirstTargetFabricType {
func makeB<B: TypeB>() -> B where B.P == Int
}
protocol SecondTargetFabricType {
func makeA<B, A: TypeA>(objcB: B) -> A where A.B == B, A.P == B.P, B.P == Int
}
the first module, for example the module for working with the network
//MARK: - First Target
class ClassB: TypeB {
required init() { }
func makeP() -> Double { return 10 }
}
// error: Cannot convert return expression of type 'ClassB' to return type 'B'
class FirstTargetFabric: FirstTargetFabricType {
func makeB<B: TypeB>() -> B where B.P == Int {
return ClassB()
}
}
the second module, for example the module for working with the storage
//MARK: - SecondTarget
class ClassA<B: TypeB>: TypeA {
let objB: B
required init(objB: B) {
self.objB = objB
}
func makeP() -> B.P { return objB.makeP() }
}
class SecondTargetFabric: SecondTargetFabricType {
func makeA<B, A: TypeA>(objcB: B) -> A where B == A.B, B.P == A.P, B.P == Int {
return A(objB: objcB)
}
}
application target
//MARK: - app
let firstFabric: FirstTargetFabricType = FirstTargetFabric()
let secondFabric: SecondTargetFabricType = SecondTargetFabric()
/// error: Generic parameter 'A' could not be inferred
let res = secondFabric.makeA(objcB: firstFabric.makeB())
I'm trying to make a service loader for pagination. I just want to pass in the class initializer an entity that will go to the network
how good would that approach be?
//MARK: - Domain
open class TypeB<T: Decodable> {
open func makeP() -> T {
fatalError("Required function have not been implemented")
}
}
open class TypeA<T: Decodable> {
open func makeP() -> T {
fatalError("Required function have not been implemented")
}
}
public protocol FirstTargetFabricType {
func makeInt() -> TypeB<Int>
func makeDouble() -> TypeB<Double>
}
public protocol SecondTargetFabricType {
func makeInt(objInt: TypeB<Int>) -> TypeA<Int>
func makeDouble(objDouble: TypeB<Double>) -> TypeA<Double>
}
//MARK: - First Target
internal class ClassB: TypeB<Int> {
override func makeP() -> Int { return 10 }
}
internal class ClassBB: TypeB<Double> {
override func makeP() -> Double { return 20 }
}
public class FirstTargetFabric: FirstTargetFabricType {
public func makeInt() -> TypeB<Int> { ClassB() }
public func makeDouble() -> TypeB<Double> { ClassBB() }
}
//MARK: - SecondTarget
internal class ClassA: TypeA<Int> {
let objB: TypeB<Int>
init(objB: TypeB<Int>) {
self.objB = objB
}
override func makeP() -> Int {
objB.makeP()
}
}
internal class ClassAA: TypeA<Double> {
let objB: TypeB<Double>
init(objB: TypeB<Double>) {
self.objB = objB
}
override func makeP() -> Double {
objB.makeP()
}
}
public class SecondTargetFabric: SecondTargetFabricType {
public func makeInt(objInt: TypeB<Int>) -> TypeA<Int> { ClassA(objB: objInt) }
public func makeDouble(objDouble: TypeB<Double>) -> TypeA<Double> { ClassAA(objB: objDouble) }
}
//MARK: - app
let first = FirstTargetFabric()
let second = SecondTargetFabric()
let objInt = first.makeInt()
let objDouble = first.makeDouble()
print(second.makeInt(objInt: objInt).makeP())
print(second.makeDouble(objDouble: objDouble).makeP())
This is the proper way of doing it with protocols, but that might become very complicated if you keep on going with associated protocols.
In method makeAa: you are trying to return TypeA as type A, and not all TypeA are A, so you have to return A instead
protocol TypeA {
associatedtype P
associatedtype B: TypeB
func makeP() -> P
init(objB: B)
}
protocol TypeB {
associatedtype P
func makeP() -> P
}
class ClassB: TypeB {
func makeP() -> Int { return 10 }
}
class ClassA<B: TypeB>: TypeA {
let objB: B
required init(objB: B) {
self.objB = objB
}
func makeP() -> B.P {
return objB.makeP()
}
}
//**Cannot convert return expression of type 'ClassA<B>' to return type 'A'**
func makeAa<B, A: TypeA>(objB: B) -> A where B.P == Int, A.P == B.P, A.B == B {
return A(objB: objB)
}
//**Generic parameter 'A' could not be inferred**
let res: ClassA = makeAa(objB: ClassB())
For the update:
Again, you have to return type B, not all ClassB are B()
// error: Cannot convert return expression of type 'ClassB' to return type 'B'
class FirstTargetFabric: FirstTargetFabricType {
func makeB<B: TypeB>() -> B where B.P == Int {
return B()
}
}
Here you have to specify generic class B that you want to create (ClassB)
/// error: Generic parameter 'A' could not be inferred
let b: ClassB = firstFabric.makeB()
let res: ClassA = secondFabric.makeA(objcB: b)
And ClassB should return the same (Int) as ClassA to combine them
//MARK: - First Target
class ClassB: TypeB {
required init() { }
func makeP() -> Int { return 10 }
}

How do I using Generic as parameter and return specific object without casting?

Does function can return specific object, when I input a specific class.
My problem:
I don't know how to return a object. take a look following code and thanks
class MyViewControler {
}
class MySplitViewController: NSSplitViewControler {
override func viewDidLoad() {
/*
* get specific object
*/
let vc = viewController(for: MyViewControler.self)
}
}
extension NSSplitViewController {
public func viewController<T>(for anClass: T) -> T.object {
guard let tClass = anClass as? AnyClass else { return nil }
var vc: NSViewController?
if let idx = self.splitViewItems.index(where: { $0.viewController.classForCoder == tClass} ) {
vc = self.splitViewItems[idx].viewController
}
}
/*
* I don't know how to return a specific object
*/
return vc
}
The signature of a method taking a type and returning an
(optional) instance of that type would be:
public func viewController<T>(for aClass: T.Type) -> T?
or, if you want to restrict it to subclasses of NSViewController:
public func viewController<T: NSViewController>(for aClass: T.Type) -> T?
The implementation can be simplified with optional binding:
extension NSSplitViewController {
public func viewController<T: NSViewController>(for aClass: T.Type) -> T? {
for item in self.splitViewItems {
if let vc = item.viewController as? T {
return vc
}
}
return nil
}
}
Or as a "one-liner":
extension NSSplitViewController {
public func viewController<T: NSViewController>(for aClass: T.Type) -> T? {
return self.splitViewItems.lazy.flatMap { $0.viewController as? T }.first
}
}