Fawkes
Fawkes

Reputation: 3961

How to constraint function generic type according to a parameter's property?

I have this enum:

enum ItemType: String {
        case image
        case movie

        var rawValue: String {
            switch self {
                case .image: return String(kUTTypeImage)
                case .movie: return String(kUTTypeMovie)
            }
        }
    }

and this function inside a class

func items<T>(for type: ItemType, completion: ([T]) -> Void) where T: NSSecureCoding {}

Now what I would like to achieve is that if the ItemType is .image I would like the completion to be inferred as of type ([UIImage]) -> Void otherwise if it is .video I would like it to be inferred as ([URL]) -> Void

Is this possible in any way in Swift? Or what would be an alternative approach to make the completion type infer according to the type provided.

Additional details:

The body of the function uses NSItemProvider loadItem instance method whose closure returns any type conforming to NSSecureCoding. So as long as I can give a type like that I don't care about what type specifically it is.

func items<T>(for type: ItemType, completion: ([T]) -> Void) where T: NSSecureCoding {
        itemProviders(for: [type]).forEach { itemProvider in
            itemProvider.loadItem(forTypeIdentifier: type.rawValue, options: nil, completionHandler: { (item: T, error: Error!) in

            })
        }
    }

Upvotes: 2

Views: 91

Answers (1)

Sweeper
Sweeper

Reputation: 274930

You can't do this because the parameter type is evaluated at runtime, while at compile time, T needs to be inferred.

A workaround is to separate this into two methods:

func itemsForImages(completion: ([UIImage]) -> Void) { ... }
func itemsForMovies(completion: ([URL]) -> Void) { ... }

And then determine which method to call:

switch itemType {
    case .image:
        itemsForImages { images in ... }
    case .movies:
        itemsForMovies { urls in ... }
}

Another alternative is to have a closure of type ([Any]) -> Void and the caller needs to cast the parameter to the correct types, but this is not so type safe.

Upvotes: 2

Related Questions