Accessing variables in protocol methods in swift - swift

can anyone please help me with this swift code, i'm a swift beginner.
i'm trying to create a login page using MVC and it looks like this enter image description here
so when the user enters their login information and the status from the JSON data is "s" for a successful login it should display some test information, and when the login status is "f" for a failed login it should stay in the same login page, i'm trying to access the status in the protocol method in the code below:
so whenever the user presses the button it should take the required action but the status in the code below is nil so whenever i run the application the information is displayed wether the login was successful or not
//this piece of code is in the LoginViewController
func didReceivedLoginInfo(info : [LoginModel]) -> String
{
self.loginData = info
print("didReceivedLOOGIN count is \(loginData!.count)")//the print is successful here
for i in 0..<loginData!.count
{
print("LoginData Info : \(loginData![i])")//the print is successful here
}
statuss = loginData![0].status
print("HERE YOU GO, THIS IS WHAT I FOUND: \(statuss!)")//the print is successful here
return statuss!
}
//this piece of code is in the LoginViewController
func buttonAction(sender:UIButton!)
{
var btnsendtag: UIButton = sender
var uName = username!.text as? String!
var uPass = password!.text as? String!
parser = LoginParser()
parser!.getLoginInfo(uName!, pass: uPass!)
var status = loginData?[0].status
print("from View Controller this is the msg: \(status)")
parser!.delegate = self
if status == "s" {
print("from View Controller this is the status: \(status)")
let vc = ViewController()
let navigationController = UINavigationController(rootViewController: vc)
self.presentViewController(navigationController, animated: true, completion: nil)
}
else {
let vc = LoginViewController()
let navigationController = UINavigationController(rootViewController: vc)
self.presentViewController(navigationController, animated: true, completion: nil)
}
}
////this piece of code is in the LoginParser
import UIKit
#objc protocol LoginParserDelegate : NSObjectProtocol
{
func didReceivedLoginInfo (info : [LoginModel]) -> String
optional func didRecivedError()
}
class LoginParser: NSObject {
// var webData: NSMutableData?
var webData: NSData?
weak var delegate : LoginParserDelegate?
var name = String ()
var model : LoginModel?
var alertView:UIAlertView?
var MainLogin : [LoginModel] = []
func getLoginInfo (usern : String, pass : String){
var post:NSString = "LID=\(usern)&PWD=\(pass)&Passcode=s#v#"
var url:NSURL = NSURL(string: "some link")!
var postData:NSData = post.dataUsingEncoding(NSUTF8StringEncoding)!
var postLength:NSString = String( postData.length )
var request:NSMutableURLRequest = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
request.HTTPBody = postData
request.setValue(postLength as String, forHTTPHeaderField: "Content-Length")
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.setValue("application/json", forHTTPHeaderField: "Accept")
var response: NSURLResponse?
let connection = NSURLConnection(request: request, delegate: self)
}
func connection(connection: NSURLConnection, didFailWithError error: NSError){
if delegate != nil {
if delegate!.respondsToSelector(Selector("didRecivedLoginInfo")){
delegate!.didRecivedError!()
}
}
}
func connection(connection: NSURLConnection, didReceiveResponse response: NSURLResponse){
webData = NSData()
}
func connection(connection: NSURLConnection, didReceiveData data: NSData){
webData! = data
}
func connectionDidFinishLoading(connection: NSURLConnection){
let responseString = NSString(data: webData!, encoding: NSUTF8StringEncoding)
print("responseString \(responseString!)")
processUserTypes ()
}
func processUserTypes ()->[LoginModel]
{
model = LoginModel()
// Process JSON
do {
let result = try NSJSONSerialization.JSONObjectWithData(webData!, options: NSJSONReadingOptions.MutableContainers)as? NSDictionary
var Login = LoginModel()
let status = result?.objectForKey("Status") as? String
let msg = result?.objectForKey("En") as? String
let employeename=result?.objectForKey("DisplayName")as? String
print("STATUS IN PARSER IS \(status!)")
if status == "s"
{
print(status!)
Login.name = employeename
Login.status = status
Login.message = msg
alertView = UIAlertView()
alertView!.title = "Login successful"
alertView!.message = "Welcome \(employeename!)"
alertView!.delegate = self
alertView!.addButtonWithTitle("OK")
alertView!.show()
MainLogin.append(Login)
print("count in login is \(MainLogin.count)")
if delegate != nil
{
delegate!.didReceivedLoginInfo(MainLogin)
}
}
else
{
Login.message = msg
alertView = UIAlertView()
alertView!.title = "Login failed"
alertView!.message = msg!
alertView!.delegate = self
alertView!.addButtonWithTitle("OK")
alertView!.show()
MainLogin.append(Login)
print("this is the main login\(MainLogin)")
}
}
catch {
if delegate != nil {
if delegate!.respondsToSelector(Selector("didRecivedLoginInfo")){
delegate!.didRecivedError!()
}
}
}
return MainLogin
}
}
can anyone help me with this?
thanks

you call didReceivedLoginInfoinside processUserTypeswhich is called inside connectionDidFinishLoading. buttonAction calls getLoginInfowhich does an async request that will call back connectionDidFinishLoading when it is done.
this getLoginInfowill return a long time before the request is send out and a respone is received and connectionDidFinishLoading is called.
=> you cannot do anything after you call getLogininfo
=>
for you button method
create the parser
set the delegate
call getLoginInfo
dont do anything with potential results here
for the didReceiveLoginInfo.
you can do further logic and maybe even ui stuff there (you have to wrap logic that affects the ui in a
dispatch_async(dispatch_get_main_queue()){
//here
}
call

Related

How to call a protocol from an extension before changing screens

I need to get a value from an extension before i click on a button that goes to another screen, how can i do that?
This is the IBAction inside viewController. When i click it makes a request to an API then send a value to global variable on the second screen:
#IBAction func enter(_ sender: UIButton) {
if let login = loginTextfield.text, let password = passwordTextfield.text {
loginManager.performLoginRequest(login, password)
resultsViewController.receivedToken = token
navigationController?.pushViewController(resultsViewController, animated: true)
}
}
extension LoginViewController: LoginManagerDelegate {
func didUpdateLogin(with login: LoginModel) -> (Bool, String) {
success = login.success
token = login.token
return (success, token)
}
}
Manager:
import Foundation
protocol LoginManagerDelegate {
func didUpdateLogin(with login: LoginModel) -> (Bool, String)
}
struct LoginManager {
var delegate: LoginManagerDelegate?
func performLoginRequest(_ login: String, _ password: String) {
let url = URL(string: "https://private-anon-1a0df64d9c-ibmfc.apiary-mock.com/login")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = """
{\n "username": "\(login)",\n "password": "\(password)"\n}
""".data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let response = response {
print(response)
if let data = data, let body = String(data: data, encoding: .utf8) {
print(body)
if let login = self.parseJSON(loginData: data) {
self.delegate?.didUpdateLogin(with: login)
}
}
} else {
print(error ?? "Unknown error")
}
}
task.resume()
}
func parseJSON(loginData: Data) -> LoginModel? {
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(LoginData.self, from: loginData)
let success = decodedData.success
let token = decodedData.data.token
let login = LoginModel(success: success, token: token)
return login
} catch {
print(error.localizedDescription)
return nil
}
}
}
My problem is, this extension is just being called after i click the button. This way resultsViewController.receivedToken is not getting the value from token.
So, how can i call didUpdateLogin (to pass it values to success and token) before clicking on the IBAction?
THe reason for this behaviour is the background thread you are using:
(1) You call loginManager.performLoginRequest(login, password) which then starts a background thread to actually work on that request.
(2) In the meantime your code continues to run, executing resultsViewController.receivedToken = token.
(3) Since (1) is not done yet, your token is still nil (or an old token).
One of many possible solutions:
Add a block to the parameters of performLoginRequest in which you call
resultsViewController.receivedToken = token
navigationController?.pushViewController(resultsViewController, animated: true)
This way you make sure that code is only called after(!) the login was successful because you wait for it. In the meantime you could show a loading spinner or something similar. Login is a task where a user simply has to wait, there is usually (depending on the app) no way around it.
The code could look something like this in the end:
loginManager.performLoginRequest(login, password) {
resultsViewController.receivedToken = token
navigationController?.pushViewController(resultsViewController, animated: true)
}
whereas your LoginManager would have a method like
func performLoginRequest(_ login: String,
_ password: String,
completion: #escaping () -> Void)
which is then used later in your Dispatch:
DispatchQueue.main.async {
let loginVC = LoginViewController()
loginVC.didUpdateLogin(login: login)
completion()
}

Load page into webview swift 2 from java class

I am developing an App for the iPhone using Xwebview which enables me to download a page then interact with the javascript on the downloaded page.
All works, but if the internet connection drops, a default local page is loaded, informing the user there is no internet connection. The page displays a retry button that, when pressed checks, the internet connection: if the connection is made the app tries to connect again to the external page and load the page into the webview.
I cannot get this to work: the code downloads the page (I can see this in my session data) but I can't get that page to load back into the webview.
override func viewDidLoad() {
super.viewDidLoad()
login()
}
func login()
{
// *********** Get stored hashkey **************
let hashcode = getHashcode()
// ********** Check network connection *********
let netConnection = Connection.isConnectedToNetwork()
print("net connection: ", netConnection)
if netConnection == true
{
if hashcode != "00000"
{
print("local key found", hashcode)
// We dont have local key
let webview = WKWebView(frame: view.frame, configuration: WKWebViewConfiguration())
//webview.loadRequest(NSURLRequest(URL: NSURL(string: "about:blank")!))
view.addSubview(webview)
webview.loadPlugin(jsapi(), namespace: "jsapi")
let url:NSURL = NSURL(string: serverLocation + onlineLoginApi)!
let session = NSURLSession.sharedSession()
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringCacheData
let paramString = "/?username=username&password=password"
request.HTTPBody = paramString.dataUsingEncoding(NSUTF8StringEncoding)
let task = session.downloadTaskWithRequest(request) {
(
let location, let response, let error) in
guard let _:NSURL = location, let _:NSURLResponse = response where error == nil else {
print("error")
return
}
let urlContents = try! NSString(contentsOfURL: location!, encoding: NSUTF8StringEncoding)
guard let _:NSString = urlContents else {
print("error")
return
}
print(urlContents)
}
task.resume()
// you must tell webview to load response
webview.loadRequest(request)
}
else{
print("local key found", hashcode)
// ********* Found local key go to site pass key over ************
let webview = WKWebView(frame: view.frame, configuration: WKWebViewConfiguration())
view.addSubview(webview)
webview.loadPlugin(jsapi(), namespace: "jsapi")
let req = NSMutableURLRequest(URL: NSURL(string:serverLocation + onlineLoginApi + "?hashcode=\(hashcode)")!)
req.HTTPMethod = "POST"
req.HTTPBody = "/?hashcode=\(hashcode)".dataUsingEncoding(NSUTF8StringEncoding)
NSURLSession.sharedSession().dataTaskWithRequest(req)
{ data, response, error in
if error != nil
{
//Your HTTP request failed.
print(error!.localizedDescription)
} else {
//Your HTTP request succeeded
print(String(data: data!, encoding: NSUTF8StringEncoding))
}
}.resume()
webview.loadRequest(req)
}
}
else{
// No connection to internet
let webview = WKWebView(frame: view.frame, configuration: WKWebViewConfiguration())
view.addSubview(webview)
webview.loadPlugin(jsapi(), namespace: "jsapi")
let root = NSBundle.mainBundle().resourceURL!
let url = root.URLByAppendingPathComponent("/www/error-no-connection.html")
webview.loadFileURL(url, allowingReadAccessToURL: root)
print("No internet connection")
}
}
class jsapi: NSObject {
// Reconnect button on interface
func retryConnection()
{
print("Reconnect clicked")
dispatch_async(dispatch_get_main_queue())
{
let netConnections = Connection.isConnectedToNetwork()
if netConnections == true {
let netalert = UIAlertView(title: "Internet on line", message: nil, delegate: nil, cancelButtonTitle: "OK")
netalert.show()
let url = self.serverLocation + self.onlineLoginApi
let hashcode = ViewController().getHashcode()
if(hashcode != "00000") {
let url = url + "?hashcode=\(hashcode)"
print("url: ", url)
}
ViewController().loadPagelive(url)
}
else{
let netalert = UIAlertView(title: "Internet off line", message: nil, delegate: nil, cancelButtonTitle: "OK")
netalert.show()
}
}
print("retryConnect end")
}
}
You try to perform the loadPagelive(url) on a new instance of your ViewController, not on the current one shown on the screen, that's why you don't see any update.
You should create a delegate or a completion block in order to execute code on you ViewController instance loaded on the screen: every time you do ViewController(), a new object is created.
You can try using the delegate pattern, which is simple to achieve. I will try to focus on the important part and create something that can be used with your existing code:
class ViewController: UIViewController {
let jsapi = jsapi() // You can use only 1 instance
override func viewDidLoad() {
super.viewDidLoad()
// Set your ViewController as a delegate, so the jsapi can update it
jsapi.viewController = self
login()
}
func loadPagelive(_ url: URL) {
// Load page, probably you already have it
}
}
class jsapi: NSObject {
weak var viewController: ViewController?
func retryConnection() {
// We check if the delegate is set, otherwise it won't work
guard viewController = viewController else {
print("Error: delegate not available")
}
[... your code ...]
// We call the original (and hopefully unique) instance of ViewController
viewController.loadPagelive(url)
}
}

User input as global variable to display text from different views

I have a main View Controller than has a "Sign In" button if a user is not logged in. I then have another view controller "Login View Controller" that checks if user exists and then logs them in. Once a user has logged in via the login View Controller. I want the text of my Sign In Button on my main view controller to change to their email that they used to login. I am using a struct to try and accomplish this. My struct email variable seems to work within my Login View Controller but then does not change the string value of my button on the main view controller. Any help would be greatly appreciated.
LoginViewController.swift
class LoginViewController: NSViewController {
#IBOutlet weak var userEmailTextField: NSTextField!
struct GloablUserEmail {
static var user_status = Bool()
static var user_email = String()
}
#IBAction func LoginPushed(_ sender: AnyObject) {
GloablUserEmail.user_email = userEmailTextField.stringValue
let requestURL: NSURL = NSURL(string: "myurl")!
let urlRequest: NSMutableURLRequest = NSMutableURLRequest(url: requestURL as URL)
urlRequest.httpMethod = "POST"
let postString = "user_email=\(GloablUserEmail.user_email)&user_password=\(user_password)"
urlRequest.httpBody = postString.data(using: String.Encoding.utf8);
let session = URLSession.shared
let task = session.dataTask(with: urlRequest as URLRequest) {
(data, response, error) -> Void in
let httpResponse = response as! HTTPURLResponse
let statusCode = httpResponse.statusCode
if (statusCode == 200) {
do{
let json = try JSONSerialization.jsonObject(with: data!, options:.mutableContainers) as? NSDictionary
if let parseJSON = json {
let resultValue = parseJSON["Status"] as? String
print("result: \(resultValue)")
self.isUserRegistered = false
if(resultValue=="Success"){
GloablUserEmail.user_status = true;
UserDefaults.standard.set(true, forKey: "isUserLoggedIn");
UserDefaults.standard.synchronize();
}
}
}catch{
print("Error parsing the JSON: \(error)")
}
}
}
task.resume()
}
}
Once the result is successful and the user is verified to exist then I assign the global user_status to true.
MainViewController.swift
class ViewController: NSViewController {
#IBOutlet weak var SignInButton: NSButton!
override func viewDidLoad() {
super.viewDidLoad()
if(LoginViewController.GloablUserEmail.user_status == false){
SignInButton.title = "Sign In"
}else if(LoginViewController.GloablUserEmail.user_status == true){
SignInButton.title = LoginViewController.GloablUserEmail.user_email
}
}
}
You can user a simple UserDefaults extension:
extension UserDefaults{
var isLoggedIn: Bool{
return bool(forKey: "loggedIn")
}
func set(loggedIn value: Bool){
set(value, forKey: "loggedIn")
synchronize()
}
}
To set login status:
let defaults = UserDefaults.standard
defaults.set(loggedIn: true)
And check its value:
let defaults = UserDefaults.standard
defaults.isLoggedIn

Swift Login app

I'm trying to make a new login app by Xcode. I had been written my code but something goes wrong with it.
The code looks like to be correct but when debugging its skip from "Task" to the end of code , any help ?
Here is my code:
#IBAction func myButton(sender: AnyObject) { //login button
let UserName = input.text!
let UserPassword = password.text!
if ( UserName.isEmpty || UserPassword.isEmpty) { return }
//send data user to server side
let myUrl = NSURL(string: "http://servertest.cf/Store.php?LoginUser=Test%40home.com&LoginPassword=123456789")
let myRequest = NSMutableURLRequest(URL: myUrl!)
myRequest.HTTPMethod = "POST"
let postString = "email=\(UserName)&Password=\(UserPassword)";
myRequest.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let Task = NSURLSession.sharedSession().dataTaskWithRequest(myRequest)
{ data , response , error in
if error != nil {
println("error\(error)")
self.myLabel.text = "wrong user name"
}
var err : NSError?
var json = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers , error: &err) as? NSDictionary
if let parseJson = json
{
var resultValue:String = parseJson["Status"] as! String!
println("result\(resultValue)")
if(resultValue == "Success" )
{
var storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
var vc: UINavigationController = storyboard.instantiateViewControllerWithIdentifier("SecondViewController") as! UINavigationController
self.presentViewController(vc, animated: true, completion: nil)
}
}
}
Task.resume()
}
The closure is queued to another queue and run in another thread. Add a breakpoint inside the closure if you want to run it line-by-line.

How to call the view controller from another view controller programmatically depending on the response from the remote server

In my app I take the server credentials in the main view controller (loginViewController.swift). When I click on 'Login' button on the 'loginViewController', its shows the app logoViewController which has the activity indicator view and the credentials value is passed to the remote server over https connection. I get a JSON array in the response from remote server which consist of message "Successful : 1" or "Unsuccessful : 0" with other app details.
For the value '1' in the JSON array I want to show the menuViewController screen and for the value '0' I want to show the RegisterViewController screen for further process in the app.
Calling of 'menuViewController'/'RegisterViewController' should happen in the background process of the 'logoViewController'.
If have tried the following code given bellow, but unable to call the 'menuViewController'/'RegisterViewController'.
Please let me know were I went wrong. Is the UIStoryboard approach is right?
Any suggestion or links or tutorial will be helpful.
import UIKit
class logoViewController: UIViewController {
#IBOutlet weak var act: UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad()
self.act.startAnimating()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// syncreq function is called from the connectionViewController.
// connectionViewController is the common class for connecting to the remote server
func syncreq (JSONdata: AnyObject) { // Proceesing for PRMS response
// Getting the value from the JSON
var Successful = self.getIntFromJSON(JSONdata as NSDictionary, key: "Successful")
println("Value of Successful : \(Successful)")
if (Successful == 0){
//let secondViewController = self.storyboard?.instantiateViewControllerWithIdentifier("registrationID") as regVC
//self.navigationController?.pushViewController(secondViewController, animated: true)
//performSegueWithIdentifier("registrationID", sender: self)
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
var setViewController = mainStoryboard.instantiateViewControllerWithIdentifier("registrationID") as RegisterViewController
self.presentViewController(setViewController, animated: false, completion: nil)
}
else if (Successful == 1){
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
var setViewController = mainStoryboard.instantiateViewControllerWithIdentifier("mnuID") as menuViewController
self.presentViewController(setViewController, animated: false, completion: nil)
}
}
func getIntFromJSON(data: NSDictionary, key: String) -> Int {
let info : AnyObject? = data[key]
// println("Value of data[key] : \(key)")
if let info = data[key] as? Int {
println("Value of value for \(key) : \(info)")
return info
}
else {
return 0
}
}
}
Thanks in advance
I have implemented a common function that will callback the calling function on the response from the remote server.
Following is the function which I have implemented.
func connect(jsonString:NSDictionary) -> NSDictionary
{
var JSONdata: AnyObject = ["" : ""] as Dictionary<String, String>
println("------------------Function connserv")
let prefs = NSUserDefaults.standardUserDefaults()
var conn_timeout: AnyObject = prefs.objectForKey("conn_timeout")!
var numberFormatter = NSNumberFormatter()
var number:NSNumber? = numberFormatter.numberFromString(conn_timeout as! String)
if let number = number {
var integer = Int(number)
println("\(integer)")
}
var recon_maxcount: AnyObject = prefs.objectForKey("recon_maxcount")!
println("recon_maxcount in Connection : \(recon_maxcount)")
println("conn_timeout in Connection : \(conn_timeout)")
var recon_interval: AnyObject = prefs.objectForKey("recon_interval")!
println("recon_interval in Connection : \(recon_interval)")
var number1:NSNumber? = numberFormatter.numberFromString(recon_maxcount as! String)
if let number1 = number1 {
var integer1 = Int(number1)
println("\(integer1)")
}
var number2:NSNumber? = numberFormatter.numberFromString(recon_interval as! String)
//if let number2 = number2 {
var integer2 = Int(number2!)
var xval = UInt32(integer2)
println("\(xval)")
//}
var IP: AnyObject = prefs.objectForKey("IP")!
var port: AnyObject = prefs.objectForKey("Port")!
println("IP in Connection : \(IP)")
println("port in Connection : \(port)")
prefs.synchronize()
println("HTTP request jsonString : \(jsonString)")
var request = NSMutableURLRequest(URL: NSURL(string: "https://\(IP):\(port)/")!)
var response: NSURLResponse?
var error: NSError?
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(jsonString, options: nil, error: &error)
request.timeoutInterval = (number as! NSTimeInterval)
request.HTTPMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
//request.setValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("gzip", forHTTPHeaderField: "Accept-encoding")
println("Firing synchronous url connection......")
println(request)
//println("Before======\(i)!")
for (var i = 0; i < number1 as! Int ; i++)
{
var error: NSError?
println("Try connecting to server: \(NSDate())")
let urlData = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: &error)
//println("response : \(response)")
//println("error : \(error)")
if error != nil || urlData!.length == 0
{
println("Error happend timeout======\(error?.code)!")
println(NSDate())
sleep(xval as UInt32)
//println("NSDate()")
JSONdata = ["0" : "0"] as Dictionary<String, String>
continue
}
else
{
println("Successfully connected : \(NSDate())")
println("\(urlData!.length) bytes of data was returned")
println(response!);
if let httpResponse = response as? NSHTTPURLResponse {
println("Status Code ------------------------------------>\(httpResponse.statusCode)")
}
println(NSString(data: urlData!, encoding: NSUTF8StringEncoding)!)
var er: NSError?
let JSONresdata: AnyObject = (NSJSONSerialization.JSONObjectWithData(urlData!, options: .MutableContainers,error: &er)!)
println("==========================================================================================")
println("JSON response : \(JSONresdata)")
JSONdata = JSONresdata as! NSDictionary
break
}
}
println("out of for loop : \(JSONdata)")
return JSONdata as! NSDictionary;
}