Fix PR inbox: decode Gitea ISO8601 dates with timezone offset
Build iOS App / Build .ipa and publish source (push) Successful in 28s

Gitea returns dates like 2026-08-12T11:09:13+02:00. The default JSONDecoder
date strategy expects a Unix timestamp number, so decoding any Date? field
threw and the whole [PullRequest] array failed — the inbox showed no PRs.
Add a shared decoder with a custom ISO8601 strategy (with and without
fractional seconds) and use it for all get/post decoding.
This commit is contained in:
Hermes committed 2026-08-12 17:51:46 +00:00
1 parent ddb6983a99
commit b2311d8007
1 file changed
+29 -3
+29 -3
View File
@@ -22,6 +22,32 @@ struct GiteaClient {
let token: String
let session: URLSession
/// Shared decoder. Gitea returns ISO8601 date strings WITH a timezone
/// offset (e.g. "2026-08-12T11:09:13+02:00"), which neither the default
/// `.deferredToDate` strategy (expects a Unix timestamp number) nor
/// `.iso8601` (expects a trailing "Z") can parse. Without this, decoding
/// any `Date?` field throws and the whole array fails — the inbox shows
/// nothing even though the data is present.
private static let decoder: JSONDecoder = {
let d = JSONDecoder()
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime,
.withFractionalSeconds]
d.dateDecodingStrategy = .custom { decoder in
let container = try decoder.singleValueContainer()
let str = try container.decode(String.self)
if let date = formatter.date(from: str) { return date }
// Fall back to a formatter without fractional seconds.
let plain = ISO8601DateFormatter()
plain.formatOptions = [.withInternetDateTime]
if let date = plain.date(from: str) { return date }
throw DecodingError.dataCorruptedError(
in: container,
debugDescription: "Cannot decode date string \(str)")
}
return d
}()
init(instanceURL: URL, token: String, session: URLSession = .shared) {
self.instanceURL = instanceURL
self.token = token
@@ -62,7 +88,7 @@ struct GiteaClient {
case 404:
throw APIError.notFound
default:
let message = (try? JSONDecoder().decode([String: String].self,
let message = (try? Self.decoder.decode([String: String].self,
from: data))?["message"]
throw APIError.httpStatus(http.statusCode, message)
}
@@ -72,7 +98,7 @@ struct GiteaClient {
query: [URLQueryItem]? = nil) async throws -> T {
let data = try await request(path, query: query)
do {
return try JSONDecoder().decode(T.self, from: data)
return try Self.decoder.decode(T.self, from: data)
} catch {
throw APIError.decoding(String(describing: error))
}
@@ -80,7 +106,7 @@ struct GiteaClient {
private func post<T: Decodable>(_ path: String, body: Data? = nil) async throws -> T {
let data = try await request(path, method: "POST", body: body)
return try JSONDecoder().decode(T.self, from: data)
return try Self.decoder.decode(T.self, from: data)
}
private func postNoBody(_ path: String) async throws {