我正在使用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
,也许使用某种委托模式?
答案 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