Can't pass data between View Controller and Model - swift

There is UIAlertController with text field in my View Controller. When user enter name of the city, this data must be transmitted to Model, when I get coordinates of this city. But I can't to pass name of the city from View Controller to Model
My UIAlertController:
class MainScrenenViewController: UIViewController {
var delegate: ILocationGroup?
#objc func locationButtonTap() {
let alert = UIAlertController(title: "Add city", message: nil, preferredStyle: .alert)
let addButton = UIAlertAction(title: "Add", style: .default) { action in
self.delegate?.addLocation(alert.textFields?.first?.text ?? "No City")
}
alert.addAction(addButton)
let cancelButton = UIAlertAction(title: "Cancel", style: .default, handler: nil)
alert.addAction(cancelButton)
alert.addTextField { textField in
textField.placeholder = "Your City"
}
present(alert, animated: true, completion: nil)
}
My Model:
protocol ILocationGroup {
func addLocation(_ name: String)
}
class LocationGroup: ILocationGroup {
var mainScreenViewController: MainScrenenViewController?
func addLocation(_ name: String) {
mainScreenViewController?.delegate = self
let url = "https://geocode-maps.yandex.ru/1.x/?apikey=fd93783b-fe25-4428-8c3b-38b155941c8c&format=json&geocode=\(name)"
guard let url = URL(string: url) else { return }
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else { return }
do {
let result = try JSONDecoder().decode(LocationData.self, from: data)
print(result.response.geoObjectCollection.metaDataProperty.geocoderResponseMetaData.boundedBy.envelope.lowerCorner)
}
catch {
print("failed to convert \(error)")
}
}
task.resume()
}
}

I think it is supposed to be var delegate: LocationGroup()
Also, I wouldn't be calling it delegate because registered delegate is a keyword in swift
https://manasaprema04.medium.com/different-ways-to-pass-data-between-viewcontrollers-views-8b7095e9b1bf

Related

How to call a function in a different class in swift

I have a MainCoordinator class extending NSObject and having the following methods:
init(presenter: UINavigationController) {
self.presenter = presenter
}
​func start() {
let mainViewController = MainViewController(userDefaults: UserDefaults.standard)
presenter.pushViewController(mainViewController, animated: false)
self.mainViewController = mainViewController
subscribeToEvents()
}
private func subscribeToEvents() {
if let viewModel = mainViewController?.viewModel {
viewModel.showOptions.subscribe(onNext: { [weak self] in
self?.showOptions()
}).disposed(by: disposeBag)
}
}
private func showOptions() {
let actionSheet = UIAlertController(title: "Would you like to open the Camera or select one from your photo library?",
message: nil,
preferredStyle: .actionSheet)
let cameraAction = UIAlertAction(title: "Use the Camera", style: .default) { [weak self] (_) in
self?.openCameraScanner()
}
let photoLibraryAction = UIAlertAction(title: "Open Photo Library", style: .default) { [weak self] (_) in
self?.openPhotoLibrary()
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
actionSheet.addAction(cameraAction)
actionSheet.addAction(photoLibraryAction)
actionSheet.addAction(cancelAction)
mainViewController?.present(actionSheet, animated: true, completion: nil)
}
}
MainViewController is a class extending UIViewController which has the following button:
lazy private var sButton: UIButton = {
let button = UIButton(type: .custom)
button.setTitle('options', for: .normal)
button.backgroundColor = UIColor.yellow
button.roundedCorners(radius: 25)
button
.rx
.tap
.bind(to: self.viewModel.showOptions)
.disposed(by: self.disposableBag)
return button
}()
This is its init method
init(userDefaults: UserDefaults) {
viewModel = MainViewModel(userDefaults: userDefaults)
super.init(nibName: nil, bundle: nil)
}
The method self.viewModel.showOptions is of type PublishSubject, whenever I tap the sButton I want the showOptions method in MainCoordinator class to be activated.
import RxSwift
class MainViewModel {
let disposeBag = DisposeBag()
//Subjects
let showScanOptions = PublishSubject<Void>()
..
init(networkable: Networkable, userDefaults: UserDefaults) {
self.networkable = networkable
self.userDefaults = userDefaults
loginResponse.value = userDefaults.loginResponse
login.subscribe(onNext: { [weak self] in
guard let username = self?.email.value, let password = self?.password.value else { return }
self?.isLoading.onNext(true)
let loginInputs = LoginInputs(email: username, password: password)
self?.networkable.login(loginInputs: loginInputs) { loginResponse in
guard let loginResponse = loginResponse else {
self?.errorSubject.onNext(nil)
self?.isLoading.onNext(true)
return
}
self?.userDefaults.loginResponse = loginResponse
self?.loginResponse.value = loginResponse
}
}, onError: { [weak self] error in
self?.isLoading.onNext(false)
self?.errorSubject.onNext(error)
}).disposed(by: disposeBag)
}
}
How is this possible to achieve??
Any help is appreciated.

How can I add alert button till my data count? How can I save my data choose from a action index?

I'm present a alert when I click the button. I choose from a list (if how much data is available.) How can I save my data choose from a list index?
You can see UI in here
My AccountServices
class AccountServices {
static let databaseReference = Database.database().reference(withPath: "Accounts").child((Auth.auth().currentUser?.uid)!)
static var account = Account()
static func saveChanges() {
databaseReference.setValue(try! FirebaseEncoder().encode(AccountServices.account))
}
static func getAccount() {
databaseReference.observeSingleEvent(of: .value, andPreviousSiblingKeyWith: { (snapshot, _) in
account = try! FirebaseDecoder().decode(Account.self, from: snapshot.value!)
})
}
}
Variable
var product: ProductViewModel?
addButton Tapped
#IBAction func addToCartButtonTapped(_ sender: UIButton) {
let alert = UIAlertController(title: "Bu ürünü hangi sepetinize eklemek istersiniz ?", message: "", preferredStyle: .actionSheet)
var indexer = 0
for cart in AccountServices.account.cart! {
if cart.product == nil{
AccountServices.account.cart![indexer].product = [Product]()
}
let action = UIAlertAction(title: cart.name , style: .default, handler: { (sender) in
if let index = alert.actions.firstIndex(where: { $0 === sender }) {
AccountServices.account.cart?[index].product?.append(self.product) `//Error: Cannot convert value of type 'ProductViewModel?' to expected argument type 'Product'`
AccountServices.saveChanges()//TODO...
}
let addAlert = UIAlertController(title: "Sepetinize Eklendi.", message: "Ürününüz sepetinize eklendi.", preferredStyle: .alert)
let okButton = UIAlertAction(title: "Tamam", style: .default, handler: nil)
addAlert.addAction(okButton)
self.present(addAlert, animated: true, completion: nil)
})
alert.addAction(action)
indexer += 1
}
let cancelaction = UIAlertAction(title: "Vazgeç", style: .cancel, handler: nil)
alert.addAction(cancelaction)
present(alert, animated: true, completion: nil)
}
}

How to add another key/value to Firebase Array

The problem that I'm facing is that I have successfully created the array and have displayed the values like so:
Users
-uid
- Name: Example
- Profile Pic URL: example12345
- email: example#example.co.uk
However, in another swift file I have successfully generated a personality type and am struggling to add this to the array so that I end up with something that looks like this:
Users
-uid
- Name: Example
- Profile Pic URL:
- email: example#example.co.uk
- personality type: INTJ
I have tried copying the code from the previous swift class to no avail
This is the code for the working firebase array
#IBAction func createAccountAction(_ sender: AnyObject) {
let usersRef = Database.database().reference().child("Users")
let userDictionary : NSDictionary = ["email" : emailTextField.text!, "Name": nameTextField.text!]
if emailTextField.text == "" {
let alertController = UIAlertController(title: "Error", message: "Please enter your email and password", preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "OK", style: .cancel, handler: nil)
alertController.addAction(defaultAction)
self.present(alertController, animated: true, completion: nil)
} else {
Auth.auth().createUser(withEmail: self.emailTextField.text ?? "", password: self.passwordTextField.text ?? "") { (result, error) in
if error != nil {
let alertController = UIAlertController(title: "Error", message: error?.localizedDescription, preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "OK", style: .cancel, handler: nil)
alertController.addAction(defaultAction)
self.present(alertController, animated: true, completion: nil)
return
}
guard let user = result?.user else { return }
let vc = self.storyboard?.instantiateViewController(withIdentifier: "ViewController") as! ViewController
self.present(vc, animated: true, completion: nil)
// HERE YOU SET THE VALUES
usersRef.child(user.uid).setValue(userDictionary, withCompletionBlock: { (error, ref) in
if error != nil { print(error); return }
let imageName = NSUUID().uuidString
let storageRef = Storage.storage().reference().child("profile_images").child("\(imageName).png")
if let profileImageUrl = self.profilePicture.image, let uploadData = UIImageJPEGRepresentation(self.profilePicture.image!, 0.1) {
storageRef.putData(uploadData, metadata: nil, completion: { (metadata, error) in
if error != nil, metadata != nil {
print(error ?? "")
return
}
storageRef.downloadURL(completion: { (url, error) in
if error != nil {
print(error!.localizedDescription)
return
}
if let profileImageUrl = url?.absoluteString {
self.addImageURLToDatabase(uid: user.uid, values: ["profile photo URL": profileImageUrl as AnyObject])
}
})
})
}
}
)}
}
}
This is the other swift file function which generates the personality type which I would like to add to the array
#IBAction func JPbtn(_ sender: Any) {
if (Judging < Perceiving){
Result3 = "P"
} else {
Result3 = "J"
}
let PersonalityType = "\(Result) \(Result1) \(Result2) \(Result3)"
print(PersonalityType)
let vc = self.storyboard?.instantiateViewController(withIdentifier: "Example") as! ViewController
self.present(vc, animated: true, completion: nil)
}
So if you are just trying to add a new key with a value, all you need to do is create a new reference like this.
guard let currentUserUID = Auth.auth().currentUser?.uid else { return }
print(currentUserUID)
let userPersonalityRef = Database.database().reference().child("users").child(currentUserUID).child("personality")
userPersonalityRef.setValue("Some Value")
When you set the value it can also be a dictionary if you want. But if your users don't all have personality make sure it optional on your data model or else It might crash your app. When you are getting your user from firebase.

How to store value and retrive it to use in next view controller after login using userdefaults?

I want to store the ngoid value in userDefaults so that I can access it in my next API call in the next viewController class. How do I do it?
Here is the code I have written:
#IBAction func loginbutton(_ sender: Any) {
let myUrl = NSURL(string: "http://www.shreetechnosolution.com/funded/ngo_login.php")
let request = NSMutableURLRequest(url:myUrl! as URL)
request.httpMethod = "POST"// Compose a query string
let postString = "uname=\(textfieldusername.text!)&password=\(textfieldpassword.text!)";
request.httpBody = postString.data(using: String.Encoding.utf8)
let task = URLSession.shared.dataTask(with: request as URLRequest){ data , response , error in
if error != nil
{
//let alert = UIAlertView()
let alert = UIAlertController(title: "Alert Box !", message: "Login Failed", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
return
}
// You can print out response object
print("*****response = \(String(describing: response))")
let responseString = NSString(data: data! , encoding: String.Encoding.utf8.rawValue )
if ((responseString?.contains("")) == nil) {
print("incorrect - try again")
let alert = UIAlertController(title: "Try Again", message: "Username or Password Incorrect", preferredStyle: .alert)
let yesAction = UIAlertAction(title: "Nochmalversuchen", style: .default) { (action) -> Void in
}
// Add Actions
alert.addAction(yesAction)
// Present Alert Controller
self.present(alert, animated: true, completion: nil)
}
else {
print("correct good")
}
print("*****response data = \(responseString!)")
do {
//create json object from data
if let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary {
if let email = json["UserName"] as? String,
let password1 = json["passowrd"] as? String {
print ("Found User id: called \(email)")
}
let msg = (json.value(forKey: "message") as! NSString!) as String
//let id = json.value(forKey: "NgoId") as! Int!
let ngoid = json.value(forKey: "NgoId") as? String
print(ngoid ?? "")
let defaults = UserDefaults.standard
defaults.set(ngoid, forKey: "ngoid")
print(ngoid!)
DispatchQueue.main.async {
self.alert = UIAlertController(title: "Alert Box!", message: "\(msg)", preferredStyle: .alert)
self.action = UIAlertAction(title: "OK", style: .default) { (action) -> Void in
let vtabbar1 = self.storyboard?.instantiateViewController(withIdentifier: "tabbar1")
self.navigationController?.pushViewController(vtabbar1!, animated: true)
}
self.alert.addAction(self.action)
self.present(self.alert, animated: true, completion: nil)
}
}
}
catch let error {
print(error)
}
}
task.resume()
}
You could use UserDefaults but if you only need to use the value on the next viewController you should use a segue for this purpose. Here is a guide of how that works. Otherwise use UserDefaults like the example below:
// To set the value
UserDefaults.standard.set(ngoid, forKey: "NgoId")
// To get the value
let id = UserDefaults.standard.string(forKey: "NgoId")
This is not a best way to save in user default and then use in next ViewController, use this overdid method
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowCounterSegue"
{
if let destinationVC = segue.destinationViewController as? OtherViewController {
destinationVC.ngoid = ngoid
}
}
}
use ngoid anywhere in your next ViewController api call.

iOS swift 3.0 Json parsing and alert issue

I'm working on login form. I'm a fresher on iOS development.
After successful login, I want to show an alert after completion of json parsing. I've parsed Ngoid inside a do while block. Now I want to pass the value "Ngoid" to the next view controller so that it can be used to fetch the further data.
Main Problem: Here is the code I have written and it gives me error to write alert it on main thread only.
As I want the "Ngoid" value for further use there, so how should I write it and what is the correct way to execute the code?
Here is the code I have written:
#IBAction func loginbutton(_ sender: Any) {
let myUrl = NSURL(string: "http://www.shreetechnosolution.com/funded/ngo_login.php")
let request = NSMutableURLRequest(url:myUrl! as URL)
request.httpMethod = "POST"// Compose a query string
let postString = "uname=\(textfieldusername.text!)&password=\(textfieldpassword.text!)";
request.httpBody = postString.data(using: String.Encoding.utf8)
let task = URLSession.shared.dataTask(with: request as URLRequest){ data , response , error in
if error != nil
{
//let alert = UIAlertView()
let alert = UIAlertController(title: "Alert Box !", message: "Login Failed", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
return
}
// You can print out response object
print("*****response = \(String(describing: response))")
let responseString = NSString(data: data! , encoding: String.Encoding.utf8.rawValue )
if ((responseString?.contains("")) == nil) {
print("incorrect - try again")
let alert = UIAlertController(title: "Try Again", message: "Username or Password Incorrect", preferredStyle: .alert)
let yesAction = UIAlertAction(title: "Nochmalversuchen", style: .default) { (action) -> Void in
}
// Add Actions
alert.addAction(yesAction)
// Present Alert Controller
self.present(alert, animated: true, completion: nil)
}
else {
print("correct good")
}
print("*****response data = \(responseString!)")
do {
//create json object from data
if let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary {
if let email = json["UserName"] as? String,
let password1 = json["passowrd"] as? String {
print ("Found User id: called \(email)")
}
let msg = (json.value(forKey: "message") as! NSString!) as String
let id = (json.value(forKey: "NgoId") as! NSString!) as String
// let alert : UIAlertView = UIAlertView(title: "Alert box!", message: "\(msg!).",delegate: nil, cancelButtonTitle: "OK")
// alert.show()
self.alert = UIAlertController(title: "Alert Box!", message: "\(msg)", preferredStyle: .alert)
print("the alert\(self.alert)")
self.action = UIAlertAction(title: "OK", style: .default) { (action) -> Void in
let viewControllerYouWantToPresent = self.storyboard?.instantiateViewController(withIdentifier: "pass1") as! ViewControllerngodetails
viewControllerYouWantToPresent.temp1 = self.id
self.present(viewControllerYouWantToPresent, animated: true, completion: nil)
}
self.alert.addAction(self.action)
self.present(self.alert, animated: true, completion: nil)
}
}catch let error {
print(error)
}
}
task.resume()
}
A pro tip:
All your UI related tasks need to be done in the main thread. Here you are presenting the alert inside a closure which executes in a background thread, thats the problem. You need to call the main queue and present alert in that block.
EDIT:
Just put your alert code in this-
For Swift 3-
Get main queue asynchronously
DispatchQueue.main.async {
//Code Here
}
Get main queue synchronously
DispatchQueue.main.sync {
//Code Here
}
Every UI update has to be on main thread:
#IBAction func loginbutton(_ sender: Any) {
let myUrl = NSURL(string: "http://www.shreetechnosolution.com/funded/ngo_login.php")
let request = NSMutableURLRequest(url:myUrl! as URL)
request.httpMethod = "POST"// Compose a query string
let postString = "uname=\(textfieldusername.text!)&password=\(textfieldpassword.text!)";
request.httpBody = postString.data(using: String.Encoding.utf8)
let task = URLSession.shared.dataTask(with: request as URLRequest){ data , response , error in
if error != nil
{
DispatchQueue.main.async {
let alert = UIAlertController(title: "Alert Box !", message: "Login Failed", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
// Present Alert Controller
self.present(alert, animated: true, completion: nil)
}
return
}
// You can print out response object
print("*****response = \(String(describing: response))")
let responseString = NSString(data: data! , encoding: String.Encoding.utf8.rawValue )
if ((responseString?.contains("")) == nil) {
print("incorrect - try again")
DispatchQueue.main.async {
let alert = UIAlertController(title: "Try Again", message: "Username or Password Incorrect", preferredStyle: .alert)
let yesAction = UIAlertAction(title: "Nochmalversuchen", style: .default) { (action) -> Void in }
// Add Actions
alert.addAction(yesAction)
// Present Alert Controller
self.present(alert, animated: true, completion: nil)
}
}
else {
print("correct good")
}
print("*****response data = \(responseString!)"
do {
//create json object from data
if let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary {
if let email = json["UserName"] as? String,
let password1 = json["passowrd"] as? String {
print ("Found User id: called \(email)")
}
let msg = (json.value(forKey: "message") as! NSString!) as String
let id = (json.value(forKey: "NgoId") as! NSString!) as String
DispatchQueue.main.async {
self.alert = UIAlertController(title: "Alert Box!", message: "\(msg)", preferredStyle: .alert)
print("the alert\(self.alert)")
self.action = UIAlertAction(title: "OK", style: .default) { (action) -> Void in
let viewControllerYouWantToPresent = self.storyboard?.instantiateViewController(withIdentifier: "pass1") as! ViewControllerngodetails
viewControllerYouWantToPresent.temp1 = self.id
self.present(viewControllerYouWantToPresent, animated: true, completion: nil)
}
self.alert.addAction(self.action)
self.present(self.alert, animated: true, completion: nil)
}
}
}catch let error {
print(error)
}
}
task.resume()
}