diff --git a/FirebaseVertexAI/Sources/Constants.swift b/FirebaseVertexAI/Sources/Constants.swift index 587b1ad283c..8f410c8768a 100644 --- a/FirebaseVertexAI/Sources/Constants.swift +++ b/FirebaseVertexAI/Sources/Constants.swift @@ -18,4 +18,10 @@ import Foundation enum Constants { /// The Vertex AI backend endpoint URL. static let baseURL = "https://firebasevertexai.googleapis.com" + + /// The base reverse-DNS name for `NSError` or `CustomNSError` error domains. + /// + /// - Important: A suffix must be appended to produce an error domain (e.g., + /// "com.google.firebase.vertexai.ExampleError"). + static let baseErrorDomain = "com.google.firebase.vertexai" } diff --git a/FirebaseVertexAI/Sources/Types/Internal/Errors/BackendError.swift b/FirebaseVertexAI/Sources/Types/Internal/Errors/BackendError.swift index 1dd88c285f1..42c75d628b6 100644 --- a/FirebaseVertexAI/Sources/Types/Internal/Errors/BackendError.swift +++ b/FirebaseVertexAI/Sources/Types/Internal/Errors/BackendError.swift @@ -36,6 +36,27 @@ struct BackendError: Error { } } +// MARK: - CustomNSError Conformance + +extension BackendError: CustomNSError { + public static var errorDomain: String { + return "\(Constants.baseErrorDomain).\(Self.self)" + } + + var errorCode: Int { + return httpResponseCode + } + + var errorUserInfo: [String: Any] { + return [ + NSLocalizedDescriptionKey: + "\(message) (\(Self.errorDomain) - HTTP \(httpResponseCode) \(status.rawValue))", + ] + } +} + +// MARK: - Decodable Conformance + extension BackendError: Decodable { enum CodingKeys: CodingKey { case error diff --git a/FirebaseVertexAI/Tests/Unit/GenerativeModelTests.swift b/FirebaseVertexAI/Tests/Unit/GenerativeModelTests.swift index a0fd9346cd7..31dbb87832d 100644 --- a/FirebaseVertexAI/Tests/Unit/GenerativeModelTests.swift +++ b/FirebaseVertexAI/Tests/Unit/GenerativeModelTests.swift @@ -506,6 +506,12 @@ final class GenerativeModelTests: XCTestCase { XCTAssertEqual(error.httpResponseCode, 400) XCTAssertEqual(error.status, .invalidArgument) XCTAssertEqual(error.message, "API key not valid. Please pass a valid API key.") + XCTAssertTrue(error.localizedDescription.contains(error.message)) + XCTAssertTrue(error.localizedDescription.contains(error.status.rawValue)) + XCTAssertTrue(error.localizedDescription.contains("\(error.httpResponseCode)")) + let nsError = error as NSError + XCTAssertEqual(nsError.domain, "\(Constants.baseErrorDomain).\(BackendError.self)") + XCTAssertEqual(nsError.code, error.httpResponseCode) return } catch { XCTFail("Should throw GenerateContentError.internalError(RPCError); error thrown: \(error)") @@ -853,6 +859,12 @@ final class GenerativeModelTests: XCTestCase { XCTAssertEqual(error.httpResponseCode, 400) XCTAssertEqual(error.status, .invalidArgument) XCTAssertEqual(error.message, "API key not valid. Please pass a valid API key.") + XCTAssertTrue(error.localizedDescription.contains(error.message)) + XCTAssertTrue(error.localizedDescription.contains(error.status.rawValue)) + XCTAssertTrue(error.localizedDescription.contains("\(error.httpResponseCode)")) + let nsError = error as NSError + XCTAssertEqual(nsError.domain, "\(Constants.baseErrorDomain).\(BackendError.self)") + XCTAssertEqual(nsError.code, error.httpResponseCode) return }