Как я могу создать подклассы, используя Argo и Swift?
Я использую Argo в приложении Swift для декодирования JSON в объекты. У меня есть JSON, как это:
"activities": [
{
"id": "intro-to-the-program",
"type": "session",
"audio": "intro-to-the-program.mp3"
},
{
"id": "goal-setting",
"type": "session",
"audio": "goal-setting.mp3"
},
{
"id": "onboarding-quiz",
"type": "quiz"
}
]
Основываясь на типе, я на самом деле хочу создать экземпляр подкласса класса Activity (ActivitySession, ActivityQuiz и т. Д.), Чтобы подкласс выполнял свое собственное декодирование.
Как я могу это сделать? Функция декодирования верхнего уровня ожидает тип возвращаемого значения Decoded<Activity>
и ни один из моих подходов пока не способен победить его.
1 ответ
Решение
Вот один из способов сделать это, включив тип, чтобы условно декодировать его и получить хорошее сообщение об ошибке, когда указан недопустимый тип.
struct ThingWithActivities: Decodable {
let activities: [Activity]
static func decode(json: JSON) -> Decoded<ThingWithActivities> {
return curry(ThingWithActivities.init)
<^> json <|| "activities"
}
}
class Activity: Decodable {
let id: String
init(id: String) {
self.id = id
}
class func decode(json: JSON) -> Decoded<Activity> {
let decodedType: Decoded<String> = json <| "type"
return decodedType.flatMap { type in
switch type {
case "session": return ActivitySession.decode(json)
case "quiz": return ActivityQuiz.decode(json)
default:
return .Failure(.Custom("Expected valid type, found: \(type)"))
}
}
}
}
class ActivitySession: Activity {
let audio: String
init(id: String, audio: String) {
self.audio = audio
super.init(id: id)
}
override static func decode(json: JSON) -> Decoded<Activity> {
return curry(ActivitySession.init)
<^> json <| "id"
<*> json <| "audio"
}
}
class ActivityQuiz: Activity {
override static func decode(json: JSON) -> Decoded<Activity> {
return curry(ActivityQuiz.init)
<^> json <| "id"
}
}
let activities: Decoded<ThingWithActivities>? = JSONFromFile("activities").flatMap(decode)
print(activities)
// => Optional(Success(ThingWithActivities(activities: [ActivitySession, ActivitySession, ActivityQuiz])))
Ключевая часть вытаскивает тип и .flatMap
над ним, чтобы условно декодировать в тип, которым он должен быть.