I am using the OpenAI API to generate text completions for prompts, but I am encountering an issue where the API response returns an error
No value associated with key CodingKeys(stringValue: "object",
intValue: nil) ("object")
I have checked that the prompt I am sending is valid and I have tested with different prompts, but the issue persists. What could be causing this problem and how can I fix it? Any help or insights would be greatly appreciated.
Thank you.
class NetworkManager{
static let shared = NetworkManager()
#frozen enum Constants{
static let key = ""
}
private var client: OpenAISwift?
private init() {}
func setUp(){
self.client = OpenAISwift(authToken: Constants.key)
}
func getResponse(input: String, completion: #escaping (Result<String, Error>) -> Void) {
let prompt = """
translate from English to Nigerian Pidgin:
\(input)
Output:
"""
client?.sendCompletion(with: prompt, model: .gpt3(.davinci), maxTokens: 60) { result in
switch result {
case .success(let response):
let output = response.choices.first?.text ?? ""
print(response)
completion(.success(output))
case .failure(let error):
completion(.failure(error))
print(error.localizedDescription)
}
}
}
networkManager.getResponse(input: inputText) { result in
switch result {
case .success(let outputText):
DispatchQueue.main.async {
self.secondTextField.text = outputText
}
case .failure(let error):
print("Error translating text: \(error)")
}
}
Related
I am learning iOS and amplify and struggling my way through implementing a custom auth flow. I have run into an issue I can't resolve. Here is my code:
//
// SessionManager.swift
// Mapwork
//
// Created by James Nebeker on 2/27/21.
//
import Foundation
import Combine
import Amplify
import AmplifyPlugins
enum AuthState {
case signUp
case login
case confirmCode(username: String)
case session (user: AuthUser)
case firstTime
case confirmSignIn
}
final class SessionManager: ObservableObject {
#Published var authState: AuthState = .firstTime
func getCurrentAuthUser() {
if let user = Amplify.Auth.getCurrentUser() {
authState = .session(user: user)
} else {
authState = .login
}
}
func showSignUpView()
{
authState = .signUp
}
func showLoginView()
{
authState = .login
}
func showConfirmationSignInView()
{
authState = .confirmSignIn
}
func signUp(username: String, email: String) {
let userAttributes = [AuthUserAttribute(.email, value: email)]
let options = AuthSignUpRequest.Options(userAttributes: userAttributes)
_ = Amplify.Auth.signUp(username: username, password: UUID().uuidString, options: options) { [weak self] result in
switch result {
case .success(let signUpResult):
if case let .confirmUser(deliveryDetails, _) = signUpResult.nextStep {
print("Delivery details \(String(describing: deliveryDetails))")
DispatchQueue.main.async {
self?.authState = .confirmCode(username: username)
}
} else {
print("Signup Complete")
}
case .failure(let error):
print("An error occurred while registering a user \(error)")
}
}
}
func confirmSignUp(for username: String, with confirmationCode: String) {
Amplify.Auth.confirmSignUp(for: username, confirmationCode: confirmationCode) { [weak self] result in
switch result {
case .success:
print("Confirm signUp succeeded")
DispatchQueue.main.async {
self?.showLoginView()
}
case .failure(let error):
print("An error occurred while confirming sign up \(error)")
}
}
}
func signIn(username: String) {
Amplify.Auth.signIn(username: username) { [weak self] result in
switch result {
case .success:
if case .confirmSignInWithCustomChallenge(_) = result.nextStep {
DispatchQueue.main.async {
self?.showConfirmationSignInView()
}
} else {
print("Sign in succeeded")
}
case .failure(let error):
print("Sign in failed \(error)")
}
}
}
func customChallenge(response: String) {
Amplify.Auth.confirmSignIn(challengeResponse: response) {[weak self] result in
switch result {
case .success:
DispatchQueue.main.async {
self?.getCurrentAuthUser()
}
print("Confirm sign in succeeded")
case .failure(let error):
print("Confirm sign in failed \(error)")
}
}
}
}
In the above code, I have copied this function from the AWS Amplify docs exactly:
func signIn(username: String) {
Amplify.Auth.signIn(username: username) { result in
switch result {
case .success:
if case .confirmSignInWithCustomChallenge(_) = result.nextStep {
// Ask the user to enter the custom challenge.
} else {
print("Sign in succeeded")
}
case .failure(let error):
print("Sign in failed \(error)")
}
}
}
But I am receiving this error:
Type of expression is ambiguous without more context
Within the switch statement. I really don't understand this because I have not changed this code, I have copied it directly from the documentation. Any help would be greatly appreciated.
Edit: Specifically, the error is appearing here:
i am unable to find some way to cast custom message to AFError
class APIClient {
#discardableResult
static func performRequest<T:Decodable>(route:APIRouter, decoder: JSONDecoder = JSONDecoder(), completion:#escaping (Result<T, AFError>)->Void) -> DataRequest {
return AF.request(route).validate(statusCode: 200..<300).responseDecodable(decoder: decoder) { (response: DataResponse<T, AFError>) in
switch response.result {
case .success(let value):
completion(.success(value))
case .failure(let error):
let err = MYError(description: "here goes some custom error message")
let customErr = err .asAFError
completion(.failure(customErr!))
}
}
}
and my custom error message struct
struct MYError : Error{
let description : String
var localizedDescription: String {
return NSLocalizedString(description, comment: "")
}
Do you know what AFError you want to cast your error to? That's the first step to your answer because the AFError is a type of error class not one specific error. Here's my approach.
First I would change the error to an enum Errors defined as an Error just like the Struct you had. Then add your custom error as a case. Define your description and specify there what you would like the error message to be for each case (specific error).
Second, define what your Error should correspond to in an AFError for each case. This way you can specify and pass the data that will be required for some of the AFErrors like the example of SomeOtherError.
enum Errors: Error {
var localizedDescription: String {
var str: String = ""
switch self {
case .MyError: str = "Some custom error message"
case .SomeOtherError: str = "This would be another error and message"
}
return NSLocalizedString(str, comment: "")
}
var asAFError: AFError? {
switch self {
case .MyError: return .explicitlyCancelled
case .SomeOtherError(let data): return .sessionDeinitialized(data)
default: return nil
}
}
case MyError
case SomeOtherError(Data)
}
Lastly use a guard statement incase you throw and error that cannot be defined as an AFError, then pass that error to your completion like normal.
class APIClient {
#discardableResult
static func performRequest<T:Decodable>(route: APIRouter, decoder: JSONDecoder = JSONDecoder(), completion:#escaping (Result<T, AFError>)->Void) -> DataRequest {
return AF.request(route).validate(statusCode: 200..<300).responseDecodable(decoder: decoder) { (response: DataResponse<T, AFError>) in
switch response.result {
case .success(let value):
completion(.success(value))
case .failure(let error):
guard let err = Errors.MyError.asAFError
else { return error }
completion(.failure(err))
}
}
}
}
I hope all you well. I have a question. I have a simple login page with email and password and also I have a user object like that
// MARK: - UserModel
struct UserModel: Codable {
let error: Bool
let desc: String
let user: User
let token: String
}
// MARK: - User
struct User: Codable {
let id: Int
let email, firstName, lastName, lang: String
let status: Int
let referer, star: String?
let phone: String?
let ip: String?
let birth, idNumber: String?
let regionID: String?
let createdAt, updatedAt: String
enum CodingKeys: String, CodingKey {
case id, email
case firstName = "first_name"
case lastName = "last_name"
case lang, status, referer, star, phone, ip, birth
case idNumber = "id_number"
case regionID = "region_id"
case createdAt, updatedAt
}
}
the return type is the upper one(UserModel). If the user entered his/her credentials true there is no problem. But troubles starts if he/she entered the wrong credentials. I can not parse the return value from the server. Always give me error that line.
And the console output is:
Rentover[2343:150674] Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.typeMismatch(Swift.Bool, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "error", intValue: nil)], debugDescription: "Expected to decode Bool but found a dictionary instead.", underlyingError: nil)): file
Here is my login request function. I used codable for simplicity.
class func requestLogIn(router: Router, completion: #escaping (Result<UserModel, Error>) -> ()) {
guard let url = setUrlComponents(router: router).url else { return }
var urlRequest = URLRequest(url: url)
urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
urlRequest.httpMethod = router.method
if router.method == "POST"{
let model = LoginModel(email: router.parameters[0], password: router.parameters[1])
urlRequest.httpBody = try? JSONEncoder().encode(model)
}
let dataTask = URLSession.shared.dataTask(with: urlRequest) { data, response, error in
guard error == nil else {
print(error?.localizedDescription)
return
}
guard response != nil else {
print("no response")
return
}
guard let data = data else {
print("no data")
return
}
let responseObject = try! JSONDecoder().decode(UserModel.self, from: data)
print(responseObject.user)
DispatchQueue.main.async {
completion(.success(responseObject))
}
}
dataTask.resume()
}
And here is my error struct.
struct LogInError: Codable, Error{
let error: Bool
let desc: String
let fields: [String] ----> 'Edit here old: let fileds: [String'
}
And last my real call function is like that
NetworkService.requestLogIn(router: Router.login(email: nameTextField.text!, passowrd: passwordTextField.text!)) { (result) in
switch result {
case .success(let userModel):
print("RESULT SUCCESS")
print("Hello \(userModel.user.firstName)")
let selectedVC = UIUtils.checkUserStatus(status: userModel.user.status)
self.navigationController?.modalPresentationStyle = .fullScreen
self.navigationController?.pushViewController(selectedVC, animated: true)
case .failure(let error):
print("RESULT FAILED")
print(error)
}
}
I followed that medium link for creating my router and network service. I am very glad and thankful if you help me with that issue. Or give me some advice about networking api's and usage.
[Edit For error response from server]
My request and response message-body frame is also like that:
Have a nice day. And good codding.
To decode two different JSON strings a convenient solution is an enum with associated types because it can represent the success and failure cases very descriptively.
First it decodes the common error key and then it decodes UserModel or LogInError
enum Response : Decodable {
case success(UserModel), failure(LogInError)
private enum CodingKeys : String, CodingKey { case error }
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let hasError = try container.decode(Bool.self, forKey: .error)
if hasError {
let errorContainer = try decoder.singleValueContainer()
let errorData = try errorContainer.decode(LogInError.self)
self = .failure(errorData)
} else {
let successContainer = try decoder.singleValueContainer()
let successData = try successContainer.decode(UserModel.self)
self = .success(successData)
}
}
}
Use it
class func requestLogIn(router: Router, completion: #escaping (Result<Response, Error>) -> ()) {
...
do {
let responseObject = try JSONDecoder().decode(Response.self, from: data)
print(responseObject)
DispatchQueue.main.async {
completion(.success(responseObject))
}
} catch {
DispatchQueue.main.async {
completion(.failure(error))
}
}
and
NetworkService.requestLogIn(router: Router.login(email: nameTextField.text!, passowrd: passwordTextField.text!)) { (response) in
switch response {
case .success(let result):
switch result {
case .success(let userModel):
print("RESULT SUCCESS")
print("Hello \(userModel.user.firstName)")
let selectedVC = UIUtils.checkUserStatus(status: userModel.user.status)
self.navigationController?.modalPresentationStyle = .fullScreen
self.navigationController?.pushViewController(selectedVC, animated: true)
case .failure(let errorData):
print(errorData)
}
case .failure(let error):
print("RESULT FAILED")
print(error)
}
}
Declare LoginError as a standard Decodable struct
struct LogInError: Decodable {
I'm working on an app where users need to login. The webapplication uses a token, that users can get from calling a webservice using their username and password. My question now is what's the best way the handle errors that can occur. What I have now:
LoginViewController.swift
self.api.token(forUsername: "a", password: "b") { (result) in
switch (result) {
case .failure(let error):
print("something when wrong \(error)")
case .success(let token):
print("token \(token)")
}
LoginAPIClient (the self.api) using https://github.com/3lvis/Networking
class LoginAPIClient {
enum ResponseError: Error {
case unexpectedDataError
case unknownError
}
enum Result {
case success(String)
case failure(Error)
}
lazy var networking: Networking = {
let networking = Networking(baseURL: APIClient.serverURL())
return networking
}()
func token(forUsername username: String, password: String, completion: #escaping (Result) -> Void) {
let parameters = ["username" : username, "password" : password]
networking.post("/api/login", parameters: parameters) { (result) in
switch result {
case .failure(let response):
return completion(.failure(response.error))
case .success(let response):
if var token = response.headers["Authorization"] as? String {
token = token.replacingOccurrences(of: "Bearer ", with: "")
return completion(.success(token))
}
return completion(.failure(ResponseError.unknownError))
}
}
}
}
Here for example I'm creating my own error return completion(.failure(ResponseError.unknownError)) in case the server responds with a successful status code (200) but somehow the Authorization header is missing from the response.
This works, the only problem is that now when I handle the error in the ViewController I don't know the exact reason why it fails. For example, from the Networking library I get an error code (400 or 401 etc) but this is lost because first it was an NSError. I could use an NSError but somehow this doesn't feel right. Could anyone point me in the right direction?
One of the solutions I thought of was to add an extra enum and then do something like this:
enum Result {
case success(String)
case networkFailure(FailureJSONResponse)
case failure(Error)
}
self.api.token(forUsername: "a", password: "b") { (result) in
switch (result) {
case .failure(let error):
print("something when wrong \(error)")
case .networkFailure(let response):
print("something when wrong \(error)")
case .success(let token):
print("token \(token)")
}
But I rather have just 1 success and 1 failure in the switch.
Every application usually has its own "Error", so in your case you can define
public enum AppError: Swift.Error, CustomStringConvertible {
case networkError(code: Int)
case anotherError(message: String?)
case underlying(Swift.Error)
public var description: String {
switch self {
case .networkError(let code):
return "Network error with code \(code)"
default://I'm not gonna cover all cases, but you should :)
return "Error"
}
}
}
then you can use your approach
enum Result {
case success(String)
case failure(AppError)
}
You can add second parameter to completion block:
func token(forUsername username: String, password: String, completion: #escaping (Result, error: ResponseError?) -> Void)
I'm trying to reuse Alamofire's Result type for own API callbacks.
Here is a shortened version of result type I'm using:
public enum Result<Value> {
case Success(Value)
case Failure(NSData?, ErrorType)
}
So for my API calls I'm using it in completion blocks:
func getUserContent(userId: String, completion: (result: Result<User>) -> Void) {
Alamofire.request(UserRouter.GetUser(userId))
.validate()
.responseJSON { (request, response, result) -> Void in
switch result {
case .Failure(_, let error):
completion(result: .Failure(nil, error))
case .Success(let value):
if let responseDict = value as? [String: AnyObject] {
do {
// synchronous call which parses response and
// creates User struct instance or throws exception
let user = try self.processUserResponse(responseDict)
completion(result: .Success(user))
} catch(let error) {
completion(result: .Failure(nil, error))
}
} else {
completion(result: .Failure(nil, MyAPIError.WrongResponseFormat))
}
}
}
}
I think its perfectly fits here but there is one issue. I have some calls with completion blocks which supposed to return either .Success with no value or .Failure.
E.g. deleteUser method should look something like:
func deleteUser(userId: String, completion: (result: Result<nil>) -> Void) {
// ... do some stuff here
}
so when I call it later I can do:
deleteUser(userId) { (result) -> Void in
switch result {
case .Success:
print("success")
case .Failure(nil, let error):
print("Error: \(error)")
}
}
But I can't create "empty" .Success. Result<nil> of course gives me a compile error. But I don't have any type to pass to some of .Success cases. Does anyone has a better solution that defining another Result Type with no type on .Success?
#ogreswamp is right! You can omit the type requirement with Void. Type Void is simply an empty tuple, in effect a tuple with zero elements, which can be written as (). Here is an example:
enum Result<T, E: ErrorType> {
case Success(T)
case Failure(E)
init(value: T) {
self = .Success(value)
}
init(error: E) {
self = .Failure(error)
}
}
Use this like
enum AuthenticationError: ErrorType {
case MissingEmail
case InvalidPassword
}
func signUp(email email: String, password: String) -> Result<Void, AuthenticationError>
You can return the result like
// Success
return Result(value: ())
// Failure
return Result(error: .InvalidPassword)
And finally, check the result
switch result {
case .Success(_):
print("Request SignUp was sent")
case .Failure(let error):
switch error {
case .InvalidEmail:
print("Invalid email")
default:
break
}
}
You will need to define your own Result type. Also note that Alamofire 3.0 uses a much different Result type that may better suit your needs.