0

I am fairly new to Swift and I am using Adafruit's Basic Chat iOS app https://github.com/adafruit/Basic-Chat that uses BLE and I am adding my own viewController and pushing that instead of the one Adafruit provides. My ViewController (ServoViewController) appears as expected.

I am communicating with an Arduino over BLE and receiving data from the Arduino and am parsing the relevant part (and printing to terminal) the data in the BLECentralViewController file.

I have set up a String variable outside of the BLECentralViewController class (so from my understanding it should be global). I read data into that variable in the BLECentralViewController and again print it successfully.

I then try to use that String variable to populate a textField in my viewController, however the textField never gets written to.

In the BLECentralViewController file I have the variables declared outside of the class like this:

var dataReceived: String = ""
public var batteryVal: String = "" // global variable

I check the incoming data from the Arduino. If it contains the String "Battery" I know it is the battery data. I can receive and get the substring (the battery %) and print it to the terminal.

func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {

        if characteristic == rxCharacteristic {
            if let ASCIIstring = NSString(data: characteristic.value!, encoding: String.Encoding.utf8.rawValue) {
                characteristicASCIIValue = ASCIIstring
                print("Value Received: \((characteristicASCIIValue as String))")
                dataReceived = (characteristicASCIIValue as String)

                if dataReceived.contains("Battery:")    {
                    print ("Battery value: ", dataReceived)
                    print("Substring: ", dataReceived.substring(from: 8))
                    batteryVal = dataReceived.substring(from: 8)
                    print("Printing batteryVal: ", batteryVal)

                }
                NotificationCenter.default.post(name:NSNotification.Name(rawValue: "Notify"), object: nil)
                cvcrd.reportValvePosition()

            }
        }
    }

In my viewController (ServoViewController) I have the following function

func reportBatteryPercent() {
        // print battery value here
        batteryField.text = batteryVal
        print("Reporting Battery Percentage here...", batteryVal)
    }

To start with I just wanted to populate the textField when ServoViewController first loads, so I called reportBatteryPercent() in viewDidLoad(). In the terminal output window I see that "Reporting Battery Percentage here..." appears, but there is no value printed in the terminal output for batteryVal and batteryField.text is empty.

Since I am pushing my ServoViewController there is no segue, so I figured a global variable would be an easy way to access that data. What am I doing wrong with accessing the global variable from my viewController?

EDIT: As per recommendations I have put the reportBatteryPercent() in viewWillAppear like this:

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

However the data still doesn't print when in the ServoViewController. I tried changing the declaration of the String from:

public var batteryVal: String = ""

to:

public var batteryVal: String = "999"

and now it prints 999 in the textField. So the problem now seems to be ServoViewController reading the batteryVal string after it has been updated by the BLECentralViewController

EDIT 2: I have confirmed the above my going back to the first viewController (BLECentralViewController) and then returning to the second viewController (ServoViewController). The correct value is now displayed.

I am away from my Apple machine now, but perhaps I could fix this by when I go to push the second ViewController, I could see if the value of interest is an empty string and sleep for a second? Would this sleep the main thread? If so how would I sleep the thread that is doing the push of the viewController?

Ross Satchell
  • 341
  • 4
  • 14
  • Do any of your print statements in the `if dataReceived.contains("Battery:")` block print anything? – GntlmnBndt Apr 23 '19 at 22:58
  • @GntlmnBndt all of those blocks print as expected. Its only when I go to the other viewController (ServoViewController) that the print statements fail to print the data. – Ross Satchell Apr 23 '19 at 23:03

3 Answers3

0

Try to call reportBatteryPercent in viewWillAppear. And you will get what you want.

Here may be some problems: global variables are empty before viewDidLoad, or just your controller is not prepared to handle this vars.

To check it, set starting values of global vars to non-empty strings.

Agisight
  • 1,778
  • 1
  • 14
  • 15
0

Since I am pushing my ServoViewController there is no segue, so I figured a global variable would be an easy way to access that data

No you still can send value to the next vc like

let vc = stroybard.....
vc.someProperty = // value
self.navigationController?.push

plus you're discouraged from using global vars as they're confusing


didUpdateValueFor is an asynchnous method so you need to make sure that the varaible you declare as global has a correct value when you push

Shehata Gamal
  • 98,760
  • 8
  • 65
  • 87
  • So it seems the issue is that didUpdateValueFor is running after the viewController push gets called. Can you suggest how I could get the right value before the push? – Ross Satchell Apr 24 '19 at 02:17
  • you can't , only you can do the push inside the callback of `didUpdateValueFor` – Shehata Gamal Apr 24 '19 at 09:47
0

Global variables are lazy, so you cannot depend on them. You should either have the variable as a static in a struct or in a class with a singleton reference.

See How to create a global variable? for more info.

GntlmnBndt
  • 249
  • 2
  • 6