Can I convert JSON file to kotlin multiplatform object in Swift?

Viewed 521

For context: I'm using kotlin multiplatform to communicate with my backend for my both apps, I'm also trying to follow Clean Architecture + MVVM. My multiplatform project holds my data layer (repositories and dtos) and my app project holds the presentation and domain layer (VC, View Model, Use Case).

I'm trying to do is mock my repository in the multiplatform project.

I have a class (DTO) on my Kotlin Multiplatform project that looks like this

@Serializable
data class LoginResponse(val data: LoginResponseBody){
    @Serializable
    data class LoginResponseBody(
        var accessToken: String,
        var lastChannel: String? = null,
        var lastDateTime: String? = null,
        var userId: String? = null,
        var transactionalKeyStatus: String? = null,
        var accessStatus: String? = null
    )
}

When I add that multiplatform project into my xcode project, the object translates to:

__attribute__((objc_subclassing_restricted))
__attribute__((swift_name("LoginResponse")))
@interface SharedEmaLoginResponse : SharedEmaBase
- (instancetype)initWithData:(SharedEmaLoginResponseLoginResponseBody *)data __attribute__((swift_name("init(data:)"))) __attribute__((objc_designated_initializer));
- (SharedEmaLoginResponseLoginResponseBody *)component1 __attribute__((swift_name("component1()")));
- (SharedEmaLoginResponse *)doCopyData:(SharedEmaLoginResponseLoginResponseBody *)data __attribute__((swift_name("doCopy(data:)")));
- (BOOL)isEqual:(id _Nullable)other __attribute__((swift_name("isEqual(_:)")));
- (NSUInteger)hash __attribute__((swift_name("hash()")));
- (NSString *)description __attribute__((swift_name("description()")));
@property (readonly) SharedEmaLoginResponseLoginResponseBody *data __attribute__((swift_name("data")));
@end;

I thought that class LoginResponse would conform to 'Decodable' but it doesn't, this is what my fake repository looks like in my xcode project

final class LoginRepositoryTest : XCTestCase, IAuthRepository {
var hasError = false

func login(loginRequest: LoginRequest, success: @escaping (LoginResponse) -> Void, failure: @escaping (ErrorResponse) -> Void, completionHandler: @escaping (KotlinUnit?, Error?) -> Void) {
    if hasError {
        failure(ErrorResponse(messages: []))
    } else {
        let data = loadStub(name: "loginResponse", extension: "json")
        let decoder = JSONDecoder()
        decoder.dateDecodingStrategy = .secondsSince1970
        
        let loginData = try! decoder.decode(LoginResponse.self, from: data)
        success(loginData)
    }
}


func getKoin() -> Koin_coreKoin {}

}

Error

Instance method 'decode(_:from:)' requires that 'LoginResponse' conform to 'Decodable'

I think I have these two options:

  • I make that kotlin class conform to 'Decodable'
  • I find a way to convert that JSON to that object without using Decodable

Any help is appreciated

2 Answers

Kotlin serialization isn't meant to confirm Decodable protocol to the class. It's meant to be used from kotlin with kotlin serialization, like this:

val response: LoginResponse = Json.decodeFromString(
    LoginResponse.serializer(),
    string
)

p.s. originally you can use Json.decodeFromString<LoginResponse>(string), but this's not yet supported on multiplatform as far as I remember

Network and JSON parsing are things which are most easily done on KMM, and you can write tests there too, but in case you still need to use it directly from iOS, you can try the following wrapper:

object JsonHelper {
    @Throws(Throwable::class)
    fun decodeRtcMsg(string: String) = 
        Json.decodeFromString(
            RtcMsg.serializer(), 
            string
        )
}

Then you can use it from swift:

do {
    try JsonHelper().decodeLoginResponse(string: "")
} catch {
    print(error)
}
Related