Home > Blockchain >  Swift- How to accept multiple (union) types as argument
Swift- How to accept multiple (union) types as argument

Time:08-10

Here is the playground to get the idea

import SwiftUI
import PlaygroundSupport

struct RectangleStyle {
    public var color: Color? = nil
    public var gradient: LinearGradient? = nil
}


struct DemoView: View {
    var body: some View {
        Rectangle()
            .fill(
                RectangleStyle(
                    gradient: LinearGradient(
                        gradient: Gradient(colors: [.red, .blue]), 
                        startPoint: .top, 
                        endPoint: .bottom
                    )
                ).gradient!
            )
            .frame(width: 200, height: 200)
    }
}

PlaygroundPage.current.setLiveView(DemoView())

As you can see, it's quite ugly. What I would like, is the ability to construct my RectangleStyle with whatever object that implement ShapeStyle (the protocol required by the fill() method on Rectangle) and be able to retrieve this ShapeStyle (A Color or a Gradient, etc...) with a single method.

If I add a init(style: ShapeStyle) on my RectangleStyle, I get the error Protocol "ShapeStyle" can only be used as a generic constraint because it has Self or associated type requirements.

And I get the same error if I try to add a func getStyle() -> ShapeStyle on my RectangleStyle.

How can I properly handle this case? The idea is just to be able to construct my RectangleStyle with any object of type compatible with the required type of Rectangle.fill() and have a method to return this object. Is is possible to let Swift know that if the argument was of type A, the return of a method will be the same type (like in Typescript)?

Thanks!

CodePudding user response:

It can be done with generics, like

struct RectangleStyle<S: ShapeStyle> {
    private let style: S

    init(_ style: S) {      // << your init !!
        self.style = style
    }

    func getStyle() -> some ShapeStyle { // << your func !!
        self.style
    }
}

and now your test:

let myStyle = RectangleStyle(LinearGradient(
                    gradient: Gradient(colors: [.red, .blue]),
                    startPoint: .top,
                    endPoint: .bottom
                ))

var body: some View {
    Rectangle()
        .fill(myStyle.getStyle())         // << here !!
        .frame(width: 200, height: 200)
}

Tested with Xcode 13.4 / iOS 15.5

demo

Test code is here

  • Related