From abf9b321111827d927cb4af0e898ff0f7d01ecac Mon Sep 17 00:00:00 2001 From: Marek Stransky Date: Fri, 26 Jul 2024 11:50:26 +0200 Subject: [PATCH] Add logging when decoding of optionals throws --- .../Responses/WMTOperationListResponse.swift | 12 +- .../WMTOperationAttributeImage.swift | 12 +- .../Screens/WMTPostApprovaScreenReview.swift | 21 +- .../Screens/WMTPreApprovalScreen.swift | 24 +- .../UserOperation/WMTOperationFormData.swift | 12 +- .../UserOperation/WMTOperationUIData.swift | 60 ++++- .../Model/UserOperation/WMTResultTexts.swift | 36 ++- .../Model/UserOperation/WMTTemplates.swift | 210 ++++++++++++++++-- .../OperationUIDataTests.swift | 5 + 9 files changed, 359 insertions(+), 33 deletions(-) diff --git a/WultraMobileTokenSDK/Operations/Model/Responses/WMTOperationListResponse.swift b/WultraMobileTokenSDK/Operations/Model/Responses/WMTOperationListResponse.swift index 5bfc2cc..41dd8c5 100644 --- a/WultraMobileTokenSDK/Operations/Model/Responses/WMTOperationListResponse.swift +++ b/WultraMobileTokenSDK/Operations/Model/Responses/WMTOperationListResponse.swift @@ -29,7 +29,17 @@ public class WMTOperationListResponse: WPNResponseArray public required init(from decoder: Decoder) throws { let c = try decoder.container(keyedBy: Keys.self) - currentTimestamp = try? c.decode(Date.self, forKey: Keys.currentTimestamp) + + if c.contains(.currentTimestamp) { + do { + currentTimestamp = try c.decode(Date.self, forKey: .currentTimestamp) + } catch { + D.error("Failed to decode \(Keys.currentTimestamp) - \(error), setting to null") + currentTimestamp = nil + } + } else { + currentTimestamp = nil + } try super.init(from: decoder) } diff --git a/WultraMobileTokenSDK/Operations/Model/UserOperation/Attributes/WMTOperationAttributeImage.swift b/WultraMobileTokenSDK/Operations/Model/UserOperation/Attributes/WMTOperationAttributeImage.swift index b8c83d3..b5d51c7 100644 --- a/WultraMobileTokenSDK/Operations/Model/UserOperation/Attributes/WMTOperationAttributeImage.swift +++ b/WultraMobileTokenSDK/Operations/Model/UserOperation/Attributes/WMTOperationAttributeImage.swift @@ -42,7 +42,17 @@ public class WMTOperationAttributeImage: WMTOperationAttribute { let c = try decoder.container(keyedBy: Keys.self) self.thumbnailUrl = try c.decode(String.self, forKey: .thumbnailUrl) - self.originalUrl = try? c.decode(String.self, forKey: .originalUrl) + + if c.contains(.originalUrl) { + do { + originalUrl = try c.decode(String.self, forKey: .originalUrl) + } catch { + D.error("Failed to decode \(Keys.originalUrl) - \(error), setting to null") + originalUrl = nil + } + } else { + originalUrl = nil + } try super.init(from: decoder) } diff --git a/WultraMobileTokenSDK/Operations/Model/UserOperation/Screens/WMTPostApprovaScreenReview.swift b/WultraMobileTokenSDK/Operations/Model/UserOperation/Screens/WMTPostApprovaScreenReview.swift index 35d6318..08fa608 100644 --- a/WultraMobileTokenSDK/Operations/Model/UserOperation/Screens/WMTPostApprovaScreenReview.swift +++ b/WultraMobileTokenSDK/Operations/Model/UserOperation/Screens/WMTPostApprovaScreenReview.swift @@ -58,10 +58,25 @@ public class WMTReviewPostApprovalScreenPayload: WMTPostApprovalScreenPayload { // MARK: Internals public required init(from decoder: Decoder) throws { - let c = try decoder.container(keyedBy: Keys.self) - attributes = (try? c.decode([WMTOperationAttributeDecodable].self, forKey: .attributes).map { - $0.attrObject }) ?? [] + + var operationAttributes: [WMTOperationAttribute] = [] + do { + var container = try c.nestedUnkeyedContainer(forKey: .attributes) + // If decoding fails log it and continue decoding until the end of container + while container.isAtEnd == false { + do { + let attribute = try WMTOperationAttributeDecodable(from: container.superDecoder()) + operationAttributes.append(attribute.attrObject) + } catch { + D.error("Error decoding WMTOperationFormData attribute: \(error)") + } + } + } catch { + D.error("No attributes in WMTOperationFormData: \(error)") + } + attributes = operationAttributes + super.init() } diff --git a/WultraMobileTokenSDK/Operations/Model/UserOperation/Screens/WMTPreApprovalScreen.swift b/WultraMobileTokenSDK/Operations/Model/UserOperation/Screens/WMTPreApprovalScreen.swift index 5d0caf8..fdf136b 100644 --- a/WultraMobileTokenSDK/Operations/Model/UserOperation/Screens/WMTPreApprovalScreen.swift +++ b/WultraMobileTokenSDK/Operations/Model/UserOperation/Screens/WMTPreApprovalScreen.swift @@ -56,8 +56,28 @@ public class WMTPreApprovalScreen: Codable { type = ScreenType(rawValue: t) ?? .unknown heading = try c.decode(String.self, forKey: .heading) message = try c.decode(String.self, forKey: .message) - items = try? c.decode([String].self, forKey: .items) - approvalType = try? c.decode(WMTPreApprovalScreenConfirmAction.self, forKey: .approvalType) + + if c.contains(.items) { + do { + items = try c.decode([String].self, forKey: .items) + } catch { + D.error("Failed to decode \(Keys.items) - \(error), setting to null") + items = nil + } + } else { + items = nil + } + + if c.contains(.approvalType) { + do { + approvalType = try c.decode(WMTPreApprovalScreenConfirmAction.self, forKey: .approvalType) + } catch { + D.error("Failed to decode \(Keys.approvalType) - \(error), setting to null") + approvalType = nil + } + } else { + approvalType = nil + } } public init(type: ScreenType, heading: String, message: String, items: [String]? = nil, approvalType: WMTPreApprovalScreenConfirmAction?) { diff --git a/WultraMobileTokenSDK/Operations/Model/UserOperation/WMTOperationFormData.swift b/WultraMobileTokenSDK/Operations/Model/UserOperation/WMTOperationFormData.swift index b3b84db..c51323e 100644 --- a/WultraMobileTokenSDK/Operations/Model/UserOperation/WMTOperationFormData.swift +++ b/WultraMobileTokenSDK/Operations/Model/UserOperation/WMTOperationFormData.swift @@ -44,7 +44,17 @@ public class WMTOperationFormData: Codable { let c = try decoder.container(keyedBy: Keys.self) title = try c.decode(String.self, forKey: .title) message = try c.decode(String.self, forKey: .message) - resultTexts = try? c.decode(WMTResultTexts.self, forKey: .resultTexts) + + if c.contains(.resultTexts) { + do { + resultTexts = try c.decode(WMTResultTexts.self, forKey: .resultTexts) + } catch { + D.error("Failed to decode \(Keys.resultTexts) - \(error), setting to null") + resultTexts = nil + } + } else { + resultTexts = nil + } var operationAttributes: [WMTOperationAttribute] = [] do { diff --git a/WultraMobileTokenSDK/Operations/Model/UserOperation/WMTOperationUIData.swift b/WultraMobileTokenSDK/Operations/Model/UserOperation/WMTOperationUIData.swift index 2f16e02..d7680a6 100644 --- a/WultraMobileTokenSDK/Operations/Model/UserOperation/WMTOperationUIData.swift +++ b/WultraMobileTokenSDK/Operations/Model/UserOperation/WMTOperationUIData.swift @@ -46,11 +46,61 @@ open class WMTOperationUIData: Codable { public required init(from decoder: Decoder) throws { let c = try decoder.container(keyedBy: Keys.self) - flipButtons = try? c.decode(Bool.self, forKey: .flipButtons) - blockApprovalOnCall = try? c.decode(Bool.self, forKey: .blockApprovalOnCall) - preApprovalScreen = try? c.decode(WMTPreApprovalScreen.self, forKey: .preApprovalScreen) - postApprovalScreen = try? c.decode(WMTPostApprovalScreenDecodable.self, forKey: .postApprovalScreen).postApprovalObject - templates = try? c.decode(WMTTemplates.self, forKey: .templates) + + if c.contains(.flipButtons) { + do { + flipButtons = try c.decode(Bool.self, forKey: .flipButtons) + } catch { + D.error("Failed to decode \(Keys.flipButtons) - \(error), setting to null") + flipButtons = nil + } + } else { + flipButtons = nil + } + + if c.contains(.blockApprovalOnCall) { + do { + blockApprovalOnCall = try c.decode(Bool.self, forKey: .blockApprovalOnCall) + } catch { + D.error("Failed to decode \(Keys.blockApprovalOnCall) - \(error), setting to null") + blockApprovalOnCall = nil + } + } else { + blockApprovalOnCall = nil + } + + if c.contains(.preApprovalScreen) { + do { + preApprovalScreen = try c.decode(WMTPreApprovalScreen.self, forKey: .preApprovalScreen) + } catch { + D.error("Failed to decode \(Keys.preApprovalScreen) - \(error), setting to null") + preApprovalScreen = nil + } + } else { + preApprovalScreen = nil + } + + if c.contains(.postApprovalScreen) { + do { + postApprovalScreen = try c.decode(WMTPostApprovalScreenDecodable.self, forKey: .postApprovalScreen).postApprovalObject + } catch { + D.error("Failed to decode \(Keys.postApprovalScreen) - \(error), setting to null") + postApprovalScreen = nil + } + } else { + postApprovalScreen = nil + } + + if c.contains(.templates) { + do { + templates = try c.decode(WMTTemplates.self, forKey: .templates) + } catch { + D.error("Failed to decode \(Keys.templates) - \(error), setting to null") + templates = nil + } + } else { + templates = nil + } } public init(flipButtons: Bool?, blockApprovalOnCall: Bool?, preApprovalScreen: WMTPreApprovalScreen?, postApprovalScreen: WMTPostApprovalScreen?, templates: WMTTemplates? = nil) { diff --git a/WultraMobileTokenSDK/Operations/Model/UserOperation/WMTResultTexts.swift b/WultraMobileTokenSDK/Operations/Model/UserOperation/WMTResultTexts.swift index 205c97d..0e1399b 100644 --- a/WultraMobileTokenSDK/Operations/Model/UserOperation/WMTResultTexts.swift +++ b/WultraMobileTokenSDK/Operations/Model/UserOperation/WMTResultTexts.swift @@ -38,9 +38,39 @@ public class WMTResultTexts: Codable { public required init(from decoder: Decoder) throws { let c = try decoder.container(keyedBy: Keys.self) - success = try? c.decode(String.self, forKey: .success) - failure = try? c.decode(String.self, forKey: .failure) - reject = try? c.decode(String.self, forKey: .reject) + + if c.contains(.success) { + do { + success = try c.decode(String.self, forKey: .success) + } catch { + D.error("Failed to decode \(Keys.success) - \(error), setting to null") + success = nil + } + } else { + success = nil + } + + if c.contains(.failure) { + do { + failure = try c.decode(String.self, forKey: .failure) + } catch { + D.error("Failed to decode \(Keys.failure) - \(error), setting to null") + failure = nil + } + } else { + failure = nil + } + + if c.contains(.reject) { + do { + reject = try c.decode(String.self, forKey: .reject) + } catch { + D.error("Failed to decode \(Keys.reject) - \(error), setting to null") + reject = nil + } + } else { + reject = nil + } } public init(success: String?, failure: String?, reject: String?) { diff --git a/WultraMobileTokenSDK/Operations/Model/UserOperation/WMTTemplates.swift b/WultraMobileTokenSDK/Operations/Model/UserOperation/WMTTemplates.swift index 861df4e..bdeebe6 100644 --- a/WultraMobileTokenSDK/Operations/Model/UserOperation/WMTTemplates.swift +++ b/WultraMobileTokenSDK/Operations/Model/UserOperation/WMTTemplates.swift @@ -36,8 +36,27 @@ public class WMTTemplates: Codable { public required init(from decoder: Decoder) throws { let c = try decoder.container(keyedBy: Keys.self) - list = try? c.decode(ListTemplate.self, forKey: .list) - detail = try? c.decode(DetailTemplate.self, forKey: .detail) + if c.contains(.list) { + do { + list = try c.decode(ListTemplate.self, forKey: .list) + } catch { + D.error("Failed to decode \(Keys.list) - \(error), setting to null") + list = nil + } + } else { + list = nil + } + + if c.contains(.detail) { + do { + detail = try c.decode(DetailTemplate.self, forKey: .detail) + } catch { + D.error("Failed to decode \(Keys.detail) - \(error), setting to null") + detail = nil + } + } else { + detail = nil + } } public init(list: ListTemplate?, detail: DetailTemplate?) { @@ -83,11 +102,61 @@ public class WMTTemplates: Codable { public required init(from decoder: any Decoder) throws { let c = try decoder.container(keyedBy: Keys.self) - self.style = try? c.decode(String.self, forKey: .style) - self.header = try? c.decode(AttributeFormatted.self, forKey: .header) - self.title = try? c.decode(AttributeFormatted.self, forKey: .title) - self.message = try? c.decode(AttributeFormatted.self, forKey: .message) - self.image = try? c.decode(AttributeId.self, forKey: .image) + + if c.contains(.style) { + do { + style = try c.decode(String.self, forKey: .style) + } catch { + D.error("Failed to decode \(Keys.style) - \(error), setting to null") + style = nil + } + } else { + style = nil + } + + if c.contains(.header) { + do { + header = try c.decode(AttributeFormatted.self, forKey: .header) + } catch { + D.error("Failed to decode \(Keys.header) - \(error), setting to null") + header = nil + } + } else { + header = nil + } + + if c.contains(.title) { + do { + title = try c.decode(AttributeFormatted.self, forKey: .title) + } catch { + D.error("Failed to decode \(Keys.title) - \(error), setting to null") + title = nil + } + } else { + title = nil + } + + if c.contains(.message) { + do { + message = try c.decode(AttributeFormatted.self, forKey: .message) + } catch { + D.error("Failed to decode \(Keys.message) - \(error), setting to null") + message = nil + } + } else { + message = nil + } + + if c.contains(.image) { + do { + image = try c.decode(AttributeId.self, forKey: .image) + } catch { + D.error("Failed to decode \(Keys.image) - \(error), setting to null") + image = nil + } + } else { + image = nil + } } public init(style: String?, header: AttributeFormatted?, title: AttributeFormatted?, message: AttributeFormatted?, image: AttributeId?) { @@ -122,9 +191,39 @@ public class WMTTemplates: Codable { public required init(from decoder: Decoder) throws { let c = try decoder.container(keyedBy: Keys.self) - style = try? c.decode(String.self, forKey: .style) - showTitleAndMessage = try? c.decode(Bool.self, forKey: .showTitleAndMessage) - sections = try? c.decode([Section].self, forKey: .sections) + + if c.contains(.style) { + do { + style = try c.decode(String.self, forKey: .style) + } catch { + D.error("Failed to decode \(Keys.style) - \(error), setting to null") + style = nil + } + } else { + style = nil + } + + if c.contains(.showTitleAndMessage) { + do { + showTitleAndMessage = try c.decode(Bool.self, forKey: .showTitleAndMessage) + } catch { + D.error("Failed to decode \(Keys.showTitleAndMessage) - \(error), setting to null") + showTitleAndMessage = nil + } + } else { + showTitleAndMessage = nil + } + + if c.contains(.sections) { + do { + sections = try c.decode([Section].self, forKey: .sections) + } catch { + D.error("Failed to decode \(Keys.sections) - \(error), setting to null") + sections = nil + } + } else { + sections = nil + } } public init(style: String?, automaticHeaderSection: Bool?, sections: [Section]?) { @@ -153,9 +252,46 @@ public class WMTTemplates: Codable { public required init(from decoder: Decoder) throws { let c = try decoder.container(keyedBy: Keys.self) - style = try? c.decode(String.self, forKey: .style) - title = try? c.decode(AttributeId.self, forKey: .title) - cells = try? c.decode([Cell].self, forKey: .cells) + + if c.contains(.style) { + do { + style = try c.decode(String.self, forKey: .style) + } catch { + D.error("Failed to decode \(Keys.style) - \(error), setting to null") + style = nil + } + } else { + style = nil + } + + if c.contains(.title) { + do { + title = try c.decode(AttributeId.self, forKey: .title) + } catch { + D.error("Failed to decode \(Keys.title) - \(error), setting to null") + title = nil + } + } else { + title = nil + } + + if c.contains(.cells) { + var decodedCells: [Cell] = [] + + var nestedContainer = try c.nestedUnkeyedContainer(forKey: .cells) + while nestedContainer.isAtEnd == false { + do { + let cell = try Cell(from: nestedContainer.superDecoder()) + decodedCells.append(cell) + } catch { + D.error("Failed to decode \(Keys.cells) - \(error), setting to null") + } + } + + cells = decodedCells + } else { + cells = nil + } } public init(style: String?, title: AttributeId?, cells: [Cell]?) { @@ -201,11 +337,51 @@ public class WMTTemplates: Codable { public required init(from decoder: Decoder) throws { let c = try decoder.container(keyedBy: Keys.self) - style = try? c.decode(String.self, forKey: .style) name = try c.decode(AttributeId.self, forKey: .name) - visibleTitle = try? c.decode(Bool.self, forKey: .visibleTitle) - canCopy = try? c.decode(Bool.self, forKey: .canCopy) - collapsable = try? c.decode(Collapsable.self, forKey: .collapsable) + + if c.contains(.style) { + do { + style = try c.decode(String.self, forKey: .style) + } catch { + D.error("Failed to decode \(Keys.style) - \(error), setting to null") + style = nil + } + } else { + style = nil + } + + if c.contains(.visibleTitle) { + do { + visibleTitle = try c.decode(Bool.self, forKey: .visibleTitle) + } catch { + D.error("Failed to decode \(Keys.visibleTitle) - \(error), setting to null") + visibleTitle = nil + } + } else { + visibleTitle = nil + } + + if c.contains(.canCopy) { + do { + canCopy = try c.decode(Bool.self, forKey: .canCopy) + } catch { + D.error("Failed to decode \(Keys.canCopy) - \(error), setting to null") + canCopy = nil + } + } else { + canCopy = nil + } + + if c.contains(.collapsable) { + do { + collapsable = try c.decode(Collapsable.self, forKey: .collapsable) + } catch { + D.error("Failed to decode \(Keys.collapsable) - \(error), setting to null") + collapsable = nil + } + } else { + collapsable = nil + } } public init(style: String?, name: AttributeId, visibleTitle: Bool?, canCopy: Bool?, collapsable: Collapsable?) { diff --git a/WultraMobileTokenSDKTests/OperationUIDataTests.swift b/WultraMobileTokenSDKTests/OperationUIDataTests.swift index 6957592..953b051 100644 --- a/WultraMobileTokenSDKTests/OperationUIDataTests.swift +++ b/WultraMobileTokenSDKTests/OperationUIDataTests.swift @@ -227,6 +227,8 @@ class OperationUIDataTests: XCTestCase { XCTAssertEqual(uiResult.templates?.detail?.sections?[0].cells?[2].visibleTitle, true) XCTAssertEqual(uiResult.templates?.detail?.sections?[0].cells?[2].canCopy, false) XCTAssertEqual(uiResult.templates?.detail?.sections?[0].cells?[2].collapsable, .collapsed) + + XCTAssertEqual(uiResult.templates?.detail?.sections?[0].cells?.count, 3) } @@ -501,6 +503,9 @@ class OperationUIDataTests: XCTestCase { "style": null, "canCopy": false, "collapsable": "COLLAPSED" + }, + { + "visibleTitle": true } ] }