GitHub Actions: Using Older Simulators

GitHub Actions is a relatively recent feature update to GitHub that allows you to run CI/CD tasks in response to repository changes. The service is free to public repositories and available to the paid tiers of private organizations.

One shortcoming I’ve encountered is that each version of Xcode only has the default shipping simulators available to it. Thus, if you want to do tests 1 or even 2 iOS versions back, you need to do some setup work. Thankfully, there is a solution less drastic than downloading the required simulator at the start of every CI run (which would slow things down terribly).

The solution comes from the fact that the default MacOS environment has many previous versions of Xcode available. You can use symbolic links to make an older simulator appear to be installed when you run xcodebuild. This can be done as a step just before you begin build / test operations:

You can see a complete list of software installed on the virtual MacOS environments on this page. You can see they maintain all current- and previous-generation versions of Xcode as well as one version from 2 generations ago. To pick a particular simulator, you just need to modify the workflow step to refer to the correct version of Xcode and name the linked simulator core appropriately.

To use the simulator, you simply need to update the destination flag of your xcodebuild command to look something like this:

-destination 'platform=iOS Simulator,name=iPhone 11,OS=13.7'

The next time you run your workflow, you should see it link to the specified simulator and use it for your tests.

Combine: “Lazy” CurrentValueSubject

This is a technique suggested to me by Jordan Gustafson (@minnesota_gus) on how to make a CurrentValueSubject that doesn’t require an initial value. This should probably be developed into an actual type which conforms to Subject, but I haven’t figured that out yet.

import Foundation
import Combine
class Example {
private var index = 1
private let valuePublisher = CurrentValueSubject<Int?, Never>(nil)
var value: AnyPublisher<Int, Never> {
valuePublisher.compactMap({ $0 }).eraseToAnyPublisher()
}
func doSomething() {
print("[Example] sending value: \(index)")
valuePublisher.send(index)
index += 1
}
}
let example = Example()
print("First subscription.")
let subscription1 = example.value.sink { print("sub1: \($0)") }
example.doSomething()
print("Second subscription.")
let subscription2 = example.value.sink { print("sub2: \($0)") }
example.doSomething()

This will produce the following output:

First subscription.
[Example] sending value: 1
sub1: 1
Second subscription.
sub2: 1
[Example] sending value: 2
sub2: 2
sub1: 2

As you can see, the first subscription did not receive a value until the first time doSomething() was called. Conversely, the second subscription—which was added after the first value had been sent—received a value immediately. Both subscriptions receive all values thereafter.

CoreImage: New Filters in iOS 14.0

iOS 14.0 recently arrived and with it, 3 new CoreImage filters:

  1. Color Absolute Difference
  2. Color Threshold
  3. Color Threshold Otsu

Unfortunately, Apple has not yet updated the CoreImage documentation to include these new filters. Instead, I’ve created a Swift playground that enumerates all of the properties of a CIFilter. Here are the results for those 3 filters:


=========================
Color Absolute Difference
=========================
[Availability]
iOS: 14
macOS: 11.0
[Categories]
Color Adjustment, Video, Interlaced, Non-Square Pixels, Still Image, Built-In
[Inputs]
Image (inputImage)
Class: CIImage
Type: Image
Description: The first input image for differencing.
Image2 (inputImage2)
Class: CIImage
Description: The second input image for differencing.
[Outputs]
outputImage

===============
Color Threshold
===============
[Availability]
iOS: 14
macOS: 11.0
[Categories]
Color Adjustment, Video, Interlaced, Non-Square Pixels, Still Image, Built-In
[Inputs]
Image (inputImage)
Class: CIImage
Type: Image
Description: The image to use as an input image. For filters that also use a background image, this is the foreground image.
Threshold (inputThreshold)
Class: NSNumber
Type: Scalar
Default: 0.5
[Outputs]
outputImage
view raw ColorThreshold.txt hosted with ❤ by GitHub

====================
Color Threshold Otsu
====================
[Availability]
iOS: 14
macOS: 11.0
[Categories]
Color Adjustment, Video, Interlaced, Non-Square Pixels, Still Image, Built-In
[Inputs]
Image (inputImage)
Class: CIImage
Type: Image
Description: The image to use as an input image. For filters that also use a background image, this is the foreground image.
[Outputs]
outputImage

I have no idea what the difference is between the Color Threshold and the Color Threshold Otsu filters. I’ll follow up if I can produce a workable demo.