Swift NavigationBar Press "Back" to get values, why? - ios

I am using some values to perform some calculations. For testing purposes I show in Label1 a value as string, since it is stored as a string and in Label2 I show a casted value as a Double since I need them at the end as doubles for my calculations.
The weird thing is, that when I access the ViewController the first time it doesn't show any values. But if I go back and klick on it again using the navigation controller it actually works. But I need the values right away cause my original intention is as I said, not showing some labels but rather making some calculations with it.
I made a little gif to show you what the problem is but I have problem with adding photos. Basically what happens is, that I click on the ViewController with the labels and nothing is showed. I go back and press again and the values will be showed in the labels.
Why is that and how can it be showed right away/ used for calculations right away
Thanks for the help. :)
class AHPfinalPreferencesViewController: UIViewController {
var ahpPrios = [AHPPriorityStruct]()
let decoder = JSONDecoder()
#IBOutlet weak var label1: UILabel!
#IBOutlet weak var label2: UILabel!
let ajkpXc = globaLajkpXc
let ajkpXijr = globaLajkpXijr
let valueA = globaLajkpXc
let valueB = Double(globaLajkpXijr)
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
UserService.ahpPref(for: User.current) { (ahpPrios) in
self.ahpPrios = ahpPrios
print("This is our AHP PRIOS", ahpPrios)
for ahpPrio in ahpPrios {
print(ahpPrio)
}
print("this is the global ajk. ", self.ajkpXc)
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Mark: - Get Data
label1.text = valueA
label2.text = "\(String(describing: valueB))"
// MARK: - Set Values for calculation
// setValues()
// ahpCalculation()
}
}
Could it be because of the globalVariables? I know that it is not the right way to do it but for my purposes its absolutely "okay"
import Foundation
import FirebaseAuth.FIRUser
import FirebaseDatabase
import FirebaseUI
import FirebaseAuth
import CodableFirebase
var globaLajkpXc: String = String()
var globaLajkpXijr: String = String()
var globaLajkpXqpa: String = String()
struct UserService {
static func ahpPref(for user: User, completion: #escaping ([AHPPriorityStruct]) -> Void) {
let ref = Database.database().reference().child("AHPRatings").child(user.uid)
ref.observe(DataEventType.value, with: { snapshot in
guard let value = snapshot.value else { return }
do {
let ahpPrios = try FirebaseDecoder().decode(AHPPriorityStruct.self, from: value)
print(ahpPrios)
// MARK: - lets store the values in the actual constants :)
let ajkpXc = ahpPrios.ajkpXc
let ajkpXijr = ahpPrios.ajkpXijr
let ajkpXqpa = ahpPrios.ajkpXqpa
globaLajkpXc = ajkpXc ?? "no Value"
globaLajkpXijr = ajkpXijr ?? "no Value"
globaLajkpXqpa = ajkpXqpa ?? "no Value"
} catch let error {
print(error)
}
})
}
}
[1]: https://i.stack.imgur.com/VKxaE.png

You are calling UserService's ahpPref in your controller's viewWillAppear. BUT you are attempting to put your valueA (globaLajkpXc's value) to your label in your controller's viewDidLoad.
So what does that mean? Do you know which of these two controller's life cycle method gets called and when they do get called?
To solve your problem, have your label assigning value code
label1.text = globaLajkpXc
move in the completion block of your ahpPref (in the viewWillAppear).
Here's the Apple's documentation about the UIViewController's lifecycle: https://developer.apple.com/library/archive/referencelibrary/GettingStarted/DevelopiOSAppsSwift/WorkWithViewControllers.html
Also, below this line: globaLajkpXqpa = ajkpXqpa ?? "no Value"
add your completion call, like:
completion([ahpPrios]).
This should make my answer above work.

Related

For-in loop requires '[UserVehicles]?' to conform to 'Sequence'; did you mean to unwrap optional? Swift

I have a data model which I made for API returns, it is something like this:
struct VehicleData: Codable {
let _embedded: Embedded
}
struct Embedded: Codable {
let userVehicles: [UserVehicles]
}
struct UserVehicles: Codable {
let id: String
let images: [String]
let userId: String
let vehicle: Vehicle
let originalPrice: OriginalPrice
let hasBasicInsurance: Bool
}
I have used callback function to pass it to my ViewController, now I want to get check in the useVehiclers list, how many vehicles hasBasicInsurance. basically, vehicleList?._embedded.userVehicles[i] = true
this is my function code to use the vehicle data in ViewController:
var vehicleManager = VehicleManager()
var vehicleList: VehicleData?
var i: Int = 0
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
vehicleManager.retrieveUserVehicle()
vehicleManager.onDataUpdate = { [weak self] (data: VehicleData) in
self?.useData(data: data)
}
tableView.dataSource = self
tableView.delegate = self
tableView.tableFooterView = UIView() //remove empty tableView cells
tableView.register(UINib(nibName: Constants.vehicleListCellNibName, bundle: nil), forCellReuseIdentifier: Constants.vehicleListToBeInsuredIdentifier)
}
func useData(data: VehicleData) {
vehicleList = data
// code below has issues....
for i in [vehicleList?._embedded.userVehicles] {
if let vechile = vehicleList?._embedded.userVehicles[i].hasBasicInsurance {
if vehicle == true {
i = i + 1
print(">>number of of insured vehidle: \(i)")
} else {
print(">>>number of of insured vehidle: \(i)")
}
}
}
}
Do you know how to fix it?
You need to supply a default value for optional as a good practise instead of force unwrap
for i in vehicleList?._embedded.userVehicles ?? [] { }
It's not clear from your code, but it looks like vehicleList is optional. It probably should not be (see Leo Dabus's comments). It is rare that it makes sense to have an optional array. That suggests there's some difference between an empty array and a missing array. If there is, then that's fine, but in most cases you should just use a non-optional array and make it empty.
Whether you fix that or not, the solution to this particular problem is to just use a non-optional value, and you have one: data. So change the loop to:
for i in data._embedded.userVehicles { ... }
From your updated question, you note "I want to get check in the useVehiclers list, how many vehicles hasBasicInsurance." It seems you want to put that value in i. If so, that would be:
func useData(data: VehicleData) {
vehicleList = data
i = data._embedded.userVehicles
.filter(\.hasBasicInsurance)
.count
}
You can also use for_each loop for this, for eg like this:
vehicleList?._embedded.userVehicles.for_each(x in /*Do your thing here*/)

How to store user data as string, and load that string in viewDidLoad

I am trying to load a value that has been inputted by the user in the viewDidLoad via a String. I am using UserDefaults to save the users value that they input into a UITextField (userValue), I then save this to the String 'search'. I am able to print out the value of search in the GoButton function, and it works fine, but when I load my ViewController as new, the value of 'search' is equal to nil. The aim here is to have the users previous search saved, and loaded into the UITextField (that is used as a search box) upon loading the ViewController.
Code Below:
class ViewController: UIViewController {
#IBOutlet weak var userValue: UITextField!
var search: String!
}
viewDidLoad:
override func viewDidLoad() {
if (search != nil)
{
userValue.text! = String (search)
}
}
Button Function:
#IBAction func GoButton(_ sender: Any) {
let userSearch: String = userValue.text!
let perference = UserDefaults.standard
perference.set(userSearch, forKey: "hello")
perference.value(forKey: "hello")
let value = perference.value(forKey: "hello") as! String
search = value
print (search) // <<this works, it prints out the users search value
}
#VishalSharma has the right idea, but the code should probably look more like…
override func viewDidLoad() {
super.viewDidLoad()
if let search = UserDefaults.standard.string(forKey: "hello") {
userValue.text = search
}
}
or even more simply…
userValue.text = UserDefaults.standard.string(forKey: "hello")
When you load, search is effectively nil.
So either you read userDefaults in viewDidload or you come through a segue: then you can load search in the prepare.
I've always found it convenient and useful to store all UserDefault properties as an extension within the same file along with their getters and setters. It is far easier to maintain, use and read. by using the #function keyword for the key you are referencing the variable's name and not a string that can be accidentally changed somewhere else in code.
UserDefaults.swift
import Foundation
// An Extension to consolidate and manage user defaults.
extension UserDefaults {
/// A value Indicating if the user has finished account setup.
/// - Returns: Bool
var finishedAcountSetup: Bool {
get { return bool(forKey: #function) }
set { set(newValue, forKey: #function) }
}
/// The hello text at the start of the application.
/// - Returns: String?
var helloText: String? {
get { return string(forKey: #function) }
set {set(newValue, forKey: #function) }
}
//etc...
}
When you use these values reference the standard settings:
//Setting
UserDefaults.standard.helloText = "Updated Hello Text"
// Getting
// for non-optional value you can just get:
let didCompleteSetup = UserDefaults.standard.finishedAcountSetup
// Otherwise, safely unwrap the value with `if-let-else` so you can set a default value.
if let text = UserDefaults.standard.helloText {
// Ensure there is text to set, otherwise use the default
label.text = text
} else {
// helloText is nil, set the default
label.text = "Some Default Value"
}
obviously, it provides nil because when view controller load the search is nil try this.
let perference = UserDefaults.standard
override func viewDidLoad() {
super.viewDidLoad()
if (perference.value(forKey: "hello") != nil) {
search = perference.value(forKey: "hello") as! String
userValue.text! = String (search)
}
}

Swift: Change UITextField Value from inside NSItemProvider

I've been battling this one all day, as you can see I'm new to Swift.
I'm trying to update a UITextField in a iOS Action Extension. Correct value logs below, and outside of the loadItem() the correct value can be set.
Weak variable in the outlet? Some sort of closure thing? Function fires asynchronously and I'm not allowed to update the UI this way once it finishes?
Thank you in advance!
class ActionViewController: UIViewController {
//outlets
#IBOutlet weak var bookmarkTitle: UITextField!
//vars
var pageTitle = ""
override func viewDidLoad() {
super.viewDidLoad()
if let inputItem = extensionContext!.inputItems.first as? NSExtensionItem {
if let itemProvider = inputItem.attachments?.first as? NSItemProvider {
itemProvider.loadItem(forTypeIdentifier: kUTTypePropertyList as String) { [unowned self] (dict, error) in
// do stuff!
let itemDictionary = dict as! NSDictionary
let javaScriptValues = itemDictionary[NSExtensionJavaScriptPreprocessingResultsKey] as! NSDictionary
print(javaScriptValues)
//this works, gets correct data. how to assign to IBOutlet?
self.pageTitle = javaScriptValues["title"] as! String
NSLog("Title From JS Preprocessor: " + self.pageTitle)
/*===================================*/
//Reference to property 'bookmarkTitle' in closure requires explicit 'self.' to make capture semantics explicit
//bookmarkTitle.text = self.pageTitle
//when the line below is used, the action extension fails to even open
self.bookmarkTitle.text = self.pageTitle
/*===================================*/
}
}
}
}
}
try to change the textfield text in Main Queue like this:
DispatchQueue.main.async {
// work that impacts the user interface
self.bookmarkTitle.text = self.pageTitle
}

UILabel.text with Singleton String is nil - Warning: Attempt to present ** whose view is not in the window hierarchy

I have a dummy question...
in one of my ViewController, I set a UILabel text with a String var coming from a singleton:
var nom: String!
var prenom: String!
override func viewDidLoad() {
.....
self.nom = User.sharedInstance.nom
self.prenom = User.sharedInstance.prenom
....
print("User: \(self.nom) - \(self.prenom)")
self.labelWelcome.text = ("Bienvenue \(self.prenom) \(self.nom)")
the print is displaying the right user value, but my View is displaying Bienvenue nil nil....
any idea ?
try this:
if let nom1 = self.nom {
print("User: \(nom1)")
}

Array appending not working across files in swift

Im currently implementing a like functionality in my app, and I can't seem to be able to get this append and retrieving to work. Here is my Button Action which is found in my ViewController file
var Liked = Favourite()
let factBook = FactBook()
#IBAction func favour() {
var currentQuote = factBook.factsArray[factIndex]
Liked.favouriteArray.append(currentQuote)
}
The Favourite struct is called from
import Foundation
struct Favourite {
var favouriteArray: [String] = []
}
(The factBook struct is the same thing except the array actually has elements inside.)
Now my goal is to get all this to display on a separate view controller called favouriteViewController:
import UIKit
class FavouriteViewController: UIViewController {
#IBOutlet var LikeQuote: UILabel!
var liked = Favourite()
override func viewDidLoad() {
super.viewDidLoad()
if liked.favouriteArray.count > 0 {
LikeQuote.text = liked.favouriteArray[0]
} else if liked.favouriteArray.count == 0 {
LikeQuote.text = "No Liked Quotes Found, Go Favour Some!"
}
}
Now when I hit the button, theoretically I should be able to append it to the favouriteArray and then be able to display it on my favouriteViewController file, however when I save it and then open viewcontroller file it defaults to the liked.favouriteArray.count=0 scenario and prints out the text no matter how many quotes I save. I just need an idea of what's going wrong in this process?
Update: If I put append Hello world into text it still does not append to element and evaluates the array value as 0.
The problem is that because you are not saving the data somewhere like into a database or file, you won't be able to retrieve it so the code you add into the viewDidLoad will not work The only way for it to work is when the UIButton was tapped check the code below. Hope that Helps
import Foundation
import UIKit
struct Favourite {
var favouriteArray = [String]()
}
class FavouriteViewController: UIViewController {
// let factBook = FactBook()
#IBOutlet var LikeQuote: UILabel!
var liked = Favourite()
override func viewDidLoad() {
super.viewDidLoad()
}
func updateContent(){
if liked.favouriteArray.count > 0 {
LikeQuote.text = liked.favouriteArray[0]
}
else if liked.favouriteArray.count == 0 {
LikeQuote.text = "No Liked Quotes Found, Go Favour Some!"
}
}
#IBAction func favour() {
// var currentQuote = liked.factsArray[factIndex]
// Liked.favouriteArray.append(currentQuote)
liked.favouriteArray.append("today is a new day ")
updateContent()
}
}

Resources