How to pass data in a closure to another scene - ios

It is my first app in swift. I am using Alamofire for my HTTP request. Coming from Android, I know it is possible to attach serialized object to navcontroller action while navigating from one screen to another.
I want to be able to perform segue after from the viewmodel subscription and attach the resultant token to the segue as I will be using it for verification at the next screen.
I have tried didSet but to no avail.
How can I do this in swift.
//MARK: Register user
#IBAction func registerUser(_ sender: Any) {
let fullName = firstNameTF.text! + " " + lastNameTF.text!
let email = emailTF.text
let password = passwordTF.text
let phone = phoneNumberTF.text
let country = countryDropDown.text
let user = User(name: fullName, email: email, password: password, country: country, phone: phone, token: nil)
var tk = ""{
didSet{
token = tk
}
}
authViewModel.registerUser(user: user).subscribe(onNext: { (AuthResponse) in
print("messaage \(String(describing: AuthResponse.message))")
self.tokens = AuthResponse.token
self.performSegue(withIdentifier: "gotoVerification", sender: self)
}, onError: { (Error) in
print("Error: \(Error.localizedDescription)")
}, onCompleted: nil) {
}.disposed(by: disposeBag)
print("token \(token)")
// AF.request(url, method: .post, parameters: user, encoder: JSONParameterEncoder.default).responseDecodable(of:AuthResponse.self){response in
//
// response.map { (AuthResponse) in
// print("messaage \(String(describing: AuthResponse.message))")
// }
//
// print("user: \(user)")
// print("response \(String(describing: response))")
// }
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? UserVerification{
//
vc.tokens = token
print("token \(token)")
}
}

You can pass the token as the sender:
self.performSegue(withIdentifier: "gotoVerification", sender: AuthResponse.token)
Then:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? UserVerification, let token = sender as? String {
vc.tokens = token
print("token \(token)")
}
}

Related

Passing documentID of Firestore using segue in Swift

I'm learning both Swift and firebase by making an app. In my current firestore, I have a database with collection name Players, and inside of the collection, there is a document with auto-documentID and two fields, such as name and email. Like this.
firestore image
I also use Firebase Auth, so whenever a user makes an account I put their email into firestore with field name email.
Here is the code for RegisterViewController where a user register their account with email and password.
import UIKit
import Firebase
class RegisterViewController: UIViewController {
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
let db = Firestore.firestore()
override func viewDidLoad() {
super.viewDidLoad()
}
//MARK: - register a new account
#IBAction func registerPressed(_ sender: UIButton) {
if let email = emailTextField.text, let password = passwordTextField.text {
Auth.auth().createUser(withEmail: email, password: password) { authResult, error in
if error != nil {
// some codes here
} else {
// Add a new document with a generated ID
var ref: DocumentReference? = nil
ref = self.db.collection(K.FStore.playersCollection).addDocument(data: [K.FStore.emailField: email, K.FStore.nameField: ""]) { (error) in
if let err = error {
print("There was an issue storing data to firestore. \(err)")
} else {
print("Document added with ID: \(ref!.documentID)")
}
}
// pass this data
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == K.registerToHome {
let homeVC = segue.destination as! HomeViewController
homeVC.documentID = ref?.documentID
}
}
self.performSegue(withIdentifier: K.registerToHome, sender: self)
}
}
}
}
}
I'm planning to check if the user has a name in their document name field in the next HomeViewController, so if the value of the name field in the document is "", I want to display an alert with a textfeild, so the user can add their name. However, in the HomeViewController, I need the documentID because I will use this code, db.collection(K.FStore.playersCollection).document(documentID).getDocument in HomeVC, so I want to pass the user's documentID (in the RegisterVC, I was able to get the value as ref?.documentID, so I just want to pass the value to HomeVC) to HomeVC. I tried using func prepare(for segue: UIStoryboardSegue, sender: Any?), but I was not able to pass the value...
Change your code to :
//Top of the your code define id :
var id = ""
else {
// Add a new document with a generated ID
var ref: DocumentReference? = nil
ref = self.db.collection(K.FStore.playersCollection).addDocument(data: [K.FStore.emailField: email, K.FStore.nameField: ""]) { (error) in
if let err = error {
print("There was an issue storing data to firestore. \(err)")
} else {
print("Document added with ID: \(ref!.documentID)")
self.id = ref!.documentID
self.performSegue(withIdentifier: K.registerToHome, sender: self)
}
}
}
//Out of registerPressed func :
// pass this data
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == K.registerToHome {
let homeVC = segue.destination as! HomeViewController
homeVC.documentID = self.id
}
}

How to pass prepare for segue swift variable login username

In Navigation Controller, from my LoginPage, I'm trying to pass the username to my app MainViewController, and display it in a label. The username of course needs to be a variable, but successfull so far only to pass and display static text, e.g., Arthur Dent:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "OpenPVOapp" {
let vc = segue.destination as! MainViewController
vc.userName = "Arthur Dent"
}
}
// LOGIN with FACEBOOK
func loginButton(_ loginButton: FBSDKLoginButton!, didCompleteWith result: FBSDKLoginManagerLoginResult!, error: Error!) {
if error != nil{
self.lblStatus.text = error.localizedDescription
}else if result.isCancelled{
self.lblStatus.text = "user cancelled Login"
}else{
//successful Login
// GET FACEBOOK INFO
let params = ["fields" : "email, name"]
let graphRequest = FBSDKGraphRequest(graphPath: "me", parameters: params)
_ = graphRequest?.start(completionHandler: { (connection, result, error) in
if error != nil {
print(error!.localizedDescription)
return
}
if let result = result as? [String:Any]{
guard let email = result["email"] as? String else {
return
}
guard let username = result["name"] as? String else {
return
}
self.lblStatus.numberOfLines = 0
self.lblStatus.text = "CURRENT USER: " + username + "\n " + email
}
})
// END GET FACEBOOK INFO
performSegue(withIdentifier: "OpenApp", sender: nil)
}
}
func loginButtonDidLogOut(_ loginButton: FBSDKLoginButton!) {
self.lblStatus.text = "user logged out"
}
In the MainViewController:
var userName:String = "Anonymous"
#IBOutlet weak var userNameLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
userNameLabel?.text = "Current User: " + userName
}
Of course replacing "Arthur Dent" with username generates !Use of unresolved identifier username.
However, moving the func prepare(for segue:... block into FBSDKGraphRequest:
self.lblStatus.text = "CURRENT USER: " + username + "\n " + email
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "Openapp" {
let vc = segue.destination as! MainViewController
vc.userName = username
}
}
goes and displays Current User: Anonymous in the MainViewController userNameLabel.
So my question is again how to configure, if possible, such a Navigation Controller prepare(for segue:... setup to pass the variable username text?
try this:
self.lblStatus.text = "CURRENT USER: " + username + "\n " + email
// just after the above line paste this
performSegue(withIdentifier: "OpenApp", sender: username)
Now get this username in prepareForSegue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "OpenPVOapp" {
let vc = segue.destination as! MainViewController
vc.userName = sender as? String ?? ""
}
}

Passing data between controller after method is done executed [SWIFT]

I'm trying to send data across another view controller once a button is pressed (I know this question looks repetitive), however, the button being pressed is processing some data. So when the button is clicked, the other view controller is popped up before the needed actual data is sent. I tried both segue calls (prepare for segue and the calling segue) but none seem to work. Here is my code:
#IBAction func login(sender: Any) {
SparkCloud.sharedInstance().login(withUser: email, password: password) { (error:Error?) -> Void in
if let _ = error {
print("Wrong credentials or no internet connectivity, please try again")
}
else {
print("Logged in")
var myPhoton : SparkDevice?
SparkCloud.sharedInstance().getDevices { (devices:[SparkDevice]?, error:Error?) -> Void in
if let _ = error {
print("Check your internet connectivity")
}
else {
if let d = devices {
for device in d {
myPhoton = device
print(myPhoton!)
}
}
}
}
}
}
}
And the segue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "loggedIn" {
if let destinationVC = segue.destination as? testViewController {
destinationVC.myPhoton = sentDevice
}
}
}
And the other view controller that is receiving the data:
var myPhoton : SparkDevice?
override func viewDidLoad() {
super.viewDidLoad()
print(myPhoton)
}
I receive 'nil', which indicates that when the data has been set, it was before it got set to the data that I wanted from the server. Can someone help me please?
You can try
#IBAction func login(sender: Any) {
SparkCloud.sharedInstance().login(withUser: email, password: password) { (error:Error?) -> Void in
if let _ = error {
print("Wrong credentials or no internet connectivity, please try again")
}
else {
print("Logged in")
var myPhoton : SparkDevice?
SparkCloud.sharedInstance().getDevices { (devices:[SparkDevice]?, error:Error?) -> Void in
if let _ = error {
print("Check your internet connectivity")
}
else {
if let d = devices {
for device in d {
myPhoton = device
print(myPhoton!)
}
self.performSegue(withIdentifier: "loggedIn", sender: myPhoton)
}
}
}
}
}
}
and remove linking the segue directly to the button action in IB
Edit
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "loggedIn" {
if let destinationVC = segue.destination as? testViewController {
destinationVC.myPhoton = sender as! SparkDevice
}
}
}
Instead of using Segue, you can also try doing it with code, i.e.
#IBAction func login(_ sender: UIButton)
{
SparkCloud.sharedInstance().login(withUser: email, password: password) {
if let _ = error
{
print("Wrong credentials or no internet connectivity, please try again")
}
else
{
print("Logged in")
var myPhoton : SparkDevice?
SparkCloud.sharedInstance().getDevices { (devices:[SparkDevice]?, error:Error?) -> Void in
if let _ = error
{
print("Check your internet connectivity")
}
else
{
if let d = devices
{
for device in d
{
myPhoton = device
print(myPhoton!)
//HERE..!!!!!
DispatchQueue.main.async {[weak self] in
let anotherController = self.storyboard?.instantiateViewController(withIdentifier: "AnotherVC") as! AnotherVC
anotherController.myPhoton = myPhoton
self.navigationController?.pushViewController(anotherController, animated: true)
}
}
}
}
}
}
}
}
In the above code, if you want to push the controller, then use:
self.navigationController?.pushViewController(anotherController, animated: true)
otherwise, if you want to present the controller, then use:
self.present(anotherController, animated: true, completion: nil)
Let me know if you still face any issues.

triggering segue after an ibaction

I'm trying to perform a segue after my IBAction has already happend.
This is my code and as you can see when I press the button I make a get request with alamofire. The problem is that the request is (as I understand) an async method so the segue will unwind and perform eve if the getPlayer method hasn't done what it's supposed to. The only way I could fix it is by putting the perfomrsegue method inside an if statement where I check for the value of person.name, but I have to press the button twice and I just can't figure out how to solve this!
#IBAction func getPlayerPressed(_ sender: UIButton) {
userDefaults.set(tagTextField.text!, forKey: "userTag")
let userTag = userDefaults.string(forKey: "userTag")
getPlayerData(with: userTag!)
performSegue(withIdentifier: "goToPlayerProfile", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let destionationVC = segue.destination as! PlayerProfileViewController
destionationVC.playerName = player.name
print(destionationVC.playerName)
print("prepared for segue")
}
func getPlayerData (with tag: String) {
let finalURL = baseURL + tag
Alamofire.request(finalURL, headers: headers).responseJSON { (response) in
if response.result.isSuccess {
print("Got player data!")
let playerJSON = JSON(response.result.value!)
self.player.name = playerJSON["name"].stringValue
print(self.player.name)
} else {
print("Error: \(response.result.error!)")
}
}
Perform segue after async alamofire request is completed.
func getPlayerData (with tag: String) {
let finalURL = baseURL + tag
Alamofire.request(finalURL, headers: headers).responseJSON { (response) in
if response.result.isSuccess {
print("Got player data!")
let playerJSON = JSON(response.result.value!)
self.player.name = playerJSON["name"].stringValue
print(self.player.name)
DispatchQueue.main.async {
self.performSegue(withIdentifier: "goToPlayerProfile", sender: self)
}
} else {
print("Error: \(response.result.error!)")
}
}
}

Passing FBSDK login manager result between view controllers

I can't transfer the login manager result between view controllers,
The segue is associated to the button and its identifier is s1.
My setup is correct.The program is crashing with green breakpoints.
here is my code:
for the first VC:
import FBSDKLoginKit
class ViewController: UIViewController {
var user_name: String?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let fbLoginManager = FBSDKLoginManager()
fbLoginManager.logIn(withReadPermissions: ["public_profile", "email"], from: self) { (result, error) in
if let error = error {
print("Failed to login: \(error.localizedDescription)")
return
}
guard let accessToken = FBSDKAccessToken.current() else {
print("Failed to get access token")
return
}
let credential = FIRFacebookAuthProvider.credential(withAccessToken: accessToken.tokenString)
FBSDKGraphRequest(graphPath: "/me", parameters: ["fields" : "email, id, locale"])
.start(completionHandler: { (connection, result, error) in
guard let result = result as? NSDictionary,
let user_name = result["user_name"] as? String,
else {
return
}
if(segue.identifier == "s1"){
if let v = segue.destination as? Re {
v.uname=user_name ?? ""
//v.uname = usr.text ?? ""
}
}
})
// Perform login by calling Firebase APIs
FIRAuth.auth()?.signIn(with: credential, completion: { (user, error) in
if let error = error {
print("Login error: \(error.localizedDescription)")
let alertController = UIAlertController(title: "Login Error", message: error.localizedDescription, preferredStyle: .alert)
return
}
})
}
}
}
And for Re,the next VC:
class Re: UIViewController {
var uname: String?
#IBOutlet weak var l1: UILabel!
var userfb: String?
override func viewDidLoad() {
super.viewDidLoad()
l1.text=uname
// Do any additional setup after loading the view.
}
}
You need to use instance variable which you have declared at top as below.
Now you have create new user_name and use another user_name
guard let result = result as? NSDictionary,
user_name = result["user_name"] as? String,// make change here
else {
return
}

Resources