-1

The .swift file is part of an iOS ObjC static lib. When I try to build, I get the build error.

Sleep is because I want to make sure that stopscan is done and then want to completely and gracefully tear down CBCentralManager.

import UIKit
import CoreBluetooth
import os  

    // Instantiate BLE central service:
    var BLE_Central_instance = BLE_Central();

 . . .  


    // Tears down BLE central
    public func stop_BLE_Central()
    {
        os_log("BLE_Central: stop_BLE_Central...")     
        BLE_Central_instance.stop_CBCentralManager()
        os_log("BLE_Central: stop_BLE_Central done.")
    }

. . .  

    @objc open class BLE_Central: NSObject
    {  
    . . .  
        @objc public func stop_CBCentralManager()                           
        {
           os_log("BLE_Central: stop_CBCentralManager...")     
            
            centralManager.stopScan()
            os_log("        Scanning stopped (ie. centralManager)")
    
            Sleep(5)
    
            centralManager = nil 
    
            data.removeAll(keepingCapacity: false)
            
     
            os_log("BLE_Central: stop_CBCentralManager done.")
       }
Doug Null
  • 7,989
  • 15
  • 69
  • 148
  • Because you don't spell it right? – El Tomato Dec 18 '20 at 00:46
  • What is misspelled? – Doug Null Dec 18 '20 at 01:37
  • 2
    There is no global `sleep`; it is a method on `Thread`, so `Thread.sleep(5)`, but don’t do that because it blocks the thread. There is no need for a sleep or delay here anyway – Paulw11 Dec 18 '20 at 01:48
  • Yes, there is. It insures all notifications have been received before gracefully tearing down logical data processing in the higher abstraction layer. – Doug Null Dec 18 '20 at 16:46
  • Does it ensure that really? How can you be sure that 5 seconds is enough? Relying on fixed delays is asking for trouble. Either they end up being too short, or you end up introducing unnecessarily long delays. Or more commonly, the time varies, so sometimes it's one and sometimes it's the other. – Tom Harrington Dec 18 '20 at 18:28

3 Answers3

1

Going through the various approaches here,

Sleep(5)

This doesn't work because there is no such function Sleep(). It's possible to fix that by using

Thread.sleep(forTimeInterval: 5)

That avoids the error but it's really bad design for a couple of reasons. The first is that you're blocking the thread for 5 seconds, and from the look of things, it's the main thread. That completely locks up your user interface during that time. No UI elements can respond if the main thread is blocked.

You can avoid that by using something like

DispatchQueue.main.asyncAfter(deadline: .now() + 5) { 
    // Do delayed stuff here
}

That doesn't block the current thread, it says, keep on running for now but do this stuff in 5 seconds. So that's better, or at least less bad.

The second reason that Thread.sleep is a terrible idea here is that it's a poor band-aid on the situation. There's no guarantee that 5 seconds is the right amount of time. It might be too much, or too little. The amount of time you need might be inconsistent. You're hoping 5 seconds is enough but you have no way to know that.

A better way is to look for the thing you actually want to know. You're using CBCentralManager and you want to know when it stops scanning. So, use the isScanning property on CBCentralManager and do your stuff when its value changes from true to false.

First, make sure to use @objc in your central manager declaration, and add an observation property:

@objc fileprivate(set) var central: CBCentralManager
var observation: NSKeyValueObservation?

Then register for notifications that isScanning has changed, and handle those changes:

    observation = observe(\.central.isScanning, options: NSKeyValueObservingOptions.new) { (manager, change) in
        guard let isScanning = change.newValue else { return }
        if !isScanning {
            // Do your delayed stuff here
        }
    }
Tom Harrington
  • 69,312
  • 10
  • 146
  • 170
0

If you want to perform something is 5 seconds time, do it like this:

DispatchQueue.main.asyncAfter(deadline: .now() + 5) { [self] in
        centralManager = nil
        data.removeAll(keepingCapacity: false)
    }

If place this code where you had your "Sleep(5)" call, it will perform the two statements in its body on the main thread 5 seconds after "now".

It will do this asynchronously, meaning that the rest of your app will keep going after this call.

mmm
  • 65
  • 10
0
Thread.sleep(forTimeInterval: 5)  // delay to allow completion of any final notification

Credit to paulw for Thread.sleep

Asesh
  • 3,186
  • 2
  • 21
  • 31
Doug Null
  • 7,989
  • 15
  • 69
  • 148