2013-12-09 9 views
8

Używam NSUserDefaults do przechowywania, czy aplikacja EULA i PP zostały zaakceptowane (między innymi) To działa dobrze ogólnie. Mogę zacząć, wyjść, następnie wrócić do aplikacji i odczyta wartość grzywny. Mogę zabić aplikację i zrestartować - odczytuje wartość domyślną. Mogę ponownie uruchomić telefon, a następnie ponownie uruchomić aplikację i odczyta wartość domyślną.Dlaczego NSUserDefaults nie są czytane po wyczerpaniu baterii IOS7

Po ponownym uruchomieniu telefonu z płaskiej baterii otwieram aplikację i pojawia się monit o ponowne zaakceptowanie umowy EULA. Dzieje się tak tylko na moim iPhone5 na IOS7. Mam 3GS na IOS6, który nie wykazuje takiego samego zachowania.

Podejrzewam, że może to być problem podobny do rozwiązanego here, ale odnosi się to do problemów związanych z uprawnieniami w pęku kluczy. Czy te same problemy z uprawnieniami miałyby zastosowanie do NSUserDefaults?

Czy ktoś napotkał podobne problemy na IOS7 z NSUserDefaults?

+0

Istnieją inne podobne pytania bez odpowiedzi na SO. Wygląda na to, że może nierozwiązany błąd. Można obejść go przy użyciu NSCoder lub Core Data. –

Odpowiedz

21

Więc po eksperymenty i googlowania wniosków doszedłem do przedstawiały się następująco:

Znacząca zmiana zmiana w rzeczywistości każdy proces, który uruchamia aplikację z tyłu ekranu blokady jest problem tutaj. Plik .plist obciążeń [NSUserDefaults defaultUser] jest chroniony (NSFileProtectionCompleteUntilFirstUserAuthentication), dzięki czemu jest niedostępny dopiero po pierwszym odblokowaniu po uruchomieniu aplikacji. Jeśli więc proces uruchamia Twoją aplikację w tle, a Twoja aplikacja próbuje uzyskać dostęp do domyślnych ustawień użytkownika, to nie może załadować pliku, a więc daje nowy pusty zestaw ustawień domyślnych użytkownika.

W moim przypadku zdarzyło się, że aplikacja poszła w stan oczekiwania na zaakceptowanie umowy EULA i PP, ponieważ odczytał z ustawień domyślnych (których nie można odczytać), że nie zostały one zaakceptowane jeszcze. Po odblokowaniu telefonu i ponownym otwarciu aplikacji - co należy zauważyć, jest już "uruchomione" - jest kilka procesów, które piszą do NSUserDefaults, niektóre w mojej aplikacji, a niektóre w bibliotekach, których używa moja aplikacja. W większości przypadków wywoływałam synchronizację na wartościach domyślnych, usuwając stare domyślne ustawienia, których nie można odczytać. Wyobrażam sobie, że może tak być w przypadku wielu osób.

Istnieje kilka różnych rozwiązań.

Najpierw napisałem klasę równoważną NSUserDefaults owijającą NSMutableDictionary i zapisującą słownik do .plist w bibliotece/Application Support. Zmieniłem ochronę pliku na NSFileProtectionNone. Uwaga: nie jest to zalecane, jeśli przechowujesz poufne informacje w tym pliku. Pamiętaj też, że musisz ustawić uprawnienia do pliku za każdym razem, gdy go napiszesz. Coś takiego:

NSError *error; 
BOOL saved = [defaultsDic writeToURL:defaultsFileUrl atomically:YES]; 
[[NSFileManager defaultManager] setAttributes:[NSDictionary dictionaryWithObject:NSFileProtectionNone forKey:NSFileProtectionKey]ofItemAtPath:[defaultsFileUrl path] error:&error]; 

Ta metoda działa dobrze, ale jak się okazało miałem inny problem z danymi, które czytałem i pisałem z pęku kluczy. Zobacz link w powyższym pytaniu, jego ten sam problem. Wartości keychain mają taką samą ochronę aż do pierwszego odblokowania po uruchomieniu aplikacji. Nie chciałem usuwać ochrony z pęku kluczy i naprawdę nie było mi zbyt dobrze z usunięciem ochrony z moich domyślnych ustawień użytkownika.

Kolejnym rozwiązaniem jest rozwiązanie problemu. Nie próbuj uzyskać dostępu do chronionych danych, jeśli aplikacja zostanie uruchomiona za ekranem blokady! Oznacza to, że muszę wykryć, że aplikacja jest uruchamiana za ekranem blokady, a następnie poczekać, aż aplikacja zostanie odblokowana, zanim przejdę do odczytu wartości domyślnych i keychain użytkownika.

Pierwszym wymaganiem jest sprawdzenie uruchomienia aplikacji, jeśli dostępne są dane chronione, więc być może w applicationDidLaunch lub innym odpowiednim miejscu.

[[UIApplication sharedApplication]isProtectedDataAvailable] 

Jeśli nie jest to prawdą, po uruchomieniu aplikacji znajduje się za ekranem blokady. Powinieneś przerwać w tym momencie i powstrzymać się od wszelkich operacji, które uzyskują dostęp do NSUserDefaults lub Keychain (lub jakiegokolwiek chronionego pliku!). Następnie trzeba poczekać na sygnał, że chronione dane stały się dostępne. AppDelegate otrzymuje następujący gdy użytkownik otwiera ekran blokady:

-(void)applicationProtectedDataDidBecomeAvailable:(UIApplication *)application 

Po otrzymaniu, które można nosić na wykonanie aplikacji.

W moim przypadku kontroluję wszystko w klasie singleton. Kiedy ta klasa jest tworzona (co zdarza się tylko po uruchomieniu aplikacji) I sprawdzić, czy zabezpieczone dane są dostępne lub nie i subskrybować NSNotificationCenter dla tego samego zgłoszenia:

[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(applicationProtectedDataDidBecomeAvailable) name:UIApplicationProtectedDataDidBecomeAvailable object:nil]; 

Więc z tej drugiej metody, problem został rozwiązany a dane pozostają chronione i wszyscy są szczęśliwi.

+0

Dzięki za odpowiedź, czy wiesz, czy to normalne, że pliki w NSDocumentDirectory zachowują się jak NSUserDefaults, gdy ekran jest zablokowany? – franck

+0

@muldercnc wielkie dzięki za udostępnienie tego. Jeśli użytkownik odblokuje ekran po 30 minutach od zainicjowania odświeżania aplikacji w tle, czy możemy nadal pobierać dane w tle? (ponieważ mamy mniej niż minutę, zanim system operacyjny nas odciąga) Co zrobić z obsługą zakończenia UIBackgroundFetchResult? – Serluca

+0

@Serluca Wierzę, że tak. Ponieważ generuje zdarzenie, Twoja aplikacja zareaguje na wydarzenie i powinna z radością zrobić to w tle, nawet po 30 minutach. Będziesz musiał spróbować oczywiście, aby to potwierdzić. Używam konsoli Device w Xcode do monitorowania wyjścia XCODE => Window => Devices, użyj opcji konsoli dla testowanego urządzenia (ponieważ możesz zrestartować urządzenie podczas debugowania). – muldercnc

1

To nadal jest zachowanie na IOS 9.0 GM.

Powiązane problemy