UIManagedDocument — как бороться с UIDocumentStateSavingError?

Я работаю над своим первым приложением iCloud. Поработав некоторое время, приложение больше не может получить доступ к UIManagedDocument из-за «UIDocumentStateSavingError». Есть ли способ узнать, какая ошибка произошла?

Это мой код для создания UIManagedDocument:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    iCloudURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];

    if (iCloudURL == nil) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [self iCloudNotAvailable];
        });
        return;
    }


    iCloudDocumentsURL = [iCloudURL URLByAppendingPathComponent:@"Documents"];
    iCloudCoreDataLogFilesURL = [iCloudURL URLByAppendingPathComponent:@"TransactionLogs"];

    NSURL *url = [iCloudDocumentsURL URLByAppendingPathComponent:@"CloudDatabase"];
    iCloudDatabaseDocument = [[UIManagedDocument alloc] initWithFileURL:url];

    NSMutableDictionary *options = [NSMutableDictionary dictionary];

    NSString *name = [iCloudDatabaseDocument.fileURL lastPathComponent];
    [options setObject:name forKey:NSPersistentStoreUbiquitousContentNameKey];
    [options setObject:iCloudCoreDataLogFilesURL forKey:NSPersistentStoreUbiquitousContentURLKey];

    iCloudDatabaseDocument.persistentStoreOptions = options;

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(documentContentsChanged:) name:NSPersistentStoreDidImportUbiquitousContentChangesNotification object:iCloudDatabaseDocument.managedObjectContext.persistentStoreCoordinator];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(documentStateChanged:) name:UIDocumentStateChangedNotification object:iCloudDatabaseDocument];


    if ([[NSFileManager defaultManager] fileExistsAtPath:[iCloudDatabaseDocument.fileURL path]]) {
        // This is true, the document exists.
        if (iCloudDatabaseDocument.documentState == UIDocumentStateClosed) {
            [iCloudDatabaseDocument openWithCompletionHandler:^(BOOL success) {
                if (success) {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        [self documentConnectionIsReady];
                    });
                } else {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        [self connectionError:iCloudConnectionErrorFailedToOpen];
                    });
                }
            }];                    
        } else if (iCloudDatabaseDocument.documentState == UIDocumentStateNormal) {
            ...
        }      
    } else {
        ...               
    }           
});

Документ уже существует, и поэтому openWithCompletionHandler: вызывается для документа. Это не удается, и запускается UIDocumentStateChangedNotification, который показывает состояние документа 5: UIDocumentStateClosed и UIDocumentStateSavingError.

После этого вызывается блок завершения. Каков правильный путь отсюда? Есть ли способ узнать, что пошло не так и какая ошибка произошла?

Я попытался повторно открыть документ в блоке завершения, но результат тот же.

Думаю, я мог бы решить проблему, просто удалив файл и создав его заново. Но это, очевидно, не вариант, когда приложение появится в магазине. Я хотел бы знать, что происходит не так, и дать пользователю возможность справиться с проблемой.

Я уже проверил другие вопросы здесь, обрабатывая UIDocumentStateSavingError (их не так много), но, похоже, они не применимы к этой проблеме.

Любая идея, как я могу узнать, в чем проблема? Я не могу поверить, что API говорит вам: «Что-то пошло не так во время сохранения, но я не скажу вам, что!»


person Andrei Herford    schedule 04.04.2012    source источник


Ответы (2)


Вы можете запросить documentState в обработчике завершения. К сожалению, если вам нужна точная ошибка, единственный способ получить ее — создать подкласс и переопределить handleError:userInteractionPermitted:

Может быть, что-то вроде этого поможет (набрано от руки без компилятора)...

@interface MyManagedDocument : UIManagedDocument
 - (void)handleError:(NSError *)error
         userInteractionPermitted:(BOOL)userInteractionPermitted;
@property (nonatomic, strong) NSError *lastError;
@end

@implementation MyManagedDocument
@synthesize lastError = _lastError;
 - (void)handleError:(NSError *)error
         userInteractionPermitted:(BOOL)userInteractionPermitted
{
    self.lastError = error;
    [super handleError:error
           userInteractionPermitted:userInteractionPermitted];
}
@end

Затем вы можете создать его следующим образом...

iCloudDatabaseDocument = [[UIManagedDocument alloc] initWithFileURL:url];

и используйте его в обработчике завершения следующим образом...

        [iCloudDatabaseDocument openWithCompletionHandler:^(BOOL success) {
            if (success) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    [self documentConnectionIsReady];
                });
            } else {
                dispatch_async(dispatch_get_main_queue(), ^{
                    [self connectionError:iCloudConnectionErrorFailedToOpen
                                withError:iCloudDatabaseDocument.lastError];
                });
            }
        }];                    
person Jody Hagins    schedule 12.04.2012

На основе отличного фрагмента @JodyHagins я создал подкласс UIDocument.

@interface SSDocument : UIDocument
- (void)openWithSuccess:(void (^)())successBlock
           failureBlock:(void (^)(NSError *error))failureBlock;
@end


@interface SSDocument ()
@property (nonatomic, strong) NSError *lastError;
@end

@implementation SSDocument

- (void)handleError:(NSError *)error userInteractionPermitted:(BOOL)userInteractionPermitted {
    self.lastError = error;
    [super handleError:error userInteractionPermitted:userInteractionPermitted];
}

- (void)clearLastError {
    self.lastError = nil;
}

- (void)openWithSuccess:(void (^)())successBlock failureBlock:(void (^)(NSError *error))failureBlock {
    NSParameterAssert(successBlock);
    NSParameterAssert(failureBlock);
    [self clearLastError];
    [self openWithCompletionHandler:^(BOOL success) {
        if (success) {
            successBlock();
        } else {
            NSError *error = self.lastError;
            [self clearLastError];
            failureBlock(error);
        }
    }];
}

@end
person neoneye    schedule 01.07.2013