4 Replies
      Latest reply on Sep 18, 2019 1:01 AM by eskimo
      sebroth Level 1 Level 1 (0 points)

        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?

        • Re: _NEHotspotConfigurationErrorDomain
          eskimo Apple Staff Apple Staff (11,955 points)

          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"

            • Re: _NEHotspotConfigurationErrorDomain
              sebroth Level 1 Level 1 (0 points)

              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?

            • Re: _NEHotspotConfigurationErrorDomain
              eskimo Apple Staff Apple Staff (11,955 points)

              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"