Swift: access Variables from other classes in different swift files - swift

I have tried to implement the solution from this answer but I just haven't been able to get it to work. I get 'Use of unresolved identifier...' for the variables I am trying to access..
I have a loginViewController.swift that gets the username and password when someone logs in..
import UIKit
class loginViewController: UIViewController {
#IBOutlet weak var userEmailTextField: UITextField!
#IBOutlet weak var userPasswordTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func loginButtonTapped(sender: AnyObject) {
let userEmail = userEmailTextField.text;
let userPassword = userPasswordTextField.text;
Then I have a PostService.swift file that gets data from a MySQL database.. It should use the username of the person logged in to access the relevant data..
import Foundation
class PostService {
var settings:Settings!
let userEmail = "steve#centrix.co.za"; //This is hard coded but needs to come from the loginViewController
let userPassword = "1234"; //This is hard coded but needs to come from the loginViewController
I have tried that and a few other suggestions and I just don't seem to be able to get it to work.. Please help..
Thanks in advance.

You can just declare
var userEmail = "";
var userPassword = "";
outside a class (it doesn't matter which Swift class). Then, dropping the let,
#IBAction func loginButtonTapped(sender: AnyObject) {
userEmail = userEmailTextField.text;
userPassword = userPasswordTextField.text;
will store the variables, and you'll just be able to use the in the PostService class.
However, is there really no connection between the LoginViewController and the PostService? You'll have to call a PostService function somewhere ... maybe you can pass the variables there?

Related

How do I check if x is in a variable in classes within a list?

I have a list called mainframe which holds classes. I want to check before adding a new username; if newusername is in mainframe.usernames perform adding the new username in.
pretty much something like this:
import UIKit
class addNewPassword: UIViewController {
var homeVC = Home()
#IBOutlet weak var createHolderItem: UITextField!
#IBOutlet weak var createHolderUsername: UITextField!
#IBOutlet weak var createHolderPassword: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func savePasswordButton(_ sender: Any) {
let holder = Holder()
holder.item = createHolderItem.text!
holder.username = createHolderUsername.text!
holder.password = createHolderPassword.text!
}
if mainframe.contains(where: { $0.username == holder.username }) {
print("test")
}
else {
homeVC.mainframe.append(holder)
homeVC.tableView.reloadData()
navigationController?.popViewController(animated: true)
}
}
I pretty much want to run a loop, within an if statement. Or am I approaching it the wrong way?
I'm new to programming, did online tutorials and trying to write my first iOS app for my aunt.
if mainframe.usernames.contains(holder.username) {
...
Use contains :
if mainframe.usernames.contains(holder.username) {
...
}

How to pass data from a view controller to a model?

I am currently working on a project that has a Create Account page represented by a view controller called CreateNewAccount and a class of the same name which accompanies it. There are four values that are inputted into this view controller: 1) firstName, 2) lastName, 3) username, and 4) password. This view controller also has a "Create Account" button that when pressed, should transfer the String values inputted in the 4 inputs to a new class called UInfoRetrieveModel, which would be classified as a Model under the MVC configuration. Unfortunately this value transference part is not working.
I then have UInfoRetrieveModel pass these 4 values directly to another Model called UserInfo which then delegates out any of these values to other view controllers on the UI side that may need them displayed. I have figured out how to pass values from UInfoRetrieveModel to UserInfo and from UserInfo (which is a model) to said view controllers but I have not figured out how to pass from a view controller, specifically CreateNewAccount, to a model, which in this case is UInfoRetrieveModel.
Basically my idea here is to have two model classes: one model that receives (UInfoRetrieveModel) and one that delegates out (UserInfo) the data values set in CreateNewAccount, in order to make the transference of data across the UI more efficient.
Below is my code for CreateNewAccount and UInfoRetrieveModel, where the transference seems to not be working:
UInfoRetrieveModel->
import Foundation
protocol UInfoRetrieveModelDelegate: class {
func credentialTransfer(data:String)
}
class UInfoRetrieveModel: NSObject {
weak var delegate: UInfoRetrieveModelDelegate?
var firstName: String = ""
var lastName: String = ""
var userName: String = ""
var password: String = ""
func retrieving(){
delegate?.credentialTransfer(data: firstName)
delegate?.credentialTransfer(data: lastName)
delegate?.credentialTransfer(data: userName)
delegate?.credentialTransfer(data: password)
}
}
CreateNewAccount->
import UIKit
class CreateNewAccount: UIViewController{
#IBOutlet weak var FNInput: UITextField!
#IBOutlet weak var LNInput: UITextField!
#IBOutlet weak var usernameInput: UITextField!
#IBOutlet weak var passwordInput: UITextField!
var uInfoRetrieve = UInfoRetrieveModel()
#IBAction func thanksForJoining(_ sender: Any) {
uInfoRetrieve.firstName = FNInput.text!
uInfoRetrieve.lastName = LNInput.text!
uInfoRetrieve.userName = usernameInput.text!
uInfoRetrieve.password = passwordInput.text!
uInfoRetrieve.retrieving()
uInfoRetrieve.delegate = self
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
extension CreateNewAccount: UInfoRetrieveModelDelegate{
func credentialTransfer(data: String) {
print(data)
}
}
You are calling retrieving() in the wrong order. Use this order instead:
#IBAction func thanksForJoining(_ sender: Any) {
uInfoRetrieve.firstName = FNInput.text!
uInfoRetrieve.lastName = LNInput.text!
uInfoRetrieve.userName = usernameInput.text!
uInfoRetrieve.password = passwordInput.text!
uInfoRetrieve.delegate = self
uInfoRetrieve.retrieving()
}
Reason: If you call retreiving() before setting the delegate, the delegate will be nil and you won't get the callback.

How to pass data from one model to another in Swift?

I am working on a project containing a "Create New Account" view controller with its accompanying Swift class called "CreateNewAccount." The user can place 4 input values into this view controller, a first name, last name, user name, and password. Upon clicking the "Create Account" button in this VC, the 4 input values are passed on to a Swift class (within the model layer of MVC, I believe) called UserInfoRetrieveModel where they are supposedly stored.
I would then like to pass these values to another Swift class (that is a model as well) called UserInfoModel, which will then delegate out the first name value to the text value of label located in a VC called "ThanksForJoining" (and its accompanying class).
I have figured out how to pass values from VC to model (CreateNewAccount to UserInfoRetrieveModel) and from model to VC (UserInfoModel to ThanksForJoining), but somewhere in my transference from model to model (UserInfoRetrieveModel to UserInfoModel) the values initially inputted into "CreateNewAccount," which I would like to pass over to the second model class UserInfoModel become nil.
Below is the code for CreateNewAccount, UserInfoRetrieve, UserInfo, and ThanksForJoining:
CreateNewAccount ->
import UIKit
class CreateNewAccount: UIViewController{
#IBOutlet weak var FNInput: UITextField!
#IBOutlet weak var LNInput: UITextField!
#IBOutlet weak var usernameInput: UITextField!
#IBOutlet weak var passwordInput: UITextField!
var uInfoRetrieve = UInfoRetrieveModel()
#IBAction func thanksForJoining(_ sender: Any) {
uInfoRetrieve.firstName = FNInput.text!
uInfoRetrieve.lastName = LNInput.text!
uInfoRetrieve.userName = usernameInput.text!
uInfoRetrieve.password = passwordInput.text!
uInfoRetrieve.delegate = self
uInfoRetrieve.retrieving()
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
extension CreateNewAccount: UInfoRetrieveModelDelegate{
func credentialTransfer(data: String) {
print(data)
}
}
UserInfoRetrieve ->
import Foundation
protocol UInfoRetrieveModelDelegate: class {
func credentialTransfer(data:String)
}
class UInfoRetrieveModel: NSObject {
weak var delegate: UInfoRetrieveModelDelegate?
var firstName: String = ""
var lastName: String = ""
var userName: String = ""
var password: String = ""
func retrieving(){
delegate?.credentialTransfer(data: firstName)
delegate?.credentialTransfer(data: lastName)
delegate?.credentialTransfer(data: userName)
delegate?.credentialTransfer(data: password)
}
}
UserInfo ->
import Foundation
protocol UserInfoModelDelegate: class {
func didReceiveDataUpdate(data: String)
}
class UserInfoModel {
weak var delegate: UserInfoModelDelegate?
let uInfoRetrieve = UInfoRetrieveModel()
func requestData() -> Array<String> {
let firstName = uInfoRetrieve.firstName
let lastName = uInfoRetrieve.lastName
let userName = uInfoRetrieve.userName
let password = uInfoRetrieve.password
delegate?.didReceiveDataUpdate(data: firstName)
delegate?.didReceiveDataUpdate(data: lastName)
delegate?.didReceiveDataUpdate(data: userName)
delegate?.didReceiveDataUpdate(data: password)
let credentials = [firstName, lastName, userName, password] as [Any]
return credentials as! Array<String>
}
}
ThanksForJoining ->
import UIKit
class ThanksForJoining: UIViewController {
var userInfo = UserInfoModel()
#IBOutlet weak var firstName: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
userInfo.delegate = self
firstName.text = userInfo.requestData()[0]
print("yo")
print(userInfo.requestData()[0])
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
extension ThanksForJoining: UserInfoModelDelegate {
func didReceiveDataUpdate(data: String) {
print(data)
}
}
UserInfoModel and CreateNewAccount do both create a new instance of UInfoRetrieveModel. You have to connect them properly for them to pass on information.
Connecting properly means (in the simplest form) one constructs the other and sets itself as the delegate of the other, so UInfoRetrieveModel can pass on data. The constructing of a child model is usually done via a computed property.
Example
struct Account {
let firstName: String, lastName: String
let userName: String, password: String
}
extension UInfoRetrieveModelDelegate: class {
createAccount(_ account: Account): Bool
}
extension UserInfoModel: UInfoRetrieveModelDelegate{
func createAccount(_ account: Account) -> Bool {
// Handling creation of account.
return success == true
}
var newUInfoRetrieveModel: UInfoRetrieveModel {
let helperModel = UInfoRetrieveModel(parent: self)
helperModel.delegate = self
return helperModel
}
}
Explanation
Yes. You usually have a Model, your data, then have something that controls access to it to make changes on your model, manages how the model is stored, maybe syncing with a cloud-service, thats the ModelController which you pass around between ViewControllers, more/other controllers you usually use incase that makes things simpler. In your case you would probably pass createAccount(the call) on to a controller/viewController which is in charge of telling the modelController to create the account and then telling one of its views/viewControllers to display the modal/whatever.
The usual way to pass data to a higher level is to have for the viewController/controller a delegate it uses to communicate with higher up, the one “responsible for actions the ViewController/controller cannot do by itself”, eg pushing data up(creation calls, modification calls, deletion calls) if it makes no sense to give it a modelController since its not control of that part of the application, etc. In your case you can of course pass a modelController to each little viewController/view, but its usually more practical/simpler to only give it to the one controlling the part and let others communicate with that currently-that-part-controlling controller/viewController.
More partical here means that you may not want CreateAccountViewController to display the success dialog, but rather another, which CreateAccountViewController can then do not by itself since it’s not on the stack anymore.

Would using Swift's Guard or if Let Keywords take care of not having to use this checkNil function?

I understand that programming in a language such as Swift, intent can be expressed in numerous ways. I'm following a tutorial and saw the code below which isn't what I'm used to seeing because the author created a function to check for nil. Could the checkNil function be avoided by simply using guard or if let statements or does improve the code somehow? It's a great tutorial and I'm simply looking to improve my knowledge of interpreting other developer's code to find ways to be more concise myself.
import UIKit
class ViewController: UIViewController {
private let rideService = DummyRideService()
private var rides = [Ride]()
#IBOutlet var from: UITextField!
#IBOutlet var to: UITextField!
#IBOutlet var ridesTableView: UITableView!
#IBAction func findRouteButtonClicked(){
let fromText = self.checkNil(from.text as AnyObject?)
let toText = self.checkNil(to.text as AnyObject?)
}
func checkNil(_ string: AnyObject?) -> String {
return string == nil ? "": string as! String
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
there are many ways to get rid of the entire checkNil(_:) nonsense quickly.
idea #1
I'd just simply make the outlets optional weak properties then use optional chaining, like:
#IBOutlet private weak var from: UITextField?
#IBOutlet private weak var to: UITextField?
then you could use this:
#IBAction func findRouteButtonClicked() {
let fromText = from?.text ?? ""
let toText = to?.text ?? ""
}
idea #2
or if you want to make it more elegant, you could create an extension on UITextField, like:
extension UITextField {
var alwaysText: String {
return self.text ?? ""
}
}
so you could replace the let lines with these more readable ones, like
#IBAction func findRouteButtonClicked() {
let fromText = from.alwaysText
let toText = to.alwaysText
}
You can use optional binding (under the optionals section) to check if the value is nil in this code:
let fromText = self.checkNil(from.text as AnyObject?)
can be changed to
if let fromText = from.text {
// Run bit of code
}
or as you aren't using that value straight away, you can do:
let fromText = from.text ?? ""
using the coalescing operator which is equivalent to using that check-for-nil function.
Functions known for usage to gain re usability , he constructs it once and uses it's code twice inside findRouteButtonClicked of course you can use guard or let statements but what if there are many strings to check their nullability in different parts of the VC

Initializing class object swift 3

i have a problem while I'm initializing object of some class. What is wrong with that? (I can upload all my code but it is large if needed)
Edit:
My view controller code:
import UIKit
class ViewController: UIViewController{
#IBOutlet weak var questionLabel: UILabel!
#IBOutlet weak var answerStackView: UIStackView!
// Feedback screen
#IBOutlet weak var resultView: UIView!
#IBOutlet weak var dimView: UIView!
#IBOutlet weak var resultLabel: UILabel!
#IBOutlet weak var feedbackLabel: UILabel!
#IBOutlet weak var resultButton: UIButton!
#IBOutlet weak var resultViewBottomConstraint: NSLayoutConstraint!
#IBOutlet weak var resultViewTopConstraint: NSLayoutConstraint!
var currentQuestion:Question?
let model = QuizModel()
var questions = [Question]()
var numberCorrect = 0
override func viewDidLoad() {
super.viewDidLoad()
model.getQuestions()
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setAll(questionsReturned:[Question]) {
/*
// Do any additional setup after loading the view, typically from a nib.
// Hide feedback screen
dimView.alpha = 0
// Call get questions
questions = questionsReturned
// Check if there are questions
if questions.count > 0 {
currentQuestion = questions[0]
// Load state
loadState()
// Display the current question
displayCurrentQuestion()
}
*/
print("Called!")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
My QuizModel code:
import UIKit
import FirebaseDatabase
class QuizModel: NSObject {
override init() {
super.init()
}
var ref:FIRDatabaseReference?
var test = [[String:Any]]()
var questions = [Question]()
weak var prot:UIPageViewControllerDelegate?
var first = ViewController()
func getQuestions(){
getRemoteJsonFile()
}
func pars(){
/*let array = test
var questions = [Question]()
// Parse dictionaries into Question objects
for dict in array {
// Create question object
let q = Question()
// Assign question properties
q.questionText = dict["question"] as! String
q.answers = dict["answers"] as! [String]
q.correctAnswerIndex = dict["correctIndex"] as! Int
q.module = dict["module"] as! Int
q.lesson = dict["lesson"] as! Int
q.feedback = dict["feedback"] as! String
// Add the question object into the array
questions += [q]
}
*/
//Protocol setAll function
first.setAll(questionsReturned: questions)
}
func getRemoteJsonFile(){
ref = FIRDatabase.database().reference()
ref?.child("Quiz").observeSingleEvent(of: .value, with: { (snapchot) in
print("hey")
let value = snapchot.value as? [[String:Any]]
if let dict = value {
self.test = dict
self.pars()
}
})
}
This isn't my all code but I think that is the most important part. In QuizModel code I'm reskining my code from getting json file to get data from firebase so you can see names of functions like 'getRemoteJSONFile' and in parse function parsing json but it isn't an issue of my problem I think
It looks like you're trying to initialize a constant before you've initialized the viewController it lives in. Your have to make it a var and initialize it in viewDidLoad (or another lifecycle method), or make it a lazy var and initialize it at the time that it's first accessed.
The problem boils down to the following:
class ViewController ... {
let model = QuizModel()
}
class QuizModel ... {
var first = ViewController()
}
The variable initializers are called during object initialization. When you create an instance of ViewController, an instance of QuizModel is created but its initialization creates a ViewController which again creates a new QuizModel and so on.
This infinite cycle will sooner or later crash your application (a so called "stack overflow").
There is probably some mistake on your side. Maybe you want to pass the initial controller to QuizModel, e.g.?
class ViewController ... {
lazy var model: QuizModel = {
return QuizModel(viewController: self)
}
}
class QuizModel ... {
weak var viewController: ViewController?
override init(viewController: ViewController) {
self.viewController = viewController
}
}
Also make sure you are not creating ownership cycles (that's why I have used weak).
In general it's a good idea to strictly separate your model classes and your UI classes. You shouldn't pass view controllers (or page view controller delegate) to your model class. If you need to communicate with your controller, create a dedicated delegate for that.