swift 4中的完成处理程序

Posted

技术标签:

【中文标题】swift 4中的完成处理程序【英文标题】:Completion Handlers in swift 4 【发布时间】:2018-03-12 07:25:18 【问题描述】:

我有一个关于使用完成处理程序的问题。我的 ios 程序中有 3 层,ViewController->Service->Networking。我需要通过视图控制器的 API 调用加载一些数据。

我在 ViewController 中定义了函数(completionHandlers),一旦数据请求完成就应该执行,并且在只存在两层时实现完成处理程序时很舒服,但在以下情况下会感到困惑:

DashboardViewController.swift

import UIKit

@IBDesignable class DashboardViewController: UIViewController 

    @IBOutlet weak var stepCountController: ExpandedCardView!
    var articles:[Article]?
    let requestHandler = RequestHandler()
    let dashboardService = DashboardService()

    override func viewDidLoad() 
        super.viewDidLoad()
        dashboardService.getDashboardData(completionHandler: getDashboardDataCompletionHandler)
    

    func getDashboardDataCompletionHandler(withData: DashboardDataRequest) 
        print(withData)
    
 

DashboardService.swift

import Foundation

class DashboardService: GeneralService 

    var requestHandler: DashboardRequestHandler

    override init() 
        requestHandler = DashboardRequestHandler()
        super.init()
    

    //this function should execute requestHandler.requestDashboardData(), and then execute convertDashboardData() with the result of previous get request
    func getDashboardData(completionHandler: @escaping (DashboardDataRequest) -> Void) 
        //on network call return
        guard let url = URL(string: apiResourceList?.value(forKey: "GetDashboard") as! String) else  return 
        requestHandler.requestDashboardData(url: url, completionHandler: convertDashboardData(completionHandler: completionHandler))
    

    func convertDashboardData(completionHandler: (DashboardDataRequest) -> Void) 
        //convert object to format acceptable by view
    

DashboardRequestHandler.swift

import Foundation

class DashboardRequestHandler: RequestHandler 

    var dataTask: URLSessionDataTask?

    func requestDashboardData(url: URL, completionHandler: @escaping (DashboardDataRequest) -> Void) 
        dataTask?.cancel()

        defaultSession.dataTask(with: url, completionHandler: (data, response, error) in
            if error != nil 
                print(error!.localizedDescription)
            

            guard let data = data else 
                return
            

            do 
                let decodedJson = try JSONDecoder().decode(DashboardDataRequest.self, from: data)
                completionHandler(decodedJson)
             catch let jsonError 
                print(jsonError)
            

        ).resume()
    

如果你看一下 DashboardService.swift 中的注释,我的问题就很明显了。我将完成处理程序从 ViewController 传递给 Service,Service 有自己的完成处理程序,它传递给 RequestHandler,其中应在服务完成处理程序 (convertDashboardData()) 之后执行视图控制器完成处理程序 (getDashboardDataCompletionHandler)

请帮助我阐明如何实现这一点。我是在尝试像这样链接完成处理程序而犯了设计错误,还是我遗漏了一些明显的东西。

谢谢

--编辑-- 我的请求处理程序实现如下:

import Foundation

class RequestHandler 
    //    let defaultSession = URLSession(configuration: .default)
    var defaultSession: URLSession!

    init() 
        guard let path = Bundle.main.path(forResource: "Api", ofType: "plist") else 
            print("Api.plist not found")
            return
        
        let apiResourceList = NSDictionary(contentsOfFile: path)
        let config = URLSessionConfiguration.default
        if let authToken = apiResourceList?.value(forKey: "AuthToken") 
            config.httpAdditionalHeaders = ["Authorization": authToken]
        
        defaultSession = URLSession(configuration: config)
    

【问题讨论】:

你能提供你对RequestHandler类型的定义吗? 完成。这只是用与我的服务器通信所需的标头初始化我的 URLSession 如果您在DashboardService.swift 中将convertDashboardData(completionHandler: completionHandler) 行切换为completionHandler,您拥有的代码应该可以工作 关于那条评论://convert object to format acceptable by view - 如果你想实现这一点,你的DashboardService 也应该扮演一个适配器的角色,所以getDashboardData 方法的声明应该是这样的:func getDashboardData(completionHandler: @escaping (SomeTypeThatAdjustsDashboardDataToFormatAcceptableByView) -> Void)。然后您必须在 DashboardService 中进行此转换 SomeTypeThatAdjustsDashboardDataToFormatAcceptableByView 将是另一个函数/闭包,不是吗? 【参考方案1】:

这种情况下使用委托比较清楚,比如这样:

protocol DashboardServiceDelegate 
    func didLoaded(_ viewModel: DashboardViewModel)


class DashboardViewController: UIViewController 
    override func viewDidLoad() 
        super.viewDidLoad()
        dashboardService.delegate = self
        dashboardService.getDashboardData()
    

extension DashboardViewController: DashboardServiceDelegate 
    func didLoaded(_ viewModel: DashboardViewModel) 
        ///
    



protocol DashboardRequestHandlerDelegate() 
    func didLoaded(request: DashboardDataRequest)


class DashboardService: GeneralService 
    private lazy var requestHandler: DashboardRequestHandler =  reqHandler in
        reqHandler.delegate = self
        return reqHandler
    (DashboardRequestHandler())

    func getDashboardData() 
        guard let url = ...
        requestHandler.requestDashboardData(url: url)
    


extension DashboardService: DashboardRequestHandlerDelegate 
    func didLoaded(request: DashboardDataRequest) 
        let viewModel = convert(request)
        delegate.didLoaded(viewModel)
    

【讨论】:

我假设在 requestHandler.requestDashboardData(url: URL) 的实现中,我必须调用 delegate.didLoaded(request) ?? 是的,你必须在从 json 解码后调用它 对不起,我花了这么长时间才接受你的回答。当您发布此答案时,我有点实现了 completionHandler 方法。我现在意识到在这种情况下我发现委托方法更干净,并实现了它,我是我的代码。谢谢

以上是关于swift 4中的完成处理程序的主要内容,如果未能解决你的问题,请参考以下文章

Swift 4 完成处理程序:数组保持为空

Swift 中的完成处理程序错误

Swift 4 tableview 不使用通用完成处理程序显示数据

在完成处理程序中隐藏http状态代码的进度条 - Swift 4

For/In 循环中的完成处理程序 - swift

完成处理程序中的 Swift 泛型