By CogitoErgoBibo


2018-05-14 17:30:17 8 Comments

I want to do something simple in Swift. I have to retrieve some setting from a device and then initialize some UI controls with those settings. It may take a few seconds to complete the retrieval so I don't want the code to continue until after the retrieval (async).

I have read countless posts on many websites including this one and read many tutorials. None seem to work for me.

Also, in the interest of encapsulation, I want to keep the details within the device object.

When I run the app I see the print from the initializing method before I see the print from the method.

// Initializing method

brightnessLevel = 100
device.WhatIsTheBrightnessLevel(level: &brightnessLevel)
print("The brightness level is \(brightnessLevel)")


// method with the data retrieval code

func WhatIsTheBrightnessLevel(level brightness: inout Int) -> CResults
{
var brightness: Int
var characteristic: HMCharacteristic
var name: String
var results: CResults
var timeout: DispatchTime
var timeoutResult: DispatchTimeoutResult



// Refresh the value by querying the lightbulb
name = m_lightBulbName
characteristic = m_brightnessCharacteristic!
brightness = 100
timeout = DispatchTime.now() + .seconds(CLightBulb.READ_VALUE_TIMEOUT)
timeoutResult = .success
results = CResults()
results.SetResult(code: CResults.code.success)
let dispatchGroup = DispatchGroup()
DispatchQueue.global(qos: .userInteractive).async
  {
  //let dispatchGroup = DispatchGroup()
  dispatchGroup.enter()
  characteristic.readValue(completionHandler:
    { (error) in
      if error != nil
        {
        results.SetResult(code: CResults.code.homeKitError)
        results.SetHomeKitDescription(text: error!.localizedDescription)
        print("Error in reading the brightness level for \(name): \(error!.localizedDescription)")
        }
       else
          {
          brightness = characteristic.value as! Int
          print("CLightBulb: -->Read the brightness level.  It is \(brightness) at " + Date().description(with: Locale.current))
          }
      dispatchGroup.leave()
    })
    timeoutResult = dispatchGroup.wait(timeout: timeout)
    if (timeoutResult == .timedOut)
      {
      results.SetResult(code: CResults.code.timedOut)
      }
     else
        {
        print("CLightBulb: (After wait) The brightness level is \(brightness) at " + Date().description(with: Locale.current))
        self.m_brightnessLevel = brightness
        }
  }
return(results)
}



Thank you!

1 comments

@u84six 2018-05-16 15:30:21

If you're going to wrap an async function with your own function, it's generally best to give your wrapper function a completion handler as well. Notice the call to your completion handler. This is where you'd pass the resulting values (i.e. within the closure):

func getBrightness(characteristic: HMCharacteristic, completion: @escaping (Int?, Error?) -> Void) {

    characteristic.readValue { (error) in

        //Program flows here second

        if error == nil {
            completion(characteristic.value as? Int, nil)
        } else {
            completion(nil, error)
        }
    }

    //Program flows here first
}

Then when you call your function, you just need to make sure that you're handling the results within the completion handler (i.e. closure):

getBrightness(characteristic: characteristic) { (value, error) in

    //Program flows here second

    if error == nil {
        if let value = value {
            print(value)
        }
    } else {
        print("an error occurred: \(error.debugDescription)")
    }
}

//Program flows here first

Always keep in mind that code will flow through before the async function completes. So you have to structure your code so that anything that's depending on the value or error returned, doesn't get executed before completion.

@CogitoErgoBibo 2018-05-16 16:01:26

Thank you very much! But I still have the problem with "you have to structure your code so that anything that's depending on the value...". The code in the initialization logically goes like this: "Get Brightness" then "Assign Value". How do I structure that so it doesn't get executed before completion?

@u84six 2018-05-16 16:08:26

All I'm saying, in your case, is that you shouldn't do anything that depends on the characteristic.value until the completion handler is called. For example, if UI is involved, meaning, you want to show the value of characteristic.value somewhere in the UI, you have to wait until the completion handler has been called before showing the results. Sometimes when dealing with UI, it makes sense to show an "activity indicator" on screen and prevent the user from using the UI until the value is available or there's an error. You should google Swift completion handler and you'll find examples.

@CogitoErgoBibo 2018-05-17 15:55:13

"you have to wait until the completion handler has been called..."... exactly :) . will the activity indicator do that? How do I get the code to wait?

Related Questions

Sponsored Content

32 Answered Questions

[SOLVED] Set padding for UITextField with UITextBorderStyleNone

27 Answered Questions

[SOLVED] How to determine the current iPhone/device model?

  • 2014-09-25 01:08:01
  • The Mach System
  • 159751 View
  • 246 Score
  • 27 Answer
  • Tags:   ios iphone swift device

19 Answered Questions

[SOLVED] iOS app with framework crashed on device, dyld: Library not loaded, Xcode 6 Beta

30 Answered Questions

[SOLVED] Determine device (iPhone, iPod Touch) with iPhone SDK

6 Answered Questions

14 Answered Questions

[SOLVED] Detect current device with UI_USER_INTERFACE_IDIOM() in Swift

3 Answered Questions

9 Answered Questions

[SOLVED] Build fat static library (device + simulator) using Xcode and SDK 4+

  • 2010-08-19 10:47:31
  • Adam
  • 119171 View
  • 276 Score
  • 9 Answer
  • Tags:   iphone xcode

24 Answered Questions

[SOLVED] How to detect iPhone 5 (widescreen devices)?

1 Answered Questions

Sponsored Content