Flight-School / AnyCodable

Type-erased wrappers for Encodable, Decodable, and Codable values
https://flight.school/books/codable
MIT License
1.28k stars 132 forks source link

How to make it interoperable with Objective C? #50

Closed tsheaff closed 3 years ago

tsheaff commented 3 years ago

I may be missing something fundamental here, but I'm having a difficult time making this library interoperate well with Objective C

Our API handler is in Swift but the object parser is in Objective C and difficult to refactor into Swift for now.

The API handler looks something like this:

public typealias JSONCodable = [String: AnyCodable]

struct MyObjectsResponse: Codable {
    let myObjects: [JSONCodable]

    enum CodingKeys: String, CodingKey {
        case myObjects
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        self.myObjects = try container.decode([JSONCodable].self, forKey: .myObjects)        
    }
}

Now in the objective-c object parser, we have something like this, where data is a single element from the myObjects array from the MyObjectsResponse above, being passed in from swift:

- (instancetype)initWithData:(NSDictionary *)data
{
    self = [super init];
    if (self) {
        self.myItemId = data[@"id"];

        NSString *myEnumString = data[@"my_enum"];
        if ([myEnumString isEqualToString:@"foo"]) {
            self.myEnum = MyEnumFoo;
        }
        if ([myEnumString isEqualToString:@"bar"]) {
            self.myEnum = MyEnumBar;
        }
    }
    return self;
}

This fails with the following exception:

-[__SwiftValue isEqualToString:]: unrecognized selector sent to instance 0x600001965600

In the debugger at the exception breakpoint:

(lldb) po data
{
    id = "AnyCodable(\"abc123\")";
    "my_enum" = "AnyCodable(\"bar\")";
}

How can I get AnyCodable to interoperate seamlessly with Objective C types and methods like NSString and isEqualToString:?

cc @mattt

minacle commented 3 years ago

Swift.String value is different to and non toll-free bridging NSString instance.
If you really want to do it, explicit casting (like as NSString in swift side) is unavoidable.

And unfortunately, NSString is not compatible to AnyCodable. See https://github.com/Flight-School/AnyCodable/issues/14#issuecomment-482815683.