0

It seems that in my iOS app (which I am writing in Xcode with Swift), functions that run after a button is pressed all run at once without regard to any sleep functions in between them.

Here is some background:

In order to create a line of communication between my iPhone app and my Raspberry Pi, I am using json storage bins (https://jsonstorage.net/). The way the app works is it updates a json bin created at the site above and sends an email as a text message to the Raspberry Pi (the Raspberry Pi uses an Adafruit FONA to handle text messages), and this triggers the Pi to gather data from the json bin. The json bin will have been updated with a string, which tells the Pi what to do. Upon receiving this string, the Pi does something and adds information to a different json bin. Then, I access this bin in my app and use it to update certain variables.

Here is the code where the functions run (a button is pressed, which pushes to a new view controller; here is the viewDidLoad() function for that new VC):

override func viewDidLoad() {
    super.viewDidLoad()
    uploadJsonData(dataString:"varsData")
    sendEmail(body:"app varsData")
    sleep(15)
    collectData()
}

Conceptually, this code should work. If I have a button that, when pressed by the user, runs the uploadJsonData() and sendEmail() functions, then the user waits 15 seconds (note that if I have a sleep(15) as part of the first button, this won't work), then the user presses another button that runs the collectData() function, then the code will work as expected, and the variables will be updated with new data immediately after that second button is pressed. However, this is not the case with the code above: instead, the variables are updated with the data from before the second json bin is updated, and the second json bin itself doesn't update until at least 15 seconds after all of the functions run, regardless of the length of the sleep function (implying that the sleep doesn't do anything, and everything runs at once, or at least the interactions with the internet all occur at once). Does anyone have ideas regarding why this is/how to fix it?

Marwen Doukh
  • 1,946
  • 17
  • 26
DOM
  • 53
  • 1
  • 8
  • First of all you shouldnt use `sleep` on main thread. Have you thought using a `Timer` or a `dispatch` function? Take a look at this thread it could give you some ideas: https://stackoverflow.com/a/32696605/5464805 – Olympiloutre Jun 03 '19 at 02:47
  • Thank you so much. I am new to Swift and would have never figured this out. The first answer at that link – the dispatch function – solved my problem (so I suppose this question is a duplicate, although I could not find that question). I really appreciate your help! – DOM Jun 03 '19 at 02:55
  • @DOM No, you do not want to use `asyncAfter`. That is far from the correct solution to this issue. – rmaddy Jun 03 '19 at 03:20
  • you're welcome. Actually this only solves your specific question: you shouldn't put a fixed time to wait for the server to be updates. The fact is that you can not exactly know when the data will be updated on the microcontroller side. you should use a `Timer`, that will run a function every X seconds, and that can detect when the datas are available on the Raspberry. – Olympiloutre Jun 03 '19 at 03:21
  • 2
    The whole premise of this question is flawed because "sleeping" is not the correct approach at all. You are working with several asynchronous processes. There are proper ways to work with such functionality but "waiting" (especially a fixed amount of time) is not one of them. – rmaddy Jun 03 '19 at 03:46
  • The idea was to figure out why the sleep function wasn't working. The solution I plan to implement is one where I have a while loop and continually check if the time has been changed in the second json bin (this time is collected on the Raspberry Pi as a datetime object and uploaded as a string). If it is not, then the loop will continue and check again until the time has been changed. Then, when the time has changed, I will break out of the loop. If the runtime exceeds 2 minutes, then I will break out of the loop. If there are still issues with this approach please let me know. – DOM Jun 03 '19 at 04:41
  • To add to that... the dispatch function will be used to delay for, for example, 0.5 seconds after each iteration of the loop. – DOM Jun 03 '19 at 04:48
  • Scratch that; I learned here: https://stackoverflow.com/questions/43308035/why-putting-a-dispatchqueue-delay-function-inside-another-dispatchqueue-has-no-e that the solution I suggested in my previous comments would not work. Using the timer, I have found an effective solution. – DOM Jun 03 '19 at 08:18

1 Answers1

-2

Hey you can use dispatchQueue for delay purpose like i have used below,

override func viewDidLoad() {
    super.viewDidLoad()
    uploadJsonData(dataString:"varsData")
    sendEmail(body:"app varsData")
    DispatchQueue.main.asyncAfter(deadline: .now() + 15) {
       collectData()
    }  
}
Ahemadabbas Vagh
  • 492
  • 3
  • 15
  • No, do not do this. Use completion handlers and properly deal with asynchronous operations. – rmaddy Jun 03 '19 at 03:20
  • what is the problem in above code ? is there any cause that will affect app performance ? – Ahemadabbas Vagh Jun 03 '19 at 03:21
  • The problem is that 15 seconds is completely arbitrary. You can not know for sure that in 15 seconds your email will be received. It can take fewer time ( therefore its not optimal ) or longer time ( and the `collectData` function will fail ). You should "listen" to your Raspberry, with a Timer for instance, and trigger the `collectData` when you are 100% certain that the email has been received. However @rmaddy this solves the original "question" even if it is not something you should see in an actual code – Olympiloutre Jun 03 '19 at 03:32
  • @Olympiloutre Sorry but I disagree that this solves the original question. It may appear to work under some conditions but that doesn't make it the correct solution to the problem. – rmaddy Jun 03 '19 at 03:34
  • @rmaddy what would you do to answer this question? A completionHandler wont work because, in my understanding, he is waiting for the Raspberry to receive the email, not the `sendEmail` method to be complete – Olympiloutre Jun 03 '19 at 03:38
  • 1
    @Olympiloutre I would certainly use completion handlers for the calls to upload data and send email. Then some sort of polling of probably required to detect when the data is updated by the Raspberry Pi. Once the data is detected, then it can be collected. – rmaddy Jun 03 '19 at 03:43
  • Totally agree with you, however the question still is, to me, "how can I wait 15 seconds to trigger a function?". This code looks more like a draft in order to test the `uploadJsonData`, `sendEmail` and `collectData` function (it is even triggered in a `viewDidLoad` after a segue). But, as I said, I agree with your methodology here – Olympiloutre Jun 03 '19 at 03:53
  • @Olympiloutre Just because the OP naively asked for the wrong solution to their problem is no reason to blindly accept an answer that gives that wrong solution. No offense to either person. The goal should be to help provide a correct solution to a problem. Not blindly offer the incorrectly requested solution. – rmaddy Jun 03 '19 at 04:05