NSURLSession убедитесь, что загрузка сработала
Как лучше всего убедиться, что загрузка работает?
Мне нужно загрузить изображения на сервер и убедиться, что загрузка прошла успешно. Если по какой-либо причине загрузка не сработала, мне придется повторить попытку позже.
В настоящее время я использую NSUrlSession для загрузки изображения:
- (void)didCreateSignature:(UIImage *)image {
BLog();
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *imageFile = [NSString stringWithFormat:@"%@/%@", documentsDirectory,@"test.png"];
NSData *imageData = UIImagePNGRepresentation(image);
[imageData writeToFile:imageFile atomically:YES];
while (![[NSFileManager defaultManager] fileExistsAtPath:imageFile]) {
[NSThread sleepForTimeInterval:.5];
}
NSURLSession *session = [self backgroundSession];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:self.uploadUrl]];
[request setHTTPMethod:@"POST"];
[request addValue:@"image/png" forHTTPHeaderField:@"Content-Type"];
NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromFile:[NSURL fileURLWithPath:signatureFile]];
[uploadTask resume];
}
Теперь давайте предположим, что у пользователя нет подключения к интернету, тогда делегат будет уволен:
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
BLog();
if (error == nil)
{
NSLog(@"Task: %@ completed successfully", task);
}
else
{
NSLog(@"Task: %@ completed with error: %@", [task originalRequest], [error localizedDescription]);
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reachabilityChanged:) name:kReachabilityChangedNotification object:nil];
self.internetReachability = [Reachability reachabilityForInternetConnection];
[self.internetReachability startNotifier];
});
}
}
Согласно документации Apple, вам следует повторить попытку позже, если статус Reachability изменился. Поэтому я подумывал добавить эти задачи в массив и запустить каждую задачу снова, как только станет доступным интернет-соединение.
Это правильный подход?
И что происходит, если пользователь закрывает приложение (через TaskManager). Как я могу убедиться, что эти задачи будут возобновлены?
1 ответ
Тем временем я решил эту проблему с помощью пользовательского класса UploadManager и сохранял загрузки в Core Data до тех пор, пока загрузка не была успешной: (Ниже код не завершен и показывает только базовую концепцию. Если кто-то заинтересован в полной реализации, пожалуйста, дайте я знаю)
@implementation UploadManager
- (void) uploadImage:(UIImage *)image toUrl:(NSString *)uploadUrl insertIntoDB:(BOOL)insertIntoDB
{
if(insertIntoDB) {
[self insertUploadTaskIntoDBWithImage:image andUploadUrl:uploadUrl];
}
// Background upload only works with files
NSString *fileName = [NSString stringWithFormat:@"%@%@", [uploadUrl sha256], @".png"];
NSString *signatureFile = [NSString stringWithFormat:@"%@/%@", NSTemporaryDirectory(), fileName];
NSData *imageData = UIImagePNGRepresentation(image);
[imageData writeToFile:signatureFile atomically:YES];
while (![[NSFileManager defaultManager] fileExistsAtPath:signatureFile]) {
[NSThread sleepForTimeInterval:.5];
}
NSURLSession *session = [self backgroundSession];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:uploadUrl]];
[request setHTTPMethod:@"POST"];
[request addValue:@"image/png" forHTTPHeaderField:@"Content-Type"];
NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromFile:[NSURL fileURLWithPath:signatureFile]];
[self showNetworkActivity:YES];
self.taskCount++;
[uploadTask resume];
}
-(void) uploadTasksInDatabase
{
NSArray* uploadTasks = [self getAllUploadTasksFromDatabase];
for(UploadTask *task in uploadTasks) {
UIImage *image = [UIImage imageWithData:task.signature];
[self uploadImage:image toUrl:task.uploadUrl insertIntoDB:NO];
}
}
/*
If an application has received an
application:handleEventsForBackgroundURLSession:completionHandler: message, the session
delegate will receive this message to indicate that all messages previously enqueued for this
session have been delivered. At this time it is safe to invoke the previously stored completion
handler, or to begin any internal updates that will result in invoking the completion handler.
*/
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
{
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
if (appDelegate.backgroundSessionCompletionHandler) {
void (^completionHandler)() = appDelegate.backgroundSessionCompletionHandler;
appDelegate.backgroundSessionCompletionHandler = nil;
completionHandler();
}
self.taskCount = 0;
NSDebug(@"All tasks are finished");
[self clearDatabase];
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
BLog();
self.taskCount--;
[[NSNotificationCenter defaultCenter] postNotificationName:[task.originalRequest.URL description] object:self];
if(self.taskCount == 0) {
[self showNetworkActivity:NO];
}
if (error == nil)
{
NSLog(@"Task: %@ completed successfully", [task.originalRequest.URL description]);
[self deleteUploadTaskWithUploadUrl:[task.originalRequest.URL description]];
}
else
{
NSLog(@"Task: %@ completed with error: %@", [task.originalRequest.URL description], [error localizedDescription]);
}
}
В приложении делегат вставьте следующее:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Try to reupload all tasks in DB
dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(backgroundQueue, ^{
[self.uploadManager uploadTasksInDatabase];
});
return YES;
}
Таким образом, он будет пытаться перезагрузить все незавершенные задачи при каждом запуске приложения.