Reputation: 1356
I am fetching data from my Gitlab-Server with Alamofire and cannot seem to set a CodingKey correctly.
Here are 2 models I use for this (abbreviated):
struct Issue: Codable {
let id: Int
var iid: Int? = nil
var project_id: Int? = nil
var state: String?
var title: String? = nil
var description: String? = nil
var labels: [Label]? = [] // custom type, see below
}
struct Label {
let id: Int
var name: String
var color: String = "#FFAABB"
var labelDescription: String = ""
}
extension Label: CustomStringConvertible {
var description: String {
return "Label: \(name)"
}
}
extension Label: Codable {
enum CodingKeys: String, CodingKey {
case id
case name
case color
case labelDescription = "description"
}
}
extension Label: Equatable {
static func ==(lhs: Label, rhs: Label) -> Bool {
return lhs.id == rhs.id
}
}
Now, when fetching my issues with Alamofire, I can see JSON coming back, e.g.
{
"id": 402,
"iid": 78,
"project_id": 34,
"title": "Feature: Prioritization / Order of issues in table view",
"description": "On the web, it is possible to have the issues ordered differently (e.g. by date added or manually).\n\nCheck, how `manually` is implemented and whether this ordering information is coming with the api.\nIf it is, could this be used to do reorders in the tableview?",
"state": "opened",
"created_at": "2020-03-26T14:08:21.355Z",
"updated_at": "2020-03-27T12:58:03.418Z",
"closed_at": null,
"closed_by": null,
"labels": [{
"id": 81,
"name": "P1",
"color": "#428BCA",
"description": "Priority 1 of 4",
"description_html": "Priority 1 of 4",
"text_color": "#FFFFFF"
}],
...
}
Encoding fails though with Alamofire showing the following error:
[Request]: GET https: //dev.appfros.ch/api/v4/projects/34/labels
[Request Body]:
None
[Response]:
[Status Code]: 200
[Headers]:
Cache-Control: max-age=0, private, must-revalidate
Content-Length: 929
Content-Type: application/json
Date: Fri, 27 Mar 2020 14:08:59 GMT
Etag: W/\"758e92848542f37113453292b85cbe17\"
Link: <https://dev.appfros.ch/api/v4/projects/34/labels?id=34&include_ancestor_groups=true&page=1&per_page=20&with_counts=false>; rel=\"first\", <https://dev.appfros.ch/api/v4/projects/34/labels?id=34&include_ancestor_groups=true&page=1&per_page=20&with_counts=false>; rel=\"last\"
Server: nginx/1.17.6
Strict-Transport-Security: max-age=31536000, max-age=31536000
Vary: Origin
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
x-next-page:
x-page: 1
x-per-page: 20
x-prev-page:
x-request-id: BdvjZaS8hl4
x-runtime: 0.052360
x-total: 5
x-total-pages: 1
[Response Body]:
[{\"id\":81,\"name\":\"P1\",\"color\":\"#428BCA\",\"description\":\"Priority 1 of 4\",\"description_html\":\"Priority 1 of 4\",\"text_color\":\"#FFFFFF\",\"subscribed\":false,\"priority\":null,\"is_project_label\":true},{\"id\":82,\"name\":\"P2\",\"color\":\"#428BCA\",\"description\":\"Priority 2 of 4\",\"description_html\":\"Priority 2 of 4\",\"text_color\":\"#FFFFFF\",\"subscribed\":false,\"priority\":null,\"is_project_label\":true},{\"id\":83,\"name\":\"P3\",\"color\":\"#428BCA\",\"description\":\"Priority 3 of 4\",\"description_html\":\"Priority 3 of 4\",\"text_color\":\"#FFFFFF\",\"subscribed\":false,\"priority\":null,\"is_project_label\":true},{\"id\":84,\"name\":\"P4\",\"color\":\"#428BCA\",\"description\":\"Priority 4 of 4\",\"description_html\":\"Priority 4 of 4\",\"text_color\":\"#FFFFFF\",\"subscribed\":false,\"priority\":null,\"is_project_label\":true},{\"id\":79,\"name\":\"WIP\",\"color\":\"#004E00\",\"description\":null,\"description_html\":\"\",\"text_color\":\"#FFFFFF\",\"subscribed\":false,\"priority\":null,\"is_project_label\":true}]
[Data]: 929 bytes
[Network Duration]: 0.3039969205856323s
[Serialization Duration]: 0.003159046173095703s
[Result]: failure(Alamofire.AFError.responseSerializationFailed(reason: Alamofire.AFError.ResponseSerializationFailureReason.decodingFailed(error: Swift.DecodingError.valueNotFound(Swift.String, Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: \"Index 4\", intValue: 4), CodingKeys(stringValue: \"description\", intValue: nil)], debugDescription: \"Expected String value but found null instead.\", underlyingError: nil)))))
Also, testing shows that when I omit the description
key of type Label
, everything works. Obviously, I want to convert the description
key to labelDescription
in my model because description
is already needed to conform to CustomStringConvertible
– but even when I remove that conformance and change my Labels
model to the following, I get the error...
struct Label {
let id: Int
var name: String
var color: String = "#FFAABB"
var description: String = ""
}
Upvotes: 1
Views: 174
Reputation: 26066
You can override init(from decoder:)
and decodeIfPresent(_,forKey:)
From its documentation:
This method returns
nil
if the container does not have a value associated with key, or if the value isnull
. The difference between these states can be distinguished with acontains(_:)
call.
In your case:
extension Label {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(Int.self, forKey: .id)
name = try container.decode(String.self, forKey: .name)
color = try container.decode(String.self, forKey: .color)
labelDescription = try container.decodeIfPresent(String.self, forKey: .labelDescription) ?? ""
}
}
Upvotes: 1