Swift 4.2 code only runs once...

Hi, I have swift 4.2 code that grabs html content and converts it to a string. Code locates character strings "sensor on" or "sensor off" and sends to UIlabel on my viewcontroller.

Runs well one time and stops. When "sensor on " or Sensor off " changes in the html content, my code will not change the Label because the code has stopped.

Xcode console output shows the code working well, but only for one time.

Any ideas why my code works once only ?.


// ViewController.swift

// Show sensor ON or OFF in UILabel


import UIKit

import WebKit


class ViewController: UIViewController

{ //A

@IBOutlet weak var alarm: UILabel!

//first time view loaded, viewcontroller will call viewdidload.

override func viewDidLoad() { //B

super.viewDidLoad()

print("step 1")

if let url = URL(string: "http://192.168.4.1")

{ //J

do { //I

let contents = try String(contentsOf: url, encoding: .ascii)

print("step 2")

print("HTML : \(contents)")

//Test for paper on roll

//Paper ?

if contents.contains("Sensor is:</2>ON") { //F

print("** sensor is on")

alarm.text = "HAS PAPER"

print("step 3")

} //F

if contents.contains("Sensor is:</2>OFF") { //G

print("step 4")

print("sensor is off")

alarm.text = "NO PAPER"

} //G

} //J

catch { //E

print("check wifi")

} //E

} //I

} //B

override func didReceiveMemoryWarning() { //L

super.didReceiveMemoryWarning()

// Dispose of any resources that can be recreated.

} //L

override func viewDidAppear(_ animated: Bool) {

super.viewDidAppear(animated)

print("\(self) " + #function)

guard let tc = self.transitionCoordinator else {print("no tc1"); return}

guard tc.initiallyInteractive else {return}

tc.notifyWhenInteractionChanges { // "changes" instead of "ends"

context in

if context.isCancelled {

print("we got cancelled")

}

}

}

override func viewWillAppear(_ animated: Bool) {

super.viewWillAppear(animated)

print("\(self) " + #function)

guard let tc = self.transitionCoordinator else {print("no tc2"); return}

guard tc.initiallyInteractive else {return}

tc.notifyWhenInteractionChanges { // "changes" instead of "ends"

context in

if context.isCancelled {

print("we got cancelled")

}

}

}

override func viewDidDisappear(_ animated: Bool) {

super.viewDidDisappear(animated)

print("\(self) " + #function)

guard let tc = self.transitionCoordinator else {print("no tc3"); return}

guard tc.initiallyInteractive else {return}

tc.notifyWhenInteractionChanges { // "changes" instead of "ends"

context in

if context.isCancelled {

print("we got cancelled")

}

}

}

} //A

Accepted Reply

Hi All,

I have cracked it.

Simply delay code execution using the Dispatch framework. GDC framework.

> DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2)) {

> runagain() //this runs my code in the main thread.

> //this code will be executed only after 2 seconds have been passed , I will eventually make this longer because I dont need to poll sensor as often.

> }

Replies

What do you mean "runs one"


Form your code, you read sensor at viewDidload, ; and viewDidload is executed once.


How should it work ? What should trigger a new read of sensor ? I see no while loop, no call to reread sensor…


PS: it is better to format code with code formatter tool (<>) like this:


class ViewController: UIViewController { //A
   
    @IBOutlet weak var alarm: UILabel!
    //first time view loaded, viewcontroller will call viewdidload.
   
    override func viewDidLoad()        { //B
        super.viewDidLoad()
        print("step 1")
        if let url = URL(string: "http://192.168.4.1")
        { //J
            do { //I
                let contents = try String(contentsOf: url, encoding: .ascii)
                print("step 2")
                print("HTML : \(contents)")
                //Test for paper on roll
                //Paper ?
                if contents.contains("Sensor is:ON")   { //F
                    print("** sensor is on")
                    alarm.text = "HAS PAPER"
                    print("step 3")
                } //F
                if  contents.contains("Sensor is:OFF") { //G
                    print("step 4")
                    print("sensor is off")
                    alarm.text = "NO PAPER"
                } //G
            } //J
            catch { //E
                print("check wifi")
            } //E
        } //I
    } //B
   
    override func didReceiveMemoryWarning() { //L
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    } //L
   
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        print("\(self) " + #function)
        guard let tc = self.transitionCoordinator else {print("no tc1"); return}
        guard tc.initiallyInteractive else {return}
        tc.notifyWhenInteractionChanges { // "changes" instead of "ends"
            context in
            if context.isCancelled {
                print("we got cancelled")
            }
        }
    }
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        print("\(self) " + #function)
       
        guard let tc = self.transitionCoordinator else {print("no tc2"); return}
        guard tc.initiallyInteractive else {return}
        tc.notifyWhenInteractionChanges { // "changes" instead of "ends"
            context in
            if context.isCancelled {
                print("we got cancelled")
            }
        }
    }
   
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        print("\(self) " + #function)
       
        guard let tc = self.transitionCoordinator else {print("no tc3"); return}
        guard tc.initiallyInteractive else {return}
        tc.notifyWhenInteractionChanges { // "changes" instead of "ends"
            context in
            if context.isCancelled {
                print("we got cancelled")
            }
        }
    }
   } //A

Hello Claude31,

Thanks for the reply.

Q. How should it work ? What should trigger a new read of sensor ? I see no while loop, no call to reread sensor…

A. I have a server thats continuously monitors a sensor. If the sensor is On or OFF the resulting HTML web page will show this. Please see HTML content below from Xcode console output. Sensor is ON at the moment(Sensor is:</2>ON). This proves the code has picked up the HTML content with sensor ON.

The swift 4.2 APP will show this on the viewcontroller screen UILabel. All good so far.

Now this is where I am lost. I change the sensor to = (Sensor is:</2>OFF) in the HTML content. Now the APP will not pick this up. It looks like it has stopped at "no tc1" or viewWillAppear function as shown below. If I run the APP again it will pick up the change in sensor to Sensor is:</2>OFF.

I am confused. My launch screen runs , the viewcontroller runs but will not refresh it seems.


I have used while loop before and it was not successful. To reread the sensor again is what I am after. I need the APP to periodically check HTML content for changes to the sensor.


Xcode console output

step 1

2018-12-06 16:53:41.812659+1000 TLMv25_11_18[17226:434286] TIC Read Status [1:0x60000048c840]: 1:57

step 2

HTML : <!DOCTYPE html>

<html>

<head>

<title>TLM Monitor</title>

<meta http-equiv="refresh" content="2">

</head>

<body>

<title></title>

<h1>TLM Eagle Sleeve.</h1>

<h2>Observing State Of Label Sensor</h2>

Click <a href="/H">here</a> to turn the LED on io27 on.<br>Click <a href="/L">here</a> to turn the LED on io27 off.<br>Click <a href="/A">here</a> to turn the LED on io13 off.<br>Click <a href="/B">here</a> to turn the LED on io13 on.<br><h2>Sensor is: </2>1<h2>Sensor is:</2>ON</body>

</html>



** sensor is on

step 3

<TLMv25_11_18.ViewController: 0x7ffa39d12ae0> viewWillAppear

no tc2

<TLMv25_11_18.ViewController: 0x7ffa39d12ae0> viewDidAppear

no tc1

Hello ,

I believe I have the answer. My UI is unresponsive when previously using while loops, for loops etc.... , the answer is to use GDC. I have a realtime application that must constantly and regularly poll sensor reading and update UI with current value of sensor.

Investigating using a timer, on each tick, it will update using GDC. This runs in the background allowing my UI interface time to run.

I have the program running using function calls at the moment, but I will use GDC asynchronously. Will readup on this today...

Hi All,

I have cracked it.

Simply delay code execution using the Dispatch framework. GDC framework.

> DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2)) {

> runagain() //this runs my code in the main thread.

> //this code will be executed only after 2 seconds have been passed , I will eventually make this longer because I dont need to poll sensor as often.

> }