I'm working on a macOS tool which uses Apple's Safari framework. When running in macOS 10.13, the tool links to and loads it from
/System/Library/PrivateFrameworks/Safari.framework
and all works fine. But when running in macOS 10.12.6, some behaviors are missing. Based on some probing with DTrace, I think that this is because my tool needs to load instead the latest Staged framework, which is here:
/System/Library/StagedFrameworks/Safari/Safari.framework
This is apparently what Safari does, because if I attach to Safari with lldb and run image list
, in 10.13 the list includes only the former path, and in 10.12.6 only the latter.
I tried the following:
NSBundle* stagedBundle = [NSBundle bundleWithPath:@"/System/Library/StagedFrameworks/Safari/Safari.framework"];
That returns nil in 10.13 because there is, at this time, no such directory. However, in 10.12.6, I get a stagedBundle
, and then:
NSBundle* privateBundle = [NSBundle bundleForClass:[BookmarksController class]];
[privateBundle unload];
[stagedBundle load];
The unloading and loading apparently works, because if I log -description
of those two bundles, before running that code the Private bundle is (loaded) and the Staged bundle is (not yet loaded), but after running that code those states are swapped, as desired.
But it is not effective. (1) If I again invoke -bundleForClass:
, passing a class known to be in both frameworks, it gives me the Private bundle. (2) If I invoke -respondsToSelector:
, passing a selector which is known to exist only in the Staged framework, I get NO.
I tried calling _CFBundleFlushBundleCaches()
, as suggested here, but that did not help.
I've also tried changing my target's FRAMEWORK_SEARCH_PATHS
, and installing the Staged framework on my Mac and linking to it, but since this post is already too long I'll just say that this resulted in more heat than light.
How can one selectively load a framework in this situation?
UPDATE
I've tried another approach. After re-reading Apple's Framework Programming Guide, even though it seems really dated, I decided that this framework needs to be weakly linked. Did this:
- In the code, removed those NSBundle
-load
and-unload
calls - In my tool's target,
- In Build Phases > Link Binary with Libraries, removed path to the Safari Private framework, because this was a strong link.
- In Build Settings > Other Linker Flags added
-weak_framework Safari
- In Build Settings > Framework Search Paths, listed paths to both frameworks' parent directories, with the Staged path before the Private path, because I want this one to load in macOS 10.12.6, where both exist.
It makes sense to me, builds and runs in both 10.13 and 10.12.6, but it is apparently still loading the undesired Private framework in 10.12.6. NSLog reports that as the bundle's path, and a class does not respond to a selector known to be in Staged framework only.
Any other ideas?