I have an app where I ask the user to login first. After a successful login I want the username to follow to all the other view controllers in the app. I have tried declaring this as a public variable, but I am not sure how to do that properly (ie. it is not working in any instance) so I am now trying a prepareForSegue approach. However when I do i get a "Could not cast value of type 'SwiftLoginScreen.LoginVC' (0x10cdd33a0) to 'SwiftLoginScreen.ActionVC' (0x10cdd3130). (lldb)" - error.
I want to be able to call up the username from any and all view controllers in the app, but I am not able to solve this. I have the username from my HomeVC and I now want it to pass on to my ActionVC.
Home VC:
import UIKit
class HomeVC: UIViewController {
#IBOutlet var usernameLabel : UILabel!
var currentUser : NSString!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(true)
let prefs:NSUserDefaults = NSUserDefaults.standardUserDefaults()
let isLoggedIn:Int = prefs.integerForKey("ISLOGGEDIN") as Int
if (isLoggedIn != 1) {
self.performSegueWithIdentifier("goto_login", sender: self)
} else {
self.usernameLabel.text = prefs.valueForKey("USERNAME") as? String
currentUser = prefs.valueForKey("USERNAME") as? String
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let destinationVC = segue.destinationViewController as! ActionVC
destinationVC.operatingUser = self.currentUser;
}
#IBAction func gotoAction(sender : UIButton) {
let appDomain = NSBundle.mainBundle().bundleIdentifier
NSUserDefaults.standardUserDefaults().removePersistentDomainForName(appDomain!)
self.performSegueWithIdentifier("goto_action", sender: self)
}
}
Action VC:
import Foundation
import UIKit
class ActionVC : UIViewController, UIImagePickerControllerDelegate{
#IBOutlet var usernameLable : UILabel!
var operatingUser : NSString!
override func viewDidLoad() {
super.viewDidLoad()
self.usernameLable.text = operatingUser as? String
}
prepareForSegue will get called for all segues involving the View Controller. In your case it is being called for a segue where the destination view controller is an instance of LoginVC, but you have a forced downcast (as!) to ActionVC - so you get an exception at runtime.
You can use an optional binding to make sure that you are handling the correct segue -
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let destinationVC = segue.destinationViewController as? ActionVC {
destinationVC.operatingUser = self.currentUser;
}
}
Your problem can be solved by adding
a username variable in AppDelegate class.
If you have a variable named userName
in AppDelegate you can access it anywhere in your app without passing it.
class AppDelegate
{
var userName = "Admin"
}
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
In your first controller you can set
the value:
appDelegate.userName = "Value"
and use the same to access it from
another controller:
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.userName
Related
I am trying to make a list of users and their passwords in one view controller, save that information in a dictionary, and send that dictionary to another view controller which asks the user to input their username/password combination to authorize the log in. (the key is the username and the value is the password). Is there a way I can send the dictionary from SecondVC to the FirstVC?
First View Controller
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var Username: UITextField!
#IBOutlet weak var Verification: UILabel!
#IBOutlet weak var Password: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
Username.delegate = self
Password.delegate = self
}
var usersDict = [String : String]()
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let des = segue.destination as? AccountViewController {
des.usersDict = usersDict
}
}
#IBAction func Authorization(_ sender: Any) {
for ( key , value ) in usersDict{
let v = key.count
var start = 0
if start <= v{
if Username.text == key{
if Password.text == value{
Verification.text = "Looks Good"
}
}
else{
start += 1
}
}
else{
Verification.text = "Yikes"
}
}
}
}
Second View Controller
class AccountViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var CreateUsername: UITextField!
#IBOutlet weak var CreatePassword: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
CreateUsername.delegate = self
CreatePassword.delegate = self
// Do any additional setup after loading the view.
}
var usersDict = [ String : String ]()
#IBAction func MakeANewAccount(_ sender: Any) {
usersDict[CreateUsername.text!] = CreatePassword.text!
}
}
I have made there dictionary, but it will only send in the beginning and won't update after creating the original account. (dictionary it is sending is empty)
With a segue add this method inside ViewController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let des = segue.destination as? AccountViewController {
des.usersDict = yourDicHere
}
}
Here's a general pattern for making a controller work with data from some object it creates, in this case a second controller.
Try applying it to your situation and let me know if you run into problems.
protocol Processor {
func process(_ dict: [String : String])
}
class FirstController: UIViewController, Processor {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let controller = segue.destination as? SecondController {
controller.delegate = self
} else {
print("Unexpected view controller \(segue.destination)")
}
}
func process(_ dict: [String : String]) {
}
}
class SecondController: UIViewController {
var delegate: Processor?
func someWork() {
if let processor = delegate {
processor.process(["Name" : "Pwd"])
} else {
print("Delegate not assigned")
}
}
}
I have tried 2 methods to pass the data from ViewController to ContainerView, with and without segue
Here is without segue method
ViewController
class DetailPostBookReviewVC: UIViewController {
var postid: String!
#IBAction func Menubutton(_ sender: Any) {
print(postid!)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "MenuBookReviewVC") as! MenuBookReviewVC
vc.favpostid = postid
}
ContainerView
class MenuBookReviewVC: UIViewController {
var favpostid = String()
#IBAction func Deletepost(_ sender: Any) {
print(favpostid)
}
}
result: favposid has Nill Value
UPDATE this is with segue method
class DetailPostBookReviewVC: UIViewController {
var postid: String!
#IBAction func Menubutton(_ sender: Any) {
print(postid!)
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?){
if (segue.identifier == "toMenuBookReviewVC") { //"toMenuBookReviewVC" is identifier
let vc = segue.destination as! MenuBookReviewVC
vc.favpostid = postid!
}
}
Pass your data like. User prepare(for:sender:)
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if
segue.identifier == "MyIdentifierInStorybiard", // Set that
let controller = segue.destination as? MenuBookReviewVC {
controller.favpostid = postid
}
}
I think you postid is not String type so print the null value
In this way, you can't pass data for the container view. if in this way without presenting controller and push controller you can use the global variable then direct pass data and use any controller you want to use.
Example
import UIKit
class ViewController: UIViewController {
var postid: String!
override func viewDidLoad() {
super.viewDidLoad()
postid = "23" // You change your post ID
}
#IBAction func Menubutton(_ sender: Any) {
favpostid = postid
}
}
var favpostid : String!
class MenuBookReviewVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print(favpostid)
}
}
For Passing Data To Container View you can use this
UserDefaults.standard.set(value, forKey: "SomeKey")
after your data is used you can clear that default value.
UserDefaults.standard.set("", forKey: "SomeKey")
I have two viewcontrollers(SignInViewcontroller.swift and ProfilePage.swift)
I want to pass the string from SignInViewcontroller to ProfilePage viewcontroller.
I created a protocol in SignInViewcontroller.And I delegate the method in ProfilePage controller.When I send the string through protocols I didn't receive that string in ProfilePage viewcontroller Where I am wrong.please help me to solve.
Here is my code:
SignInViewController.swift
protocol sendTokenDelegate: class {
func sendToken(login:String)
}
class SignInViewController: UIViewController {
weak var delegateToken:sendTokenDelegate?
func loginAzure(email: String, password: String) {
token = "abcdefgh"
self.delegateToken?.sendToken(login: token)
}
}
ProfilePage.swift
class ProfilePage: UIViewController, UITableViewDelegate, UITableViewDataSource, sendTokenDelegate {
override func viewDidLoad() {
let signInVC = SignInViewController()
signInVC.delegateToken = self
}
func sendToken(login: String) {
self.logInToken = login
print("Login Token in Profile Page is \(login)")
}
}
In that case, you need object that will store token from SignInViewController, until ProfilePage is requesting it.
class TokenStorage {
static let shared = TokenStorage()
public var token: String = ""
}
then you receive token call:
TokenStorage.shared.token = receivedToken
and in ProfilePage request it:
print(TokenStorage.shared.token)
If you are coming from SignUpViewController to ProfilePageViewController, you can pass the string values upon navigation after getting the singIn token you want from your logingAzure() I assume:
If you navigate using segues -> self.performSegue(withIdentifier: "signUpToProfile", sender: self)
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "signUpToProfile" {
if let profileVC : ProfilePageViewController =
segue.destination as? ProfilePageViewController {
profileVC.loginToken = token
}
}
}
If you are using self.navigationController?.pushViewController
let storyboard = UIStoryboard(name: "Profile", bundle: Bundle.main)
if let profileVC = storyboard.instantiateViewController(withIdentifier:
"ProfilePageViewController") as? ProfilePageViewController {
profileVC.loginToken = token
}
EDIT
If you are not going to profilePage directly from SignUpViewController,
then just save the token in your Keychain OR UserDefaults.
Do this by creating a SessionManager singleton to handle tokens and other resources when logging in or signing up
Just use "prepare for segue"
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ProfileSegue" {
if let vc = segue.destination as? ProfilePage {
vc.token = self.token
}
}
}
When you are going to NextVC that doesn't exist in the navigation controller then you have to bind data with the instance of the class when you are pushing just like that:
let vc = UIStoryboard(name: "ProfilePage", bundle: nil).instantiateInitialViewController() as! ProfilePage
vc. logInToken = "abcdefgh"
self.navigationController?.pushViewController(vc, animated: true)
class ProfilePage: UIViewController {
var logInToken = "" // you will receive the token in this variable.
}
In you case, it seems like ProfilePage doesn't exist.
NOTE:- Delegate will use just opposite case when you want to pass the value from ProfilePage to SignInViewController.
OR
If your all API wants the token so you can declare at the class level or save it to UserDefauls:
1)
var logInToken = "" // your variable visible the entire application classes
class ProfilePage: UIViewController {
}
func loginAzure(email: String, password: String) {
logInToken = "abcdefgh" //Just assign and use it
}
2)
UserDefaults.standard.set("aasdfa", forKey: "token")
let token = UserDefaults.standard.value(forKey: "token"
Also, you are doing the bad coding you have to understand the OOP's
let signInVC = SignInViewController()
signInVC.delegateToken = self
This will reperensent the seprate instance in the memory and every
object has its own properties and behavior.
Try using:
protocol sendTokenDelegate: class {
func sendToken(login:String)
}
class SignInViewController: UIViewController {
weak var delegateToken:sendTokenDelegate?
func loginAzure(email: String, password: String) {
token = "abcdefgh"
if self.delegateToken != nil{
self.delegateToken?.sendToken(login: token)
}
}
}
class ProfilePage: UIViewController, UITableViewDelegate, UITableViewDataSource, sendTokenDelegate {
override func viewDidLoad() {
//get your instantiateViewController from storyboard
let signInVC = self.storyboard?.instantiateViewController(withIdentifier: "SignInViewControllerIdentifire") as! SignInViewController
signInVC.delegateToken = self
}
func sendToken(login: String) {
self.logInToken = login
print("Login Token in Profile Page is \(login)")
}
}
I'm trying to pass data using a prepare(for segue:) function but it's showing nil in the second VC. Am I doing anything wrong?
class ViewController: UIViewController {
var first : [String] = []
#IBOutlet weak var passField: UITextField!
#IBOutlet weak var userID: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func login(_ sender: Any) {
let user : String = self.userID.text!
let password : String = self.passField.text!
if user != "" && password != "" {
let postString = ["username":user, “password”: password]
var request = URLRequest(url:URL(string:"http://mydomainhere.com/api/login")!)
request.httpMethod = "POST"
request.httpBody = try! JSONSerialization.data(withJSONObject: postString, options:.prettyPrinted)
let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
if error != nil {
print("error=\(error)")
return
}
do {
if let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any] {
let firstName = json["first_name"] as? String
let lastName = json["last_name"] as? String
self.first.append(firstName!) //putting into Array
self.performSegue(withIdentifier: "loginSegue", sender: self)
}
} catch {
print(error)
}
}
}
}
// data transfer to another controller
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "loginSegue" {
let secondController = segue.destination as? SecondVC
secondController?.name = first //passing to next VC //* here having the issue its not passing the data to next VC
print(first) // here first is printing perfectly
}
}
}
// second View Controller
class SecondVC: UIViewController {
var menu_vc : MenuViewController!
var name : [String]? // passing to this Array
override func viewDidLoad() {
super.viewDidLoad()
print(name) // here printing nil
}
}
As suggested by #Sweeper, it could very well be that your destination view controller is embedded in a UINavigationViewController, hence your segue.destination is in fact a UINavigationViewController, not a SecondVC.
You can try this code:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
var destinationViewController = segue.destination
if let navigationController = destinationViewController as? UINavigationController {
destinationViewController = navigationController.visibleViewController ?? destinationViewController
}
if let secondController = destinationViewController as? SecondVC {
secondController?.name = first
}
}
Of course the first four lines of code could be refactored in an appropriate function (even better if in an extension of UIViewController).
If that solves the problem, you can watch cs193p Stanford iOS course for further details.
In particular watch https://www.youtube.com/watch?v=HQrXM2zUPvY&index=6&list=PLPA-ayBrweUz32NSgNZdl0_QISw-f12Ai starting from the 30:20 mark.
Everything seems perfect with the below snippet
var first : [String] = []
#IBAction func btnTapped(_ sender: Any) {
let firstName = "iOS Geek"
self.first.append(firstName)
self.performSegue(withIdentifier: "MovetoSecVC", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "MovetoSecVC"{
let secVC = segue.destination as! SecondVC
secVC.name = first
print(first) // it'll print here ["iOS Geek"]
}
}
// Your Second View Controller
class SecondVC: UIViewController {
var menu_vc : MenuViewController!
var name : [String]? // passing to this Array
override func viewDidLoad() {
super.viewDidLoad()
print(name!) // it'll print here ["iOS Geek"]
}
}
I'm building an app with a container view holding a tableView controller. I create this tableView, but I don't know how to access this object again so I can call function on it. Currently there is a BucketTableViewController object being created automatically (maybe from the storyboard). Then later I want to call a function on it and create another BucketTableViewController object. I can verify they are unique with print statement on that method. How do I set a variable for an object that is the original object?
import UIKit
class FirstViewController: UIViewController {
var bigArray = ["M", "A", "R", "C"]
let delegate = UIApplication.sharedApplication().delegate as! AppDelegate
override func viewDidLoad() {
super.viewDidLoad()
reachForWebsite()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func reachForWebsite(){
let url = NSURL(...)
let task = NSURLSession.sharedSession().dataTaskWithURL(url!) {(data, response, error) in
do {
...
// HERE IS THE ISSUE
var bucketsVC = BucketTableViewController()
bucketsVC.updateBuckets(self.bigArray)
} catch let myJSONError {
print(myJSONError)
}
}
task!.resume()
}
}
You can grab a reference to it from prepareForSeque(_:sender:) in the view controller that owns the container. Make sure that identifier matches the name of the identifier you've set on the segue from the storyboard in Interface Builder. Or you can omit the identifier part if you know for certain that there are no other segues with destination's of type BucketTableViewController.
class BucketTableViewController: UITableViewController {}
class FirstViewController: UIViewController {
var bucketViewController: BucketTableViewController!
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
super.prepareForSegue( segue, sender: sender )
if let vc = segue.destinationViewController as? BucketTableViewController where segue.identifier == "embeddedBuketViewcontroller" {
self.bucketViewController = vc
}
}
}
A comment is too tight for this, so I'm making it an answer. You can make bucketsVC` an instance variable:
class FirstViewController: UIViewController {
var bucketsVS : BucketTableViewController?
func reachForWebsite(){
...
do {
self.bucketsVC = BucketTableViewController()
self.bucketsVC!.updateBuckets(self.bigArray)
} catch {
...
}
// Now you can use it anywhere within your UIViewController
}
}