Search code examples
swiftcordovaphonegap-pluginscordova-plugins

Swift plugin in Cordova causing EXC_BAD_ACCESS


I've created a custom plugin for Cordova in Swift. Generally speaking it's "working", since I'm able to communicate with it through JavaScript. Everything is fine until I try to call a method defined in one of my classes. At this moment I'm getting EXC_BAD_ACCESS error when calling locationService.authorizeLocationServices method.

import Foundation
import CoreLocation

@objc(DBT) class DBT : CDVPlugin {
    var locationService: LocationService = LocationService()

    func authorizeLocationServices(_ command: CDVInvokedUrlCommand) {
        let delegate = commandDelegate!

        locationService.authorizeLocationServices(success: {
            delegate.send(CDVPluginResult(status: CDVCommandStatus_OK, messageAs: "All OK"), callbackId:command.callbackId)
        }, fail: {
            delegate.send(CDVPluginResult(status: CDVCommandStatus_ERROR, messageAs: "Not good, not good..."), callbackId:command.callbackId)
        })
    }
}

class LocationService : NSObject, CLLocationManagerDelegate {
    var locationManager = CLLocationManager()
    var authorizationSuccessCallback: (() -> ())?
    var authorizationFailureCallback: (() -> ())?

    override init() {
        super.init()

        locationManager.delegate = self
    }

    func authorizeLocationServices(success successCallback: @escaping () -> (), fail failureCallback: @escaping () -> ()) {
        authorizationSuccessCallback = successCallback
        authorizationFailureCallback = failureCallback

        if CLLocationManager.authorizationStatus() != CLAuthorizationStatus.authorizedAlways {
            locationManager.requestAlwaysAuthorization()
        } else {
            successCallback()
        }
    }

    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        if status == CLAuthorizationStatus.authorizedAlways {
            if let callback = authorizationSuccessCallback {
                callback()
            }
        } else {
            if let callback = authorizationFailureCallback {
                callback()
            }
        }
    }
}

I've debugged the code, and it looks like this is caused by the locationService instance, but I don't know why.


Solution

  • Eventually I've solved my issue. From unknown reason, the locationService isn't instantiated. So I had to make it an optional type and instantiate it in a method. The code below works as expected.

    import Foundation
    import CoreLocation
    
    @objc(DBT) class DBT : CDVPlugin {
        var locationService: LocationService?
    
        func authorizeLocationServices(_ command: CDVInvokedUrlCommand) {
            locationService = LocationService()
    
            if let service = locationService {
                service.authorizeLocationServices(success: {
                    self.commandDelegate!.send(CDVPluginResult(status: CDVCommandStatus_OK, messageAs: "All OK"), callbackId:command.callbackId)
                }, fail: {
                    self.commandDelegate!.send(CDVPluginResult(status: CDVCommandStatus_ERROR, messageAs: "Not good, not good..."), callbackId:command.callbackId)
                })
            }
        }
    }
    
    class LocationService : NSObject, CLLocationManagerDelegate {
        var locationManager = CLLocationManager()
        var authorizationSuccessCallback: (() -> ())?
        var authorizationFailureCallback: (() -> ())?
    
        override init() {
            super.init()
    
            locationManager.delegate = self
        }
    
        func authorizeLocationServices(success successCallback: @escaping () -> (), fail failureCallback: @escaping () -> ()) {
            authorizationSuccessCallback = successCallback
            authorizationFailureCallback = failureCallback
    
            if CLLocationManager.authorizationStatus() != CLAuthorizationStatus.authorizedAlways {
                locationManager.requestAlwaysAuthorization()
            } else {
                successCallback()
            }
        }
    
        func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
            if status == CLAuthorizationStatus.authorizedAlways {
                if let callback = authorizationSuccessCallback {
                    callback()
                }
            } else {
                if let callback = authorizationFailureCallback {
                    callback()
                }
            }
        }
    }