niedziela, 21 grudnia 2014

iOS, a co to jest - odcinek siódmy: podstawy iOS cz.2

Kolejne informacje na temat iOS. W pierwszej części posta na razie tylko zarysowałem tematykę związaną z przechowywaniem danych, dodałem trochę wiecej szczegółów z komunikacji sieciowej oraz pojawiły się zagadnienia związane z życiem aplikacji, wykonywaniem w tle, wątkami, dispatcherem.  W drugiej części wrzuciłem trochę uzupełnień do poruszanych już zagadnień.

Jakieś komentarze?  Ogólnie … nic nowego mimo nowych informacji. Rozwiązania w iOS mimo czasami dość innej formy mają ideowo nieraz dużo wspólnego z rozwiązaniami Windows / Windows Phone.  Parę bardziej szczegółowych uwag:

  • system plików - izolacja aplikacji nie zaskakuje, tak jak predefiniowane foldery, backupowanie folderu przez system kojarzy mi się z nowo wprowadzonym folderem w WP 8.1
  • NSUserDefaults - przechowywanie ustawień aplikacji też nie zaskakuje, natomiast mamy tu dostęp przez aplikację Settings, iOS ma generowany interfejs do ustawień tak jak Android, w Windows nie ma na razie tego.
  • SQLite - lokalna baza wspólna dla Android, iOS i Windows/Windows Phone, nic nowego
  • przechowywanie ustawień w chmurze - w Windows mamy ustawienia między maszynami trzymane też w chmurze
  • CoreData - uniwersalna persystencja, czy w tym kierunku nie zmierza Entity Framework 7?
  • GDC - czy zlecenia wykonania czegoś w tle/ w głównym wątku UI nie jest nam znane choćby z aplikacji XAML?
  • parsowanie XML jest koszmarne, na szczęście dziś króluje JSON
  • przechodzenie aplikacji do tła i usypianie - czy tego już nie znamy?
  • kontynuacja pracy po przejściu do background:  lokalne notyfikacje,  kod w taskach ograniczonych czasowo , nasłuchiwanie zmian w geolokalizacji, sieci przez VOIP, kontynuacja odtwarzania audio, budzenie przez sprzętowe akcesoria, notyfikacje push - zaskoczenia raczej nie ma

Nihil novi sub sole…

 

Persystencja

Możliwości

  • system plików
  • NSUserDefaults
  • SQLite
  • iCloud - NSUbiquitousKeyValueStore
  • CoreData
    • z i bez SQLite
    • iCloud z CoreData

Przechowywanie danych lokalnie

  • każda aplikacja jest w piaskownicy

System plików dla aplikacji iOS

  • <App_Home>/AppName.app - folder zawierający aplikację, nie należy do niego niczego zapisywać
  • <App_Home>/Documents/ - dla danych krytycznych i plików użytkownika, folder backupowany przez system
  • <App_Home>/Documents/Inbox - dla plików nieznanych (np. załączników)
  • <App_Home>/Library/ - dla plików aplikacji i ustawień
  • <App_Home>/tmp/ - dla plików tymczasowych

Ładowanie zasobów z paczki aplikacji

NSURL* url = [[NSBundle mainBundle] URLForResource: @”film” withExtension: @”mp4”];

Prosta serializacja

NSArray, NSDictionary i NSString mają metody do zapisywania i inicjalizowania z pliku. W Objective-C serializacja wielu typów jest bezpośrednio w nie wbudowana, nie ma używania obiektów o innych typach.

NSFileManager

  • bardziej bezpośrednia kontrola nad plikami
  • enumeracja folderów
  • udostępniony protokół do notyfikowania o zmianach w pliku

NSUserDefaults

  • ustawienia użytkownika (tylko małe ilości danych)
  • XML dodawany do folderu aplikacji library

Settings bundle

  • dodanie jako szablon z nowego pliku buduje automatycznie infrastrukturę dla
    • wyświetlania ustawień w ustawieniach aplikacji
    • lokalizacji nazw ustawień

Root.plist

  • plik XML dodawany do projektu - metadane dla schematu ustawień i UI
  • zorganizowany na grupy i elementy, możliwość tworzenia hierarchii

NSUserDefaults* defaults = [NSUserDefaults  standardUserDefaults];

float zoom = [defaults floatForKey: @”zoom”];

Ustawienia aplikacji:  ekran Home –> Settings –> nazwa aplikacji

 

SQLite

  • dołączany do iOS
  • możliwość dołączenia wcześniej utworzonej bazy jako zasób lub utworzenia i wypełnienia danymi, kiedy aplikacja pierwszy raz ładuje się

sqlite3 *contactDB;

NSString* nsdbpath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent: @”baza.sqlitedb”];

const char *dbpath = [nsdbpath UTF8String];

if (sqlite3_open(dbpath, &contactDB) == SQLITE_OK)

{

         const char* sql = “select * from documents;”;

         sqlite3_stmt *selectstmt;

         if (sqlite3_prepare_v2(contactDB, sql, –1, &selectstmt, NULL) == SQLITE_OK)  {

                NSInteger primaryKey;

                while(sqlite3_step(selectstmt) == SQLITE_ROW)  {

                        char *cNumber = sqlite3_column_text(selectstmt, 1);

                }

         }

}

 

iCloud

  • możliwość współdzielenia danych pomiędzy urządzeniami
  • włączamy w ustawieniach projektu
  • słownik klucz-wartość (domyślna nazwa pliku - id paczki)
  • także alokowanie folderów

 

CoreData

  • “ORM” zapewniający automatyczną persystencję
  • możliwość przechowywania w:
    • pamięci
    • bazie SQLite
    • pliku binarnym
  • możliwość synchronizowania z iCloud (tylko przy SQLite)
    • przyrostowe aktualizacje

 

Komunikacja sieciowa

request

  • NSURLRequest  - wiecej w odc.2
  • NSMutableURLRequest
    • podklasa NSURLRequest
    • używamy przy
      • HTTP POST, PUT, DELETE
      • zmianie nagłówków HTTP

NUSRL *url = [NSURL URLWithString: @”http://serwer/app/documents”];

NSMutableURLRequest* rurl = [NSMutableURLRequest requestWithURL: url];

[rurl setValue: @”application/json”  forHTTPHeaderField: @”Accept”];

[rurl setHTTPMethod: @”GET”];

realizacja requestu

  • NSURLConnection - pobranie do pamięci, używa obiektu NSURLRequest
  • NSURLDownload - pobranie zasobu do pliku, też używa NSURLRequest

obsługa synchroniczna i asynchroniczna

asynchroniczna - inna nieco obsługa niż w odc.2

NSURLConnectionDataDelegate

  • propercja delegate nie specyfikuje jawnie listy protokołów

conn = [[NSURLConnection alloc]  initWithRequest: theRequest  delegate: self];

- (NSCachedURLResponse *) connection: (NSURLConnection *) connection  willCacheResponse: (NSCachedURLResponse *) cachedResponse  {

         return nil;

}

- (void) connection: (NSURLConnection *) connection  didFailWithError:  (NSError *) error  {

         done = YES;

         [self performSelectorOnMainThread:  @selector(parseError:)  withObject: error  waitUntilDone: NO];

}

- (void) connection:  (NSURLConnection *) connection  didReceiveData:  (NSData *) data {

         [xmlData appendData: data];

}

- (void) connectionDidFinishLoading:  (NSURLConnection *) connection {

         self.done = YES;

}

Grand Central Dispatch (GCD) 

  • przy starcie/przywracaniu aplikacja może zlecić pracę w tle
  • zlecenie przez GCD wykonania bloki kodu (anonimowa funkcja:   ^{ //kod } ) w tle i odesłanie wyniku do głównego wątku UI (aktualizacja UI podobnie jak w XAML może odbywać się tylko w głównym wątku)

[NSURLConnection sendAsynchronousRequest: req  queue: [[NSOperationQueue alloc] init] 

          completionHandler: ^(NSURLResponse *response, NSData *data, NSError *err) {

          NSDictionary* documentsData = [NSJSONSerialization JSONObjectWithData: data

          options: NSJSONReadingAllowFragments  error:nil];

           NSArray* documents = [documentsData objectForKey: @”a”];

           _objects = [NSArray arrayWithArray: documents];

           dispatch_async(dispatch_get_main_queue(), ^{

                     [self.tableView reloadData];

            });

  }];

Deserializacja

  • XML: NSXMLParser
    • implementujemy protokół NSXMLParserDelegate
  • JSON: NSJSONSerialization

parsowanie XML

NSMutableData *xmlData = [self getXMLData];

NSXMLParser *parser = [[NSXMLParser alloc]  initWithData:xmlData];

parser.delegate = self;

self.currentString = [NSMutableString string];

[parser parse];

- (void) parser: (NSXMLParser *) parser  didStartElement: (NSString *) elementName 

namespaceURI:  (NSString *) namespaceURI  qualifiedName: (NSString *) qualifiedName

attributes:  (NSDictionary *) attributeDict {

           if ([elementName isEqualToString: @”xxx”]) {

                   self.store = YES;

          }

}

- (void) parser: (NSXMLParser *) parser  foundCharacters: (NSString *) string {

           if  (store)  [currentString appendString: string];

}

- (void) parser: (NSXMLParser *) parser  didEndElement:  (NSString *) elementName

namespaceURI:  (NSString *) namespaceURI  qualifiedName: (NSString *) qName  {

          self.store = NO;

}

Autentykacja

  • można użyć NSMutableURLRequest i ustawić nagłówki dla autentykacji
  • część protokołu NSURLConnectionDataDelegate
  • przykład Apple “AdvancedURLConnections”

 

Czas życia aplikacji, wydajność, praca w tle, wątki, dispatcher

Czas życia aplikacji

  • może zostać uśpiona, gdy użytkownik naciska przycisk home
  • gdy użytkownik uruchamia ponownie aplikację, jeśli jest uśpiona zostanie przywrócona, zamiast uruchamiana
  • aplikacja pozostaje uśpiona, gdy nie było dużych żądań odnośnie pamięci
  • częstsze pozostawanie w uśpieniu i szybsze przywracanie osiągniemy ograniczając zużycie pamięci przez aplikację
  • aplikacja nie powinna też zbyt dużo robić podczas przywracania

UIApplicationDelegate

- (void) applicationWillResignActive: (UIApplication *) application { … }

- (void) applicationDidEnterBackground:  (UIApplication *) application { … }

- (void) applicationWillEnterForeground:  (UIApplication *) application { … }

- (void) applicationDidBecomeActive:  (UIApplication *) application { … }

- (void) applicationWillTerminate:  (UIApplication *) application { … }

 

self.viewController.textView.text = @“…”;

Gdy drugi raz wejdziemy na ekran home z poziomu aplikacji, pojawi się na dole lista ostatnio używanych aplikacji. Aplikację możemy zakończyć naciskając na ikonie znak “-“, który pojawia się po jej naciśnięciu.

By aplikacja zostawała w pamięci należy:

  • zredukować ilość wymaganej pamięci (poniżej 16 MB)
  • NSCache API - eliminuje dane podczas przechodzenia do tła lub przy ostrzeżeniach dotyczących pamięci
  • NSPurgableData - obiekty, które czyszczą swoją pamięć, jeśli nie są używane, dodanie ich do NSCache umożliwia temu ostatniemu czyszczenie takich obiektów w sposób bardziej agresywny

Narzędzia

  • wizualny profiler dla iOS
  • zużycie CPU, pamięci, zasobów graficznych (OpenGL ES & Core Animation), sieć, I/O, baterie
  • obsługa emulatora i fizycznego urządzenia
  • profilowanie niektórych rzeczy nie jest obsługiwane na emulatorze (np. grafika)
  • wyniki są prezentowane w czasie rzeczywistym, mogą też zapisane i odtworzone później
  • szablony dla popularnych tasków
  • zintegrowane z XCode
    • komenda: Run > Profile
    • menu: XCode | Open Developer Tool

Wykonywanie w tle

Wiele sposobów w iOS na kontynuowanie wykonywania kodu po przeniesienie do tła aplikacji:

  • lokalne notyfikacje - po wcześniejszym ich zaplanowaniu przez aplikację (np. timer dla gry lub czas do wylogowania)
  • długo trwające taski - aplikacja może zaplanować do wykonania blok kodu, kiedy jest informowana, że przejdzie do background (w zależności od zasobów, ograniczenie czasowe do 10 min)
  • odtwarzacz audio - aplikacja odtwarzająca pliki audio może kontynuować odtwarzanie po wysłaniu aplikacji do background
  • serwisy lokalizacji - aplikacja może uruchomić serwis lokalizacji w tle i być obudzona przy zmianie lokalizacji przez użytkownika
  • VoIP - aplikacja VoIP może zarejestrować się do okresowego budzenia, by sprawdzać przez sieć, czy ktoś nie dzwoni
  • aplikacje kioskowe mogą być uruchamiane w tle lub budzone jeśli są już w tle, aby odpowiedzieć na notyfikację push i pobrać nową rzecz
  • aplikacje, które potrzebują powiadomień od akcesoriów (np. Bluetooth) mogą się zarejestrować, by być notyfikowane, jeśli zewnętrzne urządzenie wyśle zdarzenie

Wątki

  • NSThread
    • najprostsze użycie - metoda NSObject performSelectorInBackground
    • większa kontrola - bezpośrednio korzystamy z NSThread
  • NSTimer
    • potrafi zaplanować periodyczne taski

Grand Central Dispatch (GCD) 

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

           //długo wykonujący się task

           …

           dispatch_async(dispatch_get_main_queue(), ^{

                    //aktualizacja UI

           });

}

 

Uzupełnienia

iOS7

  • pierwsze większe przeprojektowanie UI
  • nie tylko zmiany w UI
    • przeprojektowanie aplikacji
    • wsparcie dla 64-bit
    • serwisy w tle

Wersje w drugiej połowie 2013:

  • 7      2013      80%
  • 6      2012       17%  (większość projektów)
  • 5      2011       3% (w tym wszystkie starsze)

Możliwość warunkowej kompilacji na różne wersje lub wykrywania wersji w runtime.

Możliwość pisania aplikacji w C/C++

  • programowanie niskopoziomowe
  • dużo gier

Objective-C

  • metoda new wywołująca alloc i init:   [klasa new]
  • import
    • #import <UIKit/UIKit.h> - import frameworka
    • #import “MyClass.h” - import źródła
  • propercje mogą być zdefiniowane także w implementacji, wtedy są prywatne
  • potrzebujemy używać @synthesize lub implementować propercje otrzymane od protokołu
  • na iOS nie ma garbage collection
  • popularne atrybuty propercji
    • readonly
    • readwrite
    • nonatomic
    • atomic
    • assign - tylko proste przypisanie do zmiennej
    • weak
    • strong - inkrementuje licznik referencji
  • protokół
    • zbiór metod i propercji, opcjonalność jest domyślna
    • #pragma mark MyProtocol protocol - przed metodami implementującymi protokół, lepsza nawigacja po implementacji klasy w XCode
  • nie ma matematycznych operatorów dla NSNumber, trochę jest dla NSDecimalNumber
  • formatery
    • konwersje typów:  string –> date, string –> number, date –> sformatowany string
    • klasy:  NSNumberFormatter, NSDateFormatter
  • uproszczona inicjalizacja słownika:  NSDictionary* dictionary = @{@”key1”: @”value1”, @”key2”: @”value2”}
  • bloki mogą być prostszą implementacją delegatów niż protokoły

void (^block1)() = ^{

        …

};

block1();

void (^block2)(NSString*) = ^(NSString* param) {

        …

}

block2(@”Pro Forma”);

Uruchamianie aplikacji

Mogą być także uruchamiane przez inne aplikacje za pomocą uri (skojarzony z aplikacją prefix).

XCode

  • workspace opcjonalny dla aplikacji iOS, często stosowany w aplikacjach MacOSX
  • projekt domyślnie ma jeden target, ale możemy dodać dodatkowe (budowanie kilku aplikacji z jednego projektu ze współdzieleniem kodu)
  • plik .xcassets - asset library, plik przechowujący grafikę oraz pozwalający nią zarządzać
  • Capabilities - od XCode 5 np. Background modes
  • 2 kompilatory/debuggery
    • GCC/GDB - GCC zmodyfikowany do kompilacji Objective-C, usunięty w XCode 5.0
    • LLVM/LLDB - nowszy i domyślny
  • pliki nib
    • serializacja dowolnego grafu obiektów Objective-C (nie tylko elementów wizualnych)
    • są zależne od rozdzielczości, przy budowie uniwersalnej aplikacji trzeba tworzyć różne pliki dla iPhone i iPad
    • runtime deserializuje plik do prawdziwych obiektów
  • storyboard
    • od XCode 4, alternatywa do pliku nib, w XCode 5 domyślnym plikiem dla UI w nowych projektach
    • umożliwia używanie nieograniczonego canvas
    • nie tylko widoki, ale także dane i sekwencje pomiędzy widokami
    • tak jak w plikach nib potrzebujemy dedykowanych wersji dla konkretnych rozdzielczości urządzeń
  • możliwość zapięcia się na kilka procesów jednocześnie na używanej maszynie, wielu urządzeniach, symulatorze iOS
  • obsługa breakpointów, także ta bardziej zaawansowana, przypomina VS - definiowanie warunków, ilości powtórzeń, akcji (lepiej używać niż NSLog)

Brak komentarzy: