ios 开发 调用siri



(Problem)

Calling multiple functions in a sequence is complicated.

依次调用多个函数很复杂。

(Solution)

We have to build a chain in which its items communicate asynchronously. We can able to add new items to any position of the chain. PromiseKit is a tool which helps us to build the chain, in a structure.

我们必须建立一条链,在链中其各项进行异步通信。 我们可以将新项目添加到链的任何位置。 PromiseKit是一个工具,可以帮助我们在结构中构建链。




ios 多个UITapGestureRecognizer 设置优先级 多设备siri优先级_android

Have you ever called more than one function at the launching of an application? Of course, you have called. Probably, you have checked camera permission and location permission at the same time. What about other checks such as if the user logged in, sending the request for the first data, etc. I am going to use PromiseKit to clean this mess.

您是否曾经在应用程序启动时调用过多个功能? 当然,您已经打过电话。 可能是您同时检查了相机许可和位置许可。 关于其他检查,例如用户是否登录,发送对第一个数据的请求等,该如何处理?我将使用PromiseKit清除此混乱情况。

(What are we going to do?)

We are going to build a simple one-screen project. As soon as the first screen launches, we will check if the user did log in, camera and location permission. After the permissions, we will send two requests parallelly. When the chain completed, we will give data to the view. If any error occurred before complete the chain, the chain will interrupt and we will show an alert. I have finished a working example here. Please download and open it before we start. Don’t forget to install pods before opening it.

我们将构建一个简单的单屏项目。 第一个屏幕启动后,我们将检查用户是否登录,摄像头和位置权限。 获得许可后,我们将并行发送两个请求。 当链完成时,我们将数据提供给视图。 如果在完成链条之前发生任何错误,链条将中断,我们将显示警报。 我在这里完成了一个工作示例。 在开始之前,请下载并打开它。 不要忘记在打开吊舱之前先安装吊舱。

We have one controller. Therefore we are going to analyze step by step.

我们有一个控制器。 因此,我们将逐步分析。

class HomeController: UIViewController {
    
    // MARK: - Properties
    private let infoLabel: UILabel = {
        let label: UILabel = UILabel()
        label.frame = CGRect(origin: CGPoint.zero, size: CGSize(width: UIScreen.main.bounds.width, height: 200))
        label.textAlignment = NSTextAlignment.center
        return label
    }()
    
    private var isUserDidLogin: Bool = true
    private let locationManager: CLLocationManager = CLLocationManager()
    
    private let disposeBag: DisposeBag = DisposeBag()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = UIColor.white
        view.addSubview(infoLabel)
        
        logState("Initializing view")
        
        // Shows loading indicator here
        
        firstly {
            self.checkIfUserDidLogin()
        }.then {
            self.checkCameraPermission()
        }.then {
            self.checkLocationPermission()
        }.then {
            when(fulfilled: [self.sendFirstRequest(), self.sendSecondRequest()])
        }.done {
            self.initView()
        }.catch { error in
            AlertManager.showError(message: (error as! ErrorModel).message, controller: self)
        }.finally {
            // Hides loading indicator here
        }
    }
}


// MARK: - Private Functions
private extension HomeController {
    
    func checkIfUserDidLogin() -> Promise<Void> {
        logState("Checking if user did login")
        
        let (promise, seal) = Promise<Void>.pending()
        
        if isUserDidLogin {
            seal.fulfill(())
        } else {
            seal.reject(ErrorModel(message: "User did not login"))
        }
        
        return promise
    }
    
    func checkCameraPermission() -> Promise<Void> {
        logState("Checking camera permission")
        
        let (promise, seal) = Promise<Void>.pending()
        
        switch AVCaptureDevice.authorizationStatus(for: AVMediaType.video) {
        case AVAuthorizationStatus.authorized:
            seal.fulfill(())
        case AVAuthorizationStatus.notDetermined:
            AVCaptureDevice.requestAccess(for: AVMediaType.video) { (granted) in
                if granted {
                    seal.fulfill(())
                } else {
                    seal.reject(ErrorModel(message: "Camera permission denied"))
                }
            }
            
        case AVAuthorizationStatus.denied:
            seal.reject(ErrorModel(message: "Camera permission has been already denied"))
        default:
            seal.reject(ErrorModel(message: "Occured unknown error"))
        }
        
        return promise
    }
    
    func checkLocationPermission() -> Promise<Void> {
        logState("Checking location permission")
        
        let (promise, seal) = Promise<Void>.pending()
        
        switch CLLocationManager.authorizationStatus() {
        case CLAuthorizationStatus.authorizedAlways, CLAuthorizationStatus.authorizedWhenInUse:
            seal.fulfill(())
        case CLAuthorizationStatus.notDetermined:
            locationManager.requestAlwaysAuthorization()
            
            locationManager.rx
            .didChangeAuthorization
            .subscribe(onNext: { _, status in
                switch status {
                case CLAuthorizationStatus.authorizedAlways, CLAuthorizationStatus.authorizedWhenInUse:
                    seal.fulfill(())
                case CLAuthorizationStatus.denied:
                    seal.reject(ErrorModel(message: "Location permission has been already denied"))
                default:
                    seal.reject(ErrorModel(message: "Occured unknown error"))
                }
            }).disposed(by: disposeBag)
        case CLAuthorizationStatus.denied:
            seal.reject(ErrorModel(message: "Location permission has been already denied"))
        default:
            seal.reject(ErrorModel(message: "Occured unknown error"))
        }
        
        return promise
    }
    
    func sendFirstRequest() -> Promise<Void> {
        logState("Sending first request")
        
        let (promise, seal) = Promise<Void>.pending()
        
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3) {
            self.logState("Got first request")
            seal.fulfill(())
        }
        
        return promise
    }
    
    func sendSecondRequest() -> Promise<Void> {
        logState("Sending second request")
        
        let (promise, seal) = Promise<Void>.pending()
        
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 6) {
            self.logState("Got second request")
            seal.fulfill(())
        }
        
        return promise
    }
    
    func initView() {
        logState("Initialized view successfully")
    }
    
    func logState(_ state: String) {
        infoLabel.text = state
        NSLog(state)
    }
}

Between 41. and 55. lines, you can see the chain. PromiseKit has some block keywords to know before we start.

在41.和55.行之间,您可以看到链。 PromiseKit在我们开始之前有一些块关键字要知道。

firstly: The first block of the chain.

首先 :链的第一块。

then: The block after the firstly. Can be used more than once.

然后 :先块之后。 可以多次使用。

done: The block which calls after the chain completed successfully.

done :链成功完成后调用的块。

catch: The block which handles error.

catch :处理错误的块。

finally: The block which calls after the chain completed with success or failure.

最终 :链成功或失败后调用的块。

In the firstly block, we call the checkIfUserDidLogin() function. The function returns Promise<Void>, which means a promise with Void type. I have declared (promise, seal) object in 65. line. Seal is a resolver that resolves the promise as success or failure. The seal returns success 68. line and returns fail in 70. line.

在第一个块中,我们调用checkIfUserDidLogin()函数。 该函数返回Promise <Void> 哪一个 表示Void类型的承诺。 我已经在65行中声明了(承诺,密封)对象。 Seal是一个将诺言视为成功还是失败的解决者。 密封返回成功68行,失败返回70行。

You can see the same logic in the checkCameraPermission() and checkLocationPermission() functions.

您可以在checkCameraPermission()checkLocationPermission()函数中看到相同的逻辑。

There is a specific case exclude of them, how can we call two or more functions in parallel? Assume that we want to make multiple service calls at the same time because these multiple services are not related to each other, so they don’t have to wait for each other.

在特定情况下不包括它们,我们如何并行调用两个或多个函数? 假设我们想同时进行多个服务调用,因为这些多个服务彼此不相关,因此它们不必彼此等待。

PromiseKit has a function for that, when. In the 48. line I call the when function and give it an array which includes sendFirstRequest() and sendSecondRequest() functions. These two functions are called at the same time. When two of them are done, the chain completes, so done block at the 50. line works. It means the screen is ready to initialize view.

PromiseKit为此提供了一个功能, 。 在第48行中,我调用when函数,并给它一个包含sendFirstRequest()和sendSecondRequest()函数的数组。 这两个函数被同时调用。 当其中两个完成时,链完成,因此完成的块在50行工作。 这意味着屏幕已准备好初始化视图。

As the chain has been completed successfully, we can dismiss the loading view in the finally block in the 54. line.

由于链已成功完成,因此我们可以在54.行的final块中取消加载视图。

错误呢? (What about errors?)

The functions we called in the chain, may throw an error, so we have to make sure the chain is broken safely. PromiseKit provides the catch block, which returns Error class, to handle errors.

我们在链中调用的函数可能会引发错误,因此我们必须确保链已安全断开。 PromiseKit提供catch块,该块返回Error类,以处理错误。

In the 70. line, I called reject() function to break the chain, which means the other functions in the line will not be called. After calling the reject function, the catch block calls. I have created an error class ErrorModel, which must implement Error class.

在70行中,我调用了reject()函数来中断链,这意味着该行中的其他函数将不会被调用。 调用拒绝函数后,catch块将调用。 我创建了一个错误类ErrorModel,该类必须实现Error类。

(Conclusion)

PromiseKit provides some well-structured blocks to manage a chain. It is super easy to add or remove a call over the chain.

PromiseKit提供了一些结构良好的块来管理链。 在链上添加或删除呼叫非常容易。

Unlike PromiseKit, if you are a ‘native’ boy, you can build your similar chain by using DispatchGroup.

与PromiseKit不同,如果您是“本地”男孩,则可以使用DispatchGroup来构建类似的链。

Full project → https://github.com/demirciy/FastlaneExampleiOS

完整项目→ https://github.com/demirciy/FastlaneExampleiOS

Keep following for more!

继续关注以获取更多!

Note: Please comment if any correction or better way exists then you can help everyone.

注意 :如果有任何更正或更好的方法,请发表评论,然后您可以为所有人提供帮助。


翻译自: https://medium.com/flawless-app-stories/how-to-sequence-calls-with-promisekit-in-ios-development-3af278d49c04

ios 开发 调用siri