Welcome to FutureAppLaboratory

v=(*^ワ^*)=v

Generics Enum in Swift

| Comments

If we want handle different data type, according to the result of calling some APIs. We may need the code like this.

1
2
3
4
enum APIResponse<JsonType, ErrorMsgType> {
    case Success(JsonType)
    case Fail(ErrorMsgType)
} // compile error

But the code above cannot be compiled.

1
2
// integer overflows when converted from 'Builtin.Int32' to 'Builtin.Int8'MyPlayground.playground:148:6: error: unimplemented IR generation feature non-fixed multi-payload enum layout
//     enum APIResponse<JsonType, ErrorMsgType> {

How can we actually do this

We can use Container Class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class SuccessContainer<T> {
    init (t : T) {
        // ...
    }
}

class ErrorContainer<T> {
    init (t : T) {
        // ...
    }
}

enum APIResponse<JsonType, ErrorMsgType> {
    case Success(SuccessContainer<JsonType>)
    case Fail(ErrorContainer<ErrorMsgType>)
}

// usage

func GenAPIResponseSuccess<JsonType, ErrorMsgType>(json : JsonType) -> APIResponse<JsonType, ErrorMsgType> {
    return .Success(SuccessContainer<JsonType>(t: json))
}

func GenAPIResponseFail<JsonType, ErrorMsgType>(errorMsg : ErrorMsgType) -> APIResponse<JsonType, ErrorMsgType> {
    return .Fail(ErrorContainer<ErrorMsgType>(t: errorMsg))
}

func callbackFromAPI(response: String) -> APIResponse<String, String> {
    if (response.hasPrefix("success")) {
        return GenAPIResponseSuccess(response) // can be parsed to json
    } else {
        return GenAPIResponseFail(response) // just a plain error msg
    }
}

callbackFromAPI("success. I'm Json")
callbackFromAPI("404 or something else")

But it is awful to write bunch of code for creating container classes, for each type you need.

Better Solution?

Here is a very useful lib. https://github.com/robrix/Box

It provide a container class called Box

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public final class Box<T>: BoxType, Printable {
    /// Initializes a `Box` with the given value.
    public init(_ value: T) {
        self.value = value
    }


    /// Constructs a `Box` with the given `value`.
    public class func unit(value: T) -> Box<T> {
        return Box(value)
    }


    /// The (immutable) value wrapped by the receiver.
    public let value: T

    /// Constructs a new Box by transforming `value` by `f`.
    public func map<U>(@noescape f: T -> U) -> Box<U> {
        return Box<U>(f(value))
    }


    // MARK: Printable

    public var description: String {
        return toString(value)
    }
}

Finally

We can rewrite above code into this. Much more elegant.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
enum APIResponse2<JsonType, ErrorMsgType> {
    case Success(Box<JsonType>)
    case Fail(Box<ErrorMsgType>)
}

func GenAPIResponseSuccess2<JsonType, ErrorMsgType>(json : JsonType) -> APIResponse2<JsonType, ErrorMsgType> {
    return .Success(Box<JsonType>(json))
}

func GenAPIResponseFail2<JsonType, ErrorMsgType>(errorMsg : ErrorMsgType) -> APIResponse2<JsonType, ErrorMsgType> {
    return .Fail(Box<ErrorMsgType>(errorMsg))
}

func callbackFromAPI2(response: String) -> APIResponse2<String, String> {
    if (response.hasPrefix("success")) {
        return GenAPIResponseSuccess2(response) // can be parsed to json
    } else {
        return GenAPIResponseFail2(response) // just a plain error msg
    }
}

callbackFromAPI2("success. I'm Json")
callbackFromAPI2("404 or something else")

Comments