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
MDUploadOperation
object is delivered inside theNSNotification.object
property if it is notifying about upload progress. See below for more info. -
A
MDUploadOperations
object is delivered inside theNSNotification.object
property if it is notifying about the following events:- A new
MDUploadOperation
was added to the queue. - A
MDUploadOperation
succeeded. - A
MDUploadOperation
failed. - A
MDUploadOperation
was 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
progressNumber
attribute to determine the progress percentage (measured in the 0..1 range) of the upload operation. - Data Task: Use the
operation
property, of classNSURLSessionDataTask
for the data task that the operation is using. You may callcancel
on this object to cancel the upload. - File Name: If you are uploading several things at once, the
fileName
property contains the name of the file being uploaded. - NSProgress: Apple's
NSProgress
class allows for fine grain progress reporting, theoperationProgress
property returns the associatedNSProgress
object 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 4 months ago