Managing File Uploads
The Medable Cortex iOS SDK features an upload operations manager, that takes care of the following tasks:
- Keep an up to date list of ongoing upload operations.
- Keep an up to date list of recently successfully completed operations.
- Keep an up to date list of recently failed operations.
- Keep a progress value (percentage expressed in the 0..1 range) for each operation.
- Emit notifications for every change in the manager:
- Newly added upload.
- Upload completed or failed.
- Upload operation progress changed.
Notifications
Every time there is a significant event in the upload operations manager, it emits a notification using the string kOperationProgressChangedNotification, which is declared in MDUploadOperations.h:
/**
* String used to notify that an upload operation has changed its state.
*
* This can happen when the upload operation is started, its progress has changed
* or when it completes.
*/
extern NSString *const kOperationProgressChangedNotification;As expressed in the comment, this notification is emitted for every change in the upload operations manager.
How to listen the notifications:
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(handleUploadNotification:)
name:kOperationProgressChangedNotification
object:nil];NotificationCenter.default.addObserver(
self,
selector:
#selector(handleUploadNotification(_:)),
name: NSNotification.Name.operationProgressChanged,
object: nil
)How to handle the notifications:
- (void)handleUploadNotification:(NSNotification *)notification
{
if ([notification.object isKindOfClass:[MDUploadOperation class]])
{
MDUploadOperation *uploadOperation = (MDUploadOperation *)notification.object;
NSString *fileName = uploadOperation.fileName;
NSNumber *progress = uploadOperation.progressNumber;
NSLog(@"Uploading file: %@ Progress: %f", fileName, progress.floatValue);
}
else if ([notification.object isKindOfClass:[MDUploadOperations class]])
{
// Ongoing
NSArray<MDUploadOperation *> *ongoingUploads = [MDUploadOperations ongoingOperations];
// Completed
NSArray<MDUploadOperation *> *completedUploads = [MDUploadOperations completedOperations];
// Failed - these can be retried while the upload token is still valid. Upload tokens have an expiry date.
NSArray<MDUploadOperation *> *failedUploads = [MDUploadOperations failedOperations];
}
}func handleUploadNotification(_ notification: Notification )
{
if let uploadOperation = notification.object as? MDUploadOperation {
let fileName = uploadOperation.fileName
let progress = uploadOperation.progressNumber
print("Uploading file: " + fileName + " Progress: \(progress.floatValue)")
}
else if (notification.object as? MDUploadOperations) != nil {
// Ongoing
let ongoingUploads: [ MDUploadOperation ] = MDUploadOperations.ongoingOperations()
// Completed
let completedUploads: [ MDUploadOperation ] = MDUploadOperations.completedOperations()
// Failed - these can be retried while the upload token is still valid. Upload tokens have an expiry date.
let failedUploads: [ MDUploadOperation ] = MDUploadOperations.failedOperations()
}
}Within the notification, depending on the event, two different objects could be sent:
-
A
MDUploadOperationobject is delivered inside theNSNotification.objectproperty if it is notifying about upload progress. See below for more info. -
A
MDUploadOperationsobject is delivered inside theNSNotification.objectproperty if it is notifying about the following events:- A new
MDUploadOperationwas added to the queue. - A
MDUploadOperationsucceeded. - A
MDUploadOperationfailed. - A
MDUploadOperationwas removed from completed/failed. - Flushing operations. Flushing ongoing, succeeded and failed queues.
- A new
State
The state of all upload operations can be queried from the MDUploadOperations class by checking the the following class methods:
+ (NSArray<MDUploadOperation *> *)ongoingOperations;
+ (NSArray<MDUploadOperation *> *)completedOperations;
+ (NSArray<MDUploadOperation *> *)failedOperations;An ongoing operation is an upload task that is still uploading, i.e. it hasn't failed and it hasn't completed the upload yet.
Recently completed operations are short lived. After completion, the operation objects will stay in this list for at least 10 seconds. After this time, they'll be removed from the list.
Recently failed operations live a little longer. After failing, the operation objects will stay in this list for at least 1 minute. Again, after this time, they are be removed from the list.
Each operation is an instance of class MDUploadOperation. Here are some interesting features they provide:
- Progress: Use the
progressNumberattribute to determine the progress percentage (measured in the 0..1 range) of the upload operation. - Data Task: Use the
operationproperty, of classNSURLSessionDataTaskfor the data task that the operation is using. You may callcancelon this object to cancel the upload. - File Name: If you are uploading several things at once, the
fileNameproperty contains the name of the file being uploaded. - NSProgress: Apple's
NSProgressclass allows for fine grain progress reporting, theoperationProgressproperty returns the associatedNSProgressobject with this upload operations.
Cancel an Ongoing Operation
To cancel an ongoing operation, just call cancel on its data task, which you get from calling operation on the MDUploadOperation instance.
Here is code to cancel an ongoing operation:
MDUploadOperation *uploadOperation = [[MDUploadOperations ongoingOperations] firstObject];
[uploadOperation.operation cancel];let ongoingOperations = MDUploadOperations.ongoingOperations()
if let uploadOperation = ongoingOperations?.first {
uploadOperation.operation.cancel()
}Retry a Failed Operation
Any operation that has failed can be retried, it need not be in the failed operations set, but beware that the upload tokens used will eventually expire.
Here is code to retry an operation currently in the failed set:
MDUploadOperation *failedUploadOperation = [[MDUploadOperations failedOperations] firstObject];
[MDUploadOperations retryOperation:uploadOperation];let failedOperations = MDUploadOperations.failedOperations()
if let failedOperation = failedOperations?.first {
MDUploadOperations.retry(failedOperation)
}Updated 6 months ago