direct to new view in swiftUI after successful http request login

Viewed 855

I've been using php and a mysql database to create a simple user registration and login system. the login php files work, and I have connected it to my project in Xcode. however, I'm not too sure on how to make sure that this function runs when the login button is clicked, and only moves to the next view when the log in has been successful.

This is my swift code :

import SwiftUI


struct loginview_Previews: PreviewProvider {
static var previews: some View {
    loginview()
}
}

struct loginview: View {

// variables and fields

@State private var username : String  = ""
@State private var password : String = ""

let databaseurl = "http://localhost/login.php"

var body: some View {
        
        // put into a navigation view
        
        NavigationView{
            
            // form to enter details
            
            Form{
                
                // start section with a header for info
                
                Section(header: Text("enter your details to log in !")){
                    
                    // username field
                    
                    TextField("username", text: $username)
                        
                        .font(.headline)
                        .frame(width: 350.0)
                        .foregroundColor(.white)
                        .padding(.all, 20.0)
                        .cornerRadius(9.0)
                        .preferredColorScheme(.dark)
                    
                    TextField("password", text: $password)
                        
                        .font(.headline)
                        .frame(width: 350.0)
                        .foregroundColor(.white)
                        .padding(.all, 20.0)
                        .cornerRadius(9.0)
                        .preferredColorScheme(.dark)
                    
                } .textCase(nil)  // make header lowercase
                
                Section{
                    NavigationLink(destination: homepage()){  // link to home page view
                        
                        Text("submit")
                            
                            .foregroundColor(.white)
                            .fontWeight(.heavy)
                            .font(.system(size: 22))
                            .padding(.all, 20.0)
                            .frame(width: 175.0)
                            .preferredColorScheme(.dark)
                        
                    }
                }
                
                // button is unclickable if textfields are empty
                
                .disabled(username.isEmpty)
                .disabled(password.isEmpty)
            }
            
            .navigationBarTitle("log in") // title of form
        }
    }

//

func logindatabase(){
            
            // create NSURL - an object initialized with URLString
            
            if var requesturl = URLComponents(string: databaseurl) {
                
                requesturl.query = "username=\(username)&password=\(password)"
                print(requesturl)
                
                guard let url = requesturl.url else { return }
                
                let task = URLSession.shared.dataTask(with: url) { data, response, error in

                    
                    if error != nil {
                        print("error is \(String(describing: error))")
                        return
                    }
                    
                    // parse the response
                    
                    do {
                        // convert response to NSDictionary
                        
                        let myJSON = try JSONSerialization.jsonObject(with: data! , options: .mutableContainers) as? NSDictionary
                        
                        if let parseJSON = myJSON {
                            // create a string & get the json response
                            
                            guard let message = parseJSON["message"] as? String else { return }
                            
                            // print the response
                            
                            print(message)
                            
                        }
                        
                    } catch {
                        print(error)
                    }
                }
                task.resume()
            }

        }
 }

and this is my login.php file :

<?php

//include the db operation file
require_once 'dboperations.php' ;

// create a response array
$response = array();

if ($_SERVER['REQUEST_METHOD'] == 'GET') {

    if (isset($_GET['username']) && isset($_GET['password'])) {

        $db = new dboperation();

        if ($db->userlogin($_GET['username'], $_GET['password'])) {
            $response['error'] = false;
            $response['user'] = $db->displayuser($_GET['username']);
        } else {
            $response['error'] = true;
            $response['message'] = 'invalid username or password';
        }

    } else {
        $response['error'] = true;
        $response['message'] = 'parameters are missing';
    }

} else {
    $response['error'] = true;
    $response['message'] = "Request not allowed";
}

echo json_encode($response);

which does work successfully.

1 Answers

Here's a very simple version of what you're talking about:

class LoginManager : ObservableObject {
    @Published var isLoggedIn = false
    
    func doLogin(username: String, password: String) {
        //in here, you'll do your network call
        //I've mocked it with a simple async call for now
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
            //set this once you get the correct response from your server
            //this triggers isActive on the NavigationLink below
            self.isLoggedIn = true
        }
    }
}

struct ContentView : View {
    @ObservedObject private var loginManager = LoginManager()
    
    @State var username = ""
    @State var password = ""
    
    var body: some View {
        NavigationView {
            Button(action: {
                loginManager.doLogin(username: username, password: password)
            }) {
                //Login fields...
                Text("Log me in")
            }.overlay(
                NavigationLink(destination: LoggedInView(), isActive: $loginManager.isLoggedIn) {
                    EmptyView()
                }
            )
        }
    }
}

struct LoggedInView : View {
    var body: some View {
        Text("Logged in")
    }
}

Note that instead of a NavigationLink I'm using a Button for the initial action. Then, that calls a function called doLogin which is a @Published property saying whether the user is logged in.

If isLoggedIn is true, isActive on the NavigationLink is triggered, sending the user to the next view.

Some things to consider:

  1. In the real world, you should never be sending usernames/passwords over a GET request -- it's just too insecure
  2. In the event that you don't want the user to just be able to use the Back button to go back to the initial login screen, you may not actually wan to use NavigationLink at all -- you might just want to conditionally display a view:
if loginManager.isLoggedIn {
  LoggedInView()
} else {
  LoginForm()
}
Related