About 150 airports in the U.S. have TSA PreCheck, an expedited security line for pre-screened travellers. If you fly often, you’ll find it very convenient as the line’s usually short, and you can forego having to remove your shoes, jacket, and belt, as well as having to separate your laptop and “3-1-1 liquids” from the rest of your carry-on luggage. There is a price to be paid for this — a registration fee, along with providing the U.S. Customs and Border Protection agency enough info to do a background check on you, and there’s an interview as well. If you’re an American or Canadian with a NEXUS card, you’re already in the TSA PreCheck program.
The app would make a good “iPad programming 101” assignment; a newbie developer should be able to complete it in a day, and someone with even a couple of months’ of experience should be able to complete it in an hour.
Developer Kevin Burke wondered how much the TSA paid to have the app developed. As a government agency, they’d have put out an RFP to companies that do contracts for government work, and none of them come cheap. He filed a Freedom of Information Act request, and mirabile dictu, he got some answers in the form of two documents:
It turns out that the $1.4 million dollars covers the cost of a larger project, which includes other mobile applications, software to support an “enhanced staffing model” for the TSA, and all the project management that such an undertaking requires.
With that in mind, it’s time to fire up Xcode and start on the path to that sweet, sweet government contract money. Why should IBM have all the fun?
If you’re new to iOS programming or even programming in general, the TSA Randomizer app is a good starter project. You might want to try writing it on your own before following the steps below. Here’s a hint that should help — you’ll need to know how to do these things in order to write the app:
How to generate a random number
How to display an image on the screen
How to change the contents of an image on the screen
How to know when the user has tapped on the screen
How to write the app
Open up Xcode and do the File → New → Project… dance. As you may have already suspected, you’re going to specify that this is a Single View Application:
Give the project a name (I called mine TSARandomizer), and select iPad from the Devices drop-down menu:
Once you’ve chosen a place to save the project and it’s been written to disk, you’ll be taken to the General page for your project. Under Device Orientation, make sure that Portrait is checked and that all other orientations are unchecked:
Click the screenshot to see it at full size.
Edit ViewController.swift and change its contents to the following:
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var arrowImageView: UIImageView!
let leftArrowImage = UIImage(named: "arrow-left")!
let rightArrowImage = UIImage(named: "arrow-right")!
override func viewDidLoad() {
super.viewDidLoad()
let tapGestureRecognizer = UITapGestureRecognizer(target:self, action:#selector(arrowImageViewTapped))
arrowImageView.userInteractionEnabled = true
arrowImageView.addGestureRecognizer(tapGestureRecognizer)
displayRandomizedArrow()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func arrowImageViewTapped() {
displayRandomizedArrow()
}
func displayRandomizedArrow() {
if arc4random_uniform(10) < 5 {
arrowImageView.image = leftArrowImage
}
else {
arrowImageView.image = rightArrowImage
}
}
}
This is all the logic needed to make the app work. The displayRandomizedArrow method is currently set so that 50% of the time, the left arrow is selected, and the other 50% of the time, the right arrow appears. You can adjust this to taste.
Time to build what little user interface there is. Open Main.storyboard and put an Image View control on the view. This will contain the arrows. We want it to fill up the screen, so set its constraints so that all its sides are 20 pixels from the view:
Click the screenshot to see it at full size.
In our code, we have an outlet variable named arrowImageView. We need to connect it to the image view we just added. Select the view and open the Connections Inspector. Drag from the circle to the right of New Referencing Outlet to the View Controller icon above the view, as shown in the screenshot below.
Click the screenshot to see it at full size.
Select arrowImageView from the pop-up menu that appears.
Click the screenshot to see it at full size.
Finally, we need to add the arrow images to our app: arrow-left.png and arrow-right.png. Open Assets.xcassets and drag the left-arrow and right-arrow images into it.
Click the screenshot to see it at full size.
Fire it up in the simulator or deploy it to your iPad, and start bidding on some TSA RFP’s — you’ve got a functioning TSA randomizer app!
small professional teams (5 or fewer users in an organization that isn’t considered an “enterprise”, which Microsoft defines an organization with more than 250 PCs or makes more than US$1 million in annual revenues),
use in on-line or in-person classroom training and education, and
use in academic research projects
If Windows is your development platform, Xamarin’s cross-platform mobile dev tools come baked into all editions of Visual Studio, even the free-as-in-beer Community Edition, which you can download from the Visual Studio page. If your preferred development platform is Mac OS, Xamarin Studio, a full-featured stand-alone IDE that borrows a few tricks from Visual Studio. In this article, I’ll take you on a quick tour of Xamarin Studio Community, as seen on my MacBook Pro, iPhone 6S, and Moto G running Android Marshmallow.
The installation process is pretty straightforward, and the length of time it takes will depend largely on your internet connection speed. Here’s a screenshot that I took of the installer at the start of the process:
Once installed, I did the oh-so-familiar File → New → Solution… dance and was presented with this window:
Note the options offered in the left column:
Cross-platform development, which you can do in one of two ways:
Using Xamarin.Forms, a “universal” UI framework that allows you to target a single cross-platform UI that tries to be a native as possible on both Android and iOS, and
a more “traditional” way, where you write Android- and iOS-specific UI code while writing common logic that both with use
iOS apps,
Android apps,
Mac applications, and
“other” applications, which include:
console projects,
GTK# 2.0 projects,
NUnit libraries,
F# tutorials,
ASP.NET and ASP.NET MVC projects
For this walkthrough, I’m going to go with Xamarin.Forms to write something to deploy to both Android and iOS. When you do this, you’re presented with a couple of configuration dialogs, shown below. I’m going to name my project Magic8Ball…
Once past the configuration dialogs, you’re presented with your project. Xamarin Studio’s interface is a pretty standard IDE: solution tree in the left column, editor in the upper right-hand quadrant, and debug and console panes below the editor:
If you look at the left-hand solution tree column, you should notice that it’s divided into three parts:
Magic8Ball, the main project
Magic8Ball.Droid, the Android-specific parts of the solution, and
Magic8Ball.iOS, the iOS-specific parts of the solution.
Right now, Magic8Ball.iOS is marked in bold, which indicates that it’s the startup project. If you were to run the app right now with an iOS simulator as the target, here’s what you’d see:
Left-clicking on Magic8Ball.Droid and selecting Set As Startup Project makes it the startup project…
…and here’s what it looks like on the emulator. Note that Xamarin’s Android projects, by default, use a dark color scheme (which you can change with a simple configuration tweak):
In the newly-created “Hello World” template that you get when you first create a Xamarin.Forms solution, everything happens in the Application class instance. In the case of my app, that instance lives in Magic8Ball.cs. Not how the entire UI is set up in the constructor, App():
using System;
using Xamarin.Forms;
namespace Magic8Ball
{
public class App : Application
{
public App ()
{
// The root page of your application
MainPage = new ContentPage {
Content = new StackLayout {
VerticalOptions = LayoutOptions.Center,
Children = {
new Label {
XAlign = TextAlignment.Center,
Text = "Welcome to Xamarin Forms!"
}
}
}
};
}
protected override void OnStart ()
{
// Handle when your app starts
}
protected override void OnSleep ()
{
// Handle when your app sleeps
}
protected override void OnResume ()
{
// Handle when your app resumes
}
}
}
I changed the code to pass off the UI creation to a view class that I’m calling MainPage:
using System;
using Xamarin.Forms;
namespace Magic8Ball
{
public class App : Application
{
public App ()
{
// The root page of your application
MainPage = new MainPage();
}
protected override void OnStart ()
{
// Handle when your app starts
}
protected override void OnSleep ()
{
// Handle when your app sleeps
}
protected override void OnResume ()
{
// Handle when your app resumes
}
}
}
I decided to create a class called EightBall to handle all the predictions. Using File → New → File…, I was presented with the New File dialog, where I selected General on the left-hand column and Empty Class in the center column:
Here’s the contents of the file as given to me by Xamarin…
using System;
namespace Magic8Ball
{
public class EightBall
{
public EightBall ()
{
}
}
}
…and here it is once I’d filled it in:
using System;
using System.Collections.Generic;
namespace Magic8Ball
{
public class EightBall
{
List<string> answers = new List<string>() {
"Yes.",
"Sure thing.",
"But of course!",
"I'd bet on it.",
"AWWW YISSS!",
"No.",
"Nuh-uh.",
"Absolutely not!",
"I wouldn't bet on it",
"HELL NO.",
"Maybe",
"Possibly...",
"Ask again later.",
"I can't be certain.",
"Clouded by the Dark Side, the future is."
};
Random randomAnswerSelector = new Random();
public string getAnswer() {
return answers[randomAnswerSelector.Next(answers.Count)];
}
}
}
With the model class done, it was time for a view class, created with File → New → File… again. This time, I selected Forms from the left-hand column, and Forms ContentPage from the center column, specifying that I wanted a ContentPage, an onscreen container for containing a single view. It’s often used to represent a “screen” in an app:
Here’s the contents of the file when initially generated by Xamarin…
using System;
using Xamarin.Forms;
namespace Magic8Ball
{
public class MainPage : ContentPage
{
public MyPage ()
{
Content = new StackLayout {
Children = {
new Label { Text = "Hello ContentPage" }
}
};
}
}
}
…and here it is once I’ve defined my user interface and added a reference to the EightBall class:
using System;
using Xamarin.Forms;
namespace Magic8Ball
{
public class MainPage : ContentPage
{
EightBall eightBall = new EightBall();
public MainPage ()
{
var layout = new StackLayout {
Padding = 20,
VerticalOptions = LayoutOptions.Center
};
var answerButton = new Button {
Text = " I need an answer! ",
BackgroundColor = Color.Blue,
TextColor = Color.White,
FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(Button)),
FontAttributes = FontAttributes.Bold,
HorizontalOptions = LayoutOptions.CenterAndExpand
};
var answerLabel = new Label {
Text = " ",
FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
FontAttributes = FontAttributes.Bold,
HorizontalOptions = LayoutOptions.CenterAndExpand,
XAlign = TextAlignment.Center
};
answerButton.Clicked += (object sender, EventArgs e) => {
answerLabel.Text = eightBall.getAnswer();
};
layout.Children.Add (answerButton);
layout.Children.Add (answerLabel);
Content = layout;
}
}
}
…and that’s it — we have a basic “Magic 8-Ball” app! Here’s what it looks like running on iOS 9…
Screenshot taken on my iPhone 6S running iOS 9.2.
…and here’s what it looks like running on Android Marshmallow:
Screenshot taken on my Moto G (second generation) running Android Marshmallow (6.0).
Charles Petzold, the guy who literally wrote the book on Windows development, was commissioned to write a book on building cross-platform mobile apps with Xamarin.Forms. At 27 chapters and nearly 1,200 pages, Creating Mobile Apps with Xamarin.Forms is the most complete document on the topic, and it’s free-as-in-beer to download. You’ll definitely want to get your hands on this.
For those of you looking for an online course, edX is offering a course called Introduction to Xamarin.Forms. It’s an introductory-level course, and is available for free. However, if you’d like to get a “Verified Certificate” for completing the course, you can apply for one for a mere $25.
The two videos below, shot at Microsoft’s recent Build 2016 conference, show more about Xamarin and its tools in action. Here’s the shorter one (27:19 long)…
…and here’s the longer one, running at one hour, which features a complete session from the conference, Cross-Platform Mobile with Xamarin:
Weather apps are great programming exercises, as they combine a number of different features, including fetching information from online sources, and geolocation. In this series, we’ll build a weather app from scratch, starting with a simple, no-user-interface, only-developers-could-love, bare-bones weather app, and over time, turn it into something better.
This first article looks at where we’ll get the weather data for our app and signing up for a free subscription to that weather source. We’ll then retrieve weather data from that source, first manually, then programatically. At the end of this article, you’ll have a very basic weather app; it won’t have a user interface, and the way it presents information is more developer-friendly than user-friendly, but it will perform a key task: retrieve the current weather for a specified location.
Step 1: Get an OpenWeatherMap API key for current weather data
There are a number of weather APIs to choose from, and for this tutorial, we’ll use OpenWeatherMap’s API. It’s simple to use, and you can make up to 60 calls a minute on their free API subscription plan, which should be more than enough for testing purposes and personal use.
If you go to OpenWeatherMap’s API page (pictured below), you’ll see that they offer all sorts of weather data. For this app, which answers the question “What’s the weather like right now for a given place?”, we’ll use their current weather data API. Click on the API doc button under Current weather data:
and you’ll be taken to this page. Under the Free column, click the Get API key and Start button:
which will take you to yet another page. Click the Sign up button…
…which takes you to a page where you provide a little info:
Once you’ve submitted your info, you’ll have created an OpenWeatherMap account. You’ll be sent to a page that shows your API key (pictured below). If you ever need to look up this hey again, you can log into the site and click on the Home link near the upper right-hand corner of the page, which will take you to your account information, which includes your API key.
Copy the API key, and get ready to use it in the next step!
Step 2: Test your API key using your browser
OpenWeatherMap’s current weather data API accepts calls in the format shown below:
CityNameGoesHere is replaced with the name of the city whose current weather data you want to fetch, and
YourAPIKeyGoesHere is replaced with your API key.
For example, if you want the weather for Tampa, and your API key is abcdef1234567890 (don’t bother using this key; it’s not real and for example purposes only), you’d use this call:
Try constructing your own call, using the format above, along with Tampa for the city and your API key after the part that goes APPID=, and enter it into your browser’s address bar. You should be taken to a very plain page displaying something that looks something like the text below:
The data that OpenWeatherMap returns is a JSON dictionary. Its keys are:
coord: A dictionary containing the geographic coordinates for the weather data, with the following keys:
lon: Longitude, in degrees
lat: Latitude, in degrees
weather: An array that usually contains a single dictionary containing the qualitative data about the weather. The dictionary contains the following keys:
main: The general category for the current weather. This is usually a single word such as Clear, Clouds, Rain, Thunderstorm, Snow, and so on.
description: A slightly longer description of the current weather. Where main may specify Clouds, this value may be few clouds, broken clouds, scattered clouds, etc.
base: This is described in the API documentation as an “internal parameter” and probably useful only to OpenWeatherMap’s developers.
main: A dictionary containing the quantitative data about the weather. The dictionary contains the following keys:
temp: The current temperature, expressed in Kelvin by default.
pressure: Atmospheric pressure, expressed in hPa (hectopascal, which is equivalent to 1 millibar). This is the pressure at sea level, if there’s no data for the atmospheric pressure at sea level or ground level.
humidity: Humidity, expressed as a percentage.
temp_min: Minimum temperature at the moment — the deviation from the current temperature, which you’ll find in large metropolitan areas, expressed in Kelvin by default.
temp_max: Maximum temperature at the moment — the deviation from the current temperature, which you’ll find in large metropolitan areas, expressed in Kelvin by default.
sea_level: Atmospheric pressure at sea level, expressed in hPa (hectopascal, which is equivalent to 1 millibar).
grnd_level: Atmospheric pressure at ground level, expressed in hPa (hectopascal, which is equivalent to 1 millibar).
wind: A dictionary containing data about the wind, with the following keys:
speed: The wind speed, expressed in meters per second by default.
deg: The direction that the wind is coming from, expressed in degrees.
clouds: A dictionary containing data about cloud cover, with the following keys:
all: The amount of cloud cover, expressed as a percentage.
dt: The time when the weather data was provided, in UTC, formatted as Unix time (number of seconds since January 1, 1970, 00:00:00 UTC).
sys: A dictionary containing system information, as well as a number of useful items. It contains these keys:
type: This is described in the API documentation as an “internal parameter” and probably useful only to OpenWeatherMap’s developers.
id: This is described in the API documentation as an “internal parameter” and probably useful only to OpenWeatherMap’s developers.
message: This is described in the API documentation as an “internal parameter” and probably useful only to OpenWeatherMap’s developers.
country: The two-letter ISO country code for the weather data’s location. Useful when you want to be sure that you’re getting the weather for St. Petersburg in the U.S. or in Russia.
sunrise: The time when the sun will rise at the weather data’s location, in UTC, formatted as Unix time (number of seconds since January 1, 1970, 00:00:00 UTC).
sunset: The time when the sun will set at the weather data’s location, in UTC, formatted as Unix time (number of seconds since January 1, 1970, 00:00:00 UTC).
id: OpenWeatherMap’s internal ID number for the city corresponding to the weather data’s location.
name: OpenWeatherMap’s internal name for the city corresponding to the weather data’s location.
cod: This is described in the API documentation as an “internal parameter” and probably useful only to OpenWeatherMap’s developers.
Once you’ve confirmed that you can manually get current weather forecasts using your API key and browser, let’s do it programatically with an app.
Step 3: Create a basic app to get the data from OpenWeatherMap
Start a new project by doing the File → New → Project… dance and then selecting Single View Application.
Our first iteration won’t have any user interface. It’ll simply try to communicate with OpenWeatherMap, and print the results to the console. Once we’ve confirmed that we’ve got our API communications code working, we’ll add a simple user interface, and I’ll leave making it fancier as an exercise for you.
Many tutorials are happy to stick all the code in a view controller in order to keep the tutorial simple. While it may make writing the tutorial simpler, I think it results in cluttered view controllers and teaches bad programming habits. I’d much rather keep the code in the view controller limited to handling user interactions and put what some people call the “business logic” of our app — in this case, the code that communicates with OpenWeatherMap and extracts weather data from what it sends back — into a separate model class.
With that in mind, we’ll create a new class by selecting File → New → File… from the menu bar, then selecting Swift File:
Give the new file the name WeatherGetter.swift. It will be the home of a class we’ll call WeatherGetter, which will house the code to communicate with OpenWeatherMap and provide the weather data that the view controller will eventually use.
Change the contents of WeatherGetter.swift so that it contains the following code:
import Foundation
class WeatherGetter {
private let openWeatherMapBaseURL = "http://api.openweathermap.org/data/2.5/weather"
private let openWeatherMapAPIKey = "YOUR API KEY HERE"
func getWeather(city: String) {
// This is a pretty simple networking task, so the shared session will do.
let session = NSURLSession.sharedSession()
let weatherRequestURL = NSURL(string: "\(openWeatherMapBaseURL)?APPID=\(openWeatherMapAPIKey)&q=\(city)")!
// The data task retrieves the data.
let dataTask = session.dataTaskWithURL(weatherRequestURL) {
(data: NSData?, response: NSURLResponse?, error: NSError?) in
if let error = error {
// Case 1: Error
// We got some kind of error while trying to get data from the server.
print("Error:\n\(error)")
}
else {
// Case 2: Success
// We got a response from the server!
print("Data:\n\(data!)")
}
}
// The data task is set up...launch it!
dataTask.resume()
}
}
You’ve probably figured out that you should replace the string YOUR API KEY HERE with the API key that you got in step 1 and used in step 2. You’re probably eager to see this code in action, so we’ll defer the explanation of how it works until we’ve got the app up and running.
We now need to create an instance of the WeatherGetter class and call its getWeather() method. We’ll do it in the viewDidLoad() method of the view controller:
Now that we’ve got the WeatherGetter class and a way to instantiate and use it, let’s run the app. Remember that right now, it doesn’t have a user interface, and outputs everything using print statements, which will appear in the output pane of XCode’s debug area:
Step 4: Run the app (and tweak it)
Run the app. You’ll see some disappointing text in the debug area’s output pane that will be similar to this:
2016-04-02 09:03:42.316 SimpleWeather[51150:6547853] App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file.
Error:
Error Domain=NSURLErrorDomain Code=-1022 "The resource could not be loaded because the App Transport Security policy requires the use of a secure connection." UserInfo={NSUnderlyingError=0x7f89ab201e20 {Error Domain=kCFErrorDomainCFNetwork Code=-1022 "(null)"}, NSErrorFailingURLStringKey=http://api.openweathermap.org/data/2.5/weather?q=Tampa&APPID=***YOUR_APP_ID_HERE***, NSErrorFailingURLKey=http://api.openweathermap.org/data/2.5/weather?q=Tampa&APPID=***YOUR_APP_ID_HERE***, NSLocalizedDescription=The resource could not be loaded because the App Transport Security policy requires the use of a secure connection.}
We’ve just run into a security feature of iOS networking that by default requires HTTP connections to be of the secure and encrypted HTTPS variety. Someday, all HTTP communication will be done as HTTPS, but that day hasn’t come yet.
The first thing we should do is see if we can communicate with OpenWeatherMap using HTTPS. Let’s try changing the URL in WeatherGetter.swift so that it starts with https: instead of http:…
let weatherRequestURL = NSURL(string: "https://api.openweathermap.org/data/2.5/weather?q=\(city)&APPID=\(openWeatherMapAPIKey)")!
If we run the app again, we get a different, but equally disappointing message in the debug output pane:
Error:
Error Domain=NSURLErrorDomain Code=-1004 "Could not connect to the server." UserInfo={NSUnderlyingError=0x7fd6940016c0 {Error Domain=kCFErrorDomainCFNetwork Code=-1004 "(null)" UserInfo={_kCFStreamErrorCodeKey=61, _kCFStreamErrorDomainKey=1}}, NSErrorFailingURLStringKey=https://api.openweathermap.org/data/2.5/weather?q=Tampa&APPID=***YOUR_APP_ID_HERE***, NSErrorFailingURLKey=https://api.openweathermap.org/data/2.5/weather?q=Tampa&APPID=***YOUR_APP_ID_HERE***, _kCFStreamErrorDomainKey=1, _kCFStreamErrorCodeKey=61, NSLocalizedDescription=Could not connect to the server.}
So now we know that OpenWeatherMap doesn’t accept HTTPS connection requests. Luckily for us, we can change the default connection security policy for our app so that iOS allows it to communicate using plain HTTP. We can do this by adding a couple of extra rows to Info.plist:
Click the screen shot to see it at full size.
Info.plist is an XML file that contains configuration information, and it’s included in every iOS project. In order to make it more readable, Xcode presents it in a nice list interface. If you prefer, you can edit it in its plain XML form by right-clicking on Info.plist in the Project Navigator and selecting Open As → Source Code in the menu that appears.
To enable our app to use plain HTTP communication, we want to do two things in Info.plist:
Add a new dictionary item with the key App Transport Security Settings
Add an item to the App Transport Security Settings dictionary with the key Allow Arbitrary Loads and the boolean value YES
Rather than describe how you do this, I made a video of the process that should be pretty easy to follow:
In the end, you should have this as part of your Info.plist:
Once that’s done, don’t forget to change the URL in WeatherGetter.swift so that it starts with http: and not https:…
let weatherRequestURL = NSURL(string: "http://api.openweathermap.org/data/2.5/weather?q=\(city)&APPID=\(openWeatherMapAPIKey)")!
Now try running the app. If you’re connected to the internet and OpenWeatherMap.org’s server is running normally, you should see something like the following in the debug output pane:
At least now we’re getting some data. Let’s make it a little more human-readable. In WeatherGetter.swift, let’s tweak the else clause so it looks like this:
else {
// Case 2: Success
// We got a response from the server!
print("Raw data:\n\(data!)\n")
let dataString = String(data: data!, encoding: NSUTF8StringEncoding)
print("Human-readable data:\n\(dataString!)")
}
Now, when we run the code — and assuming your internet connection and the OpenWeatherMap server are working — the output should look something like this:
As you can see, the stuff after “Human-readable data:” looks like the output you got when you manually got the weather data from OpenWeatherMap in step 2.
Step 5: Play with the app, and keep an eye open for the next installment in this series!
Here’s what your WeatherGetter.swift file should look like…
import Foundation
class WeatherGetter {
private let openWeatherMapBaseURL = "http://api.openweathermap.org/data/2.5/weather"
private let openWeatherMapAPIKey = "YOUR API KEY HERE"
func getWeather(city: String) {
// This is a pretty simple networking task, so the shared session will do.
let session = NSURLSession.sharedSession()
let weatherRequestURL = NSURL(string: "\(openWeatherMapBaseURL)?APPID=\(openWeatherMapAPIKey)&q=\(city)")!
// The data task retrieves the data.
let dataTask = session.dataTaskWithURL(weatherRequestURL) {
(data: NSData?, response: NSURLResponse?, error: NSError?) in
if let error = error {
// Case 1: Error
// We got some kind of error while trying to get data from the server.
print("Error:\n\(error)")
}
else {
// Case 2: Success
// We got a response from the server!
print("Raw data:\n\(data!)\n")
let dataString = String(data: data!, encoding: NSUTF8StringEncoding)
print("Human-readable data:\n\(dataString!)")
}
}
// The data task is set up...launch it!
dataTask.resume()
}
}
…here’s what ViewController.swift should look like…
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let weather = WeatherGetter()
weather.getWeather("Tampa")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
…and if you prefer to edit Info.plist as raw XML, here’s what it should look like:
Try some experiments. Here are a few of my suggestions:
What happens if you run the app in Airplane Mode?
What happens if you change the URL in WeatherGetter to a non-existent one, such as openweathermoop.org?
How would you go about extracting the weather information from the JSON returned by the server?
How would you display the weather information to the user?
In the next installment in this series, we’ll take a step back and cover “iOS/Swift networking 101”, taking a closer look at the various objects used inside the WeatherGetter class.
If you were to send an email with the orange Send + mic drop button instead of the standard blue Send button, your email message gets sent with a couple of key changes:
It adds an animated GIF of the scene from the Minions movie where Bob the Minion, now King of England, ends a royal address with a “mic drop”, a gesture where a speaker, at the end of a performance or speech, drops the microphone on the ground as a way of saying “I was just so impressive, there’s no point in anyone speaking after me” or “I just won this debate or rap battle”, and
because the feature is supposed to give you the last word, it ensures that the sender NEVER sees any reply to the email.
When used jokingly among friends while discussing a trivial topic — say, a debate about the 1980s fantasy film Ladyhawke, just like the one in the novel Ready Player One — this feature can be pretty funny. It may strain relations if you use it to reply to a pro-Donald Trump mass email that your ultra-conservative vaguely racist aunt sent out to her large family cc: list.
The problem with the Send + mic drop button is that it’s all too easy to use it by accident. It’s located in about the same place as the plain Send button. The different color should be a dead give-away that something’s different, but given that web apps, especially Google web apps, change their user interfaces quite often, it’s all too easy for someone to click on the new button with unexpected, and possibly unpleasant results.
Here’s one complaint posted to the Gmail Help Forum, posted by someone identifying themselves as Allen Pashby:
It reads:
Thanks to Mic Drop I just lost my job. I am a writer and had a deadline to meet. I sent my articles to my boss and never heard back from her. I inadvertently sent the email using the “Mic Drop” send button.There were corrections that needed to be made on my articles and I never received her replies. My boss took offense to the Mic Drop animation and assumed that I didn’t reply to her because I thought her input was petty (hence the Mic Drop). I just woke up to a very angry voicemail from her which is how I found out about this “hilarious” prank.
Google have since disabled their April Fool’s feature and have updated their blog post announcing it to start with the following paragraph:
UPDATE: Well, it looks like we pranked ourselves this year. 😟 Due to a bug, the Mic Drop feature inadvertently caused more headaches than laughs. We’re truly sorry. The feature has been turned off. If you are still seeing it, please reload your Gmail page.
Lessons you can learn
While I’ve found it instructional to learn from my own mistakes, it’s far more enjoyable and less stressful and embarrassing to learn from others’ mistakes. Here’s what you can take away from Google’s mic drop snafu:
When you’ve got a platform with a large number of users (the number that gets bandied about when talking about total Gmail users is one billion), you’re going to have a large number of use cases. Many people use Gmail as their primary business email tool, and there’s a good chance that a number of business conversations and relationships were derailed, at least temporarily, by a mic drop message.
People expect consistency, especially from tools they use daily, and when that consistency is broken, the experience is degraded. Remember what happened when Microsoft removed the start menu in Windows 8?
When you change your user interface often, people stop noticing user interface changes.As designer Douglas Bowman wrote when he resigned in 2009, their approach to design often takes a test-driven approach, where they present one group of users with one design, another group of users with an alternate design, and they choose the winning design based on user responses. This approach requires performing little experiments which involve making changes to the interfaces that users see. We’ve become so used to these changes that we often don’t notice them until they’re pointed out. Often, these changes are minor, such as the time they showed different users different shades of blue to see which one of 41 possible shades would perform better. However, in cases where the change in behavior is significant, such as with the mic drop, surprising the user can be problematic.
Warn users when you’re about to perform something “destructive” or radically different. Typically, when you’re about to delete a file, photo, or some other piece of data in an application, you’re presented with an “Are you really sure you want to do this?” message. Google could’ve made a similar message appear after the user pressed Send + mic drop button that explained what was about to happen and offered a way to cancel.
Nobody’s always alert. This feature was introduced at midnight Pacific time (GMT -7), April 1, 2016, which meant that for a lot of users, it was late at night or early in the morning, meaning that a lot of users were either working late or early, when alertness is low. Coupled with the fact that people stop noticing user interface changes when they change so often, this can lead to trouble.
Apple are working on fixing the problem with a 9.3.1 release, but if you’re still on iOS 9.2 and your iDevice asks if you’d like to update to 9.3, follow the advice from Game of Thrones’Syrio Forel and say “Not today!”
How to check which version of iOS your iDevice is running
Here’s how you can tell which version of iOS your iPhone, iPad, or iPod Touch is running. From your Home screen, tap on the Settings icon (pictured above), which will take you to the Settings screen. Yours should look similar to mine:
About halfway down the screen, you should find an item marked General (highlighted in the screen shot above). Tap on it, and you’ll be taken to the General screen:
The version number appears in the Version item (highlighted in the screen shot above), which should appear near the bottom of the screen. That’s an actual screen shot from my iPhone, and you can see that I’m running iOS 9.2.1.
How to temporarily disable JavaScript if you’ve already updated to iOS 9.3
As I wrote earlier, in a world where so many websites make use of JavaScript, Apple’s suggested workaround for people who’ve already installed iOS 9.3 will most likely degrade your web browsing experience. Many websites, especially those with app-like functionality may not work at all. Still, if you must disable JavaScript, here’s how you do it.
Click on the Settings icon, which will take you to the Settings screen. Scroll down until you see the Safari item, then tap on it:
You’ll be taken to the Safari settings screen. Scroll down all the way to the bottom and tap on the Advanced item:
This will take you to the Advanced settings screen for Safari. One of the items you’ll see on this screen is the JavaScript switch, which you should set to the off position (with the switch in its left position, and not showing any color):
Once the iOS 9.3.1 update comes out and the problem with links is fixed, repeat this procedure to turn JavaScript back on.
…as have a few of my first attempts at build-it-yourself furniture, especially the discount stuff, whose instructions and diagrams are even less comprehensible than IKEA’s.