swift - How to pass a case of type "function" as a parameter? - Stack Overflow

admin2025-04-21  2

I've been using Codable enums to encode my classes to JSON, and I've hit a roadblock:

enum MyEnum: Codable {
    case case1(data: Data)
    case case2(str: String)
    case case3(url: URL)    
}

// ...

myFunc(myEnumType: MyEnum.case1)

func myFunc(
        myEnumType: MyEnum // ?
    ) -> MyEnum {
                
        switch myEnumType {
        
        case .case1:
            // insert processing...
            return myEnumType(data: ...)
        case .case2:
            // ...
            return myEnumType(str: ...)
        case .case3:
            // ...
            return myEnumType(url: ...)
            
        }
        
        
    }

Because MyEnum.case1 is of type function, I couldn't use MyEnum, so I had to resort to this very ugly solution[1] [2] [3]:

enum MyEnum: Codable {
    case case1(data: Data)
    case case2(str: String)
    case case3(url: URL)    
}

// :(
enum MyEnumType {
    case case1
    case case2
    case case3
}

// ...

func myFunc(
        myEnumType: MyEnumType // :(
    ) -> MyEnum {

My question

How can I pass my case to the function as a parameter?

What I've tried

This is what I don't want:

  1. Very generic type[4] [5]:
typealias MyEnumType<Arg1> = (Arg1) -> MyEnum

As this could just be any function returning MyEnum. This doesn't guarantee it's a case of MyEnum.

  1. Initializing the case:
myFunc(myEnumType: MyEnum.case1(data: dummyData))

I don't want to initialize the case with dummy data. myFunc is the one that's supposed to initialize the case with actual data, then return the initialized case.

  1. Passing the enum:
myFunc(myEnumType: MyEnum)

This is irrelevant: I want to know what case I'm looking at in the function in order to init each enum uniquely.

My ideal solution

Something like:

func myFunc<T>(myEnumType: MyEnum.T) -> MyEnum {

Which ensures the uninitialized case I've passed to the function is indeed a case of the MyEnum enum.

I'm open to changing the function call or the function, as long as I'm not initializing the case or having to redeclare every case.


References

  1. Answer 2: Swift enum property without initializing the enum case with an associated value?
  2. Allow Swift function parameter to be of multiple types
  3. Swift-Generics: "Cannot specialize non-generic type"
  4. How to take a function of any number of parameters of any type as a parameter in Swift
  5. Answer 5: Swift enum property without initializing the enum case with an associated value?

Relevant research I couldn't use:

  • How to access passed Enum argument in Swift 3
  • Possible to pass an enum type name as an argument in Swift?
  • Swift: Test class type in switch statement
  • Passing Class/Instance Function as a Parameter in a Function
  • Enum case '...' is not a member of type '...'
  • Passing various enum case as function param in Swift

I've been using Codable enums to encode my classes to JSON, and I've hit a roadblock:

enum MyEnum: Codable {
    case case1(data: Data)
    case case2(str: String)
    case case3(url: URL)    
}

// ...

myFunc(myEnumType: MyEnum.case1)

func myFunc(
        myEnumType: MyEnum // ?
    ) -> MyEnum {
                
        switch myEnumType {
        
        case .case1:
            // insert processing...
            return myEnumType(data: ...)
        case .case2:
            // ...
            return myEnumType(str: ...)
        case .case3:
            // ...
            return myEnumType(url: ...)
            
        }
        
        
    }

Because MyEnum.case1 is of type function, I couldn't use MyEnum, so I had to resort to this very ugly solution[1] [2] [3]:

enum MyEnum: Codable {
    case case1(data: Data)
    case case2(str: String)
    case case3(url: URL)    
}

// :(
enum MyEnumType {
    case case1
    case case2
    case case3
}

// ...

func myFunc(
        myEnumType: MyEnumType // :(
    ) -> MyEnum {

My question

How can I pass my case to the function as a parameter?

What I've tried

This is what I don't want:

  1. Very generic type[4] [5]:
typealias MyEnumType<Arg1> = (Arg1) -> MyEnum

As this could just be any function returning MyEnum. This doesn't guarantee it's a case of MyEnum.

  1. Initializing the case:
myFunc(myEnumType: MyEnum.case1(data: dummyData))

I don't want to initialize the case with dummy data. myFunc is the one that's supposed to initialize the case with actual data, then return the initialized case.

  1. Passing the enum:
myFunc(myEnumType: MyEnum)

This is irrelevant: I want to know what case I'm looking at in the function in order to init each enum uniquely.

My ideal solution

Something like:

func myFunc<T>(myEnumType: MyEnum.T) -> MyEnum {

Which ensures the uninitialized case I've passed to the function is indeed a case of the MyEnum enum.

I'm open to changing the function call or the function, as long as I'm not initializing the case or having to redeclare every case.


References

  1. Answer 2: Swift enum property without initializing the enum case with an associated value?
  2. Allow Swift function parameter to be of multiple types
  3. Swift-Generics: "Cannot specialize non-generic type"
  4. How to take a function of any number of parameters of any type as a parameter in Swift
  5. Answer 5: Swift enum property without initializing the enum case with an associated value?

Relevant research I couldn't use:

  • How to access passed Enum argument in Swift 3
  • Possible to pass an enum type name as an argument in Swift?
  • Swift: Test class type in switch statement
  • Passing Class/Instance Function as a Parameter in a Function
  • Enum case '...' is not a member of type '...'
  • Passing various enum case as function param in Swift
Share Improve this question edited Jan 23 at 8:39 DarkBee 15.5k8 gold badges72 silver badges118 bronze badges asked Jan 22 at 22:42 benhatsorbenhatsor 2,0339 silver badges21 bronze badges 4
  • 1 You could use a macro to generate the MyEnumType in your "ugly" solution. Does that make it less ugly? – Sweeper Commented Jan 22 at 23:05
  • 2 Why the downvote? This is clearly a very well researched and asked question. – Alexander Commented Jan 23 at 2:45
  • 1 What are you doing with the result of myFunc(myEnumType: MyEnum.case1) ? I am not sure what the issue is here - you are not making an instance of MyEnum, MyEnum.case1 is just a function - do you want to create myFunc accepting a function or an instanec of MyEnum ? In any case it'd be helpful if you show your actual use case here – Petar Commented Jan 23 at 8:02
  • Could it be a viable alternative to replace the enum with a protocol and a structure/class for each case? – Joakim Danielson Commented Jan 23 at 8:41
Add a comment  | 

1 Answer 1

Reset to default 1

In your "ideal solution", you mention a nested type MyEnum.T. This can be generated by a macro. Here is an example implementation:

// declaration:

@attached(member, names: named(T))
public macro RawCases() = #externalMacro(module: "...", type: "RawCases")

// implementation:

enum RawCases: MemberMacro {
    static func expansion(of node: AttributeSyntax, providingMembersOf declaration: some DeclGroupSyntax, in context: some MacroExpansionContext) throws -> [DeclSyntax] {
        let caseNames = declaration.memberBlock.members
            .compactMap { $0.decl.as(EnumCaseDeclSyntax.self) }
            .flatMap(\.elements)
            .map(\.name)
        return [DeclSyntax(
            try EnumDeclSyntax("enum T") {
                for name in caseNames {
                    "case \(name)"
                }
            }
        )]
    }
}

Usage:

@RawCases
enum MyEnum: Codable {
    case case1(data: Data)
    case case2(str: String)
    case case3(url: URL)
}

func f(_ value: MyEnum.T) -> MyEnum {
    switch value {
        case .case1:
            .case1(data: Data([1, 2, 3]))
        case .case2:
            .case2(str: "Something")
        case .case3:
            .case3(url: URL(filePath: "/foo/bar"))
    }
}
转载请注明原文地址:http://anycun.com/QandA/1745225525a90456.html