[Solved]- Error message when trying to read variable from Photon in iOS Swift

I’m getting error message in Xcode when i try to read a variable from my Photon. I’ve created an app that can log in and view my devices but I can’t read a variable.

The error message is:

Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0) 

Xcode does also print this in the console:

fatal error: unexpectedly found nil while unwrapping an Optional value 
(lldb)

This is the part of the code that the error occurs and the error points at the line with myPhoton!.getVariable…

@IBAction func readTapped(sender: AnyObject) {
    
   myPhoton!.getVariable("myVariable", completion: { (result:Any?, error:NSError?) -> Void in
        if let _ = error {
            print("Failed reading myVariable from device")
        }
        else {
            if let status = result as? Int {
                print("Photon status: \(status).")
            }
        }
    })   
}

Im in Xcode 7.3.1.

Does the error point to a particular line in your code? I would guess from looking at that code, that myPhoton is nil. You can either use the debugger, or put a print statement at the beginning of the readTapped function to see if that’s true (try print(myPhoton) to see what it gives).

Yes you’re right! I commented out “myPhoton!.getVariable…” code and added “print(myPhoton)” and it is nil.

But how am I going to get my “getVariable” code to work? Do I need to replace “myPhoton” with the particular name of my device which I am trying to read a variable from or do I need to do something else?

No, the name of the object is not the issue. You need to show the code for where you create myPhoton and how you declare it, so we can see what’s wrong.

Alright! Here is my code:

import UIKit

class variableViewController: UIViewController {
    
    var myPhoton : SparkDevice?
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    

    /*
    // MARK: - Navigation

    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        // Get the new view controller using segue.destinationViewController.
        // Pass the selected object to the new view controller.
    }
    */

  
    
    
    @IBAction func myDevicesTapped(sender: AnyObject) {
        SparkCloud.sharedInstance().getDevices { (sparkDevices:[AnyObject]?, error:NSError?) -> Void in
            if error != nil {
                print("Check your internet connectivity")
            }
            else {
                if let devices = sparkDevices as? [SparkDevice] {
                    print(sparkDevices)
                    for device in devices {
                        if device.name == "myNewPhotonName" {
                            self.myPhoton = device
                        }
                    }
                }
            }
        }
    }


@IBAction func readTapped(sender: AnyObject) {
    
    //    print(myPhoton)
    
    myPhoton!.getVariable("myVariable", completion: { (result:Any?, error:NSError?) -> Void in
        if let _ = error {
            print("Failed reading myVariable from device")
        }
        else {
            if let status = result as? Int {
                print("Photon status: \(status).")
            }
        }
    })
 }
}

Two questions. Do you have a device whose name is “myNewPhotonName” (that string needs to be the actual name of the device you’re trying to access)? Are you invoking (by a button press, I presume) myDevicesTapped before you invoke readTapped? If the answer to both of those is yes, then I would put a print statement just above self.myPhoton = device to make sure your code reaches that point. Otherwise, I don’t see anything wrong with your code.

Alright, that solves my problem! It was the name of the device, it was not “myNewPhotonName”. Thanks!

But then I have another question. How can I read variables from different devices? Like how can I change the device that myPhoton is accessing between different buttons?

In the function myDevicesTapped, you are getting the full list of all your devices, so you only need to extend the code a little to check for other names, and assign those to other variables. You don’t change which device myPhoton points to, you create more variables (myPhoton2, myPhoton3, etc.) that each point to a particular device.

for device in devices {
    if device.name == "oneOfMyNames" {
        self.myPhoton = device
    }else if device.name == "anotherOfMyNames" {
        self.myPhoton2 = device
    }else if device.name == "stillAnotherName" {
        self.myPhoton3 = device
    }    
}

Be sure to declare myPhoton2, myPhoton3, etc. like you did for myPhoton.

Thanks a lot for the help, Ric!

1 Like

@Ric thanks for the help here!