Wednesday, November 25, 2020

My Toothbrush Timer App: Early Struggles in Going Mobile

LiveCode is an amazing computer programming language and software development environment. The apps you make with it can be run on Macintosh computers, Windows computers, Apple mobile (iOS via iPhone or iPad), and Android. The only thing it hasn't yet figured out completely is online web apps, though the promise of LiveCode's ability to export to HTML5 remains very exciting (though mostly yet unfulfilled).

With that opening lovefest of a paragraph behind me, I now turn to some frustrations. To be fair, the frustrations aren't really with LiveCode, but mainly with Apple and how the Apple Computer Company makes compliance with its software application standards a moving target. 

The first step in taking my toothbrush interval timer to a mobile platform, specifically the iPhone, is to test the app in Apple's iPhone simulator, which is a part of the Xcode development software. To get that to work, one has to have all of the "stars" aligned, with the stars in this case being a metaphor for using the right version of LiveCode with the right version of Xcode and with the right version of MacOS on my laptop. In the end, I did get it all to work, but only after many hours spent downloading different versions of LiveCode, different versions of Xcode, and finally biting the bullet and updating my MacOS. And even then, I had to resort to searching the LiveCode user forums for a last bit of advice to make it all work. I detail my journey here with the hope that it might help others (or most likely just me in a few months time because I will have forgotten all of this by then).

The Challenging in Getting "Everyone to Play Nice" Together

In the end, here are the system and software specifications that are "playing together" well:

  • MacOS version: 10.15.2
  • Xcode version: 11.4
  • LiveCode version: 9.6.0

But, they didn't at first. 

Let's start at the beginning. I had not made a mobile app since I made my little game Lunar Hotel Shuttle, the inaugural project for this blog. This app first appeared in the app store on November 28, 2012. Yes, that's almost exactly eight years ago, so yes, it's been a long time since I made a mobile app. Due to a very faded memory of that experience I knew that all of my software had to be aligned to work, so I crossed my fingers as I clicked the "test" button in LiveCode after choosing iOS as my standalone application setting. If it all worked, then just clicking the "test" button would automatically launch Xcode's simulator followed by LiveCode packaging and bundling the app for mobile, then exporting the app to the simulator for testing. But, did I mention this all only happens if everything is aligned properly? Right, sorry to repeat myself. Not surprisingly, it didn't work.

No worries, I thought. I quickly found this handy dandy reference page on the LiveCode web site showing all of the different configurations one could use:

https://livecode.com/docs/9-5-0/faq/faq/

My Mac's OS was Catalina 10.15.1 and my version of Xcode was 11.1.1. So, I downloaded LiveCode 9.6.0 DP-3 since that was the version of LiveCode that seemed to align with the MacOS and Xcode. That's when the frustration really began. I began exploring one rabbit hole after another, though I did learn a lot along the way. LiveCode 9.6.0 DP-3 could not be "verified" by Apple. After some Googling, I learned a way to bypass Mac's verification process:

https://support.apple.com/en-us/HT202491

LiveCode opened, but I still had no luck getting the iPhone simulator to work. So, I went ahead and downloaded the LiveCode 9.6.0 (this is the "stable" version, not the "developer preview" version). Alas, I was greeted with a message from LiveCode (that kept popping up dozens of times) that I had to upgrade my MacOS to 10.15.2 for my version of Xcode to work. I am always loathe to update my operating system given some very bad past experiences. But, I decided to do it anyway. Well, the only option my Macintosh's update button was giving me was a major upgrade to MacOS Big Sur and no way was I going to do that. So, after a little more Googling, I found Apple's MacOS download page and did the upgrade. Remember, all of these software downloads and installations take time, particularly given my unusually slow Internet speed here in rural Georgia. Plus, I do have a day job, so I can only work on this other "hobby" periodically throughout the day. So, the hours are ticking by. But, as I finished, my confidence now was very high for my next test.

And Again It Didn't Work

At this point, if I had rum in the house I would have started drinking. But, I need to back up a little bit because it almost worked, and more importantly, this was not the first time it had almost worked. That is, the iPhone simulator did launch and LiveCode was giving me messages that it was dutifully creating all of the needed "bundles." But the toothpaste timer app just never appeared on the simulated iPhone. Within about 10 seconds or so, a message popped up from LiveCode asking me if I wanted to keep trying given how long it was taking for the app to load. I pressed "yes" and the message popped up again about 10 seconds later. I eventually said no. When I got this far in some of my previous testing, I just assumed that the stars were still not aligned. That, it turns out, was a faulty assumption. It was at this point I turned to the LiveCode forums and the very helpful community of LiveCode programmers.

LiveCode Forums to the Rescue

I quick found this forum thread in the "iOS Deployment" topic area: https://forums.livecode.com/viewtopic.php?t=30859

A LiveCode developer named Panos offered the advice of opening the simulator first in Xcode and to wait until the simulated iPhone shows the home screen. Then, go back to LiveCode and try to test the app. Well, that worked! Here are the step to do just that:

  • Right-click on the Xcode app icon in the Macintosh's dock.
  • Choose: "Open Developer tool > Simulator"
  • Make sure the simulated iPhone boots all the way to the home screen.
  • Then, and only then, choose to "test" your app on the simulator from within LiveCode.

Here's a screen shot:


Yeah, pretty ugly, but the most important thing is that the app export and open successfully in the simulator.

A key point to be made and emphasized here is all of the right software was aligned the first time I saw the simulator launch. It was just that the simulated phone wasn't "turned on" yet. Maybe if I kept clicking "yes" to all of those message it would eventually have worked, but who knows. I'm just happy I now know what to do in order to move on.

Time, Finally, to Begin Revising the App for Mobile

Yes. I am finally ready to start the work to revise my app for the mobile platform.

I'll end by including some links to two other useful web pages:

A final good "lesson learned" worth sharing is to search the LiveCode Forums earlier in the troubleshooting process.




Monday, November 23, 2020

My Toothbrush Timer: On the Cusp of Going Mobile

A couple of days ago I had a few extra minutes in the morning to work on my app. I thought I would quickly add the option to pause the timer. This sounded simple enough and I figured I could add this feature in 15 or 20 minutes. Well, about three hours later I had it more or less figured out. You just never know where app development will take you.

The concept sounds simple enough - just stop everything with one click and restart it all with another click. But, this proved to be a more thornier challenge than it would seem at first glance. It stems from the fact that I'm using LiveCode's function the seconds as the way to keep the timer and the intervals accurate. (I explained how the seconds works in LiveCode in my previous blog posting. I recommend reading that first before continuing here.)

Here's a short explanation:

When the timer begins it saves the current time (in seconds) in a variable. For simplicity sake, let's just say that the seconds is equal to 0. The app then continually checks the current time and puts those seconds into the display. When the Stop button is pressed, the current time at that moment is saved into another variable and the timer is stopped. Of course, the seconds - time, in other words - just keeps going. When the Start button is pressed to start the timer up again I can't just restart the timer based the seconds at that moment because the time elapsed includes the time that the timer was paused. So, I have to subtract the paused time from the total time and only show the "running" time in the display. Here's the solution I programmed into the app:

0 seconds: Start the timer

50 seconds: Pause the timer. At this point, 0:50 remains on the display.

150 seconds: Start the timer back up. 150-50 is 100 seconds. So the display needs to show the following:

Current Time (150) - Original Starting Time (0) - Total Time Paused (100) = Total timer seconds to be displayed

And, every time the user pressed the Stop button, the total time paused must be increased accordingly.

All I can say is that I couldn't get it to work for about three hours. Somewhere my logic was failing me. In the end, I think the problem was that I had this algorithm sitting in the wrong place, namely inside the LloydStopWatch procedure I explained in my previous post. After I moved it and a few more tweaks, things seemed to work, more or less. I say "more or less" because there is still a small bug. Somehow one second is sometimes added to the total time elapsed. The problem is minor, but annoying. Here's what can happen. Imagine the timer with two intervals, each two seconds apart. The first interval is signaled with a bell and the second with chimes. So, the pattern is a timer going off every two seconds, first with a bell and then with chimes. So, an alert should be given only when an even number appears on the display. However, sometimes when pause is pressed, the alert sounds moves to odd numbers instead, but at two second intervals. It seems to resemble a kind of "rounding" error, but I can't find the source. So, after those three hours, I decided to just "let it go."

Displaying the Time Correctly

I made one other improvement to the app, namely showing the timer not just in seconds, but in minutes and seconds using the conventional display of Minutes:Seconds. This was a fun challenge to program. So, consider that a total of 147 seconds have gone by. What should the display read? A quick mental calculation results in 2 minutes and 27 seconds, or 02:27. That same mental calculation is shown in this code:

1:    put trunc(varTimeElapsed/60) into varDisplayMinutes  
2:    put (varTimeElapsed)-(varDisplayMinutes*60) into varDisplaySeconds  
3:    if the length of varDisplaySeconds < 2 then put "0"&varDisplaySeconds into varDisplaySeconds  
4:    put varDisplayMinutes&":"&varDisplaySeconds into line 1 of field "my time"  

In line 1, we divide the total seconds elapsed (varTimeElapsed) by 60. Dividing 147 by 60 gives you 2.45. Well, we just care first about the number of minutes. The function trunc truncates, or chops off, any decimal that remains. So, we are left with just two. That goes into the variable varDisplayMinutes.

Now we have to figure out the remaining number of seconds. All we need to do is take the total seconds elapsed (again, varTimeElapsed) and subtract from it the number minutes we just calculated.  Of course, we have to convert those minutes back into seconds first (varDisplayMinutes*60). The result is line 2: 147-120 = 27. We put the 27 into the variable varDisplaySeconds.

Lines 4 pretties everything up before displaying the time elapsed in the field "my time." Using the concatenation symbol "&" we stitch together minutes, a colon in the middle, followed by the seconds.

Adding a Fourth Interval Option and Alert Sound Options

In my last blog post, I alluded to the fact that having at least four intervals is preferred. So, this version of the app added that as well. I also had some fun finding and adding a menu of alert sounds. I downloaded several sound effects using the .wav sound format from this web site:

https://www.wavsource.com/sfx/sfx.htm

Here's a screen shot of the current version of the app paused after 97 seconds:



One More Important Step to Go

The app works well enough for me to brush my teeth, except for one thing. I don't want to put my laptop on the bathroom sink. So, the obvious next step is to "go mobile." This will require quite a few changes to the app's interface, such as replacing all of the text input fields with selector wheels. 

I also will need to move the app to my iPhone for testing. I am not looking forward to this. It will first require me to relearn how to create a "provisioning profile" in my Apple Developer Account using my iPhone's serial number and then relearning how to get the app from my computer to the iPhone. I've done it before and I'm sure I can do it again. But, it's been a few years and my memory of doing it isn't very pleasant.

Thursday, November 19, 2020

My Toothbrush Timer: Adding Initial Functionality

I've made progress on this app over the past few weeks, despite only being able to work on it now and then. As usual, my progress in building the app is outpacing my blogging about its construction. So, this blog post is at least the first of two in an effort to catch up. Here I give a report of the design of the app as of about two weeks ago.

Here is a screenshot of the app while running:


One obvious thing I need to work on is showing the time elapsed in the format of minutes:seconds. Using the example of brushing my teeth for 2 minutes, these intervals alert me half-way through (to switch from lower to upper teeth), then a warning 50 seconds later that I only have 10 seconds left, followed by the 2 minute bell. Really, this last alert is not necessary because the toothbrush I use just turns off automatically at 2 minutes. If I wanted to switch between lower and upper every 30 seconds, then just one interval setting of 30 would be needed. In the example of giving myself alerts while making a 12-minute conference presentation - a typical time limit - I would likely add alerts at intervals of 6 minutes (half-way point), 4 minutes (the 10-minute mark), 1 minute (just one minute remaining), and 45 seconds (to give myself 15 seconds to thank everyone and get off the stage). Of course, that would require four intervals, not three, so I would need to add one more interval to the app. (Hint hint: That's a clue about what's coming up in next blog post.)

Telling the Time in LiveCode with the Seconds


Having an accurate timer is fundamental to many applications. The best way to develop a timer in LiveCode is with the function the Seconds. My timer app starts by checking to see what the time is now in seconds. As I write this, it is 7:48:11 am on Wednesday, November 18, 2020. The idea is to make that the starting time when the Start button is pressed. Then, all timers are checking to see how much time has passed since then. So, if the first timer is set to go off 30 seconds later, the time will need to be 7:48:41. But if you think about it, this standard format for telling time is based on a past starting point using an arbitrary system we call the Julian calendar. Today's date and time uses as its starting point the likewise artibrary big "changover" from Before the Common eEa (BCE) to the Common Era (CE), which is just the secular version of BC and AD. The key word there is "arbitrary." Really, any starting point would do as long as we all recognize and accept it as the starting point. In LiveCode, the seconds is based on a similar starting point called the eon, defined in the LiveCode dictionary as follows: 
LiveCode uses midnight, January 1, 1970 as the start of the eon. Date and time functions are computed from that date.
So, the function the seconds gives you the current time in seconds since since very arbitrary starting point. As I write this sentence, the current time, in seconds, according to LiveCode is 1605704460. But now, as I write this sentence the time is 1605704492. So, if you subtract the first from the second you get 32 seconds. That interval is the basis for how my app works.

Doing Two (or More) Things at Once

The important thing to note is that while the timer is running, the app is constantly checking to see if the user presses any buttons. In contrast, many apps work by waiting until the user presses a button to perform a task. The app does the task, then waits for the user to press another button. If the task takes a long time to complete, the user has to wait until the task is completed before any other buttons can be pressed. Doing one thing at a time versus doing multiple things at the same time may sound like a subtle difference, but it requires a special programming technique to pull this off.

Here is the code for the app's engine called MainEventLoop, a procedure I wrote to run everything in the background while the user chooses timing intervals and clicks buttons to stop and stop the app's timer:

1:  on MainEventLoop  
2:    if varRunMainLoop is true then  
3:     lock screen  
4:     put the seconds into varSecondsEnd  
5:     LloydStopWatch  
6:     unlock screen  
7:    end if  
8:    put the pendingMessages into tMessages   
9:    repeat for each line aLine in tMessages   
10:     if item 3 of aLine is "mainEventLoop" then cancel item 1 of aLine   
11:    end repeat    
12:    send "mainEventLoop" to me in 50 milliseconds  
13:  end MainEventLoop  
14:
15:  on LloydStopWatch    
      The magical code goes here
16:  end LloydStopWatch  

A procedure called MainEventLoop just runs and runs by looping forever while the app is running. This works by having it "call itself" in line 12 in a very short amount of time (i.e. 50 milliseconds). I know, that's a very weird idea. It's usually very hard for new programmers to grasp. I know it was for me. In that short amount of time, it completes any assigned task while "watching" to see if the user does something, such as clicking a button. There is nothing special about 50 ms. It could be called in one second, but 50 ms is plenty of time for the app to do its work.

Another procedure, LloydStopWatch, has all of the code that actually computes how many seconds have transpired since starting the timer. This code also keeps track of the intervals and knows when play a chime at the end of each interval. Note that line 5 is where the LloydStopWatch procedure is triggered, but only if the variable varRunMainLoop is true. This variable controls whether the timer is active. If the user clicks the Start button it becomes true. If the user clicks the Stop button, it switches to false.

So, how does one get MainEventLoop to start running. The answer is in the code for the button "Start:"

1:  on mouseup  
2:    put 0 into varTimeCurrentLoop  
3:    put 0 into varIntervalTotal  
4:    put 1 into varIntervalTurn  
5:    put false into varRestartInterval  
6:    put empty into field "notes"  
7:    put empty into field "Interval Totals"  
8:    put empty into varTimeLog  
9:    put field "Iminutes1" into varIminutes1  
10:    put field "Iseconds1" into varIseconds1  
11:    put field "Iminutes2" into varIminutes2  
12:    put field "Iseconds2" into varIseconds2  
13:    put field "Iminutes3" into varIminutes3  
14:    put field "Iseconds3" into varIseconds3  
15:    put true into varRunMainLoop  
16:    put the seconds into varSecondsBegin  
17:    put varSecondsBegin into varSecondsBeginInterval  
18:    MainEventLoop  
19:  end mouseup  

Lines 2-18 initiate all of the main fields and variables, essentially getting them ready for the start of a new timing session. Line 15 sets the variable varRunMainLoop to true and line 18 initiates the MainEventLoop procedure.

Lines 9-14 takes whatever interval values the user had entered and passes them on to unique variables for each for use within the LloydStopWatch procedure.

Line 16 essentially checks the current time and becomes the moment in time from which the time elapsed is figured. This value is held by the variable varSecondsBegin. Line 4 in the MainEventLoop procedure continually checks the current time, puts it in a variable named varSecondsEnd, then calls the LloydStopWatch procedure. The time elapsed in seconds is simply the difference between these two values. This simple subtraction problem is happening every 50 ms.

Interestingly, I first put the line "put the seconds into varSecondsEnd" into the LloydStopWatch procedure. But, I experienced some weirdness in the operation of the app. After the app had been running for about 10 minutes, the app started skipping a bunch of seconds which, of course, ruined the reliability of the app. I really couldn't figure out why. I tried all sorts of programming hacks, but none worked. So, in a bit of desperation, I moved this line to the MainEventLoop procedure and it solved the problem. All I can say I had a hunch it would work. I think the problem had something to do with the recursive nature of this MainEventLoop procedure. I've experienced weirdness in this coding technique in previous apps. I actually asked for help in one of the LiveCode forums a couple of years ago about this. Lines 8-11 and the lock/unlock screen code were suggested to me by an expert LiveCode user which solved the problem for me back then. So, I've continued to use this code anytime I use this looping technique. In short, you start building up a backlog of calls for the MainEventLoop procedure and eventually all hell breaks loose. Lines 8-11 clear out all previous calls for the procedure.

Finally, the timer stops when the user presses the "Stop" button - here is the code for the Stop button:

1:  on mouseup  
2:    put false into varRunMainLoop  
3:  end mouseup  

By changing the value of varRunMainLoop to false, it stops the loop within MainEventLoop that had been triggering the LloydStopWatch procedure.

Short List of Things to Do

Actually, the list is quite long, but here are some of the obvious revisions and updates that are needed:

  • Add a fourth timer interval
  • Change the elapsed time format
  • Allow the user to pause the timer
  • Add a choice of alert sounds
  • Add a mobile interface design, including spinner wheels to select time intervals and a vibrate option for alerts