如何使用AppSync S3ObjectManager更新视图的进度栏?

时间:2019-01-04 05:36:49

标签: ios swift aws-appsync

我正在使用AWSAppSyncClient上传文件,但是我正在努力将上传进度钩子与视图连接起来。

AWSAppSyncClient是使用S3ObjectManager初始化的应用程序委托的属性。对象管理器方法upload可以通过AWSTransferUtilityUplaodExpression访问上传进度:

  expression.progressBlock = {(task, progress) in
    DispatchQueue.main.async(execute: {
      // Can we update the controller's progress bar here?
      print("Progress: \(Float(progress.fractionCompleted))")
    })
  }

我的控制器通过调用perform来调用上传:

var appSyncClient: AWSAppSyncClient? // retrieved from the app delegate singleton

appSyncClient?.perform(mutation: CreatePostMutation(input: input)) { (result, error) in ... 

我正在努力解决的问题:如何为S3ObjectManager提供对控制器的引用?我想在每个控制器中实例化AWSAppSyncClient,也许使用某种委托模式?

1 个答案:

答案 0 :(得分:1)

在每个视图控制器上实例化一个新客户端可能是矫kill过正。设置和拆卸需要花费一些时间和系统资源,并且在任何情况下,您都希望将这些活动与视图控制器分开,只是为了分离职责。

注册每个对象的侦听器实际上不是一个好方法,因为将突变排队等待最终异步传递。您的代表想法似乎是此时最好的方法。

注意:下面的代码未经测试,也不是线程安全的。

例如,您可以声明一个单例委托,该委托为需要报告进度的单个视图管理观察者:

class AppSyncS3ObjectManagerProgressWatcher {
    typealias ProgressSubscription = UUID
    static let shared = AppSyncS3ObjectManagerProgressWatcher()
    private var watchers = [UUID: AppSyncS3ObjectManagerProgressDelegate?]()

    func add(_ watcher: AppSyncS3ObjectManagerProgressDelegate) -> ProgressSubscription {
        let subscription = UUID()
        weak var weakWatcher = watcher
        watchers[subscription] = weakWatcher
        return subscription
    }

    func remove(_ subscription: ProgressSubscription?) {
        guard let subscription = subscription else {
            return
        }
        watchers[subscription] = nil
    }
}

extension AppSyncS3ObjectManagerProgressWatcher: AppSyncS3ObjectManagerProgressDelegate {
    func progressReportingExpression(forDownloadingObject object: AWSS3ObjectProtocol) -> AWSS3TransferUtilityDownloadExpression {
        let expression = AWSS3TransferUtilityDownloadExpression()
        expression.progressBlock = { _, progress in
            self.didReportProgress(forDownloadingObject: object, progress: progress)
        }
        return expression
    }

    func progressReportingExpression(forUploadingObject object: AWSS3ObjectProtocol & AWSS3InputObjectProtocol) -> AWSS3TransferUtilityUploadExpression {
        let expression = AWSS3TransferUtilityUploadExpression()
        expression.progressBlock = { _, progress in
            self.didReportProgress(forUploadingObject: object, progress: progress)
        }
        return expression
    }

    func didReportProgress(forDownloadingObject object: AWSS3ObjectProtocol, progress: Progress) {
        for watcher in watchers.values {
            watcher?.didReportProgress(forDownloadingObject: object, progress: progress)
        }
    }

    func didReportProgress(forUploadingObject object: AWSS3ObjectProtocol & AWSS3InputObjectProtocol, progress: Progress) {
        for watcher in watchers.values {
            watcher?.didReportProgress(forUploadingObject: object, progress: progress)
        }
    }
}

只要使S3TransferUtility符合S3ObjectManager,您都将执行以下操作:

extension AWSS3TransferUtility: AWSS3ObjectManager {

    public func download(s3Object: AWSS3ObjectProtocol, toURL: URL, completion: @escaping ((Bool, Error?) -> Void)) {

        let completionBlock: AWSS3TransferUtilityDownloadCompletionHandlerBlock = { task, url, data, error -> Void in
            if let _ = error {
                completion(false, error)
            } else {
                completion(true, nil)
            }
        }

        let progressReportingExpression = AppSyncS3ObjectManagerProgressWatcher
            .shared
            .progressReportingExpression(forDownloadingObject: s3Object)

        let _ = self.download(
            to: toURL,
            bucket: s3Object.getBucketName(),
            key: s3Object.getKeyName(),
            expression: progressReportingExpression,
            completionHandler: completionBlock)
    }

    public func upload(s3Object: AWSS3ObjectProtocol & AWSS3InputObjectProtocol, completion: @escaping ((_ success: Bool, _ error: Error?) -> Void)) {
        let completionBlock : AWSS3TransferUtilityUploadCompletionHandlerBlock = { task, error -> Void in
            if let _ = error {
                completion(false, error)
            } else {
                completion(true, nil)
            }
        }

        let progressReportingExpression = AppSyncS3ObjectManagerProgressWatcher
            .shared
            .progressReportingExpression(forUploadingObject: s3Object)

        let _ = self.uploadFile(
            s3Object.getLocalSourceFileURL()!,
            bucket: s3Object.getBucketName(),
            key: s3Object.getKeyName(),
            contentType: s3Object.getMimeType(),
            expression: progressReportingExpression,
            completionHandler: completionBlock
            ).continueWith { (task) -> Any? in
            if let err = task.error {
                completion(false, err)
            }
            return nil
        }
    }
}

然后在进度报告视图中:

override func awakeFromNib() {
    super.awakeFromNib()
    progressSubscription = AppSyncS3ObjectManagerProgressWatcher.shared.add(self)
}

func didReportProgress(forUploadingObject object: AWSS3InputObjectProtocol & AWSS3ObjectProtocol, progress: Progress) {
    // TODO: Filter by object local URI/key/etc to ensure we're updating the correct progress
    print("Progress received for \(object.getKeyName()): \(progress.fractionCompleted)")
    self.progress = progress
}

正如我指出的那样,此代码未经测试,但是应该为您概述一种通用的方法。我欢迎您的反馈,并想听听您最终采用哪种方法。

最后,请随时在我们的问题页面上打开功能请求:https://github.com/awslabs/aws-mobile-appsync-sdk-ios/issues