首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在应用程序中包含iOS 12+应用程序接口调用,同时仍然支持iOS<12用户?

如何在应用程序中包含iOS 12+应用程序接口调用,同时仍然支持iOS<12用户?
EN

Stack Overflow用户
提问于 2019-11-16 22:54:36
回答 2查看 323关注 0票数 4

请参阅下面的简单代码片段。对于我的应用程序,我需要包含对CoreTelephony的一部分CTCellularPlanProvisioningRequest的调用。此类可用于iOS 12+。我在iOS 10和11上仍然有一个庞大的用户群需要支持,所以我将我的调用包含在'if #available`中,如下所示:

代码语言:javascript
复制
if #available(iOS 12, *) {
    let cpProvioningRequest = CTCellularPlanProvisioningRequest()
    print("cpProvioningRequest: \(cpProvioningRequest)")
} else {
    print("No iOS 12+ available")
}

不过,当我在运行iOS 11.4的模拟器设备上运行此代码时,应用程序会在加载阶段崩溃。在运行iOS 12或13的模拟器上,调用工作正常。当我用CTCellularPlanProvisioningRequest注释掉这两行代码时,这个应用程序可以在fine iOS 11上运行。

我在11.4上得到的错误是:

代码语言:javascript
复制
dyld: Symbol not found: _OBJC_CLASS_$_CTCellularPlanProvisioningRequest
  Referenced from: /Users/frans/Library/Developer/CoreSimulator/Devices/25E0C601-5D38-4B41-A807-3575BC23AAB9/data/Containers/Bundle/Application/4DC30320-77F6-4107-A83A-EF0F5C5B464D/TestNewAPIOldiOS.app/TestNewAPIOldiOS
  Expected in: /Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 11.4.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/CoreTelephony.framework/CoreTelephony
 in /Users/frans/Library/Developer/CoreSimulator/Devices/25E0C601-5D38-4B41-A807-3575BC23AAB9/data/Containers/Bundle/Application/4DC30320-77F6-4107-A83A-EF0F5C5B464D/TestNewAPIOldiOS.app/TestNewAPIOldiOS

我的线程视图显示:

代码语言:javascript
复制
dyld`__abort_with_payload:
    0x10d2500d4 <+0>:  movl   $0x2000209, %eax          ; imm = 0x2000209 
    0x10d2500d9 <+5>:  movq   %rcx, %r10
    0x10d2500dc <+8>:  syscall 
->  0x10d2500de <+10>: jae    0x10d2500e8               ; <+20>
    0x10d2500e0 <+12>: movq   %rax, %rdi
    0x10d2500e3 <+15>: jmp    0x10d24e601               ; cerror_nocancel
    0x10d2500e8 <+20>: retq   
    0x10d2500e9 <+21>: nop    
    0x10d2500ea <+22>: nop    
    0x10d2500eb <+23>: nop    

我的问题是:如何确保此对CTCellularPlanProvisioningRequest的调用在iOS 12+上正常工作,同时不会对iOS<12造成崩溃或其他负面影响?

为了完整起见,下面是完整的类。部署目标设置为iOS 10。我知道CTCellularPlanProvisioningRequest的先决条件,在我的完整应用程序中,我已经填写了这些。我在Xcode11.2.1和Xcode10上进行了测试,我尝试了几个不同的设备模拟器,运行各种iOS12之前的版本。我无法在运行iOS 10或11的物理设备上运行此程序,因为我无法访问它们。

代码语言:javascript
复制
//  TestNewAPIOldiOS
//
//  Created by Frans Glorie on 15/11/2019.
//  Copyright © 2019 Frans Glorie. All rights reserved.
//

import UIKit
import CoreTelephony

class ViewController: UIViewController {

    // Functions
    override func viewDidLoad() {
        super.viewDidLoad()

        let ctSubscriber = CTSubscriber()
        print("Subscriber: \(ctSubscriber)")

        if #available(iOS 12, *) {
            let cpProvioningRequest = CTCellularPlanProvisioningRequest()
            print("cpProvioningRequest: \(cpProvioningRequest)")
        } else {
            print("No iOS 12+ available")
        }
    }
}

谢谢!

法兰斯

EN

回答 2

Stack Overflow用户

发布于 2019-12-08 19:44:26

我终于收到了苹果的回复,他们的建议似乎起作用了:“在审查了您的反馈后,我们为您提供了一些额外的信息。

在我们这方面有一个已知的问题,但您可以通过显式地与框架进行弱链接来解决这个问题。为此,请转到构建阶段,“将二进制文件与库链接”部分,在其中添加CoreTelephony,并将status字段从required设置为optional。这就修复了现在的崩溃。我们已经为你提到的问题提交了一个单独的bug,并将在未来的版本中修复它。谢谢!“

我试过了,它似乎工作得很好。还需要进一步的测试,但这似乎解决了问题。希望这对任何人都有帮助。

不幸的是,这里之前发布的解决方法都只解决了问题的一部分。

票数 2
EN

Stack Overflow用户

发布于 2019-11-23 20:40:18

由于CTCellularPlanProvisioningRequest是作为变通方法的NSObject子类,因此您可以使用iOS11及更低版本的failsafe通过obj-c动态运行时对其进行实例化

代码语言:javascript
复制
if let classAvailable = NSClassFromString("CTCellularPlanProvisioningRequest") {
    let cpProvioningRequest = (classAvailable as! NSObject).init()
    print("cpProvioningRequest: \(cpProvioningRequest)")
}

这样做将不会在二进制文件中创建对_OBJC_CLASS_$_CTCellularPlanProvisioningRequest符号的依赖关系。

更新

在我的答案中的OP注释之后,下面是动态调用addPlan的代码。

代码语言:javascript
复制
if let requestClassAvailable = NSClassFromString("CTCellularPlanProvisioningRequest"),
    let provisioningClassAvailable = NSClassFromString("CTCellularPlanProvisioning") {
    if let cpProvisioningRequestType = requestClassAvailable as? NSObject.Type,
        let cpProvisioningType = provisioningClassAvailable as? NSObject.Type {

        let cpProvisioningRequest = cpProvisioningRequestType.init()
        let cpProvisinioning = cpProvisioningType.init()

        let addPlanSelector = NSSelectorFromString("addPlanWith:completionHandler:")
        if let methodIMP = cpProvisinioning.method(for: addPlanSelector) {
            unsafeBitCast(methodIMP,to:(@convention(c)(Any?,Selector,Any?,((UInt) -> Void)?)->Void).self)(cpProvisinioning, addPlanSelector, cpProvisioningRequest, nil)   
        }
    }
}

这还远不是完美的,但也许在你得到苹果的答复之前,它就足够了。

顺便说一句,我在我的答案here中介绍了Swift中动态调用的详细信息

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58891940

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档