razor118
razor118

Reputation: 473

Swift generic class initializer

I found a problem with designing result class in swift

struct Result<T>{
    private let data: T?
    let error: NSError?
    var isSuccess: Bool {
        get {
            return data != nil
        }
    }

    init(data: T) {
        self.data = data
        self.error = nil
    }

    init(error: NSError) {
        self.data = nil
        self.error = error
    }
}

Usage should look like this

Result(data: "something") // T as string

The problem occurs when I want to pass an error

Result(error: errorFromSomewhere) //T is not specified

Below is real application usage:

class ParseRegistrationProvider: RegistrationProvider {
    func register(model: RegistrationForm) -> Promise<Result<String>> {
        return Promise { accept, reject in
            let user = PFUser()
            user.username = model.nickName
            user.password = model.password
            user.email = model.emailAdreess
            user.signUpInBackgroundWithBlock({ (isSuccess, error) -> Void in
                if isSuccess {
                    accept(Result(data: "OK"))
                } else {
                    var errorResult = Result(error: error!) //causes error
                    reject(errorResult)
                }
            })
        }
    }
}

"errorResult" causes compiler error:

Generic parameter T could not be inferred

Update: this approch works correctly:

Result<String>(error: errorFromSomewhere)

Upvotes: 1

Views: 484

Answers (3)

Tom Pelaia
Tom Pelaia

Reputation: 1285

To answer your question directly, you could just declare errorResult as:

var errorResult : Result<String>

That would fix the problem immediately. However, I think the better solution is to use an enum instead of a struct as it is more appropriate (unless there is more to it than you posted).

enum Result<T> {
    case Data(T)
    case Error(NSError)

    var success : Bool {
        switch self {
        case .Data:
            return true
        case .Error:
            return false
        }
    }
}

Upvotes: 0

Nyi Nyi
Nyi Nyi

Reputation: 788

I'd like to suggest to use variant type for Result like this cause it is more compact and can be used in pattern matching naturally:

enum Result<T>{
    case Data(T?)
    case Error(NSError)
    var isSuccess: Bool{
        get{
            switch self{
            case .Data(_?):
                return true
            default:
                return false
            }
          }
    } }

Your code may become like this:

class ParseRegistrationProvider: RegistrationProvider {
    func register(model: RegistrationForm) -> Promise<Result<String>> {
        return Promise { accept, reject in
            let user = PFUser()
            user.username = model.nickName
            user.password = model.password
            user.email = model.emailAdreess
            user.signUpInBackgroundWithBlock({ (isSuccess, error) -> Void in
                if isSuccess {
                    accept(Result<String>.Data("OK"))
                } else {
                    var errorResult = Result<String>(error: error!) //causes error
                    reject(errorResult)
                }
            })
        }
    }

}

Upvotes: 1

Tali
Tali

Reputation: 869

When you use classes instead of structs, you can define one base class (e.g. ErrorResult with init(error: NSError)) and one derived Result<T>) with init(data: T)).

This way, you entirely side-step the generic parameter. However, you potentially add some run-time overhead because of using a class.

Upvotes: 0

Related Questions