_NEHotspotConfigurationErrorDomain

Unfortunately My App won't run anymore because of a missing symbol:


dyld: Symbol not found: _NEHotspotConfigurationErrorDomain

Referenced from: /private/var/containers/Bundle/Application/905F594E-CBAC-43C6-8B9D-7EF29E3C3E6A/Runner.app/Frameworks/part1.framework/part1

Expected in: /System/Library/Frameworks/NetworkExtension.framework/NetworkExtension

in /private/var/containers/Bundle/Application/905F594E-CBAC-43C6-8B9D-7EF29E3C3E6A/Runner.app/Frameworks/dashcam.framework/part1


This is a new error and only started showing this morning when I tried compiling our iOS App with Xcode11 GM Seed.

Our deployment target is 12.4.



Has the symbol moved?

Accepted Reply

Bug number FB7285688.

Thanks for that.

Are those references to the configuration error what causes the trouble?

Yes. If I comment out lines 11 through 29 of your test code, the reference to

NEHotspotConfigurationErrorDomain
goes away. If I then uncomment lines 28 through 29, it comes back.

Unfortunately can not use

if #available(iOS 13.0, *) {
as it would still crash.

Right. The

#available
guard prevents you from running that code but the issue here is that the linker has added a strong reference to the symbol, and it does that because it thinks the symbol is available all the way back to iOS 11.

It’s tricky to work around this because just touching the error codes, as shown below, triggers the reference )-:

print(NEHotspotConfigurationError.Code.invalid)

The best option I can think of right now is to bounce into

NSError
space:
if let error = error as NSError? {
    switch (error.domain, error.code) {
        … case clauses here …
    }
}

Then declare the

NSError
equivalents of the required values under a new name:
let NEHotspotConfigurationErrorDomainWorkaround: String = {
    … tricky code here …
}()

enum NEHotspotConfigurationErrorWorkaround: Int {
    case alreadyAssociated = 13
}

Note It’s OK to hard code the 13 here because the error codes are coming from Objective-C, which means they can’t change (changing them would break Objective-C clients).

Now, implement the

case
clause as follows:
case (NEHotspotConfigurationErrorDomainWorkaround, NEHotspotConfigurationErrorWorkaround.alreadyAssociated.rawValue):
    … your code here …

Then all you need to do is write the body of

NEHotspotConfigurationErrorDomainWorkaround
. Easy! Nah, not really. Here’s what I eventually come up with:
let NEHotspotConfigurationErrorDomainWorkaround: String = {
    let RTLD_DEFAULT = UnsafeMutableRawPointer(bitPattern: -2)
    if let sym = dlsym(RTLD_DEFAULT, "_NEHotspotConfigurationErrorDomain") {
        // If `NEHotspotConfigurationErrorDomain` is available, use its value.
        return String(cString: sym.assumingMemoryBound(to: CChar.self))
    } else {
        // If not, meaning we’re on iOS 12 or earlier, use a hard-coded value
        // that matches the value used on those systems.
        return "NEHotspotConfigurationErrorDomain"
    }
}()

Wow, that’s a whole barrel of un-fun.

WARNING I’ve tested bits of this code but haven’t done a full end-to-end test on iOS 12 and iOS 13. Let me know how it goes (-:

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Replies

This seems to be a bug in the availability macros for

NEHotspotConfigurationErrorDomain
. The header lists it as follows:
NEHSCFG_EXPORT NSString * const NEHotspotConfigurationErrorDomain 
API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(macos, watchos, tvos);

but in reality it’s not available prior to iOS 13. Please file a bug about that, then post your bug number here, just for the record.

The easiest workaround is to simply not reference that symbol. After all, we got by without it prior to iOS 13 (-:

Alternatively, you can load up the symbol dynamically, using your preferred dynamic linking API (

NSBundle
,
CFBundle
,
dlopen
). Let us know if you need help with that.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Bug number FB7285688.


Yes, please share more details, @eskimo.


I am puzzled to how to load a single symbol like this. We are not referencing the symbol, but the swift version of it.


let configuration = NEHotspotConfiguration(
    ssid: device.address,
    passphrase: password ?? "",
    isWEP: false
)
configuration.joinOnce = false

NEHotspotConfigurationManager.shared.apply(configuration) { (error) in
    if let error = error {
        switch error {
        case NEHotspotConfigurationError.invalid,
                NEHotspotConfigurationError.invalidSSID,
                NEHotspotConfigurationError.invalidWPAPassphrase,
                NEHotspotConfigurationError.invalidWEPPassphrase,
                NEHotspotConfigurationError.invalidEAPSettings,
                NEHotspotConfigurationError.invalidHS20Settings,
                NEHotspotConfigurationError.invalidHS20DomainName,
                NEHotspotConfigurationError.userDenied,
                NEHotspotConfigurationError.`internal`,
                NEHotspotConfigurationError.pending,
                NEHotspotConfigurationError.systemConfiguration,
                NEHotspotConfigurationError.unknown,
                NEHotspotConfigurationError.joinOnceNotSupported,
                NEHotspotConfigurationError.applicationIsNotInForeground:
            os_log(.error, log: self.log, "error while connecting: %{public}s", "\(error)")
            self.startAutoConnect()
            
        case NEHotspotConfigurationError.alreadyAssociated:
            os_log(.default, log: self.log, "already connected")
        default:
            if (error as NSError).code == 13 {
                return emitter(.completed)
            }
            
            os_log(.error, log: self.log, "unknown error while connecting: %{public}s", "\(error)")
            self.startAutoConnect()
        }
        return emitter(.completed)
    }
}


Are those references to the configuration error what causes the trouble?

Have now solved the APp crashes by commenting the code above.

Unfortunately can not use if #available(iOS 13.0, *) { as it would still crash.

Bug number FB7285688.

Thanks for that.

Are those references to the configuration error what causes the trouble?

Yes. If I comment out lines 11 through 29 of your test code, the reference to

NEHotspotConfigurationErrorDomain
goes away. If I then uncomment lines 28 through 29, it comes back.

Unfortunately can not use

if #available(iOS 13.0, *) {
as it would still crash.

Right. The

#available
guard prevents you from running that code but the issue here is that the linker has added a strong reference to the symbol, and it does that because it thinks the symbol is available all the way back to iOS 11.

It’s tricky to work around this because just touching the error codes, as shown below, triggers the reference )-:

print(NEHotspotConfigurationError.Code.invalid)

The best option I can think of right now is to bounce into

NSError
space:
if let error = error as NSError? {
    switch (error.domain, error.code) {
        … case clauses here …
    }
}

Then declare the

NSError
equivalents of the required values under a new name:
let NEHotspotConfigurationErrorDomainWorkaround: String = {
    … tricky code here …
}()

enum NEHotspotConfigurationErrorWorkaround: Int {
    case alreadyAssociated = 13
}

Note It’s OK to hard code the 13 here because the error codes are coming from Objective-C, which means they can’t change (changing them would break Objective-C clients).

Now, implement the

case
clause as follows:
case (NEHotspotConfigurationErrorDomainWorkaround, NEHotspotConfigurationErrorWorkaround.alreadyAssociated.rawValue):
    … your code here …

Then all you need to do is write the body of

NEHotspotConfigurationErrorDomainWorkaround
. Easy! Nah, not really. Here’s what I eventually come up with:
let NEHotspotConfigurationErrorDomainWorkaround: String = {
    let RTLD_DEFAULT = UnsafeMutableRawPointer(bitPattern: -2)
    if let sym = dlsym(RTLD_DEFAULT, "_NEHotspotConfigurationErrorDomain") {
        // If `NEHotspotConfigurationErrorDomain` is available, use its value.
        return String(cString: sym.assumingMemoryBound(to: CChar.self))
    } else {
        // If not, meaning we’re on iOS 12 or earlier, use a hard-coded value
        // that matches the value used on those systems.
        return "NEHotspotConfigurationErrorDomain"
    }
}()

Wow, that’s a whole barrel of un-fun.

WARNING I’ve tested bits of this code but haven’t done a full end-to-end test on iOS 12 and iOS 13. Let me know how it goes (-:

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Are there some news regarding this topic? I mean...iOS 13 is out now with a few updates already, and this is still an issue. I guess it won´t be solved ever in the iOS SDK code?

I believe that the iOS 13.2 SDK in Xcode 11.1b2 has a ‘fix’ for this (r. 54134493), where fix is in quotes because it just reverts to the pre-iOS 13 SDK definition for

NEHotspotConfigurationError
.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"