Reading environment variables from iOS and macOS unit tests

In some environments like the CI/CD you might need to read environments variables from your iOS or macOS unit tests.

A typical example might be reading secrets or configuration that is CI/CD specific and you do not want to store it in source control.

In Swift you can read environment variables using the ProcessInfo.processInfo.environment dictionary, for example

let host = ProcessInfo.processInfo.environment["apiHost"] ?? "default fallback host"

The problem is this code does not just work, there is one other step you need to do first.

You need to edit your Test scheme and add every environment variable you want to read to the Environment Variables section.

You can rename the variables but for simplicity and readability you should probably keep the same names.

Swift  Xcode 

Graying out images in Cocoa

I have been working on a macOS application recently where I encountered an interesting task to grey out flags for countries that are disabled.

I first tried overlaying a semi-transparent gray view (fun fact, you cannot directly set a background for a view in Cocoa, you need to use the layer) over the flag image but it did not look very good.

Luckily there is a way to convert a NSImage to grayscale directly in Cocoa.

You first need to create a bitmap representation your NSImage

let bitmap = NSBitmapImageRep(cgImage: cgImage)

convert it to grayscale

let grayscale = bitmap.converting(to: .genericGray, renderingIntent: .default)

and then construct an NSImage from the result.

let grayImage = NSImage(size: grayscale.size)
greyImage.addRepresentation(grayscale)

Putting it all together as an NSImage extension might look like this

[Read More]
Swift  Xcode  Cocoa 

Web scraping with Swift

In a few projects in the past I needed to do web scraping to get some data from websites that did not offer access via an API. I was using C# at the time and scraping web with Html Agility Pack was quite easy.

I now spend most of my time in macOS because of work projects so when I needed to do some web scraping again I did not want to install and set up Mono to do it again in C#. I decided to go with Swift, as I am now quite comfortable with the language after 4 years of using it daily.

SwiftSoup

The first thing I need to do was to found some library to parse HTML, some Swift equivalent to Html Agility Pack. I found SwiftSoup.

SwiftSoup allows you to access DOM in HTML documents and also HTML fragments. The usage is quite simple, you just need to know a thing or two about HTML.

Example

Let’s say you want to parse the Hacker News main page and scrap posts containing some specific keywords.

This is quite an artificial example but the idea is simple. You use the developer tools in your browser of choice to see the HTML of the parts of a website that you are interested in and try to get to them descending and filtering the DOM using SwiftSoup.

You first need to read the website and parse it

let content = try String(contentsOf: URL(string: "https://news.ycombinator.com/")!)
let doc: Document = try SwiftSoup.parse(content)

Looking at the HTML you can see it uses a table layout and all the posts are in a rows of a table with a class called itemlist.

[Read More]
Swift  Xcode 

Faster way to download and install Xcode

As an iOS developer you need to periodically update your Xcode, on your own machine and on your CI/CD server if you automate your development workflow. There are multiple ways to do this so do not waste your time and use the fastest way possible.

Forget Mac App Store, use Apple Developer Portal

Installing Xcode from the Mac App Store might seem like a convenient way to do so but it is too slow and inflexible. You cannot use the Mac App Store to install multiple version of Xcode at the same time if you need them, like when testing with a Xcode beta for an upcoming iOS release. Download from the Mac App Store is incredibly slow and sometimes not even available for days after release (like 11.2.1).

The place to go is the Apple Developer Portal where you can find all the Xcode versions, including the betas.

Faster download with aria2

Downloading Xcode from the Apple Developer Portal is faster than using the Mac App Store, but it can be made even better. You just need to use the right tools.

Install aria2 from Homebrew and uses this Ruby script from Ian Dundas:

#!/usr/bin/env ruby

print "What is the URL of your Apple Downloads resource?\nURL:"
url = gets.strip

print "What is the ADCDownloadAuth cookie token:\nADCDownloadAuth: "
token = gets.strip

command = "aria2c --header \"Host: adcdownload.apple.com\" --header \"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\" --header \"Upgrade-Insecure-Requests: 1\" --header \"Cookie: ADCDownloadAuth=#{token}\" --header \"User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 10_1 like Mac OS X) AppleWebKit/602.2.14 (KHTML, like Gecko) Version/10.0 Mobile/14B72 Safari/602.1\" --header \"Accept-Language: en-us\" -x 16 -s 16 #{url} -d ~/Downloads"

exec(command)

This script downloads the given Xcode by URL from the Apple Developer Portal, but uses up to 16 separate connections to do so. You will see a significant download speed improvement.

Make sure you use the “More” site at https://developer.apple.com/download/more/ even for downloading the latest version of Xcode.

When copying the ADCDownloadAuth cookie make sure you copy the correct value, Safari adds all kinds stuff around it when you just use “copy value”.

Faster install with xip and deleting previous Xcode first

When you install the Xcode xip file you need to extract it. You can double click it in Finder and wait or you can use

xip -x Xcode11.xip

Using xip from the command line is much faster because it does not verify the file signature like double clicking in Finder. Of course this is a potential security risk, so it is up to you to decide if it is worth it.

Another trick is not to drag the extracted Xcode.app to /Applications immediately but delete the existing /Applications/Xcode.app first. I guess this is related to Finder first getting the list of those thousands of files in the Xcode.app before the update.

iOS  Swift  Xcode 

Changing UIApplication base class

When developing an iOS application you might get into a situation when you need to change the UIApplication base class. It is often a requirement when using various MDM SDKs, like the Mobile Iron AppConnect SDK. There are two ways to do that in a Swift application, both with some advantages and disadvantages.

Declarative method with Info.plist

The first method to change the UIApplication base class is using Info.plist. It is quite simple, you just need to add a new key NSPrincipalClass with a string value representing the name of the desired class, like AppConnectUIApplication when using the Mobile Iron AppConnect SDK.

<key>NSPrincipalClass</key>
<string>AppConnectUIApplication</string>

No actual code changes are required.

Code method with main.swift

The second method is a bit more complicated but more flexible at the same time. First you need to remove @UIApplicationMain from your AppDelegate class definition. Then you add a main.swift to the root of your project that looks like this

import AppConnect
import UIKit

UIApplicationMain(
    CommandLine.argc,
    CommandLine.unsafeArgv, 
    ACUIApplicationClassName,
    NSStringFromClass(AppDelegate.self)
)

The third parameter in the UIApplicationMain call is the name of the desired class, ACUIApplicationClassName in this example.

[Read More]
iOS  Swift  Xcode