-1

I'm currently working on the currency rates app. I have a main ViewController which has a UILabel which presents parsed rate for selected currencies. Also, there is a button which is used for changing one of the currency rates in order to get a new rate (second ViewController is presented with N tableView cells).
I have some troubles with transferring data of new selected value to the main ViewController. When I select a new rate, I get this error for UILabel:

Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value

I guess I know how to fix it - create a variable inside main and set its value inside second viewcontroller, but i don't know how do it properly. Here is my code. Thanks in advance.

class MainVC: UITableViewController {

    var convertedValue: String = ""

    // some stuff

    func parseCurrency(top: String) {
        let URL_Exchange = URL(string: "MY URL\(top)")!
        let task = URLSession.shared.dataTask(with: URL_Exchange) {(data, response, error) in
            guard let data = data else { return }
            let queue = DispatchQueue(label: "result")
            queue.async {
                let JSON = String(data: data, encoding: .utf8)
                let jsonData = JSON!.data(using: .utf8)!
                let result: Rates = try! JSONDecoder().decode(Rates.self, from: jsonData)
            
                let currencies = result.conversion_rates?.dictionary
                var doubleVar:Double = 0
                for item in currencies! {
                    if (item.key == self.BottomCurrency.sublabel) {
                        doubleVar = item.value as! Double
                    }
                }
            
                // this value is the text to print inside "Result" Label
                let mult = Double(round(10000*doubleVar * self.InputText)/10000)
             

                self.convertedValue = String(mult)
                DispatchQueue.main.async {
                    if self.convertedValue == "0" {
                        self.Result.text = String(0)
                    } else {
                        // Here I get an error
                        self.Result.text = self.formatNumber(num: Double(self.convertedValue))
                    }
                }
            }
    
        }
        task.resume()
}

Code for second VC:

class SecondVC: UITableViewController {
    private var temp: Int = 0

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        if isBeingDismissed {
            // updating temp value...
            let vc = storyboard!.instantiateViewController(withIdentifier: "main") as! MainVC
            vc.parseCurrency(top: Currencies[temp].short)
            // or to call vc.convertedValue = something
            // vc.parseCurrency(...)
        }
    }
  • 1
    Does this answer your question? [Passing data between view controllers](https://stackoverflow.com/questions/5210535/passing-data-between-view-controllers) – rbaldwin Jul 03 '21 at 15:16
  • This question is the same as the other: https://stackoverflow.com/questions/5210535/passing-data-between-view-controllers?noredirect=1&lq=1 – Alexander Jul 03 '21 at 15:28

1 Answers1

1

If I had to guess, the reason that your code is crashing is that the property Result you are calling on a freshly created instance of main view controller, is actually an IBOutlet pointing to a UILabel! which is not yet created.

Important!

There is a great amount of resources available on the internet on passing data between view controllers so I am pointing you towards already existing post with huge amount of answers: Passing data between view controllers

However, I will describe one of the available ways to solve it below

Delegation

One way to solve it would be to use a "traditional" UIKit pattern which is Delegation.

You simply create a protocol, that will enable a way for the secondVC communicate with mainVC

protocol SecondVCDelegate: AnyObject {
    func secondVCDidSelect(currency: String)
}

Then you add a weak property on the secondVC

class SecondVC: UITableViewController {

    weak var delegate: SecondVCDelegate?

    // ...

}

And now you should be able to pass any value you want to the delegate. For the sake of the example, you could do it on the didSet observer of the temp property

class SecondVC: UITableViewController {

    private var temp: Int = 0 {
        didSet {
            delegate?.secondVCDidSelect(currency: Currencies[temp].short)
        }
    }

    weak var delegate: SecondVCDelegate?

}

Personally I would call the delegate the moment user is selecting the value, and not store it in the temp property

the only thing missing is setting a mainVC as the delegate of the secondVC and conforming to the protocol. Perfect place to do it is at the moment you are presenting the secondVC from mainVC

let viewController: SecondVC = // init SecondVC or load from storyboard
viewController.delegate = self
// present secondVC
extension MainVC: SecondVCDelegate {
    func secondVCDidSelect(currency: String) {
        parseCurrency(top: currency)
    }
}

I encourage you to read more about Delegation since this is a very popular pattern in the iOS development

Delegation in Swift by John Sundell

Witek Bobrowski
  • 3,749
  • 1
  • 20
  • 34
  • answer is brilliant! Well, it worked for me for a half. Text inside UILabel updated but I still get the same error. Your thought about not created label inside instance makes the sense. Do you know any methods or ways to handle this issue? I will be grateful! – sashafromlibertalia Jul 03 '21 at 15:39
  • why do you even call `instantiateViewController` to init a mainVC, is it deallocated? I thought you are presenting secondVC on top of the mainVC so there would be no reason to create a new mainVC – Witek Bobrowski Jul 03 '21 at 15:45
  • as a rule of thumb when working with storyboards, you should avoid doing any view setup before `viewDidLoad` is called. So move any label setup to the `viewDidLoad`. Also there is a property on a `UIViewController` to check if view is loaded, its called `isViewLoaded` – Witek Bobrowski Jul 03 '21 at 15:47
  • ```isViewLoaded``` solved the issue. Thanks a lot!!! You were right about presenting secondVC on top of the main - I do exactly what you've described. Honestly, I have a huge lack of knowledge about swift development 'cause I'm a first year student and didn't get much time to practice and read documentation. Hope to fix it during summer :) – sashafromlibertalia Jul 03 '21 at 16:08
  • You are welcome and good luck! Make sure to take advantage of all the available content from people like John Sundell or Paul Hudson. Their website are goldmines of amazing content! – Witek Bobrowski Jul 03 '21 at 16:14