2

I've put together the following class (with the help of others in Swift variable comparison where type is not known).

What I'm trying to achieve is to pass in a closure to SearchOption and when calling getSelectedItemAsString it will run the closure passing in the searchOption as a parameter and return the result.

But because selectedOption is a generic type it's complaining Cannot convert value of type '[T]' to expected argument type '[CustomStringConvertible]' in getSelectedItemAsString()

public func getSelectedItemAsString() -> String {
    if self.searchOptionToString != nil && self.selectedOption != nil {
        return self.searchOptionToString!(selectedOption!)
    }
    return ""
}

I'm not sure what to do here. The implementation of converting the searchOption to a string will be different for each SearchOption which is why I need the closure.

The full class

public typealias searchOptionToCloudSearchQuery = ([CustomStringConvertible]) -> String

public class SearchOption<T: Equatable> {

    private var title: String
    private var allowAny: Bool
    private var allowMultiple: Bool
    private var dependencies: [SearchOption]?

    private var selectedOption: [T]?

    private var searchOptionToString: searchOptionToCloudSearchQuery?

    init(title: String, allowAny: Bool, allowMultiple: Bool, dependencies: [SearchOption]?) {
        self.title = title
        self.allowAny = allowAny
        self.allowMultiple = allowMultiple
        self.dependencies = dependencies
    }

    public func setSelectedItem(selectedOption: T) -> Void {
        if self.selectedOption == nil || !self.allowMultiple{
            self.selectedOption = [T]()
        }
        self.selectedOption?.append(selectedOption)
    }

    public func getSelectedItem() -> [T]? {
        return self.selectedOption
    }

    public func setSearchOptionToCloudSearchQueryClosure(closure: searchOptionToCloudSearchQuery) -> Void {
        self.searchOptionToString = closure
    }

    public func getSelectedItemAsString() -> String {
        if self.searchOptionToString != nil && self.selectedOption != nil {
            return self.searchOptionToString!(selectedOption!)
        }
        return ""
    }

}

The implementation

var make: SearchOption = SearchOption<String>(title: "Make", allowAny: true, allowMultiple: true, dependencies: nil)

make.setSelectedItem("Vauxhall")

var closure: searchOptionToCloudSearchQuery = {(selectedOptions) in

    var stringBuilder = ""
    for item in selectedOptions {
        if item is String {
            stringBuilder += item as! String
        }
    }
    return stringBuilder

}

make.setSearchOptionToCloudSearchQueryClosure(closure)
make.getSelectedItemAsString()

The Error

Playground execution failed: /var/folders/b5/3jvkp2jd5q5fmps2hrw91781cmf7f_/T/./lldb/570/playground442.swift:38:61: error: cannot convert value of type '[T]' to expected argument type '[CustomStringConvertible]'
            return self.searchOptionToString!(selectedOption!)
                                              ~~~~~~~~~~~~~~^
Community
  • 1
  • 1
Richard Poole
  • 591
  • 1
  • 5
  • 21

1 Answers1

1

I've managed to solve my problem.

Rather than using a typealias in the implementation I'm defining the type at the same time as the closure:

var closure: ([String]) -> String = {(selectedOptions) in

    var stringBuilder = ""
    for item in selectedOptions {
        stringBuilder += item
    }
    return stringBuilder

}

Then moving the typealias into the class itself with the generic type on the parameter, this ensures that the closure type and searchOption type match:

public class SearchOption<T: Equatable> {

    public typealias searchOptionToCloudSearchQuery = ([T]) -> String

    /.../

    public func getSelectedItemAsString() -> String {
        if self.searchOptionToString != nil && self.selectedOption != nil {
            return self.searchOptionToString!(selectedOption!)
        }
        return ""
    }

}
Richard Poole
  • 591
  • 1
  • 5
  • 21