From bbeb799ac2a686b27ae28a43c2d3eef874ce1d67 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Thu, 25 Jun 2026 10:53:31 +0200 Subject: [PATCH] feat(ios): add keyman-version to package-version check For Keyman for iOS, add the new `keyman-version` parameter to the api.keyman.com/package-version call so that updates to packages that are not supported on the current version of Keyman will not be offered. This supports the scenario where an updated keyboard or lexical model depends on a newer version of Keyman. Note that an older version of the keyboard or lexical model package will not be offered (the user can still download and install an older version manually, but generally, the recommended solution is to upgrade Keyman; there would be significant cost to add support for querying and installation of older version keyboards on the server side, for limited benefit). Also change order of handling for response so that error responses are recognized even if non-error fields are present. (The current api endpoint, before keymanapp/api.keyman.com#325 lands, can return `kmp` and `version` fields even when an `error` field was present, and the iOS code would see that as a valid response for upgrade, when it shouldn't. The keymanapp/api.keyman.com#325 change ensures that the additional fields are not set if there is an `error` field.) Relates-to: keymanapp/api.keyman.com#325 --- .../Classes/Queries/Queries+PackageVersion.swift | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Queries/Queries+PackageVersion.swift b/ios/engine/KMEI/KeymanEngine/Classes/Queries/Queries+PackageVersion.swift index 69ca1e15a91..769089fdbc5 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Queries/Queries+PackageVersion.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Queries/Queries+PackageVersion.swift @@ -52,10 +52,10 @@ extension Queries { func dictionaryReducer(container: KeyedDecodingContainer, category: String) -> ([String: ResultComponent], Queries.IDCodingKey) throws -> [String: ResultComponent] { return { (dict, id) -> [String: ResultComponent] in var dict = dict - if let entry = try? container.decode(ResultEntry.self, forKey: id) { - dict[id.stringValue] = entry - } else if let error = try? container.decode(ResultError.self, forKey: id) { + if let error = try? container.decode(ResultError.self, forKey: id) { dict[id.stringValue] = error + } else if let entry = try? container.decode(ResultEntry.self, forKey: id) { + dict[id.stringValue] = entry } else { throw FetchError.decodingError(category, id.stringValue) } @@ -116,7 +116,10 @@ extension Queries { return URLQueryItem(name: resourceField, value: key.id) } - urlComponents.queryItems = queryItems + [URLQueryItem(name: "platform", value: "ios")] + urlComponents.queryItems = queryItems + [ + URLQueryItem(name: "platform", value: "ios"), + URLQueryItem(name: "keyman-version", value: Version.current.plainString) + ] let message = "Querying package versions through API endpoint: \(urlComponents.url!)" os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message) SentryManager.breadcrumb(message)