Weak linking of frameworks with greater deployment targets

Right. Like (modern) Objective-C, Swift infers which things to weak-link based on availability and deployment target. You should be able to do that just fine in your framework: always load it, but mark the new stuff as guarded with availability.

2 Likes

Looks like it does not work at the framework level. While it work fine with symbols, some tests with Xcode 11 shows that Xcode does not properly infer that the whole SwiftUI framework must be weak-link.

No, no. Weak-linking the whole framework does take some extra work (which I'd have to go look up, or which you can brute-force by adding -weak_framework MyFramework to all your clients' "Other Linker Flags"), but my point was that you shouldn't have to weak-link the whole framework for the situation you described:

  • Your deployment target should be iOS 12
  • You should build against the iOS 13 SDK
  • You should mark any uses of SwiftUI with @available.
  • If this fails on iOS 12, it means that some part of SwiftUI is missing its own availability annotations.
1 Like

Yeah, it doesn’t work with tests at all. You have to guard availability inside the test methods for it to be effective.

I'll be surprise it works by just declaring symbols as weak.
dyld try to unconditionally load any frameworks that is declared on the load commands at launch time and abort if one is missing.

I have a dynamic library containing the following code:


int test() {
	return 0;
}

I build it using clang -shared -o libFoo.dylib lib.c

I then create a simple app with no reference on libFoo symbols:


#include <stdio.h>

int main() {
	printf("hello world\n");
	return 0;
}

Built it: clang libFoo.dylib -o app app.c

Now, remove libFoo.dylib and try to launch the tool:

$ ./app
dyld: Library not loaded: libFoo.dylib
  Referenced from: ./app
  Reason: image not found
fish: './app' terminated by signal SIGABRT (Abort)

Which is the exact same error that I have trying to launch an app using SwiftUI on previous OS, dyld abort as it can't found SwiftUI framework image.

That should definitely work as long as the framework is indeed weak-linked. If it's not, then either the SwiftUI folks have missed some availability (more likely), or there's something in Xcode (or in your project specifically?) that's causing it to be strong-linked. It's supposed to Just Work™. Please file a bug at https://feedbackassistant.apple.com!

Feel free to have a look at it: FB6170959

1 Like

I also filed a similar issue FB6187287 where I could not get weak linking of SwiftUI to work when using UIViewControllerRepresentable. Ideally I want to allow compilation with both Xcode 10 and Xcode 11, with a deployment target of iOS 12.

My motivation here is to enable a development workflow with Xcode Previews that is backwards compatible to iOS 12.

@jrose So, if I get things correctly, I should be able to simply write import SwiftUI without problems, even if my app's deployment target is iOS 12, and only insert availability check for types of SwiftUI, right? And if that doesn't work that means there's an issue with SwiftUI, right?

Yep, that's the intent. There's also a chance there's a bug in the compiler itself if it uses a strong symbol reference instead of a weak one for a declaration that does have proper availability annotations.

Wouldn't that also mean that checks like those would be unnecessary:

#if os(OSX)
import AppKit
#else
import UIKit
#endif

and one could just write

import AppKit
import UIKit

?

That can't be, right? I'm totally confused... :neutral_face:

Weak-linking says "this must be present at link-time, but does not need to be present at run time". It's also a linker consideration; the compiler always needs things to be present (so it knows how to use them).

1 Like

Got it! Thanks for your patience ;)

This seems to be a bug still (Xcode 11.2.1) in Swift package manager projects at least. In my case I was not using SwiftUI but was importing CryptoKit and running on macOS 10.14.6 (CryptoKit is macOS 15+ only).

FB7471728

This is a significant bummer, needless to say.

@haikuty This bug should be fixed in swift-5.1-branch (and master, for that matter). Can you see if a Swift 5.1 development snapshot toolchain from swift.org works for you? You won't be able to submit to the App Store with that compiler, but it will at least confirm that the fix will work when it ships.

1 Like

Thank you. Unfortunately, the November 7, 2019 toolchain does not seem to have fixed the issue I'm seeing.

Seems like I should make a simple test package that demonstrates the issue and file a bug somewhere…

A reproducer would be very helpful. You may want to try a “trunk” (master branch) snapshot too—it’s possible that I’m mistaken about it being fixed in swift-5.1-branch.

Edit: Actually, I think you may have downloaded the 5.1.2 release at the top of the page. You should try the one under the “Swift 5.1 Development” heading, which is dated December 3, 2019 (or later).

:) Yep! Good catch! Going too fast… :)
Well, in the mean time, I now have a simple test program to demonstrate it…

Hmmm. Still seeing the issue.

Confirmed from build log that it's using the Dec 3 master trunk snapshot:

/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2019-12-03-a.xctoolchain/usr/bin/swiftc

repo is here if you want to try:

https://github.com/haikusw/testwlbug

I just build it (inside Xcode so as to make selecting the toolchain easy) and then execute the built object in Terminal from within the DerivedData Build folder.

You have to build and run it (or at least run it…) on macOS 10.14.6 (or macOS < 10.15.x) for the CryptoKit dylib to be missing and thus see the problem which is:

dyld: Library not loaded: /System/Library/Frameworks/CryptoKit.framework/Versions/A/CryptoKit
Referenced from: /Users/haikuty/Desktop/testwlbug 2019-12-04 13-18-42/Products/usr/local/bin/testwlbug
Reason: image not found
Abort trap: 6

I'm using Xcode Version 11.2.1 (11B500) running in macOS 10.14.6 (18G103).

Quite possible I'm using if #available() incorrectly. The "*" at the end isn't something I wanted to include but Xcode wouldn't compile without it and suggested it as the "fix-it". ¯_(ツ)_/¯

Think I may have found the issue?

I disassembled the generated binary with Hopper and there appears to be a lazy witness table accessor for a CryptoKit stuff that the compiler seems to have generated that is outside the test for #available os; that seems like it could make the linker think weak linking was not possible... ???
(I haven't looked at what the Swift compiler is doing before so it's likely I'm out in left field here, but it's the only thing I can see in the binary that looks suspicious…).

Here's the routine as Hopper disassembled it into code:

int _$s9CryptoKit8InsecureO10SHA1DigestVAeA0E0AAWl() {
    rax = *lazy protocol witness table cache variable for type CryptoKit.Insecure.SHA1Digest and conformance CryptoKit.Insecure.SHA1Digest : CryptoKit.Digest in CryptoKit;
    var_8 = rax;
    if (rax == 0x0) {
            rax = type metadata accessor for CryptoKit.Insecure.SHA1Digest(0xff);
            rax = swift_getWitnessTable(*protocol conformance descriptor for CryptoKit.Insecure.SHA1Digest : CryptoKit.Digest in CryptoKit, rax, rax);
            *lazy protocol witness table cache variable for type CryptoKit.Insecure.SHA1Digest and conformance CryptoKit.Insecure.SHA1Digest : CryptoKit.Digest in CryptoKit = rax;
            var_8 = rax;
    }
    rax = var_8;
    return rax;
}

UPDATE: (since this forum doesn't allow more than 3 replies in a row by the same person (o.O)):

Filed a bug at bugs . swift . org since it seems like that might be a better place:

https://bugs.swift.org/browse/SR-11907

Terms of Service

Privacy Policy

Cookie Policy