Search code examples
swiftmodelcontrollerdelegatesswift-protocols

Swift - Passing data from model to controller using delegate design pattern


i am a swift learner, and need assistance in passing data

Here i have ViewController and CoinManager model. What i am trying to do is call didUpdatePrice method that's declared inside my ViewController from CoinManager, this function is responsible to update the UI. I have implemented CoinManagerDelegate protocol in ViewController, but every time i am trying to call didUpdatePrice method it throws an error like this:

Type '(any CoinManagerDelegate)?' has no member 'didUpdatePrice'

When i tried to declare delegate in my ViewController like this:

coinManager.delegate = self

it also returns an error like this:

Cannot assign value of type 'ViewController' to type '(any CoinManagerDelegate)?.Type'

Here's my ViewController code:

import UIKit

class ViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate, CoinManagerDelegate {
   
    var coinManager = CoinManager()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        coinManager.delegate = self
        currencyPicker.dataSource = self
        currencyPicker.delegate = self
    }
 
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1
    }
    
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return coinManager.currencyArray.count
    }
    
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        return coinManager.currencyArray[row]
    }
    
    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        let selectedCurrency = coinManager.currencyArray[row]
        coinManager.getCoinPrice(for: selectedCurrency)
    }
    
    @IBOutlet weak var currencyPicker: UIPickerView!
    @IBOutlet weak var currencyLabel: UILabel!
    @IBOutlet weak var bitcoinLabel: UILabel!
    
    func didUpdatePrice(price: String, currency: String) {
        DispatchQueue.main.async {
            self.bitcoinLabel.text = price
            self.currencyLabel.text = currency
        }
    }
    
}

And here's my CoinManager model code:

import Foundation

protocol CoinManagerDelegate: NSObjectProtocol {
    func didUpdatePrice(price: String, currency: String)
}

struct CoinManager {
    var delegate = CoinManagerDelegate?.self
    
    let baseURL = "https://rest.coinapi.io/v1/exchangerate/BTC"
    let apiKey = "#API_KEY"
    
    let currencyArray = ["AUD", "BRL","CAD","CNY","EUR","GBP","HKD","IDR","ILS","INR","JPY","MXN","NOK","NZD","PLN","RON","RUB","SEK","SGD","USD","ZAR"]
    
    func getCoinPrice(for currency: String){
        let urlString = "\(baseURL)/\(currency)?apikey=\(apiKey)"
        if let url = URL(string: urlString) {
            let session = URLSession(configuration: .default)
            let task = session.dataTask(with: url) { data, response, error in
                if error != nil {
                    print("error")
                }
                if let safeData = data {
                    let data = parseJSON(safeData)
                    let dataString = String(format: "%.2f", data!)
                    self.delegate.didUpdatePrice(price: dataString, currency: currency)
                }
            }
            task.resume()
        }
        
    }
    
    func parseJSON(_ data: Data) -> Double? {
        let decoder = JSONDecoder()
        do{
            let decodedData = try decoder.decode(CoinData.self, from: data)
            let lastPrice = decodedData.rate
            
            return lastPrice
        } catch {
            return nil
        }
    }
}

Any help is much appreciated! On the internet, this method should works fine. But it didn't work on my machine. I've tried to surf the internet and implemented their answers, but it didn't help me, i tried to ask chatGPT as well but the result is nil TwT. Thanks a lot fellow coders!


Solution

  • change the following in your code this should work at your end in ViewController change coinManager.delegate = ViewController to coinManager.delegate = self and in CoinManager update the following

    var delegate = CoinManagerDelegate?.self
    

    to

    var delegate : CoinManagerDelegate?

    on call back with delegate first make sure delegate connection is successfully connect

    if let del = self.delegate
    {
         del.didUpdatePrice(price: dataString, currency: currency)
    }