Apple has released iOS 14, and perhaps one of their most exciting - albeit long overdue - new features is the introduction of widgets.

I’ve added some photo, news, calendar, and weather widgets to my home screen and I'm pretty happy with this change. Who knew having more control over your device would be so fun? Well, Android users knew, but that’s a topic for another day.

2020-10-04_09-47-33.png

Although I’m liking my widgets, I wish they were more exciting. I already have a lot of ways to check the weather, see photos and review my day’s activities, etc.

However, what I really care about is ice cream.

Wisconsin is the home of a Midwestern restaurant chain called Culver’s. Culvers has really good burgers and other entrée items, but what they’re really known for is their frozen custard.

Culver’s has a new Flavor of the Day (FOD) every, er, well, every day. While we don’t eat frozen custard every day, we do like to indulge from time to time, especially when we see a FOD that we like. The second most common question in our house (after “Dad, why are you such a nerd?") is “Dad, what’s the Flavor of the Day?”.

Well, I could look up their website and find out that information. Like an animal. But c’mon, this is 2020. This is the year of the iPhone Widget.

This sounded like a job for Scriptable, an iPhone app that lets you create custom applets using Javascript. When iOS widgets were introduced Scriptable added a simple example script (called “News in Widget”) for creating and adding a custom widget to your iPhone’s home screen.

In the provided example, the script calls an API to download articles from a news site, then displays the headlines in a widget.

Unfortunately Culver’s does not have a Flavor of the Day API, so to accomplish this we’re going to have to roll our own, and to do this we’re going to need to do some good old-fashioned web scraping. Also unfortunately, this is probably where I’m going to lose people, because this is going to require writing some server-side code and having a place to host it.

I’m going to use PHP to write my server-side script. This language has a built-in cURL function that makes it easy to grab the contents of a website.

However, once we’ve grabbed the contents of the website, we need to find the information we need. Fortunately, other - smarter - people have tackled this issue and we don’t have to reinvent the wheel. I’m going to use a PHP library called simplehtmldom. Simplehtmldom is really simple: You just extract data from your captured HTML using regular CSS selectors.

So, the next step is to figure out where the data we want is located in our HTML. This can be done by going to the website we want to scrape, opening up the developer tools, and inspecting the HTML elements we’re interested in.

In this case, we see the name of the Flavor of the Day embedded in a <strong> tag that is a child of a div with a specific class. In addition, it would be cool if we could display an image in our widget, so let’s capture the source of the FOD’s image while we’re at it.

culvers_code.png

My flavorScraper.php script is shown below. In the end, it’s pretty simple. It

  1. Includes the simplehtmldom script

  2. Identifies the website we want to scrape

  3. Gets the website’s HTML using function that uses cURL

  4. Parses the HTML using CSS selectors to grab our data

  5. Echos the data as a JSON object

This script was uploaded to a server so it can be called from my Scriptable script.

flavorScraper.php

<?php
// we'll use simple_html_dom.php to parse the contets of our website
// documentation: http://simplehtmldom.sourceforge.net/
include ("path_to_file/simple_html_dom.php");

// the website I want to scrape for data
// this is specific to the location nearest me
$url = "https://www.culvers.com/restaurants/my_town";

$html = new simple_html_dom();
$str = curl($url);
$html->load($str);

$flavorOfTheDay = [];
// the css selector for the name of the flavor of the day
$flavorOfTheDay['name'] =  $html->find("div.ModuleRestaurantDetail-fotd h2 strong")[0]->plaintext;
// the css selector for the image, and this case we're getting the URL from the src attribute. In this case it did not include the "https:" so we need to prepend it to the URL
$flavorOfTheDay['image']= "https:" . $html->find("div.ModuleRestaurantDetail-fotd img")[0]->src;

// return the data in JSON format
echo json_encode($fod);

// use curl to grab the contents of the the website
 function curl($url) {
     // Assigning cURL options to an array
     $options = Array(
         CURLOPT_RETURNTRANSFER => TRUE,  // Setting cURL's option to return the webpage data
         CURLOPT_FOLLOWLOCATION => TRUE,  // Setting cURL to follow 'location' HTTP headers
         CURLOPT_AUTOREFERER => TRUE, // Automatically set the referer where following 'location' HTTP headers
         CURLOPT_CONNECTTIMEOUT => 120,   // Setting the amount of time (in seconds) before the request times out
         CURLOPT_TIMEOUT => 120,  // Setting the maximum amount of time for cURL to execute queries
         CURLOPT_MAXREDIRS => 10, // Setting the maximum number of redirections to follow
         CURLOPT_USERAGENT => "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1a2pre) Gecko/2008073000 Shredder/3.0a2pre ThunderBrowse/3.2.1.8",  // Setting the useragent
         CURLOPT_URL => $url, // Setting cURL's URL option with the $url variable passed into the function
         CURLOPT_HTTPHEADER=>["Cookie: RoadblockSayCheeseCurds=0"]
     );

     $ch = curl_init();  // Initialising cURL
     curl_setopt_array($ch, $options);   // Setting cURL's options using the previously assigned array data in $options
     $data = curl_exec($ch); // Executing the cURL request and assigning the returned data to the $data variable
     //echo "<br><br>data: " . $data;
     curl_close($ch);    // Closing cURL
     return $data;   // Returning the data from the function
 }

Now we just need the Scriptable script.

To create this script I copied their “News in Widget” example and modified it for my needs. It simply:

  1. Calls the flavorScraper.php script from my server

  2. Creates a widget

  3. Takes the JSON returned from flavorScraper.php

  4. Extracts the Flavor of the Day text and image and displays them

FlavorScraper (Scriptable App):

let item = await loadItem()
let widget = await createWidget(item)
// Check if the script is running in
// a widget. If not, show a preview of
// the widget to easier debug it.
if (!config.runsInWidget) {
  await widget.presentMedium()
}
// Tell the system to show the widget.
Script.setWidget(widget)
Script.complete()

async function createWidget(item) {
 
  let imgURL = item.image;
  let w = new ListWidget()
  if (imgURL != null) {
    let imgReq = new Request(imgURL)
    let img = await imgReq.loadImage()
    w.backgroundImage = img
   
  }

  let titleTxt = w.addText(item.name)
  titleTxt.font = Font.boldSystemFont(32)
  titleTxt.textColor = Color.blue()
  titleTxt.centerAlignText()
  // Add spacing below headline.
   w.addSpacer(12);
  // Add spacing below content to center it vertically.
  w.addSpacer()
  return w
}
  
async function loadItem() {
  // the url to the server where I'm hosting flavorScraper.php
  let url = "https://www.myServerSite.com/flavorScraper.php"
  let req = new Request(url)
  let json = await req.loadJSON()
  return json
}

Now, we just need to add a new Scriptable widget to our home screen and tell the widget we want to display the widget from this flavorScraper script.

IMG_5148.png

And, voilà! We now can see the Flavor of the Day at a moment’s notice!

It’s a good start. I should probably play with the font color (I needed something that was visible in night mode - where the background is black - and in daytime mode).

If I find the ambition to improve on this, I might make a short list of my family's favorites, and, if the FOD of the day is a favorite, add something attention-getting such as a different colored background, some additional text, or maybe a few emojis.

Posted
AuthorTodd Zarwell
CategoriesTech

Six months ago, I wrote about my thoughts on the difficulties of teaching kids to write code. There were a few iPad apps that seemed to have potential to introduce kids to programming.

Unfortunately I've had a hard time finding a way to introduce my sons to these apps. After all, it's kind of a slippery slope. I want this to be fun, and I'm afraid if it seems like too much of a chore right off the bat it might be a while before I can try again. Also, when you open up the iPad there are potential distractions just two clicks away. If the app comes off as boring or too educational I'll get a "can I play Minecraft instead?" within minutes.

So, I've been biding my time and waiting for the right moment. Then, lo and behold, we were signing my oldest son up for supper activities this Spring when I saw an appealing option: "Coding with Kodable". Ooh, that sounded interesting. After reading the description I learned that Kodable is another "learn to code" app in the same vein as the ones I mentioned in my blog post last December. Well, of course we signed up for it and my son will be finishing up his "camp" today.

It has gone really well. He did it with other kids his age, so it was fun, and he really did learn core concepts in programming. Today was the first time I dropped him off at his class, so I hung around for a few minutes and watched my son work through the app's puzzles. I was pretty impressed with what I saw.

Now, keep in mind that we're talking about 7 year olds here. They're still learning how to write and spell, so typing in complicated blocks of code is out of the question. The goal is to start with the basics.

What is a computer program? It's a sequential list of instructions that tell a computer what to do. So, that's where Kodables starts. In this case, the instructions are geared towards guiding a "Fuzz" through a maze.  (The optometrist in me can't help but notice this is also a great exercise for laterality / directionality, spatial reasoning, etc). The child simply drags a series of arrows onto the screen to guide their character though its course. Just like in programming, the order of the instructions counts. Also, there can be more than one way to accomplish a task, but there is often a way that involves the least amount of instructions or takes a better approach (in Kodables, the better approach is the one that leads the Fuzz past the most coins). 

The next major concept is conditional statements. A program needs accept input then make a decision as to what to do next. In most programming languages this is done in an IF/THEN statement, eg) IF (weight > 200) THEN diet. In codables the condition the child can apply a color to their arrows that essentially says "if your Fuzz encounters a red square turn down".

Conditional statements on Kodable. The instructions are the arrows in the top left side of the screen. Note the arrow with the red background - this is a conditional statement.

Conditional statements on Kodable. The instructions are the arrows in the top left side of the screen. Note the arrow with the red background - this is a conditional statement.

 

As people improve at coding they learn an important lesson: Don't work any harder than you have to. You oftentimes find yourself writing out the same sets of instructions multiple times. Whenever you find yourself in this situation you have to ask yourself if there's an easier way. Working smart,  not just hard, can result in huge time savings. In programming, two fundamental concepts that can help with this are loops and functions.

Loops can be used when repeating the same task two or more times. Consider the following maze on Kodables:

Loops on Kodable.&nbsp;

Loops on Kodable. 

The instructions would go like this:

  1. go right
  2. go up
  3. go right
  4. go up
  5. go right

That wasn't too bad to type out, but what if we had to do it ten more times? Or a hundred more times? This is where loops come in handy. A lop would essentially say:

  1. go right
  2. go up
  3. repeat the above steps 100 times

Functions are very similar in a lot of ways, except we don't necessarily duplicate the same instructions repeatedly. Instead, we save a set of instructions as a block, or function, that we can use, or "call", at any time. Consider this Kodable maze:

Functions in Kodable. Note the three steps that are included in the function (seen to the right of the curly brackets { } on the right hand side)

Functions in Kodable. Note the three steps that are included in the function (seen to the right of the curly brackets { } on the right hand side)

To get through this maze the instructions would look like this:

  1. go right
  2. at the pink box, turn down
  3. go right
  4. go up 
  5. go right
  6. at the pink box, turn down
  7. go right

Notice any patterns here? Steps 1-3 are identical to steps 5-7. The "programmer" astutely noticed this and made a function. The function is located on the right side by the curly brackets { }, and contains the same block of instructions as steps 1-3 above. So, the new instructions will look like this:

  1.  run the function
  2. go up
  3. run the function

It doesn't look like much, but this is one of hallmarks of efficient coding and can save tons of lines of code, not to mention time,  as programs get longer and longer.

A Kodable bug

A Kodable bug

Another very important feature kids will learn from Kodables is debugging. Coders rarely write a perfect program on the first try. Programming is very much a trial and error process: Write some code, run it, see where it screwed up, rewrite some code, and repeat. Just like real coding, you can run your program and watch each step as it executes to localize where the problem is coming fro. In Kodables there's segments where it purposely gives you a program with errors in it, and actually places a cartoon bug on the maze at the location of the problem. When you fix the programming "bug", your Fuzz rolls over the cartoon bug and squashes it. I know from experience there's nothing more satisfying than squashing those bugs.

In summary, Kodables seems to be a great way to get kids started in programming. It teaches them to look at a problem and break it into little parts, then give the computer stepwise instructions to solve the problem. It teaches concepts such as conditionals, loops, and functions. It also teaches debugging and, as a result, the acceptance of the inevitability of errors and the dedication needed to troubleshoot and fix your bugs.

In the future, kids that want to continue with coding will need to learn a programming language and its syntax. However, in my opinion, learning the basics of programming is much more important. And, after learning one programming language, picking up others is relatively simple once you master the fundamentals.

 

PS My 5 year old came along with me when we dropped my 7 year old off at Kodables camp today. After he saw his big brother solving puzzles on an iPad he wanted to do the same thing. We went to a coffee shop, downloaded the app, and spent an hour and a half "programming". He picked it up very quickly and we had a lot of fun together.



Posted
AuthorTodd Zarwell
CategoriesTech

I've rewritten my contact lens calculator on EyeDock. The new version does a better bob of showing its calculations by displaying information on optic crosses. It's also more flexible with how it accepts keratometry and refraction input. Lastly, I've added a SPE / CPE lens calculator that uses Thomas Quinn OD's nomograms (the same ones we used to create the GPLI calculator). 

Here's a little video introduction. I hope you find it useful!

- Todd


Posted
AuthorTodd Zarwell
appleIIe

I spend a fair amount of time thinking about how to introduce my three young boys to programming. As much as personal computers have evolved over the last 30 years, I feel learning to code has gotten more complicated.

Computers weren't very accessible until about 1976, when Steve Wozniak developed the Apple. My grade school, like just about every other school in the country, got an Apple II that sat at the back of the classroom. Considering that computers had always been room-sized objects that only existed in movies, it seemed like the future had arrived.

It seems kind of crazy now, but there were actually very few things we could do with the computer at that time. We had a couple games, such as Oregon Trail.  We quickly discovered that the Apple II came with a BASIC compiler, so we started to learn how to use it. During breaks between classes, three or four of us would huddle around the computer and type in programs from magazines like Enter (I wrote about my love of Enter a while back). 

So what is different nowadays? Why should learning to code be more complicated in 2014 than it was in 1980? I can think of at least a couple reasons.

Kids are harder to impress. When I was a kid, just hitting letters on a keyboard and seeing them show up on a screen was pretty thrilling. Until then, the only thing I'd every seen in a screen was whatever the TV station decided to show me. Even cooler, typing

10 PRINT "Todd" 

20 GOTO 10

would print my name an infinite number of times. What could be cooler than that?

Today, my kids play with computers all the time. I hate to admit it, but I frequently take a powerful one out of my pocket to keep them entertained when I need a little peace and quiet. They have pretty high standards: an app the prints 

Drew

Drew

Drew

Drew

isn't going to impress them.

Coding is a lot more complicated. Computers can do so much more than they ever could before. However, this adds countless layers of complexity and, as a result, creates a much larger barrier to entry.

When I wrote little programs on an Apple IIe, I just fired up the computer, fired up BASIC, and started typing. Today, it's a little more abstract. For most, the easiest place to start would be to write some javascript and HTML in a text editor and run it in a web browser. However, this still requires multiple steps along which multiple things could go wrong. After this option, things get a lot more complicated: proprietary software, frameworks, fancy IDEs, etc. might be required just to get started, which would likely be out of reach of any kids that don't have a programming- savvy adult to help them out.

I do think things are improving, however. Apple's new Swift programming language includes Playgrounds, which allows newbies to write small amounts of code and immediately see the results.

"Playgrounds make writing Swift code incredibly simple and fun. Type a line of code and the result appears immediately."

Definitely a much easier way to start experimenting with programming concepts.

Maybe it's because I now have small kids and it's on my radar, but it seems like there's a lot more awareness of the need to teach kids to code. The Hour of Code seems to be a great program that has introduced a lot of kids (and the President) to coding.

There are now a number of iPad apps that teach programming (Tynker, Hopscotch, Scratch). For the little kids, this doesn't mean coding - 6 year olds aren't going to write lines of Objective-C. However, they teach the systematic approach to programming: breaking your problem down into tiny little problems and solving each one in an elegant way. In my opinion, this is the most important part of writing code. Once you've got this down it doesn't really matter what language you're learning

And here's something else that just showed up in my RSS feed today: BitsBox, from a couple employees of Google's Sketch:

"Each month a surprise comes in the mail, filled with dozens of programs to type in. Like the computer magazines of the 1980s, we invite kids into learning with irresistible projects."

It's a Kickstarter project, but it sounds like a great way to get kids excited about programming. Kids love getting packages in the mail, after all!

As a dad, and a geek, I'm really excited that there seems to be a renewed emphasis on teaching kids to code

 

Posted
AuthorTodd Zarwell

I just released a new app to the App store, and is now available to download. It's a Park's Three Step app, similar to the one I have on the EyeDock website

It's been a long time since I made a new iPhone app. I wrote the original EyeDock and CLCalcs on a very early version of iOS (it was iPhone OS at the time). Back then, iPhones only had 3.5" screens and iPads didn't exist.

I've opened up XCode and modified those apps a few times since their inception, but mostly just to fix things. It usually worked like this:

  1. new version of iOS comes out
  2. Crap, my app doesn't work!
  3. Hurriedly,  with a thin sheen of sweat on my brow, try to fix app before I got too many complaints

My apps were pretty much like an old watch: I fixed them when they broke, but I broke out in a cold swear whenever I opened them up and looked at the insides.

I decided a few months ago I really need to make some changes to the EyeDock app. I don't like the way it downloads its data, it's not optimized for the 4" iPhone screens, and I really need an iPad version. 

I'd stayed tuned to the developments in iOS and knew that things were evolving rapidly. In light of Apples has added or changed their APIs, in conjunction with the major changes I wanted to make to my old apps, I thought it might be best to relearn the ropes. What better way to do this than to make a new app from scratch.

In conclusion, I bring you the Parks Three Step app. It's my first universal app (it works on both the iPhone and iPad). I too advantage of many of the new things that have come along with the last 3-4 versions of iOS, including parallax, blocks, autolayout, and ARC.  These things may not mean a lot to my non-iOS-developer readers, but this essentially means I'm caught up on the modern way of doing things.

Now time to rebuild that watch.

 

Posted
AuthorTodd Zarwell

There's been a lot of buzz in the iPhone developer world about Rate this App dialog boxes. You know the ones I mean, the ones that pop up and say would you like to review this app? and give you some buttons like yes no or later.
The controversy started with a John Gruber link to a post saying that we should all give one star reviews when they have one of these dialog boxes in the apps. Instapaper developer Marco Arment jumped in with his thoughts, which essentially came down to "don't do it". They continued with some discussion on their podcasts The Talk Show and Accidental Tech Podcast.
However, I think they slightly missed the point on why developers do this. They seem to be under the impression that developers were trying to get as many reviews as possible in an effort to gain the ranking systems in iTunes. 

I am now a little embarrassed to admit it, but I have one of these rating dialogs in my app. My goal, however, was never to get more ratings. My goal was to get good ratings. And by good I don't just mean positive, but also well-thought-out. One of the great things about these popups is that you can choose to show them to the people who are actually using your app by only showing the dialog box after the app has been opened a certain number of times. That way you you'll hopefully get a review from someone who is finding your creation useful. As the app store ratings are riddled with ridiculous comments like "I'm giving this dictionary app 2 stars because it doesn't stream live music", hopefully some informed reviews from experience users will balance things out.

On the other hand, I definitely see the points behind these rants against "rate this app" dialogs. It's a bad user experience. When someone opens your app it's to DO something, and that something is being interrupted. When I added this this "feature" it wasn't a very common practice at the time. Furthermore, I set it to only pop up when the user opened a the new version of the app for the 30th time or so. Since I have made very few updates over the last three years my users have seen this popup at most 2-3 times over the last 3 years.

However, this practice has become a lot more prevalent. Now that so many apps are doing it the cumulative effect is annoying. I think I'll remove this in my next update, but no promises when that will be . . .

 

 

Posted
AuthorTodd Zarwell

Well, I've been pretty quiet lately. Things have been a little busy at home, and I've been spending what free time I have finishing up my new soft lens searches for EyeDock. It's taken me about 18 months (way too long!), but I'm pleased with the results. 

I've written a short page on EyeDock about the new features in these searches so I won't repeat here. I will, however, include the screencast I made introducing the new searches.  

I'd love to hear your feedback! 

 

A new feature for EyeDock's soft lens searches: Enter a refraction and EyeDock will take care of vertexing to the corneal plane, transposing (if needed), and will find appropriate lenses. Even more helpful, it will find the closest available power for the chosen lens and give an estimation of the best potential VA.

Posted
AuthorTodd Zarwell