While adding visual intelligence support to Unite, our native GroupMe client for macOS, we discovered an interesting crash that would only reproduce on the oldest macOS release we support, macOS 13 “Ventura”:
Symbol not found: _$s9VisionKit13ImageAnalyzerC13AnalysisTypesV12visualLookUpAEvgZ
Referenced from: /Users/USER/Desktop/Unite.app/Contents/MacOS/Unite
Expected in: /System/Library/Frameworks/VisionKit.framework/Versions/A/VisionKit
This surprised us at first glance because looking at the headers, ImageAnalyzer
is marked as available on macOS 13.0. Moreover, neither AnalysisTypes
nor visualLookUp
have any availability macros that suggest these were introduced in a later release, which allowed the code to compile just fine. We suspect the availability annotations here are incorrect and visualLookUp
was indeed introduced in a newer release of macOS.
While we could gate this code on a newer OS version, we would have to find which one is the correct one. Instead, we chose to use the lesser-known #_hasSymbol
Swift macro which, together with @_weakLinked
imports, allows you to gate code on a particular symbol being available:
@_weakLinked import VisionKit
...
if #_hasSymbol(ImageAnalyzer.AnalysisTypes.visualLookUp) {
self.analyzeImageIfNecessary()
}
This allows us to only support our visual intelligence feature on systems that have the necessary symbol, and we don’t have to worry about which ones they are. Most importantly, the app no longer crashes on launch on macOS 13.
Most of the time, the availability macros in Apple SDKs are correct, and the Xcode-suggested fix-its for if #available
checks suffice. But when they don’t, checking for the presence of a symbol can be a viable workaround that gracefully keeps things working on older operating systems.