Ответ 1
Таким образом, после экспериментов и поисковых запросов, я пришел к следующим выводам:
Значительное изменение обновления и фактически любой процесс, запускающий приложение за экраном блокировки, является проблемой здесь. Файл .plist(NSUserDefaults defaultUser) загружает защиту (NSFileProtectionCompleteUntilFirstUserAuthentication), так что он недоступен только после первой разблокировки после запуска приложения. Поэтому, если процесс запускает ваше приложение в фоновом режиме, и ваше приложение пытается получить доступ к умолчанию по умолчанию пользователя пользователя, он не может загрузить файл и поэтому дает вам новый пустой набор пользовательских значений по умолчанию.
Что здесь произошло в моем случае, так это то, что приложение перешло в состояние ожидания, когда EULA и PP будут приняты, поскольку он считывает значения по умолчанию (которые не могут быть прочитаны), что они еще не были приняты, После разблокировки телефона и повторного открытия приложения, которое, пожалуйста, обратите внимание, уже "запущено" - есть некоторые процессы, которые пишут NSUserDefaults, некоторые в моем приложении и некоторые из библиотек, которые использует мое приложение. В большинстве случаев я вызывал синхронизацию по умолчанию, поэтому уничтожал старые значения по умолчанию, которые невозможно было прочитать. Я предполагаю, что это может случиться для многих людей.
Существует несколько различных решений.
Сначала я написал класс, эквивалентный NSUserDefaults, обертывающий NSMutableDictionary и сохраняющий словарь в .plist в библиотеке/поддержке приложений. Я изменил защиту файла на NSFileProtectionNone. Обратите внимание, что это не рекомендуется, если вы храните конфиденциальную информацию в этом файле. Также обратите внимание, что вы должны устанавливать разрешения для файла каждый раз, когда вы его пишете. Что-то вроде:
NSError *error;
BOOL saved = [defaultsDic writeToURL:defaultsFileUrl atomically:YES];
[[NSFileManager defaultManager] setAttributes:[NSDictionary dictionaryWithObject:NSFileProtectionNone forKey:NSFileProtectionKey]ofItemAtPath:[defaultsFileUrl path] error:&error];
Этот метод работает нормально, но, как оказалось, у меня возникла еще одна проблема с данными, которые я читал и писал из брелка. См. Ссылку в моем вопросе выше, ее та же проблема. Значения keychain имеют одинаковую защиту вплоть до первой разблокировки после запуска приложения. Я не хотел удалять защиту из брелка, и на самом деле мне было не очень удобно снимать защиту с моих пользовательских настроек по умолчанию.
Итак, следующим решением является решение этой проблемы. Не пытайтесь получить доступ к защищенным данным, если приложение запущено за экраном блокировки! Это означает, что я должен обнаружить, что приложение запускается за экраном блокировки, а затем дождитесь, пока приложение будет разблокировано, прежде чем я начну читать мои значения по умолчанию для пользователя и значения привязки.
Первое требование - проверить запуск приложения, если доступны защищенные данные, возможно, в applicationDidLaunch или в другом месте.
[[UIApplication sharedApplication]isProtectedDataAvailable]
Если это не так, когда приложение запущено, вы находитесь за экраном блокировки. Вы должны остановиться на этом этапе и воздерживаться от любых операций, которые обращаются к NSUserDefaults или Keychain (или к любому защищенному файлу, если на то пошло!). Затем вам нужно дождаться сигнала о том, что защищенные данные стали доступными. Приложение appDelegate получает следующее, когда пользователь открывает блокировку экрана:
-(void)applicationProtectedDataDidBecomeAvailable:(UIApplication *)application
Как только вы получите это, вы можете продолжить выполнение своего приложения.
В моем случае я управляю всем в одноэлементном классе. Когда этот класс создается (что происходит только при запуске приложения), я проверяю, доступны ли защищенные данные или нет, и подписаться на NSNotificationCenter для того же уведомления:
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(applicationProtectedDataDidBecomeAvailable) name:UIApplicationProtectedDataDidBecomeAvailable object:nil];
Итак, с помощью этого второго метода проблема решена, и данные остаются защищенными, и все довольны.