Async XCTest Assertion Helpers

SwiftAsyncAssert by Angu (@angu@techhub.social):

Instead of writing

import XCTest

func test_should_succeed() async {
    do {
        let isTrue = try await shouldSucceed()
        XCTAssertTrue(isTrue)
    } catch {
        XCFail("Should not throw an error")
    }
}

conveniently write

import SwiftAsyncAssert

func test_should_succeed() async {
    await AsyncAssertTrue(try await shouldSucceed())
}

func test_should_throwError() async {
    await AsyncAssertThrowsError(try await shouldFail())
}

Less code for your convenience when testing!

I ran into this, too, recently and wondered how everyone’s dealing with this. Because this is so annoying, I expected more outcries on the interwebs every day.

Maybe nobody is writing tests?

Either way: These helpers look very nice, and I’ll try them for sure. Should be part of the XCTest libray in my opinion. I seriously wonder why there’s no async XCTest assertion functions for this already.

If you’re just running into this for main actor isolation: One workaround that got suggested to me is to annotate the XCTestCase subclass itself with @MainActor to circumvent having to await isolated calls everywhere. (That doesn’t help for non-main actor-isolated calls, obviously.)

Detach Xcode Console -- via Terminal

Teaser image

I have been complaining on social media about the Xcode Console sticking to the bottom, and how I’d prefer a horizontal split etc. And there have been good suggestions to e.g. open a new Console tab or window. (Thanks Dominik Hauser for the tip!) That didn’t stick because I needed to adjust the Xcode behaviors and sometimes that didn’t compose well with what I’ve been doing.

Continue reading …

Copilot for Xcode Works Okay

I’ve never touched GitHub Copilot in all these years, but everyone seems to be very happy with it. People recommend Copilot for all kinds of refactorings and repetetive tasks. So I figured I might give it a try and see how it works. Just yesterday, I used the Copilot Xcode plugin to write a lot of boilerplate for me. I can confirm it does its job.

Continue reading …

Xcode Requires A Lot of Data for Swift Package Resolution

Teaser image

This picture shows one of the weird annoyances with Xcode and Swift packages. One package resolution step swallowed 45MB of my data. You’ve likely heard it elsewhere: when iOS developers need to work with Swift packages and Xcode but have a shoddy internet connection, tough luck! Xcode will fail to build because it’s “Resolving Packages” step inevitably fails.

Continue reading …

DevCleaner for Xcode

Found DevCleaner for Xcode yesterday and gave it a spin. It’s freeware on the Mac App Store with a tip jar.

Developers who ever nuked their Derived Data folder (I have an alias in my .profile for that) know how much stuff Xcode collects over the years. And then there’s outdated iOS Simulator versions nobody needs anymore. And log files, of course, by Xcode.

I’m pleased to say that DevCleaner does a good job at enabling me to select what I want to remove from the set of things that could be obsolete.

I have Xcode 14 beta installed (to my dismay), so DevCleaner suggested all Xcode 13.x related things could go away. It tries to be smart and helpful here, but I kept the Xcode 13.4.1 logs just in case. The option is there.

Where in the past I would occasionally nuke all Derived Data, DevCleaner shows these folders in a sensible way, with a reference to the project location. I usually create throwaway projects to test and debug something in my Downloads folder. The derived data of these projects is absolutely useless after a month, week, or sometimes even a day. Since DevCleaner shows where the original project was located, I’ve been able to delete data for every long-gone project that was in my Downloads folder.

I cannot stress enough how cool I find this.

Deleting derived data folders is often hard to do by name alone: when I download a new version of a 3rd part dependency that I am using in my apps, and open the project in my Downloads folder, and play around with it, then I have two derived data folders for this dependency, but only one is relevant for the actual project. Either I don’t care and delete both when I know building them is trivial, or I don’t bother at all and they stay around forever.

I really, really like this feature.

Would be even better if I could sort or filter by origin path, but it’s ok the way it is.

How to Fix Mach-O Header Code 0x72613c21 When You Try to Export Your App in Xcode

Teaser image

I was preparing a test build to check if linking against a new library worked fine in production. Trying to distribute the app using my Developer ID (but this would also have happened in a step before uploading to the App Store), I got this: Found an unexpected Mach-O header code: 0x72613c21 Oops? Like probably most developers, I have absolutely no clue about many crucial steps in the app making process. Especially stuff like code signing and most build settings elude me – I pick up pieces over the years, but not with the same speed with which I’m getting better at writing apps. So I didn’t understand what’s going on at all here. The search results I got weren’t that useful, so this post is meant to fill the vacuum for the next person running into this.

Continue reading …

That Weird Obstable of Interface Builder When You Get Started With iOS Development

A very strange obstacle to overcome when you just get started with iOS or macOS programming is to use custom views or view controllers. How does this magic even work? And what do the steps mean that you have to perform? When you use a Xcode template, you get a Storyboard or a Xib with the most important components for an initial, empty app to display something on screen. This is accompanied by an AppDelegate and a custom ViewController class. If you don’t know anything and just get started developing for iOS, say, then this is a good place to start hacking away: after all, the bare-bones infrastructure has been handled for you.

Continue reading …

Xcode Bug Turns Out To Be the Hardened Runtime Setting

What I called an Xcode bug last week turned out to be a broken project configuration that was absolutely my fault. Back when Hardened Runtime was introduced, I switched it on at the Xcode project level, not just for the app target. When you set “Enable Hardened Runtime” on the project level, all targets inherit this setting: the app, the unit tests, and the UI tests. With Xcode 11, it now turns out that hardening the runtime for the test runner broke an internal library lookup in perfect accordance with the utility of the Hardened Runtime settings.

Continue reading …

Xcode 11 Displays Crash Logs in Context of Your Projects

For all throughout human history, you had to symbolicate your user’s crash logs using atos or a 3rd party tool to get human-readable output. Otherwise, you will not know the symbol or method name of your app’s parts that are involved and see memory offsets instead. But with Xcode 11, double-clicking a .crash file now opens Xcode instead of Console, and you can view the crash log in the context of a project. That’s very nice.

Continue reading …

Use Shared NSUserDefaults for XPC and Interface Builder Nibs

The default Interface Builder approach of using a “Shared User Defaults Controller” object breaks down if your app preferences are not stored in the standard place. Both my apps The Archive and the Word Counter for Mac are comprised of multiple executable applications. For The Archive, it’s the main app and the Quick Entry popup window. They share some settings, like which theme and font size is used. To share these settings, I rely on NSUserDefault (or just UserDefaults in Swift now). I cannot use the UserDefaults.standard, though, because that is tied to the currently running app’s bundle ID. In the case of the main app, it’s the ID of the main app; but for the Quick Entry helper – or any helper app –, it’s the helper’s bundle ID. This way, the defaults dictionaries are not shared.

Continue reading …

Put Test Files Next to Production Code for Better Discoverability?

The Kickstarter engineering team posted an interesting proposal: put your XCTestCase files into the same directory and group as your production code. Do keep the separate target ownership, of course.

One clear benefit is directory tree or file management, and discoverability of tests for people browsing the code: In most cases, you have FooService in FooProject/FooApp/FooService.swift, and FooServiceTests in FooProject/FooAppTests/FooServiceTests.swift. With nested folders, you need to navigate around even more. I got used to this Xcode default. But I also got annoyed by this split at the root level when I browsed code on GitHub. Multiple tabs in a modern browser to the rescue, this was never that problematic, given the test directory is structured in a similar way. If not, good look finding the file you’re looking for. (I hate that the RxSwift project, which I often had to browse to look up an implementation detail, has files that contain only a path reference to the real file, similar to ../../Core/SomethingSomething.swift, making tests pretty hard to find).

I will try this approach with a library module that I develop as a sub-project of The Archive and report back.

How to Create Fixture Files for Unit Tests

This is mostly a reminder for my future self: It doesn’t suffice to create a TXT file and add it to the target in the file inspector (⌘⌥1) to be able to read it as part of the bundle. You also have to drag it into the “Copy Bundle Resource” build phase of the target so it get, well, bundled into the product. (In my case, it was the test target.)

screenshot of Xcode
The .md file was part of the test target but loading it from the NSBundle failed until I added it to the proper Build Phase
  1. Create an empty file or add an existing file to the target’s group in Xcode and make it part of the proper target,
  2. drag it into “Copy Bundle Resources” build phase,
  3. load it using NSBundle(forClass: TheTestCase.self).URLForResource("filename", withExtension: "txt").

Storing PLIST or JSON files for structured data works just as well. I happen to require plain text flavors most of the time and always wonder why the loading fails for a couple of minutes.

Reusable View Components from Nibs

Just found this today in a Slack channel. It seems we can actually reference and re-use Xib files by overriding awakeFromCoder!

If we are loading from placeholder view, we create a real view and then we transfer some common properties from it and all it’s subviews, then we just return that instance in place of placeholder, otherwise we just return normal view (This method is implemented on NSObject so we can call super, but this still should be done with method swizzling instead of category smashing).

There’s a sample project on GitHub. Let’s tear it apart.

It works like this:

const int kNibReferencingTag = 616;

@implementation UIView (NibLoading)
// ...
- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder
{
  if (self.tag == kNibReferencingTag) {
    //! placeholder
    UIView *realView = [[self class] loadInstanceFromNib];
    realView.frame = self.frame;
    realView.alpha = self.alpha;
    realView.backgroundColor = self.backgroundColor;
    realView.autoresizingMask = self.autoresizingMask;
    realView.autoresizesSubviews = self.autoresizesSubviews;

    for (UIView *view in self.subviews) {
      [realView addSubview:view];
    }
    return realView;
  }
  return [super awakeAfterUsingCoder:aDecoder];
}

When the kNibReferencingTag is set, the current instance (self) is treated as a prototype. From that prototype we transfer common properties to the realView which is properly loaded from a Nib. That means it doesn’t go the kNibReferencingTag path but the usual path, deferring to super.

The sample app contains SomeView with its own Xib that should be reused.

In the app’s Xib, we have a scene that uses it as follows:

<scenes>
    <!--View Controller-->
    <scene sceneID="4">
        <objects>
            <viewController id="2" customClass="ViewController" sceneMemberID="viewController">
                <view key="view" contentMode="scaleToFill" id="5">
                    <rect key="frame" x="0.0" y="20" width="768" height="1004"/>
                    <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                    <subviews>
                        <view tag="616" contentMode="scaleToFill" id="VTu-Cz-9kE" customClass="SomeView">
                            <rect key="frame" x="113" y="216" width="100" height="100"/>
                            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                            <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
                        </view>
                        <view tag="616" contentMode="scaleToFill" id="7te-Q6-moz" customClass="SomeView">
                            <rect key="frame" x="199" y="446" width="100" height="100"/>
                            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                            <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
                        </view>
                        <view tag="616" contentMode="scaleToFill" id="7vx-xh-xRD" customClass="SomeView">
                            <rect key="frame" x="375" y="231" width="100" height="100"/>
                            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                            <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
                        </view>
                    </subviews>
                    <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
                </view>
            </viewController>
            <placeholder placeholderIdentifier="IBFirstResponder" id="3" sceneMemberID="firstResponder"/>
        </objects>
    </scene>
</scenes>

I know, Xib files aren’t the best to read. Here’s what it boils down to:

  • Create a scene using a view controller of type ViewController.
  • In its main view place 3 subviews …
    • of type SomeView,
    • all with the tag 616,
    • and a few standard color properties – apparently all set to white. It doesn’t matter anyway since the SomeView.xib will dictate what it really looks like.

Unlike @IBDesignable components, you won’t have any live preview in Interface Builder. Just empty placeholder boxes.

iRamDisk May Speed Up Your Xcode Compilation Times

Teaser image

I code on a late 2011 Mac Mini with 8 GB RAM and a 256 GB SSD. This machine is a ton faster than my old MacBook Air was. Compilation still takes time, but it seems that iRamDisk helps a bit.

Lately I wondered if I could cut down the 30s compilation time (3mins with a clean build folder) if I had a Mac Pro, or MacBook Pro, or iMac, or whatever next tier device. Faster cores, more cores, doesn’t matter. But buying a Mac for $3k is out of question at the moment. So I’ve been looking for other tricks.

The only thing that’s faster than a SSD is RAM. That’s when I found the app iRamDisk. It offers an option to move the derived data folder to a virtual disk in RAM. It seems I can reduce the clean build time from 3mins to 2mins with that. Xcode needs about 1.5GB derived data for the Word Counter – at least that’s how big I had to make the RAM disk to not get filled quickly. The overall memory pressure is a bit higher, the Mac is using swap more, but compilation becomes faster at last.

There’s a downside, too: after every reboot the RAM disk is empty, so Xcode has to index the project again and compile fresh at least once a day. Depending on your usage this can be annoying or not be a problem at all.

You can download a trial from the website or buy it for $19.99 on the Mac App Store.

Don't Build on El Capitan Without Checking App Transport Security

I got burned this week. Pretty bad. I shipped a small bugfix release for my Mac app Word Counter a couple of days ago to prepare for the “big one” coming this week. Naturally, I built that version on my El Capitan dev machine. I pushed the update to my server. Updates using Sparkle worked. – But now users of that version cannot ever update to the next version. Because I haven’t thought about ATS.

Continue reading …

Make Custom Debug Build Configurations Play Nicely With CocoaPods

To test the Word Counter during development, I have long used a special build target. But that doesn’t scale well if all I want is change a preprocessor macro to switch from file-based to in memory storage, for example. I’ve never played around with build configurations in Xcode. They work well with Schemes, though, and setting up a build configuration and a scheme is much less overhead than maintaining a custom target.

Continue reading …