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
}