What is 'hook' mentioned at Meet mergeable libraries Session

Hi everyone,

I'm studying Mergeable libraries. (in an iOS platform)

In general, with the knowledge I knew (may not be accurate), Static Library cannot use resources except to add a separate resource bundle to the Main App Target, so I understand that the main app uses dynamic library.

But In "Meet mergeable libraries Session", it was said that below "Up until iOS 12, the runtime needed the framework's binary to discover bundles, but mergeable frameworks won't have binaries in them by the time the process is running. Good news! In iOS 12, a hook was added to enable lookup for this scenario. That does mean if you rely on bundle lookup support, you should update your minimum deployment version to iOS 12 or later to use mergeable libraries."

I don't know what 'hook' is mentioned below.

I have a dynamic library that only handles the resources(images, colors - xcassets) and those assets compiled and become Assets.car file

I set Build Mergeable Library to Yes in the build setting of this dynamic library, and I set Create Mergeable Binary to Automatic in the Main App (which relies on this library).

When the app bundle includes the framework, of course the resource is loaded, but if I set the library to do not embedded in the project setting, I cannot read the resource.

I wonder if the Merged Binary (Main App) has a library merged, and if I need to add a separate framework to the App Bundle simply because of resources.

Thank you.

  • After posting the question, I looked up the flag -no_merged_libraries_hook at the https://keith.github.io/xcode-man-pages/ld.1.html page, and it seems that bundle lookup is performed on the merge binaries, not on the mergeable framework.

    But in order to actually use resources, I think we need to include resources in the merged binary itself.

Add a Comment

Accepted Reply

it takes advantage of static linking

Yes.

but I think the bundle size for the app will be bigger

No. The Mach-O image for the framework (Test736404.app/Contents/Frameworks/FFF1.framework/Versions/A/FFF1) is effectively empty. The code that would normally be there has been statically linked into the app itself (Test736404.app/Contents/MacOS/Test736404). So, the framework exists on disk as:

  • An empty Mach-O image

  • A file system hierarchy containing your resources

The first won’t significantly increase the size of your app and the second is required.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Replies

But in order to actually use resources, I think we need to include resources in the merged binary itself.

That depends on what you mean by “binary” (-:

Consider an app, Test736404, with two embedded frameworks, FFF1 and FFF2. The app has Create Merged Binary set to Automatic, and the frameworks both have Build Mergeable Library set. Each framework has code like this:

import Foundation

public class FFF1Root {
    public init() {
    }
    public func test() {
        print("FFF1Root.test()")
        let b = Bundle(for: FFF1Root.self)
        let u = b.url(forResource: "test", withExtension: "txt")!
        let s = try! String(contentsOf: u, encoding: .utf8)
        print(s)
    }
}

and an test.txt resource file.

Note For the second framework, replace FFF1 with FFF2 everywhere.

When you build the app for release [1], you get a structure like this:

% find Test736404.app 
Test736404.app
…
Test736404.app/Contents/MacOS/Test736404
…
Test736404.app/Contents/Frameworks/FFF2.framework/Versions/A/Resources/test.txt
…
Test736404.app/Contents/Frameworks/FFF2.framework/Versions/A/FFF2
…
Test736404.app/Contents/Frameworks/FFF1.framework/Versions/A/Resources/test.txt
…
Test736404.app/Contents/Frameworks/FFF1.framework/Versions/A/FFF1
…

The FFF1 and FFF2 frameworks still exist, but the Versions/A/FFF1 and Versions/A/FFF1 files are just vestigial. The linker has placed the implementations of FFF1Root and FFF2Root in the app itself, Contents/MacOS/Test736404. OTOH, both test.txt files are present in their respective frameworks, and Bundle(for:) is able to return the right framework for each class.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Remember that Create Merged Binary only affects release builds.

Thank you for your answer. Eskimo!

Looking at your answer, it seems that the framework should be embeds on the app even though the framework is statically linked to the app, is it correct that I understand? If I understand correctly, it takes advantage of static linking, but I think the bundle size for the app will be bigger

it takes advantage of static linking

Yes.

but I think the bundle size for the app will be bigger

No. The Mach-O image for the framework (Test736404.app/Contents/Frameworks/FFF1.framework/Versions/A/FFF1) is effectively empty. The code that would normally be there has been statically linked into the app itself (Test736404.app/Contents/MacOS/Test736404). So, the framework exists on disk as:

  • An empty Mach-O image

  • A file system hierarchy containing your resources

The first won’t significantly increase the size of your app and the second is required.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"