Home > Software engineering >  Creation of a callback function
Creation of a callback function

Time:04-20

I am trying to create an application using CoreLocation, that also has a protocol LocationProtocol with locationDidFailWithError function in it, and a separate class Location. Here's the function in Location:

//gets called when user doesn't allow his location to be accessed
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
     self.delegate?.locationDidFailWithError(error: error)
} 

The ViewModel conforms to the LocationProtocol. What I need is to show an alert with an option to go to Settings app whenever user denies access to his location in case he wants to still allow the location to be accessed. So I need to create a callback function in the ViewModel that can be used in the ViewController to show the alert. I have already created the alert with an extension of UIViewController with a function called alert().

How would I create this callback function in the ViewModel and use it in the ViewController?

Any help is appreciated.

CodePudding user response:

All you need is having a reference to your ViewController inside your ViewModel. Delegating is a Swift way. You can start with creating a protocol:

protocol LocationVcInterface: AnyObject {
    
    func showAlert()
    
}

This protocol will be holding all actions, which your ViewController should be able to do and which your ViewModel should be aware of.

After that make your ViewController conform this protocol:

class LocationVC: UIViewController {
    
    func alert() {
        // shows alert
    }
    
}

extension LocationVC: LocationVcInterface {
    
    func showAlert() {
        self.alert()
    }
    
}

Then simply add a reference inside your ViewModel and make it weak:

class LocationViewModel {
    
    weak var vcInterface: LocationVcInterface?
    
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        self.delegate?.locationDidFailWithError(error: error)
        vcInterface?.showAlert()
    }
    
}

The last thing is to assign a value to this reference somewhere in your code, let's say:

class LocationVC: UIViewController {
    
    private let viewModel = LocationViewModel()
    
    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName: nibName, bundle: Bundle)
        viewModel.vcInterface = self
    }
    
    func alert() {
        // shows alert
    }
    
}

CodePudding user response:

Add a callback closure to viewModel.

public var locationAcessDeniedCallBack: (() -> Void)?

In viewModel class, invoke this callback closure in the delegate method.

func locationDidFailWithError(error: error) {
    locationAcessDeniedCallBack?()
}

In the ViewController where you initialise the viewModel add this callback and show the alert inside it.

viewModel.locationAcessDeniedCallBack = { [weak self] in 
    self?.showAlert(). //present the UIAlertController here
}
  • Related