Category Archives: Apple

Using Apple’s New WeatherKit REST API

In late March 2020 Apple purchased the Dark Sky app, and along with it the Dark Sky API. No longer accepting sign ups the Dark Sky API will go offline on March 31, 2023. If you’re a developer that needs a reliable weather API, Apple is now providing WeatherKit. Typical “Kit” APIs are only available on Apple devices as libraries, but in this case, WeatherKit does have a REST web service available. Let’s look at how to use it.

The WeatherKit API does require an Apple developer account and access to the Developer Console to configure. Though the developer account costs $99 a year, that includes 500,000 WeatherKit API calls per month. For me personally that is a much better deal than a service such as a AccuWeather or OpenWeather.

Provisioning

To get started with WeatherKit we need to do some provisioning in the Developer Console. The first step will be to create a key.

Select Certificates, Identifiers & Profiles and then Keys. Click the blue circle with white cross to add a new key. We’ll call the key myweatherapp. Check the box next to WeatherKit.

Click Continue and then Register on the Register a New Key screen.

Make note on this screen that you’re about to download a new key, and once you’ve done so you won’t be able to again. This key is necessary to access the WeatherKit REST API (you’ll be signing tokens with it), so keep it in a safe place.

Download your key, and also make note of the Key ID. We’re going to use it later. Look in your Downloads folder for AuthKey_KEYID.p8. In our case the filename is AuthKey_9U5ZXJ4Y65.p8.

Once you’ve downloaded your key, click Done.

Now that we have our key, it’s time to provision our service identifier. Click on Identifiers and once again, the blue circle with white cross. Choose Services ID and Continue.

We’re going to use the reverse domain name notation for the service, i.e., it.iachieved.myweatherapp.

Click Continue and then Register.

Preparing the Keys

The .p8 file downloaded is a plain text file containing an elliptic curve private key in PKCS#8 format. It is not encrypted. We’re going to want the key in PEM format, so let’s convert it with openssl:

openssl pkcs8 -nocrypt -in AuthKey_9U5ZXJ4Y65.p8 -out AuthKey_9U5ZXJ4Y65.pem

NB: The option -nocrypt is required!

We need the public key component as well for signing JWT tokens, so obtain it with openssl:

openssl ec -in AuthKey_9U5ZXJ4Y65.pem -pubout > AuthKey_9U5ZXJ4Y65.pub

You should now have two files which are the private and public key.

Creating and Signing a JWT

Let’s recall how accessing a REST API with a JSON Web Token works.

Apple runs the WeatherKit API service, and in the Developer console you created a key. Apple kept a copy of the public key which it will use to verify JWT signatures. Your application is going to construct a JWT and sign it with the private key. This signed JWT will be presented as a bearer token to the API. If Apple can validate your signature and that your token contents identify provisioned WeatherKit services, you’re golden.

We’ll use jwt.io to create a JWT by hand.

The JWT to access the WeatherKit API must contain the following header elements:

  • alg – ES256
  • kid – the Key ID obtained when creating your key
  • id – your Developer Account Team ID concatenated with a period, and then your Services Identifier (the reverse domain name)
  • typ – JWT

The JWT payload must contain the following:

  • iss – your Developer Account Team ID
  • sub – the Services identifier (the reverse domain name)
  • iat – the standard “issued at” timestamp in Unix epoch time
  • exp – an expiration timestamp in Unix epoch time

Here is an example in jwt.io:

When I need a quick copy-paste of the iat and exp I use this Python one-liner:

python -c'import time; n=int(time.time()); print("\"iat\": %d," % n); print("\"exp\": %d," % (n+3600))'

Once you’ve constructed your token’s contents it’s time to sign it. In jwt.io this is accomplished by pasting the public and private key contents into the Verify Signature inputs.

If successful, you should see something like:

The encoded and signed token is your bearer token that is presented to the WeatherKit API for authentication and authorization. Note the contents of the private key and the bearer token were blurred, but the JWT contents were not. You can construct the same contents, but unless they’re signed with the private key of our provisioned service they’re unusable. Thus it is important to keep your private key private!

Calling the API

With a bearer token in hand you can make calls to the WeatherKit API!

The first call we’ll make is to determine what WeatherKit API services are available for the GPS location 32.779167/-96.808891, which happens to be Dallas, Texas. In these examples <TOKEN> should be replaced with the bearer token.

curl "https://weatherkit.apple.com/api/v1/availability/32.779167/-96.808891?country=US" -H 'Authorization: Bearer <TOKEN>'

This returns:

["currentWeather","forecastDaily","forecastHourly","forecastNextHour","weatherAlerts"]

In other words, for this location, we can obtain the current weather, daily forecast, hourly forecast, forecast for the next hour, and weather alerts. We’ll just check the current weather.

curl "https://weatherkit.apple.com/api/v1/weather/en_US/32.779167/-96.808891?dataSets=currentWeather" 
     -H 'Authorization: Bearer <TOKEN>'

A few things to note here:

  • the route changed to /api/v1/weather/
  • the inclusion of a locale code (e.g., en_US)
  • the query parameter dataSets

The result of the call:

{"currentWeather":{"name":"CurrentWeather","metadata":{"attributionURL":"https://weatherkit.apple.com/legal-attribution.html","expireTime":"2022-09-11T14:57:09Z","latitude":32.779,"longitude":-96.809,"readTime":"2022-09-11T14:52:09Z","reportedTime":"2022-09-11T14:52:09Z","units":"m","version":1},"asOf":"2022-09-11T14:52:09Z","cloudCover":0.41,"conditionCode":"PartlyCloudy","daylight":true,"humidity":0.70,"precipitationIntensity":0.00,"pressure":1021.51,"pressureTrend":"rising","temperature":22.17,"temperatureApparent":22.72,"temperatureDewPoint":16.52,"uvIndex":3,"visibility":26705.41,"windDirection":348,"windGust":30.99,"windSpeed":18.18}}

The units are returned in metric, and annoyingly the condition code will need to be mapped to make it user friendly (“Partly Cloudy” instead of “PartlyCloudy”).

A C++ JWT Signing Implementation

We used the jwt.io site to quickly cobble together a usable bearer token, but if you’re building an actual application to make requests to the WeatherKit API you’re going to want to implement JWT signing in code.

Here’s an example in C++ utilizing the jwt-cpp header-only library.

WeatherKit REST API Documentation

The complete REST API documentation for WeatherKit can be found at https://developer.apple.com/documentation/weatherkitrestapi.

Swift on Linux In 2021

It has been nearly 6 years since Apple first open sourced the Swift language and brought it to Linux. In that time we’ve seen the rise (and sometimes fall) of server-side frameworks such as Zewo, Kitura, and Vapor as well as porting Swift to SBC devices such as the Raspberry Pi and Beaglebone.

I recently checked in with some folks in the Swift ARM community to find out if there was an easy way to install the latest version of Swift on Ubuntu. FutureJones pointed me to a Debian-based repository he’s been working on at Swiftlang.xyz. A nicely put together repo, Swiftlang.xyz supports multiple flavors of Debian and Ubuntu OSes as well as both x86 and ARM architectures! I’ve installed Swift 5.4 and the upcoming 5.6 release with great success.

Using https://swiftlang.xyz/public/ is a piece of cake with an installer script to configure your apt repository list automatically. arkansas below is an Ubuntu VM running on Apple Silicon with Parallels.

Type curl -s https://swiftlang.xyz/install.sh | sudo bash to get started.

I want to use Swift 5.6 so I’ll select option 2 which will include the dev repository in my apt sources.

Now it’s time to install Swift through the provided swiftlang package. apt-get install swiftlang is all it takes.

Once installed let’s kick the tires a bit. First, typing swift in the terminal will bring up the REPL:

To really test things out let’s hit a REST API and destructure the response. For this we’ll use ReqRes and just grab a user with GET https://reqres.in/api/users/2.

And now, some code!

Yes You Can Run Homebrew on an M1 Mac

One of the reasons I took the plunge and bought an M1-based Mac is to test out its performance and suitability as a developer. An essential developer application on the Mac is Homebrew, the “missing package manager for macOS.” Although you cannot install Homebrew today to manage ARM-compiled packages, you can install Homebrew in the Rosetta environment and leverage the x86 packages.

I can’t take credit for coming up with the idea, that would go to OSX Daily, but I have a few improvements to share. I’m going to use iTerm2, and so should you.

Right click on your iTerm application icon and select Duplicate. Rename iTerm copy to something like iTerm x86 or iTerm Rosetta.

Now, right click on your new iTerm icon and click on Get Info and then check Open using Rosetta.

Open your iTerm Rosetta application and install Homebrew! Once installed you should be able to use brew install in the iTerm Rosetta application and use those installed packages seamlessly between the two environments. You won’t, however, be able to use brew install in your arm64 iTerm application (you’ll get Error: Cannot install in Homebrew on ARM processor in Intel default prefix).

Keeping Track

If you’re working in both x86 and ARM environments on your M1 Mac it is easy to lose track which iTerm you are in. We can use a little zsh-foo to help us out. Add the following snippet to the end of your ~/.zshrc:

This little snippet takes advantage of iTerm2’s custom escape codes by setting the background to Intel blue if the arch command returns i386 (which it does if running in Rosetta).

We can do one better, however, by changing our iTerm Rosetta icon. Create your own icon, or, right-click the image below and select Copy Image. Then right-click your iTerm Rosetta application and select Get Info. In the upper-left click on the icon until you see a highlight around it and then paste the new icon image (Command-V).

Launch your iTerm Rosetta application and it’s much easier to distinguish between it and your “native” version.

Detecting macOS Universal Binaries

Apple has transitioned from different instruction set architectures several times now throughout its history. First, from 680×0 to PowerPC, then from PowerPC to Intel x86.
And now, in 2020 from Intel to ARM.

During the first transition 680×0 code ran in an emulator. In subsequent transitions Apple has utilized the translation application Rosetta. From Apple’s documentation, “Rosetta is meant to ease the transition to Apple silicon, giving you time to create a universal binary for your app. It is not a substitute for creating a native version of your app.”

So, how can you tell if an application is already a “universal binary” that provides both x86 and ARM instructions? Open Terminal and find the application’s executable code. For standard macOS applications it is located in /Applications/Application.app/Contents/MacOS/. For example, Safari’s executable is at /Applications/Safari.app/Contents/MacOS/Safari. Now, we’re going to use the file Unix command to give us information as to the contents.

% file /Applications/Safari.app/Contents/MacOS/Safari
Safari.app/Contents/MacOS/Safari: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit executable x86_64] [arm64e:Mach-O 64-bit executable arm64e]
Safari.app/Contents/MacOS/Safari (for architecture x86_64): Mach-O 64-bit executable x86_64
Safari.app/Contents/MacOS/Safari (for architecture arm64e): Mach-O 64-bit executable arm64e

From this you can see that the Safari binary contains executable code for both the x86_64 (Intel) architecture and arm64e (ARM).

As of this writing, November 24, 2020, a few notable applications that are already shipping universal binaries, such as Google Chrome and iTerm2. Of course, Apple’s flagship applications such as Safari, Xcode, Numbers, etc. all support the new ARM instruction set.

I’ve written a quick Ruby script to iterate through the executables in /Applications. To run on your machine:

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/iachievedit/detectUniversalBinary/main/detectUniversalBinary.rb)"

macOS Bundle Install and OpenSSL Gem

From time to time you run into an issue that requires no end of Googling to sort through. That was my case with using bundler and the OpenSSL gem on macOS Big Sur. Your Gemfile has the following contents:

gem "openssl"

and when running bundle install you’re greeted with this nonsense:

extconf.rb:99:in `<main>': OpenSSL library could not be found. You might want to use --with-openssl-dir=<dir> option to
specify the prefix where OpenSSL is installed. (RuntimeError)
...
An error occurred while installing openssl (2.2.0), and Bundler cannot continue.

Using gem install “only” required the following incantation:

gem install openssl --install-dir vendor/bundle -- --with-openssl-dir=/usr/local/Cellar/openssl@1.1/1.1.1h

For the life of me, though, I couldn’t figure out how to apply the --with-openssl-dir to bundle!

Well, dear reader, here is how:

bundle config path vendor/bundle
bundle config build.openssl --with-openssl-dir=/usr/local/Cellar/openssl@1.1/1.1.1h

The first line is obligatory and is the “modern” way of setting bundle‘s install path to vendor/bundle (which odds are you want anyway). The build.openssl setting will use the remaining information to pass to gem when installing openssl.

It goes without saying that the exact path used is dependent on your environment; for my Mac the OpenSSL headers and libraries were hanging out in the brew cellar.

Hopefully this post saves someone an hour or so!

macOS Big Sur Battery Percentage In the Menu Bar

After upgrading to macOS Big Sur beta I noticed that the battery percentage disappeared from my menu bar. One would think getting it back would be as simple as clicking on the battery icon in the menu and selecting Show Percentage, and how could you fault someone for assuming that?

Yet that is a path that leads to both ruin and no menu option to show the battery percentage. Now, to do that you need to go to System Preferences, and find the new preference pane named Dock & Menu Bar:

Click on it and scroll down the left-hand side of the pane until you find the Battery item. Click to discover Show Percentage.

Select Show Percentage and Big Sur will happily once again provide you with what I consider a useful bit of information for determining how much battery you have left.

macOS 10.15 Catalina Adds Additional Filesystem Restrictions

macOS 10.15 (Catalina) has added additional Privacy restrictions that require user intervention before applications can access the certain portions of the filesystem. Not only that, but taking screenshots for this post required permissions to be explicitly granted to Skitch. If you find that Skitch only gives you a screenshot of your Mac’s background, this is the post for you. The fact that macOS 10.15 introduces new security and privacy safeguards is unsurprising as we got introduced to stricter automation controls in Catalina’s predecessor.

Let’s look at what happens when we try to cd ~/Documents in the macOS 10.15 Terminal app:

We’re greeted by a dialog box presented by Finder requesting specific permission for Terminal to access files in the Documents folder:

"Terminal" would like to access files in your Documents folder. Once this permission is granted, Terminal can access the contents of ~/Documents. If the permission isn’t granted, trying to ls ~/Documents results in something along the lines of:

# ls ~/Documents
ls: Documents: Operation not permitted

Updating which filesystem access permissions have been granted for a given application can be accomplished in the Security & Privacy preference panel. In this example Terminal has been granted access to the Documents folder whereas iTerm has not been granted any access.

It was an added bonus while writing this post that even trying to take a screenshot with Skitch on Catalina prompted a dialog requesting explicit access. The final result was that I’ve now authorized Skitch to capture my screen:

There has been much speculation regarding Apple’s WWDC 2019 announcement of Sign in with Apple as to its real intent. What is clear (to me, at least) is that Apple is positioning itself as the guardian of security and privacy (compared to Facebook and Google who are clearly not in the interest of safeguarding either), whether it is Sign in with Apple or tighter controls around what applications can access your data on your own Mac.

TLS 1.3 Support Coming with Safari 12.1

With the landing of macOS 10.14.4 Beta (18E194d), Safari has versioned up from 12.0.3 to 12.1, and includes with it support for TLS 1.3!

Pointing Safari 12.1 at tls13.iachieved.it (which only runs TLS 1.3) returns

Your User Agent is: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1 Safari/605.1.15 with TLS_AES_256_GCM_SHA384 selected as the cipher. Note: If your current browser doesn’t support TLS 1.3 it will not be able to connect with tls13.iachieved.it.

Interesting in standing up your own TLS 1.3 webserver or see what browsers and clients support TLS 1.3? Come check out our instructions on configuring NGINX to do just that.

DevOps ToolChain, WikiPedia, CC BY-SA 4.0

Not authorized to send Apple events to System Events

As others have written about, Apple appears to be making a hash out of the ability to automate tasks on macOS 10.14 (Mojave). I get it. In the age of hyper-connectivity there is a continuous assault on our computing environments by bad actors looking to extort some cash or otherwise ruin our day. Something needs to safeguard our systems against random scripts aiming to misbehave. Enter Mojave’s enhanced privacy protection controls and event sandboxing.

With earlier versions of Mojave the new event sandboxing mechanism was considerably flaky. In some versions (notably Beta 4), you’d intermittently be presented with Not authorized to send Apple events to System Events when attempting to execute AppleScript applications. As of Mojave Beta 8 (18A371a) I have found that the authorization functionality is at least consistent in prompting you for permission.

As a test, open a Terminal window and enter the following:

osascript -e 'tell application "Finder"' -e 'set _b to bounds of window of desktop' -e 'end tell'

You will get different results depending upon your current automation privacy settings. If you’ve never given permission to Terminal to send events to Finder you’ll see an authorization dialog like this:

If you’ve already given permission (as shown in the example Privacy panel below), the AppleScript tell will succeed and you’ll see something like -2560, 0, 1920, 1440 (the bounds coordinates).

But wait, there’s more! If you had previously given permission, and then revoked it by unchecking the permission in the Privacy panel, you’ll get execution error: Not authorized to send Apple events to Finder. (-1743).

Automation Implications

A lot of folks (myself included) write AppleScript applications that perform some set of tasks. That is sort of the whole point of automation. Again, I get it. If my machine is compromised with a rogue application or script, it could do some serious damage. But that will always be the case.

Now I can imagine how this will be resolved, and it’s going to include me forking over some money to get a verified publisher certificate. My certificate will be signed by Apple, and that signing certificate will be trusted by the OS a priori, and I’ll have to sign my scripts somehow, and so on. That’s the only way I can see this panning out, unless the plan is to literally break the automation capabilities with AppleScript. If you envision a different solution, please leave a comment!

Faster Carthage Builds

I love Carthage for managing external dependencies in my Xcode projects. What I don’t care for is that most READMEs have you edit your Cartfile and then run carthage update. By default this will compile the dependencies for every target that the 3rd party framework supports: iOS, tvOS, watchOS, and macOS. If I’m working on an iOS-only project that’s a significant waste of my time.

Lucky for us you can bypass all that and specify only the platform you want to build for with the --platform argument:

notification git:(master) carthage update --platform ios
*** Fetching SQLite.swift
*** Checking out SQLite.swift at "8b95a0c4a884f35e5102cd306f634680255473bb"
*** xcodebuild output can be found in /var/folders/b_/mwxmgbj530l2l5jzl6ygnqtr0000gn/T/carthage-xcodebuild.P0EL0n.log
*** Building scheme "SQLite iOS" in SQLite.xcodeproj

You can also specify a number of platforms using a comma-separated list: carthage update --platform tvos,ios.

The --platform argument can also be used with the build command: carthage build --platform ios

So, stop wasting time building for platforms you don’t need. Use --platform and call out only those your project supports!