Dhruv
Dhruv

Reputation: 1799

Crashes on changing screen - iBeacon iOS

I am getting error when I navigate to other screen (onBackButton) after adding my iBeacon code to find distance between two devices.

Here is my sample code:

import Foundation
import UIKit
import SlideMenuControllerSwift
import CoreLocation
import QuartzCore
import CoreLocation
import CoreBluetooth

class PokemonViewController: UIViewController , CLLocationManagerDelegate, CBPeripheralManagerDelegate{

    var beaconRegion: CLBeaconRegion!
    var bluetoothPeripheralManager: CBPeripheralManager!
    var isBroadcasting = false
    var dataDictionary = NSDictionary()
    var isSearchingForBeacons = false
    var lastFoundBeacon: CLBeacon! = CLBeacon()
    var lastProximity: CLProximity! = CLProximity.unknown

    var locationManager: CLLocationManager!

    var my_uuid : String = ""
    var other_uuid : String = ""

    override func viewDidLoad() {
        super.viewDidLoad()

        navigationItem.title = "Find Me"
        var image = UIImage(named: "arrow")
        image = image?.withRenderingMode(UIImageRenderingMode.alwaysOriginal)

        self.navigationItem.leftBarButtonItem = UIBarButtonItem(image: Shared().imageRotatedByDegrees(oldImage: image!, deg: 180), style: UIBarButtonItemStyle.plain, target: self, action: #selector(onBackButton))
        navigationController?.navigationBar.barTintColor = UIStyle().hexStringToUIColor("#e2041a")
    }

    override func viewDidAppear(_ animated: Bool) {
        self.navigationController?.navigationBar.titleTextAttributes = [ NSFontAttributeName: UIFont(name: "Avenir Next", size: 20)!]
        Pokemon.isPokemon = true
        self.bluetoothPeripheralManager = CBPeripheralManager(delegate: self, queue: nil, options: nil)
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        self.slideMenuController()?.removeLeftGestures()
        self.slideMenuController()?.removeRightGestures()

        //self.setNavigationBarItem()

        let nav = self.navigationController?.navigationBar
        nav?.barStyle = UIBarStyle.black
        nav?.tintColor = UIColor.white
        nav?.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.white]

        let url = "\(Webcall().url)getuserdetails?user_id=\(userIdInvite)&token=\(UserDefaults.standard.object(forKey: "user_token") as! String)"
        getUserDataRequest(url)
    }

    override func viewDidDisappear(_ animated: Bool) {
    }

    override func viewWillDisappear(_ animated: Bool) {
        locationManager.stopMonitoring(for: beaconRegion)
        locationManager.stopRangingBeacons(in: beaconRegion)
        locationManager.stopUpdatingLocation()

        isSearchingForBeacons = !isSearchingForBeacons
        bluetoothPeripheralManager.stopAdvertising()
        isBroadcasting = false
    }

    func onBackButton()  {
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let resultViewController = storyboard.instantiateViewController(withIdentifier: "RadarViewController") as! RadarViewController
        _ = navigationController?.popViewController(animated: true)
    }
}

extension PokemonViewController {
    func getUserDataRequest(_ url: String) {

        var url = url
        print(url)
        url = url.replacingOccurrences(of: "+", with: "%2B", options: .literal, range: nil)

        //present(Validation().waitAlert(), animated: true, completion: nil)
        Shared().showLoaderGif(view: self.view)

        _ = URLSession.shared

        let urlPath = URL(string: url)
        let request = NSMutableURLRequest(url: urlPath!)
        request.timeoutInterval = 60
        request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringLocalCacheData
        request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
        request.httpMethod = "POST"

        let dataTask =  URLSession.shared.dataTask(with: request as URLRequest,completionHandler: {(data, response, error) -> Void in

            if((error) != nil) {
            }else {

                let statusCode = (response as! HTTPURLResponse).allHeaderFields
                _ = statusCode["Connection"]!


                _ = NSString(data: data!, encoding:String.Encoding.utf8.rawValue)
                let _: NSError?
                let jsonResult: AnyObject = try! JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
                print(jsonResult)
                let flag = jsonResult["flag"] as! String
                let message = jsonResult["message"] as! String
                DispatchQueue.main.async {
                    Shared().hideLoaderGif(view: self.view)
                    if flag == "1" {
                        let data = jsonResult["data"] as? [String: AnyObject]
                        self.my_uuid = data?["my_udid"]! as! String
                        self.other_uuid = data?["usr_udid"]! as! String

                        self.switchBroadcastingState()

                    }else {
                        let uiAlert = UIAlertController(title: "Hello \(UserDefaults.standard.object(forKey: "user_name") as! String)", message: message , preferredStyle:.alert)
                        self.present(uiAlert, animated: true, completion: nil)

                        uiAlert.addAction(UIAlertAction(title: "Ok", style: .default, handler: { action in}))
                    }
                }

            }
        })
        dataTask.resume()
    }
}

//iBeacon
extension PokemonViewController {
    func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
        print("State -> ->", peripheral.state)
        switch peripheral.state {
        case .poweredOff:
            if isBroadcasting {
                switchBroadcastingState()
                print("Power Off ->->")
            }
        case .poweredOn:
            print("Power On ->->")
        case .resetting:
            print("resetting ->->")
        case .unauthorized:
            print("unauthorized ->->")
        case .unsupported:
            print("unsupported ->->")
        default:
            print("default ->->")
        }
    }

    func switchBroadcastingState() {
        if !isBroadcasting {
            if bluetoothPeripheralManager.state == .poweredOn {
                print("My UUID: \(self.my_uuid)")
                print("Other UUID: \(self.other_uuid)")

                /* It prints
                My UUID: 1D2E9DF1-7A0E-4299-A755-AE11EBBC2C72
                Other UUID: 58B2E595-F4CC-4BAD-8817-F09CD15DB94C*/

                let myUuid = UUID(uuidString: "\(self.my_uuid)")
                let major: CLBeaconMajorValue = UInt16(Int(1))
                let minor: CLBeaconMinorValue = UInt16(Int(1))
                beaconRegion = CLBeaconRegion(proximityUUID: myUuid!, major: major, minor: minor, identifier: "com.iMeetUp")

                dataDictionary = beaconRegion.peripheralData(withMeasuredPower: nil)
                bluetoothPeripheralManager.startAdvertising((dataDictionary as! [String : Any]))

                isBroadcasting = true
            }
        } else {
            bluetoothPeripheralManager.stopAdvertising()
            isBroadcasting = false
        }


        self.locationManager = CLLocationManager()
        self.locationManager.delegate = self

        let otherUuid = UUID(uuidString: "\(self.other_uuid)")
        beaconRegion = CLBeaconRegion(proximityUUID: otherUuid!, identifier: "com.iMeetUp")

        beaconRegion.notifyOnEntry = true
        beaconRegion.notifyOnExit = true
        beaconRegion.notifyEntryStateOnDisplay = true
        if !isSearchingForBeacons {
            locationManager.requestAlwaysAuthorization()
            locationManager.startMonitoring(for: beaconRegion)
            locationManager.startUpdatingLocation()
        } else {
            locationManager.stopMonitoring(for: beaconRegion)
            locationManager.stopRangingBeacons(in: beaconRegion)
            locationManager.stopUpdatingLocation()
        }
        isSearchingForBeacons = !isSearchingForBeacons
    }

    func locationManager(_ manager: CLLocationManager!, didStartMonitoringFor region: CLRegion) {
        print("Called Called Called 1 -> -> ->")
        locationManager.requestState(for: region)
    }

    func locationManager(_ manager: CLLocationManager!, didDetermineState state: CLRegionState, for region: CLRegion) {
        if state == CLRegionState.inside {
            print("Called Called Called 2 -> -> ->")
            locationManager.startRangingBeacons(in: beaconRegion)
        }
        else {
            print("Called Called Called 3 -> -> ->")
            locationManager.stopRangingBeacons(in: beaconRegion)
        }
    }

    func locationManager(_ manager: CLLocationManager!, didEnterRegion region: CLRegion) {
    }

    func locationManager(_ manager: CLLocationManager!, didExitRegion region: CLRegion) {
    }

    func locationManager(_ manager: CLLocationManager!, didFailWithError error: Error) {
        print("Error -> -> didFailWithError : ", error)
    }
    func locationManager(_ manager: CLLocationManager!, monitoringDidFailFor region: CLRegion?, withError error: Error) {
        print("Error -> -> monitoringDidFailFor : ", error)
    }

    func locationManager(_ manager: CLLocationManager!, rangingBeaconsDidFailFor region: CLBeaconRegion, withError error: Error) {
        print("Error -> -> rangingBeaconsDidFailFor : ", error)
    }

    func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon]!, in region: CLBeaconRegion) {
        print("founddddddd")
        var shouldHideBeaconDetails = true

        if let foundBeacons = beacons {
            if foundBeacons.count > 0 {
                if let closestBeacon = foundBeacons[0] as? CLBeacon {
                    print("Found Device: ", foundBeacons)
                    if closestBeacon != lastFoundBeacon || lastProximity != closestBeacon.proximity  {
                        //lastFoundBeacon = closestBeacon as CLBeacon
                        //lastProximity = closestBeacon.proximity

                        var proximityMessage: String!
                        print("Distance M: ", closestBeacon.accuracy)
                        switch closestBeacon.proximity {
                        case CLProximity.immediate:
                            proximityMessage = "Distance: \(String(format: "%.2f", closestBeacon.accuracy))m"

                        case CLProximity.near:
                            proximityMessage = "Distance: \(String(format: "%.2f", closestBeacon.accuracy))m"

                        case CLProximity.far:
                            proximityMessage = "Distance: \(String(format: "%.2f", closestBeacon.accuracy))m"

                        default:
                            proximityMessage = "Not Found!"
                        }

                        shouldHideBeaconDetails = false
                        //lblBeaconDetails.text = "Beacon Details:\nMajor = " + String(closestBeacon.major.int32Value) + "\nMinor = " + String(closestBeacon.minor.int32Value) + "\nDistance: " + proximityMessage + "\nDistance (m): \(String(format: "%.2f", closestBeacon.accuracy))"

                    }
                }
            }
        }
    }
}

Here is the screen-shot of the error which I got in Xcode: enter image description here

Any help will be highly appreciated.

Upvotes: 0

Views: 195

Answers (2)

Jon Rose
Jon Rose

Reputation: 8563

CLLocationManager delegate is assign (unowned(unsafe)) not weak. You must set the delegate to nil in your deinit.

Upvotes: 0

Pratik Patel
Pratik Patel

Reputation: 1421

This is clearly showing that even if you are stopAdvertising BLE, it is not stoping to advertise data and hence when next advertisement broadcast, it is crashed because it could not find its delegate.

So my suggestion is to stop everything in deinit method. Because when we are going back to previous screen, we should deinit all assign objects, so that app is not crashed.

Make sure it called deinit function in that class.

Upvotes: 0

Related Questions