From 3c04b85ece977aefd5f22fde6c1f3103a1703220 Mon Sep 17 00:00:00 2001 From: mosherBT Date: Tue, 22 Sep 2026 13:50:14 -0400 Subject: [PATCH 1/2] Remove request URL from error strings --- Source/Misc/Networking.swift | 9 +++++++ Source/OptableSDK.swift | 21 +++++++++------ Tests/Unit/OptableSDKHelpersTests.swift | 34 +++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 8 deletions(-) diff --git a/Source/Misc/Networking.swift b/Source/Misc/Networking.swift index 47d8cf4..fb9fa96 100644 --- a/Source/Misc/Networking.swift +++ b/Source/Misc/Networking.swift @@ -175,3 +175,12 @@ enum HTTPStatusCode: Int { (500 ..< 600).contains(rawValue) } } + +// MARK: - HTTPURLResponse +extension HTTPURLResponse { + /// Range check rather than an `HTTPStatusCode` lookup, so that a 2xx the enum + /// does not enumerate is still treated as success. + var isSuccess: Bool { + (200 ..< 300).contains(statusCode) + } +} diff --git a/Source/OptableSDK.swift b/Source/OptableSDK.swift index 308213f..f6b710a 100644 --- a/Source/OptableSDK.swift +++ b/Source/OptableSDK.swift @@ -314,13 +314,13 @@ extension OptableSDK { api.dispatch(request: request, completionHandler: { data, response, error in guard let response = response as? HTTPURLResponse, error == nil, data != nil else { if let err = error { - completion(.failure(OptableError.identify("Session error: \(err)"))) + completion(.failure(OptableError.identify("Session error: \(OptableSDK.generateSessionErrorDescription(with: err))"))) } else { completion(.failure(OptableError.identify("Session error: Unknown"))) } return } - guard HTTPStatusCode(rawValue: response.statusCode)?.isSuccess == true else { + guard response.isSuccess else { let errDesc = OptableSDK.generateEdgeAPIErrorDescription(with: data, response: response) completion(.failure(OptableError.identify(errDesc, code: response.statusCode))) return @@ -343,13 +343,13 @@ extension OptableSDK { api.dispatch(request: request, completionHandler: { data, response, error in guard let response = response as? HTTPURLResponse, error == nil, data != nil else { if let err = error { - completion(.failure(OptableError.targeting("Session error: \(err)"))) + completion(.failure(OptableError.targeting("Session error: \(OptableSDK.generateSessionErrorDescription(with: err))"))) } else { completion(.failure(OptableError.targeting("Session error: Unknown"))) } return } - guard HTTPStatusCode(rawValue: response.statusCode)?.isSuccess == true else { + guard response.isSuccess else { let errDesc = OptableSDK.generateEdgeAPIErrorDescription(with: data, response: response) completion(.failure(OptableError.targeting(errDesc, code: response.statusCode))) return @@ -377,13 +377,13 @@ extension OptableSDK { api.dispatch(request: request, completionHandler: { data, response, error in guard let response = response as? HTTPURLResponse, error == nil else { if let err = error { - completion(.failure(OptableError.witness("Session error: \(err)"))) + completion(.failure(OptableError.witness("Session error: \(OptableSDK.generateSessionErrorDescription(with: err))"))) } else { completion(.failure(OptableError.witness("Session error: Unknown"))) } return } - guard HTTPStatusCode(rawValue: response.statusCode)?.isSuccess == true else { + guard response.isSuccess else { let errDesc = OptableSDK.generateEdgeAPIErrorDescription(with: data, response: response) completion(.failure(OptableError.witness(errDesc, code: response.statusCode))) return @@ -400,13 +400,13 @@ extension OptableSDK { api.dispatch(request: request, completionHandler: { data, response, error in guard let response = response as? HTTPURLResponse, error == nil else { if let err = error { - completion(.failure(OptableError.profile("Session error: \(err)"))) + completion(.failure(OptableError.profile("Session error: \(OptableSDK.generateSessionErrorDescription(with: err))"))) } else { completion(.failure(OptableError.profile("Session error: Unknown"))) } return } - guard HTTPStatusCode(rawValue: response.statusCode)?.isSuccess == true else { + guard response.isSuccess else { let errDesc = OptableSDK.generateEdgeAPIErrorDescription(with: data, response: response) completion(.failure(OptableError.profile(errDesc, code: response.statusCode))) return @@ -459,6 +459,11 @@ extension OptableSDK { return msg } + static func generateSessionErrorDescription(with error: Error) -> String { + let error = error as NSError + return "\(error.domain) \(error.code): \(error.localizedDescription)" + } + static func generateGAMTargetingKeywords(from targetingData: NSDictionary?) -> NSDictionary? { guard let targetingData, diff --git a/Tests/Unit/OptableSDKHelpersTests.swift b/Tests/Unit/OptableSDKHelpersTests.swift index 704f3a9..dacb7a4 100644 --- a/Tests/Unit/OptableSDKHelpersTests.swift +++ b/Tests/Unit/OptableSDKHelpersTests.swift @@ -142,6 +142,40 @@ class OptableSDKHelpersTests: XCTestCase { XCTAssertFalse(message.contains("data:")) } + // MARK: SessionErrorDescription + func test_generateSessionErrorDescription_excludesFailingURL() { + let error = NSError( + domain: NSURLErrorDomain, + code: NSURLErrorTimedOut, + userInfo: [ + NSLocalizedDescriptionKey: "The request timed out.", + NSURLErrorFailingURLStringErrorKey: "https://example.com/v2/targeting?id=e%3Aabc123&osdk=ios", + ] + ) + + let message = OptableSDK.generateSessionErrorDescription(with: error) + XCTAssertTrue(message.contains(NSURLErrorDomain)) + XCTAssertTrue(message.contains("\(NSURLErrorTimedOut)")) + XCTAssertTrue(message.contains("The request timed out.")) + XCTAssertFalse(message.contains("targeting")) + XCTAssertFalse(message.contains("abc123")) + } + + // MARK: HTTPURLResponse.isSuccess + func test_isSuccess_coversFull2xxRange() throws { + let url = try XCTUnwrap(URL(string: "https://example.com")) + + for statusCode in [200, 201, 204, 206, 207, 226] { + let response = try XCTUnwrap(HTTPURLResponse(url: url, statusCode: statusCode, httpVersion: nil, headerFields: nil)) + XCTAssertTrue(response.isSuccess, "\(statusCode) should be success") + } + + for statusCode in [100, 301, 400, 404, 500] { + let response = try XCTUnwrap(HTTPURLResponse(url: url, statusCode: statusCode, httpVersion: nil, headerFields: nil)) + XCTAssertFalse(response.isSuccess, "\(statusCode) should not be success") + } + } + // MARK: Version func test_version_notUnknown() { // Should resolve to something like ios-- From fad53d831ff70c792f6a36b1e5ef5e45c4c56bd2 Mon Sep 17 00:00:00 2001 From: mosherBT Date: Tue, 22 Sep 2026 14:00:50 -0400 Subject: [PATCH 2/2] comments --- Source/Misc/Networking.swift | 9 --------- Source/OptableSDK.swift | 8 ++++---- Tests/Unit/OptableSDKHelpersTests.swift | 15 --------------- 3 files changed, 4 insertions(+), 28 deletions(-) diff --git a/Source/Misc/Networking.swift b/Source/Misc/Networking.swift index fb9fa96..47d8cf4 100644 --- a/Source/Misc/Networking.swift +++ b/Source/Misc/Networking.swift @@ -175,12 +175,3 @@ enum HTTPStatusCode: Int { (500 ..< 600).contains(rawValue) } } - -// MARK: - HTTPURLResponse -extension HTTPURLResponse { - /// Range check rather than an `HTTPStatusCode` lookup, so that a 2xx the enum - /// does not enumerate is still treated as success. - var isSuccess: Bool { - (200 ..< 300).contains(statusCode) - } -} diff --git a/Source/OptableSDK.swift b/Source/OptableSDK.swift index f6b710a..b17ccc4 100644 --- a/Source/OptableSDK.swift +++ b/Source/OptableSDK.swift @@ -320,7 +320,7 @@ extension OptableSDK { } return } - guard response.isSuccess else { + guard (200 ..< 300).contains(response.statusCode) else { let errDesc = OptableSDK.generateEdgeAPIErrorDescription(with: data, response: response) completion(.failure(OptableError.identify(errDesc, code: response.statusCode))) return @@ -349,7 +349,7 @@ extension OptableSDK { } return } - guard response.isSuccess else { + guard (200 ..< 300).contains(response.statusCode) else { let errDesc = OptableSDK.generateEdgeAPIErrorDescription(with: data, response: response) completion(.failure(OptableError.targeting(errDesc, code: response.statusCode))) return @@ -383,7 +383,7 @@ extension OptableSDK { } return } - guard response.isSuccess else { + guard (200 ..< 300).contains(response.statusCode) else { let errDesc = OptableSDK.generateEdgeAPIErrorDescription(with: data, response: response) completion(.failure(OptableError.witness(errDesc, code: response.statusCode))) return @@ -406,7 +406,7 @@ extension OptableSDK { } return } - guard response.isSuccess else { + guard (200 ..< 300).contains(response.statusCode) else { let errDesc = OptableSDK.generateEdgeAPIErrorDescription(with: data, response: response) completion(.failure(OptableError.profile(errDesc, code: response.statusCode))) return diff --git a/Tests/Unit/OptableSDKHelpersTests.swift b/Tests/Unit/OptableSDKHelpersTests.swift index dacb7a4..c53753d 100644 --- a/Tests/Unit/OptableSDKHelpersTests.swift +++ b/Tests/Unit/OptableSDKHelpersTests.swift @@ -161,21 +161,6 @@ class OptableSDKHelpersTests: XCTestCase { XCTAssertFalse(message.contains("abc123")) } - // MARK: HTTPURLResponse.isSuccess - func test_isSuccess_coversFull2xxRange() throws { - let url = try XCTUnwrap(URL(string: "https://example.com")) - - for statusCode in [200, 201, 204, 206, 207, 226] { - let response = try XCTUnwrap(HTTPURLResponse(url: url, statusCode: statusCode, httpVersion: nil, headerFields: nil)) - XCTAssertTrue(response.isSuccess, "\(statusCode) should be success") - } - - for statusCode in [100, 301, 400, 404, 500] { - let response = try XCTUnwrap(HTTPURLResponse(url: url, statusCode: statusCode, httpVersion: nil, headerFields: nil)) - XCTAssertFalse(response.isSuccess, "\(statusCode) should not be success") - } - } - // MARK: Version func test_version_notUnknown() { // Should resolve to something like ios--