-1

Im not sure why i am getting this nil error on optional when attempting to print. Can somebody provide any input? It doesn't make sense that I am able to output the value from the aplicationContext, however when I attempt to get value from applicationContext["hearRate"] i get nil.

    func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
        print("didReceiveApplicationContext\n\t\(applicationContext)")
        let hrValue = applicationContext["heartRate"] as? String
        print(applicationContext["hearRate"])
        print(hrValue)
        //heartRate.text = hrValue
    }
console - output
2021-03-10 18:41:04.623716-0700 Trainer+[1824:759737] Metal API Validation Enabled
session active state
didReceiveApplicationContext
    ["heartRate": 00BPM]
nil
Optional("00BPM")
Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value: file Trainer_/ViewController.swift, line 57
2021-03-10 18:41:04.999001-0700 Trainer+[1824:759754] Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value: file Trainer_/ViewController.swift, line 57

Update - Ignore typo, found issue with IBOutlet

    func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
        print("didReceiveApplicationContext\n\t\(applicationContext)")
        let hrValue = applicationContext["heartRate"] as? String
        print(applicationContext["heartRate"])
        print(hrValue)
        //heartRate.text = hrValue
        heartRate.text = "test"
    }
2021-03-11 12:45:05.482464-0700 Trainer+[1873:817768] Metal API Validation Enabled
session active state
didReceiveApplicationContext
    ["heartRate": 91BPM]
Optional(91BPM)
Optional("91BPM")
Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value: file Trainer_/ViewController.swift, line 58
2021-03-11 12:45:05.887523-0700 Trainer+[1873:817966] Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value: file Trainer_/ViewController.swift, line 58

the func session(didReceiveApplicationContext), from watchconnectivity is working as intended. I believe that my problem comes from me moving the heartRate label to a 'view container'. I added the watchconnectivity at the same time that I moved the label and assumed that it would work.

This may require a different question, not sure, how can i pass data along multiple view controllers that are visible at the same time. Since I am creating the session on the ViewController, I don't yet know how to pass the data to other view controllers without passing a full 'self' reference of the ViewController, to the DetailViewController. Coming from Java and PHP, i feel like this is not good coding practice.

I am thinking about extensions/delegates, and prototypes. I understand them to a slight degree, but I am currently working on an app and my main goal is to have a working prototype. I'll come back and refactor the code fix and fix any gaping vulnerabilities. If any body with experience can provide me with any reliable resources. Tired of hitting the next page of google as i cant find the answers that I am looking for lol.

Update #3

class ViewController: UIViewController, WCSessionDelegate {
    
    @IBOutlet weak var cont: DetailViewController!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        if WCSession.isSupported() {
            connectivitySession = WCSession.default
            connectivitySession?.delegate = self
            connectivitySession?.activate()
        }
    }
    func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
        print("didReceiveApplicationContext\n\t\(applicationContext)")
        let hrValue = applicationContext["heartRate"] as? String
        print(applicationContext["heartRate"])
        print("container\n\t\(cont.heart.text)")
        cont.heart.text = "test"
    }
    ******
}

// class assigned to 'view container' in ViewController in SB
class DetailViewController: UIView {
    
    @IBOutlet weak var heart: UILabel!
    //    @IBOutlet weak var hearRate: UILabel!
    @IBOutlet weak var sessionTimer: UILabel!
    @IBOutlet weak var currentActivity: UILabel!
    @IBOutlet weak var startButton: UIButton!
    @IBOutlet weak var activityTimer: UILabel!
}

Output 
2021-03-11 15:22:15.294727-0700 Trainer+[1938:854826] Metal API Validation Enabled
2021-03-11 15:22:15.628977-0700 Trainer+[1938:854826] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Trainer_.ViewController 0x102e04ec0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key menuContainer.'
*** First throw call stack:
(0x1986ad9d8 0x1aca33b54 0x1985be6e4 0x19986aca8 0x19a84f6a4 0x19ab5f254 0x198598298 0x19ab5b218 0x19a856d88 0x19a857114 0x19a8577e8 0x19af56448 0x19af55c5c 0x19af56af0 0x19af67730 0x19b17aba0 0x19a434be4 0x19af19ca0 0x19af1a044 0x19aa59ab8 0x1a79d7704 0x1a79ff130 0x1a79e4e60 0x1a79fee44 0x101291528 0x1012949f0 0x1a7a23e60 0x1a7a23b28 0x1a7a23ffc 0x19862dbf0 0x19862daf0 0x19862ce9c 0x1986273e0 0x198626ba0 0x1af38f598 0x19af182f4 0x19af1d874 0x1abda7b54 0x100f75a08 0x100f75980 0x100f75a4c 0x198305568)
libc++abi.dylib: terminating with uncaught exception of type NSException
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Trainer_.ViewController 0x102e04ec0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key menuContainer.'
terminating with uncaught exception of type NSException
(lldb) 
  • Hi, thank you for asking, please read the question, and notice the second set of code and output, plus the additional information that I have added. Let me know if i need to clarify anything for you. Appreciate the constructive comment.] – Irving Luna Mar 11 '21 at 20:52
  • I think i understand your comment now. The original question was everything prior to the Update portion of my question, i edited the original question, but noticed that it was closed and that others are unable to answer. Created this new one with all previous content, so I and if somebody else has a similar question, can get the answere without having to jump through posts. – Irving Luna Mar 11 '21 at 20:56
  • dude, im not sure how to answer your question, without sounding sarcastic or contradictive? did you look at the 2nd block of code? or at least read the Update line? – Irving Luna Mar 11 '21 at 21:03
  • yea i can see how it can get confusing, i simply figured that no one bothers with closed posts. – Irving Luna Mar 11 '21 at 21:08
  • Did you check that heartRate is not nil ? This could come from incorrect IBOutlet defined. try print heartRate. You can also write the line as : heartRate?.text = htvValue – Ptit Xav Mar 11 '21 at 21:59
  • @PtitXav yes this is definetly an issue. The heart rate returned nil, because i moved the label to a 'view container'. i've been trying several attempts now to load the heart rate outlet from the view controller, but no luck. I've read articles to move the session singleton to app delegate, but as soon as I start coding with this method, I start to run in to the same issue. I have also considered extensions/delegates, but being a couple months, in I don't want to make everything over complicated, just was something that will work, then fix issues and optimize. – Irving Luna Mar 11 '21 at 22:27
  • @PtitXav i found this article, its exactly what i want to happen, however, i am once again stuck, its not working for me, from [here](https://stackoverflow.com/questions/66575388/swift-getting-nil-when-attempting-to-get-value-from-key-that-exists) `containerView1.containerView1Label.text = "Container view 1 lable"` when i attempt, i get **reason: '[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key menuContainer.'** – Irving Luna Mar 11 '21 at 22:34
  • A possible solution is to set a tag to the label (eg 1234) inside storyboard and the. Try to find it with containerView.viewWithTag(1234). The best should be to define a custom container view with the label as an IBOutlet and make the connection in storyboard. – Ptit Xav Mar 13 '21 at 14:09
  • @PtitXav thanks for the tags tip, i'll try it as a current work around, but I believe that I already have set up what you are suggesting as the best option, please see update #3, please confirm if this is what you are talking about or if its diffferent. – Irving Luna Mar 15 '21 at 18:09
  • Yes , update 3 is it. – Ptit Xav Mar 15 '21 at 21:44

1 Answers1

0

answered my own question, by chaning from uiviewcontroller to name of custom class.

This is for a basic watch connection session for iphone that uses multiple view controllers on same screen.

Delegates, extensions, etc, is not required. Will need to setup your segue, secondary controller, and connections.

class ViewController: UIViewController, WCSessionDelegate {
    
    var variable: Any? // Your variable type
    
    var sessionVariable: WCSession?
    
    var secondVC: SecondViewController!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // start session
        if WCSession.isSupported() {
            sessionVariable = WCSession.default
            sessionVariable?.delegate = self
            sessionVariable?.activate()
        }
    }
    
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // using segue.identifier, save live ViewController to your variable
        if segue.identifier == "Storyboard Segue Identifier" {
            if let destinationVC = segue.destination as? SecondViewController {
                secondVC = destinationVC
            }
        }
    }

    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
        print("session active state")
    }
    
    //using dispatchqueue, to update label on main thread, use self.secondVC.variable to update.
    func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
        let contextValue = applicationContext["context"] as? String
        DispatchQueue.main.async {
            self.secondVC.variable.text = contextValue
        }
    }
}

class HealthViewController: UIViewController {
    
    
    @IBOutlet weak var varialble: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}