Sven Schmidt alerted me to the fact that you can check in Package.resolved to pin Swift package versions in Xcode-managed projects if you add the file from within the generated project.xcworkspace file in your .xcodeproj bundle:
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.)
My recent posts were about SwiftUI quirks and annoyances, so I want to share a short success story for a change: You can @import Swift Packages in Objective-C code just fine. (At least if they are Objective-C compatible.) I updated my WordCounter app to rely less on Carthage and use Swift packages where possible instead.
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.
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.
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.
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.
The Notarization API changed a bit, so my old scripts broke. Revisiting the approach, it’s delightfully simple to notarize an app nowadays if you use the Apple toolchain on macOS. I haven’t watched the WWDC 2022 video on Notarization but did take the documentation link there into account.
So for quite some time, I had the Swift Build System Integration flag enabled to speed up my builds. It was good. I set it, it worked, I forgot about it. With Xcode 13.3 and 13.3.1, now, we got a lot of dependency cycle warnings out of nowhere. Clean build works, but any build afterwards would occasionally fail.
Dominik Hauser maintains the illustrated xcode.tips page I’ve mentioned on Twitter here and there because the graphics are just lovely and the tips are really useful to become a proficient user of Xcode. It’s a complex piece of software, and the illustrations capture a tip in a single graphic, with shortcuts and UI hints and all.
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.
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.
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.
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.
This time, I don’t want to find out if the app is compiled as debug/release mode; I want to find out if the debugger is attached. I found an Objective-C answer on StackOverflow which is based on HockeyApp-iOS.
When I pointed out that Sparkle finds its XPC services on its own, I mentioned you can see how it does this by searching for “SPUXPCServiceExists”. If you take a look at the use of this function, you’ll see something like this in the Objective-C (!) source files:
In the previous post, I talked about how to download the “modern” Sparkle updater branch called ui-separation-and-xpc that allows you to update sandboxed apps and migrate your code to use the new types. This time, we’re going to use the actual XPC services that do the grunt work.
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.
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.
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.)
Create an empty file or add an existing file to the target’s group in Xcode and make it part of the proper target,
drag it into “Copy Bundle Resources” build phase,
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.
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).
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:
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.
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.
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.
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.