Home > Back-end >  Authentication Error in the login screen using Swift decodable encodable
Authentication Error in the login screen using Swift decodable encodable

Time:03-14

New to iOS app building using swiftui and rest api to build a new app. After going through many artilces etc wrote this for login screen

SignIn class

    import Foundation

   enum AuthenticationError: Error{
    case invalidCredentials
    case custom(errorMessage : String)
     }

 class SignIn {


   struct LoginRequest : Codable
  {
      let UserName : String
      let Password : String
  }

struct LoginResponse : Codable
{
    let AuthToken : String?
    let Message : String?
    let IsSuccess : Bool?
}

  func SignInUser(UserName : String, Password : String, completion : @escaping (Result<String, 
   AuthenticationError>)->Void)
   {
    guard let url = URL(string: "http://xx.xxx.xxx.x:port/api/home/login")
            else
            {
                completion(.failure(.custom(errorMessage: "Invalid URL")))
                return
            }
    
    let body = LoginRequest (UserName: UserName, Password: Password)
    
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.setValue("application/json", forHTTPHeaderField: "Accept")
    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
    let encodeData = try? JSONEncoder().encode(body)
    print(String(data: encodeData!, encoding: .utf8)!)
    request.httpBody = encodeData
    
    URLSession.shared.dataTask(with: request) {(data, response, error) in
        
        guard let data = data , error == nil else
        {
            completion(.failure(.custom(errorMessage: "No Data")))
            return
        }
        
        guard let loginResponse = try? JSONDecoder().decode(LoginResponse.self, from: data)
                else
                {
                    completion(.failure(.invalidCredentials))
                    return
                }
        guard let token = loginResponse.AuthToken else
        {
            completion(.failure(.invalidCredentials))
            return
        }
        
        completion(.success(token))
        
    }.resume()


 }
 }

SignInViewModel

  import Foundation

  class SignInViewModel : ObservableObject {
    @Published  var username : String = ""
    @Published  var password : String = ""

func LogIn()
{
    SignIn().SignInUser(UserName: username, Password: password)
 {  **Line:#17**   
        result in
        switch result{
        case .success(let token):
            print(token)
        case .failure(let error):
            print(error.localizedDescription)
        }
      }
   }
}

SignInActivity

import SwiftUI

  struct SignInActivity: View {
@StateObject private var loginVM = SignInViewModel()

@State private var Username:String = ""
@State private var Password:String = ""

var body: some View {
    VStack(){
        
        Image("logo").resizable().aspectRatio(contentMode:.fit)
        Spacer()
       
        Text("Welcome! Sign In to Continue ")
            .font(.system(size: 20)).fontWeight(.semibold)
            .foregroundColor(Color.blue)
       Spacer()
        
        VStack(){
            
        HStack(alignment: .center){
            
            Image(systemName: "person.circle.fill")
                .foregroundColor(Color.blue)
                .padding(.leading,8)
               
            TextField(
                "username", text:$loginVM.username)
                .font(.system(size: 14))
                .padding(.top,10)
                .padding(.bottom,10)
               
             
            
             //   .overlay(VStack{
             //       Divider().offset(x:0,y:15)
             //   })
                
        }.background(Color("text_bg"))
                .cornerRadius(10)
            .padding(.trailing,25)
            .padding(.leading,25)
            .padding(.bottom,15)
            .textInputAutocapitalization(.never)
            .disableAutocorrection(true)
       
                      
       
        
        HStack(alignment: .center)
        {
            Image(systemName: "lock.circle.fill")
                .foregroundColor(Color.green)
                .padding(.leading,8)
            TextField(
                "password", text:$loginVM.password
            ).font(.system(size: 14))
                .padding(.top,10)
                .padding(.bottom,10)
           //     .overlay(VStack{
           //     Divider().offset(x:0,y:15)
           // })
                
                
        }.background(Color("text_bg"))
                .cornerRadius(10)
        .padding(.trailing,25)
        .padding(.leading,25)
        .textInputAutocapitalization(.never)
        .disableAutocorrection(true)
        
        }
       
        HStack()
        {
            Button(action: {
                loginVM.LogIn()
            })
            {
            Text("Sign In")
                    .padding(7)
                    .font(Font.system(size: 15))
                    
.foregroundColor(.white).background(Color.green).cornerRadius(6).shadow(radius: 5)
        }
        }.padding(.top)
        Spacer()
        Spacer()
    
       
    }.ignoresSafeArea(.keyboard)
    
}

The API is working fine as I have already made the complete app in android(released) and it working fine. Now trying to made the app for iOS.

On debug the error is

completion  ()  0x0000000106b27820 ScreenLearning`closure #1 (Swift.Result<Swift.String, 
 ScreenLearning.AuthenticationError>) -> () in ScreenLearning.SignInViewModel.LogIn() -> () at 
  SignInViewModel.swift:17

on run the error is AutheticationError 1.

Cant figure out what is going on.

CodePudding user response:

To do basic authentication, try using the following approach, instead of insecurely sending the user and password as json in the request body. (note: userName and password in lowercase)

func SignInUser(userName : String, password : String, completion : @escaping (Result<String, AuthenticationError>)->Void) {
    
    // -- here 
    guard !userName.isEmpty && !password.isEmpty else {
        completion(.failure(.custom(errorMessage: "Invalid userName or password")))
         return
     }

    // -- here should use https
    guard let url = URL(string: "http://xx.xxx.xxx.x:port/api/home/login")
    else {
        completion(.failure(.custom(errorMessage: "Invalid URL")))
        return
    }

    // -- here 
    let hash = "\(userName):\(password)".data(using: .utf8)!.base64EncodedString() 
    
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.setValue("application/json", forHTTPHeaderField: "Accept")
    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
    // -- here
    request.addValue("Basic \(hash)", forHTTPHeaderField: "Authorization")  

    URLSession.shared.dataTask(with: request) {(data, response, error) in
        
        guard let data = data , error == nil else{
            completion(.failure(.custom(errorMessage: "No Data")))
            return
        }
        
        guard let loginResponse = try? JSONDecoder().decode(LoginResponse.self, from: data)
        else {
            completion(.failure(.invalidCredentials))
            return
        }
        
        // -- here
        guard let token = loginResponse.ResponseData?.AuthToken else {
            completion(.failure(.invalidCredentials))
            return
        }
        
        completion(.success(token))
        
    }.resume()
}
   

Note: you are using http instead of the required https. Change to https or add the appropriate NSAppTransportSecurity in your Info.plist.

EDIT:

Note, according to the response data, you should have this model:

struct ResponseData: Codable {
    let AuthToken: String?
    // ....
}

struct LoginResponse: Codable {
    let Message: String?
    let IsSuccess: Bool?
    let ResponseData: ResponseData?
}
  • Related