poniedziałek, 22 grudnia 2014

iOS, a co to jest - odcinek ósmy: podstawy iOS cz.3

Dziś, w okresie przedświątecznym chciałbym dokonać “małej rehabilitacji” iOS. Otóż pokazywałem starodawną listę UITableView oraz widoki w postaci plików .xib. Nie pisałem wcześniej, ale nie podobał mi się layout, brakowało mi w nim wydaje się podstawowych rzeczy, nie czułem dobrej kontroli nad nim, widząc tylko wszędzie sztywne liczby, dające różne efekty na różnych ekranach.  Dziś “odkryłem” albo “bliżej poznałem” kilka rzeczy zbliżających bardziej do współczesności:

  • storyboard - wrzucany do standardowo do nowych projektów zamiast .xib, pozwala definiować kilka ekranów i przejścia między nimi, wszystko łącznie z naciśnięciem przycisku mającego wyświetlić inny ekran zrobimy używając zero linii kodu, wszystko za pomocą designera. Tak dobrze to było zdaje się w Sketchflow w Blend, który ma już nie być wspierany w Blend 2015. Pewnym minusem w Xcode jest bardziej skomplikowany powrót z ekranu, przeciągamy przycisk na poprzedni ekran na wyjście, ale dodatkowo zdaje się musi być nawet pusta akcja w kodzie
  • bardziej elastyczny layout - nie widziałem ostatnio w .xib czegoś takiego, dziś w designerze storyboard podefiniowałem warunki na layout  i mogę cieszyć się już wycentrowanym przyciskiem na ekranie niezależnie od jego orientacji i rozdzielczości, podobnie z pozycjonowaniem względem dołu czy góry. Powiew cywilizowanego świata nie oznacza pełnej cywilizacji, trzeba tych warunków na layout trochę się nadefiniować, w tym na szerokość i wysokość i co jakiś czas taki layout “naprawiać” wybierając odpowiednią opcję z menu XCode.
  • UICollectionView - uwolniony layout dla kolekcji, nie jesteśmy więc tylko skazani na tradycjonalistyczną pionową listę z komórkami. Hurra! Nie poznałem na razie szczegółów, ale już sama możliwość czegoś takiego jak “wrappanel” czy bardziej wymyślnego układania elementów znowu daje mi poczucie przybliżenia do świata współczesnego.

Zaczynałem od podstaw (które i tak były znacząco lepsze niż w zamierzchłych czasach, jak słyszałem, że ktoś liczy referencje na piechotę), teraz zaczynam przyglądać się iOS 7, już jest lepiej, ale nie od razu Kraków zbudowano. Jak spojrzę kiedyś na iOS 8 powinno być jeszcze lepiej.  Mam tu na myśli możliwości samej platformy, ponieważ od początku używam najnowszego XCode z projektami na 8.1 i te rzeczy, o których piszę są wciąż obsługiwane.  Takie wędrówki przez historię ostatnich dwóch, trzech lat  są pożyteczne, pozwoli mi to spojrzeć na różne rzeczy w sposób bardziej świadomy. Przykładowo widziałem gdzieś .xib w Xamarin, teraz będę zainteresowany, czy wspiera też storyboard.

 

Objective-C

  • NSDate*, NSNumber*, NSDecimalNumber*
  • NSNumber, NSDecimalNumber
    • [x stringValue]
  • NSDecimalNumber
    • [NSDecimalNumber decimalNumberWithString: str];
  • NSNumberFormatter
    • [formatter numberFromString: str]
  • [x isKindOfClass: [NSNumber class]]
  • NSDictionary
    • [dict copy]

MVC

  • UIViewController
    • view
      • addSubview
  • UIWindow (self.window w AppDelegate) spodziewa się ustawienia propercji rootViewController
    • zwykle jest to ustawiane przez Storyboard
    • old school: ustawianie w kodzie w AppDelegate
  • w nadpisanej metodzie drawRect w view:

CGContextBeginPath(context);

CGContextAdRect(context, self.bounds);

CGContextFillPath(context);

  • Storyboard
    • więcej możliwości, bardziej funkcjonalny designer (np. zoom!)
    • więcej niż jeden view controller
      • przeciągamy drugi view kontroler z narzędzi do designera (pierwszy już jest w nim)
      • na jednym z kontrolerów przeciągamy przycisk
      • przycisk łączymy z widokiem drugiego kontrolera (wybieramy np. present modally)
      • możliwość wyboru rodzaju tranzycji między ekranami
      • po odpaleniu naciśniecie przycisku spowoduje zmianę widoku na ten drugi
      • na drugim widoku umieszczamy przycisk, który łączymy z exit ekranu i pozwala wybrać nam przygotowaną wcześniej w kodzie kontrolera akcję
      • możemy teraz po odpaleniu aplikacji wracać z drugiego ekran na pierwszy
    • można też łączyć z plikami .xib
  • layout widoków - dwa sposoby
    • springs and struts - prosty system do dodania możliwości rozciągania lub pozycjonowania widoków w oparciu o zmiany w layout
    • auto-layout - oparty na wymuszaniu system layoutu
      • nowsza technologia (domyślnie od iOS6 spring & struts layout jest tłumaczony w runtime na auto-layout)
      • domyślnie włączony od XCode 5
      • zaznaczamy element w drzewie wizualnym designera
      • definiujemy constraint-y poprzez Editor –> Pin –> Width i to samo dla Height (np. wartości minimalne, maksymalne lub równe)
      • wyrównujemy element np. Editor –> Align –> Horizontal Center in Container
      • brakujące warunki - okno dialogowe w XCode, wybieramy Update Frame
      • ustawiamy element wertykalnie poprzez Editor –> Pin –> Bottom Space to Superview
  • w designerze można wybrać rodzaj ekranu do podglądu

         Obsługa storyboard w view kontrolerze:

  -(IBAction) goBackToFirst: (UIStoryboardSegue*) sender {

  }

Capture4

Plusy:

  • w pełni deklaratywna nawigacja między ekranami z doborem tranzycji!
  • wreszcie mamy elastyczny layout ze środkowaniem lub podawaniem położenia względem np. góry czy dołu

Minusy:

  • przykładowo wyśrodkowanie przycisku w pionie i poziomie przycisku wymaga wielu czynności w XCode:
    • zdefiniowania warunków na długość i szerokość
    • wycentrowanie w poziomie
    • naprawienie problemów z layoutem
    • wycentrowanie w pionie
    • naprawienie problemów z layoutem

Wersje

  • przed zmianą docelowej wersji z iOS 7 na 6.1 trzeba zmienić architekturę projektu na wersję bez 64-bit
  • widok w iOS 7 zajmuje cały ekran, także na pasku systemowym (w iOS 6 pasek systemowy jest czarny)

 

Widoki na dane

  • UITableView
    • lista pionowa
    • predefiniowany layout komórek
  • UICollectionView
    • elastyczny layout
    • nie ma predefiniowanego layoutu komórek
      • “wrappanel” (elementy układane horyzontalnie obok siebie i przenoszone do następnych wierszy)
      • po okręgu
    • od iOS 6

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)

sobota, 20 grudnia 2014

iOS, a co to jest - odcinek szósty: podstawy iOS cz.1

Poruszając się w tematach związanych z iOS wreszcie przyszła pora na zagadnienia związane z … aplikacjami na iOS.  Na pierwszy ogień musiały pójść takie podstawy jak: kod związany z uruchamianiem aplikacji i przygotowaniem ekranu, układanie kontrolek na ekranie i łączenie ich z kodem, idea MVC na platformie Apple, nawigacja między ekranami obejmująca pasek nawigacyjny u góry lub zakładki na dole ekranu.

Co chciałbym dziś skomentować?  Znajdzie się kilka tematów:

  1. jeszcze coś z Objective-C:  w nowszych odmianach znika potrzeba pisania @synthesize, warto też rozumieć atrybuty propercji, bardzo istotne są protokoły odpowiadające jakby interfejsom, a także delegatom (zdaje się, że gdzieś zawieruszyły mi się wcześniej), skrótowy zapis inicjalizacji tablicy w nowszych wersjach języka, coś na kształt refleksji z programowanym opóźnieniem czasowym, kategorie klasowe (odpowiedniki method extensions)
  2. MVC w Apple: widok potrafiący sam się rysować (trochę mam skojarzenia z Windows Forms), podwidoki, którymi często są bardziej specjalizowane formy - kontrolki; na porządku dziennym jest tutaj kontroler trzymający referencje do widoków i kontrolek i zarządzający nimi, interfejs graficzny możemy definiować albo w kodzie kontrolera (coś na kształt code-behind Windows Forms czy rzadziej technologii XAML) oraz przy pomocy designera w plikach XML *.xib (niestety ten XML jak podejrzałem jest znacznie bardziej rozdmuchany niż XAML i bardziej skomplikowany, czasami operuje jakimiś identyfikatorami liczbowymi, wydaje mi się bardziej służyć do zapisywania pracy designera niż do ręcznej edycji). Robienie referencji w kodzie kontrolera do elementów w XML w XAML polega na nadaniu x:Name. W XCode trzeba element przeciągnąć w odpowiedni sposób na kod interfejsu kontrolera, trochę bardziej niebanalne. Podpięcie akcji do przycisku też nie odbywa się tak jak w Visual Studio, trzeba umiejętnie przeciągnąć przycisk z designera na kod implementacji kontrolera. Dodatkowo jeśli chcemy, by kontroler rozmawiał z bardziej złożoną kontrolką, musi wtedy zazwyczaj implementować protokół (podobnie było z interfejsem w aktywności w Androidzie), co w przypadku pliku .xib wiąże się z przeciągnięciem kontrolki na właściciela pliku jako delegat.
  3. lista:  tym razem znalazłem nieco prostszy sposób podpinania UITableView pod źródło danych, jak używamy UITableViewController to część rzeczy zostanie zautomatyzowana, w tym łączenie widoku z kodem, ale i tak potrzebujemy oprogramować co najmniej trzy metody, by ukazała się najprostsza lista ze stringami. Udziwnienia wynikają po części z dość jawnego uwidocznienia w każdym przypadku grupowania i wirtualizacji. Programista nadpisująć metodę  określa ilość sekcji, musi też w swoim kodzie w razie stwierdzenia braku komórki utworzyć ją na nowo, co jest pomyślane na wypadek przewijania większych ilości danych. Jednak przy kilku elementach również trzeba brak obsługi takich rzeczy oprogramować, co moim zdaniem nie jest wygodne.
  4. UIWebView - rzecz używana nie tylko przez aplikacje hybrydowe firm trzecich, ale podobno i przez samego Apple w niektórych systemowych aplikacjach. Ładowanie html, adresu to rzeczy znane także z rozwiązań Microsoft. Nie wiem czemu w iOS położono taki nacisk na przechwytywanie nawigacji z HTML, co pozwala wywołać kod Objective-C przy odmowie zezwolenia. Z poziomu Objective-C można też przekazać kawałek kodu JavaScript do wykonania. W Windows/Windows Phone mamy podobne rozwiązania, które są bardziej uniwersalne (np. wywoływanie kodu aplikacji nie tylko w odpowiedzi na nawigację). Podpięcie narzędzi Web przeglądarki Safari jest czymś, co się prosiło i dobrze, że jest. W Windows/Windows Phone możemy diagnozować wykonywanie strony Web w emulatorze za pomocą przeglądarki Internet Explorer.
  5. nawigacja - ze świata Hub i Pivot-a cofnąłem się do świata prostego paska nawigacji i zakładek (zakładki też są w Android). Ale zacznijmy od samego nawigowania. W WinRT XAML nawiguje się do typu strony, ewentualnie przekazuje parametry. W Caliburn Micro tworzy się instancję view modelu i to ją przekazuje się do nawigacji. W iOS tworzy się instancję view kontrolera i ustawia jako bieżącą prezentację, albo kładziemy na stosie kontrolerów, jeśli używamy UINavigationController. Możemy też nic nie robić, jeśli korzystamy z UITabBarController. W przypadku UINavigationController i UITabBarController musimy je wcześniej zbudować i odpowiednio ustawić. Niby wszystko logiczne, ale mamy dwa byty - widoki i kontrolery – niekoniecznie oferujące separację interfejsu od logiki (jaką mamy w MVVM w XAML czy Angularze, a nie mamy w Android w Javie czy w XAML bez MVVM), niewizualne kontrolery sterują nawigacją, natomiast na związane z nimi wizualizacje wydaje się, że mamy dość ograniczony wpływ. W Windows Phone Silverlight był pasek aplikacyjny, który miał bardzo ograniczoną funkcjonalność i to z nim kojarzą mi się elementy wizualne iOS, przynajmniej ich część. W WinRT XAML a tym bardziej WinJS Microsoft uczynił pasek nawigacyjny zwykłą kontrolką, z którą możemy zrobić co chcemy bez żadnych ograniczeń. W iOS czuję wciąż mocno skrępowany…

 

Poniżej notatki, do których wpadły dwa screenshoty z moich improwizacji.

 

Intro

iOS

  • bazuje na Unix
  • pierwsze wydanie w 2007
  • bazuje na MacOSX
  • multi-touch UI
  • urządzenia:  iPhone, iPod Touch, iPad
  • symulator na MacOSX
  • oficjalne IDE: Xcode (free w App Store)

Architektura iOS

  • aplikacja
  • Cocoa Touch
    • GameKit, MapKit, iAd
  • UIKit (korzysta z Cocoa Touch)
  • Media
  • Core Services
  • Foundation (np. obsługa wejścia/wyjścia, Bluetooth)
  • Core OS

Niektóre API w postaci bibliotek C (AddressBook)

Jak uruchamiana jest aplikacja iOS ?

  • start procesu Unix - wywołanie main
  • UIApplicationMain
    • uruchamia message loop dla wejścia od użytkownika
    • parametr AppDelegate
      • interakcja poprzez wywołanie didFinishLaunchingWithOptions
      • typowo ładuje widok (lub kontroler widoku) do wyświetlenia

#import <UIKit/UKit.h>

#import “AppDelegate.h”

int main(int argc, char *argv[])

{

         @autoreleasepool  {

                 return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));

         }

}

Projekt

  • Jeden projekt na aplikację
  • XCode może obsługiwać też projekty z Cordovą

UIResponder

  • klasa bazowa dla wszystkich elementów wizualnych, także dla AppDelegate
  • obsługa zdarzeń dotykowych, także bardziej zawansowanych gestów

Proste API dla dotyku

  • - (void) touchesBegan:  (NSSet *)touches  withEvent: (UIEvent *)event;
  • także:  tochesMoved, touchesEnded, touchesCancelled
  • nadpisanie w UIResponder

XCode

  • Debug nawigator:  +:  Add Exception Breakpoint…,  Break: On Throw  - zatrzymanie się na wyjątku w kodzie

Symulator

  • lokalizacja, pamięć, przychodzące rozmowy…
  • część API wymaga fizycznego urządzenia (np. camera)

Objective-C

  • etykiety przez parametrami są opcjonalne i wchodzą w skład nazwy metody
  • propercje

@implementation X

NSString*  _name;

- (NSString *) name {

          return _name;

}

- (void)  setName:  (NSString *) name {

          _name = name;

}

@end

    • @synthesize - zlecenie kompilatorowi wygenerowania metod dostępu do propercji, opcjonalne słowo od XCode 4.4, gdzie kompilator generuje metody dostępu automatycznie bez używania @synthesize
  • zarządzanie pamięcią
    • liczenie referencji
      • retain inkrementuje licznik dla obiekt
      • release dekrementuje licznik referencji
    • programista jest odpowiedzialny za czyszczenie referencji (gdy liczba referencji jest zero obiekt może być zwolniony)
    • metoda release czyści pamięć
    • słowo kluczowe autorelease we wcześniejszych wersjach XCode
    • obecnie zazwyczaj zostawiamy to kompilatorowi dzięki Automatic Reference Counting (ARC)
    • zarządzanie pamięcią przy implementacji propercji jest istotne, zachowanie implementacji wpływa na liczenie referencji do obiektu
  • popularne atrybuty propercji
    • readonly - tylko getter
    • nonatomic - nie jest thread safe
    • weak - nie inkrementuje licznika referencji
    • retain - inkrementuje licznik referencji

@interface X : NSObject

@property (nonatomic, weak)  UIColor* color;

@end

  • protokoły
    • zbiór metod obowiązkowych lub opcjonalnych do zaimplementowania
    • słowa kluczowe optional i required
    • opcjonalność jest domyślna, jeśli nie ma słowa kluczowego
    • specyfikacja protokołów w definicji interfejsu klasy (wygląda jak składnia generyków, interfejs może zadeklarować więcej niż jeden protokół)

@protocol Y

- (void) display: (NSString*) msg;

@end

@interface X:  NSObject<Y>

- (void) display:  (NSString*) msg;

@end

@protocol  UIApplicationDelegate<NSObject>

@optional

  • słowo kluczowe id
    • może być użyte, kiedy nazwa klasa nie jest znana

@protocol Y;

 

@interface X: NSObject

@property (nonatomic, weak) UIColor* theColor;

@property (nonatomic, strong) id<Y> delegate;

@end

 

@protocol Y<NSObject>

- (void) setTheColor: (UIColor*) color;

@end

  • typy numeryczne
    • NSInteger, NSUInteger - definicje typedef bazujące na typach C int i uint
    • NSDecimalNumber - klasa Objective-C
  • BOOL
    • definicja typedef
      • YES (1)
      • NO (0)
    • można używać TRUE/FALSE, ale większość kodu używa YES/NO
  • kolekcje
    • zwykle po dwa warianty dla każdego typu
      • NSArray
        • od XCode 4.5 przy inicjalizacji można stosować uproszczoną składnię:

                                         NSArray* array = @[[UIColor redColor], [UIColor greenColor]];

      • NSMutableArray
      • NSDictionary
      • NSMutableDictionary
    • nie ma generyków, obiekty pobierane z kolekcji trzeba rzutować
  • enumeracja z “for”
    • klasa musi wspierać protokół NSFastEnumeration
    • for (UITouch* touch in touches) {}
    • szybka i bezpieczna (wyjątek jest wyrzucany, jeśli kolekcja jest modyfikowana przez inny wątek)
  • refleksja z opóźnieniem
    • [obj performSelector: @selector(setTheColor)  withObject: color  afterDelay: 5]

 

MVC

kontroler ViewController

  • hybrydowy kontroler
  • zawsze ma związany z nim widok, może trzymać podwidoki

widok UIView - powiązany jest z nim ViewController

UIScreen –> UIWindow –> UIView –> UIButton, …

można stworzyć klasę dziedziczącą po UIView w Xcode i nadpisać w niej metodę drawRect (surowe rysowanie)

- (void) drawRect: (CGRect) rect

{

         CGContextRef context = UIGraphicsGetCurrentContext();

         CGColorRef redColor = [UIColor redColor].CGColor;

         CGContextSetFillColorWithColor(context, redColor);

         CGContextFillRect(context, self.bounds);

}

w implementacji AppDelegate w metodzie didFinishLaunchingWithOptions:

MyUIView *view = [[MyUIView alloc] initWithFrame:  CGRectMake(20,30,100,100)];

[self.window.rootViewController.view addSubview:view]

ViewController

  • każdy ViewController może być rootem, nie ma specjalnej klasy
  • UIWindow spodziewa się jednego, zazwyczaj tworzonego i ustawianego w AppDelegate
  • klasa bazowa dla wszystkich view kontrolerów w iOS, może programowo tworzyć i zarządzać swoim widokiem i podwidokami

tworzymy klasę dziedziczącą po UIViewController (opcja:  target na iPad, opcja:  z XIB dla UI) i w jego metodzie viewDidLoad dodajemy:

MyUIView *view = [[MyUIView alloc] initWithFrame:  CGRectMake(20,30,100,100)];

UILabel* label = [[UILabel alloc]  initWithFrame: CGRectMake(130, 10, 200, 100)];

label.text = @”Ina different stylee”;

[self.view addSubview:view];

[self.view addSubview:label];

a w AppDelegate.m w didFinishLaunchingWithOptions wstawiamy:

self.window.rootViewController = [[MyViewController alloc]  init ];

Interfejs możemy też budować w bardziej wygodny sposób przy pomocy wizualnego interface builder’a. Generuje on pliki nib (Next-Step Interface Builder):

  • pliki XML z rozszerzeniem .xib
  • zwykle opisują obiekty UIView

Przy kreowaniu view kontrolera zaznaczamy opcję tworzącą dodatkowo plik XIB.

Xcode: prawym przyciskiem Open As… –> Source code  - podgląd XML

 

Widoki UIView

Framework dostarcza klas dziedziczących po UIView, których zwykle używa się do budowy UI (za wyjątkiem gier)

Łączenie UI z kodem w kontrolerze.

Jak w jednym z poprzednich odcinków zaznaczam obiekt w designerze, naciskam CTRL i przeciągam do kodu z interfejsem. Tworzę element będący referencją do obiektu tworzonego na podstawie pliku nib. Połączenie między nimi to outlet (podobnie jak x:Name w XAML). Teraz mogę operować w kontrolerze na kontrolkach np. w viewDidLoad:

self.label.text = @”Pro Forma”;

Popularne UI View

  • używane jako cały widok
    • UITableView
      • lista elementów
      • dedykowany UITableViewController
        • implementacja protokołów
          • UITableViewDataSource
            • metody wymagane: numberOfRowsInSection, cellForRowAtIndexPath
            • metody opcjonalne
          • UITableViewDelegate
            • metody opcjonalne: najczęściej didSelectRowAtIndexPath
        • nie jest wymagany, ale bardzo pomocny

Tworzymy plik dziedziczący po UITableViewController z plikiem XIB.  Style = Grouped włącza widok grupowy, z podziałem na sekcje w UITableView.

AppDelegate, w metodzie didFinishLaunchingWithOptions:

self.window.rootViewController = [[TTViewController alloc]  initWithNibName:@"TTViewController" bundle:nil ];

       @interface TTViewController:  UITableViewController

       @property   (strong,  nonatomic)  NSArray*  data;

       @end

@implementation TTViewController

- (void) viewDidLoad

{

        …

        self.data = @[@”Kaenżet”, @”Pro Forma”, @”Kult”, @”Zuch Kazik”];

}

- (NSInteger) numberOfSectionsInTableView:  (UITableView *) tableView

{

         return 1;

}

- (NSInteger) tableView: (UITableView *) tableView  numberOfRowsInSection:  (NSInteger) section

{

         return self.data.count;

}

- (UITableViewCell *) tableView:  (UITableView *) tableView  cellForRowAtIndexPath:  (NSIndexPath*)  indexPath

{

         static NSString *CellIdentifier = @”Cell”;

         UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: CellIdentifier forIndexPath:indexPath];

         if  (cell == nil)  {

                 cell = [[UITableViewCell alloc] initWithStyle:  UITableViewCellStyleDefault  reuseIdentifier: CellIdentifier];

         }

         NSInteger row = indexPath.row;

         NSString* str = [self.data  objectAtIndex: row];

         cell.textLabel.text = str;

         return cell;

}

@end

    • UIWebView
      • używane przez wiele aplikacji Apple np. AppStore, Music
      • używane przez PhoneGap / Cordovę
      • JavaScript to Native
        • implementujemy w view kontrolerze protokół UIWebViewDelegate
        • shouldStartLoadWithRequest - wywoływane dla każdego nawigacyjnego zdarzenia
          • klikanie na <a>
          • zmiana window.location
          • inspekcja żądań nawigacji (true - zezwalamy; false - nie zezwalamy, możliwość wykonania kodu w iOS)
      • iOS to JavaScript
        • stringByEvaluatingJavaScriptFromString - wykonanie kodu JavaScript wewnątrz uruchomionej strony
        • ładowanie danych
          • loadData
          • loadHTMLString
          • loadRequest
      • debugowanie
        • metoda klasowa _enableRemoteInspector - włącza narzędzia debugujące WebKit dla UIWebView w symulatorze, nie można jej pozostawić do publikacji w appstore
        • http://localhost:9999/
        • od Safari 6 i Xcode 4.5 wbudowane w Safari: Develop –> iPhone Simulator (menu Develop włącza się w Safari-> Preferences –> Advanced)

- (BOOL) webView:  (UIWebView *)webView  shouldStartLoadWithRequest:  (NSURLRequest *) request

      navigationType: (UIWebViewNavigationType)  navigationType  {

              NSString* path = request.mainDocumentURL.relativePath;  //request.URL.path

              if  ([path isEqualToString: @”/xxx”])  {

                     …

                     return FALSE;

              }

              return TRUE;

}

[mWebView  stringByEvaluatingJavaScriptFromString: @”document.querySelector(‘#msg’).innerText=’ina different stylee’”];

kontroler viewDidLoad:

[self.webView loadHTMLString: @”<html>…</html>”  baseURL: nil];

       Wiązanie protokołu UIWebViewDelegate z widokiem XIB - zaznaczone WebView łączymy wizualnie delegat z File’s Owner, wcześniej deklarujemy obsługę protokołu UIWebViewDelegate przez kontroler

 

  • kontrolki używane typowo jako podwidoki
    • UIButton
    • UILabel
    • UISlider
    • UIActivityIndicatorView
    • UIProgressView
    • UITextField
    • UISwitch
    • UIImageView
  • UIPopoverController
    • tylko na iPad

 

Nawigacja

Odpowiadają za nią kontrolery ViewController, nie UIWindow czy UIView.

Nawigacja oparta na view kontrolerach używa właściwości Objective-C zwanymi klasowymi kategoriami (duck typing) do wstrzyknięcia właściwości i metod do innych definicji interfejsów klas (coś ala extension methods).

Wstrzykiwanie paska z zakładkami

interface UIViewController (UITabBarControllerItem)

@property (nonatomic, retain) UITabBarItem *tabBarItem;

@property (nonatomic, readonly, retain) UITabBarController *tabBarController;

@end

UINavigationController

  • zarządza stosem view kontrolerów (i powiązanymi z nimi widokami)
  • używamy jako rootViewController
    • rysuje pasek nawigacyjny
    • umieszcza ViewController na szczycie stosu
  • przycisk + (chyba że wyłączymy przycisk back)
  • możemy
    • zmieniać przyciski na pasku nawigacyjnym
    • dodawać przyciski do paska nawigacyjnego
    • pokazywać i dostosowywać toolbar na dole widoku

Protokół paska nawigacyjnego

@protocol UINavigationControllerDelegate <NSObject>

@optional

- (void) navigationController: (UINavigationController *) navigationController  willShowViewController: (UIViewController *) viewController  animated: (BOOL) animated;

- (void) navigationController:  (UINavigationController *) navigationController didShowViewController: (UIViewController *) viewController animated: (BOOL) animated;

@end

Tworzymy pierwszy ekran z dwoma przyciskami oraz dwa ekrany. Podobnie jak w jednym z wcześniejszych odcinków przycisk z kodem łączymy przeciągając wizualnie z Ctrl na implementację odpowiedniego kontrolera.

- (IBAction) navigateToDocuments:  (id) sender  {

         DocumentsTableViewController* vc = [[DocumentsTableViewController alloc] init];

         //[self presentViewController: vc  animated: YES  completion: nil]; //bez kontrolera nawigacyjnego

        [self.navigationController pushViewController: vc  animated: YES];        

}

AppDelegate.m w didFinishLaunchingWithOptions

ViewController* vc = [[ViewController alloc] initWithNibName: @”ViewController” bundle: nil];

self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController: vc];

Na pasku nawigacyjnym u góry po przejściu do wskazanego ekranu pojawia się po lewej stronie przycisk Back.

Pasek nawigacyjny wyświetla tytuły ekranów, jeśli w view kontrolerach ustawimy właściwość title (w viewDidLoad). Na przycisku Back pojawia się nazwa ekranu, do którego możemy powrócić.

self.title = @”Dokumenty”;

Do danego widoku możemy dodać Bar Button Item (ustawiamy Identifier i Title). Łączymy z interfejsem (referencja) i implementacją (akcja) w kodzie przez przeciągnięcie.  W odpowiednim kontrolerze w viewDidLoad możemy ustawić nowo dodany przycisk:

self.navigationItem.rightBarButtonItem = self.editButton;

 

iOS Simulator Screen Shot 20 Dec 2014 11.28.21

UITabController

  • zakładki na dole ekranu

Protokół paska z zakładkami:  UITabBarControllerDelegate

AppDelegate.h

@property (strong, nonatomic)  UITabBarController *viewController;

AppDelegate.m

self.viewController = [[UITabBarController alloc] init];

self.viewController.viewControllers = @[vc, …];

 

iOS Simulator Screen Shot 20 Dec 2014 12.10.44

poniedziałek, 15 grudnia 2014

Windows 10 build 9901

Do sieci wyciekł całkiem godny zainteresowania nowy build 9901.  Co w nim znajdziemy? 

  • działającą Cortanę!
  • odświeżone UI
    • nowe tapety
    • ciemny nieprzezroczysty pasek zadań (ciemniejsza wersja koloru niż wybrany w systemie kolor foreground)
    • paski okien aplikacji Modern
      • bez ikon aplikacji
      • większe przyciski
      • przycisk “hamburger” zastępuje funkcjonalnie przycisk okna
      • nowy przycisk pełnego ekranu
    • koniec panelu Settings w Charms Bar !
      • ustawienia uruchamiane za pomocą menu, pojawiają się w oknie aplikacji Modern, a nie przy prawej krawędzi ekranu
      • skrót WIN + I zamiast panelu Settings uruchamia aplikację Settings
    • odświeżony interfejs systemowych aplikacji Modern - m.in:
      • lewy sidebar nawigacyjny
      • bardziej stonowany wygląd i kolory
      • większa przewaga błękitu i ciemnej tonacji
  • wyszukiwanie w Windows Store dużej liczby aplikacji Windows Phone (w przyszłości uruchamianie) !
    • Make More Space - aplikacja tylko dla Windows Phone HTC, znajdowana jest w Windows Store, póki co próba instalacji kończy się błędem
    • Camera Beta - preinstalowana w systemie aplikacja rodem z Windows Phone
  • nowe systemowe aplikacje Modern
    • Store (Beta) - druga, eksperymentalna wersja obok dotychczasowej aplikacji Store (nowy bardziej webowy wygląd, nawigacja pionowa zamiast panoramy, aplikacje Windows Phone)
    • Camera Beta - bardzo zbliżona do aplikacji Lumia Camera na Windows Phone
    • Remind Me - działa w tle
    • Get Started - niebiesko-ciemno-biała stylistyka
    • Contact Support - jeszcze jakby nie działa
    • XBox - interfejs o zmienionej stylistyce, jakby odpowiednik XBox SmartGlass
  • nowe / odświeżone wersje systemowych aplikacji Modern
    • Settings - obejmuje jeszcze więcej obszarów systemu, ekran nawiązujący wizualnie do panelu sterowania, w Windows Update w Control Panel pojawiła się informacja odsyłająca do nowego rozwiązania,  opcja Backup z użyciem One Drive w sekcji Update & Recovery
    • Calculator - ciemne przyciski
    • Photos - boczny sidebar z nawigacją, ciemniejsza stylistyka, ustawienia wewnątrz aplikacji podobne do zakładki z aplikacji Settings, przypomina obecnie XBox Music
    • Alarm - błękitno-czarna stylistyka, nowy interfejs, niebieski panel Settings w oknie aplikacji po prawej stronie
    • Sound Recorder - błękitno-czarna stylistyka
    • Windows Feedback - boczny sidebar w kolorze jasno niebieskim
    • Maps

Build 9901 nie ma łatwej możliwości aktualizacji w ramach Windows Insider, ale w przyszłości można sobie zainstalować ponownie 9874 lub dokonać aktualizacji przy pomocy obrazu iso.

Linki

piątek, 12 grudnia 2014

iOS, a co to jest - odcinek piąty: Swift cz.3

Dziś jeszcze o Swift, przy czym już bez dużej awangardy. Wymienię mniejsze rzeczy, które zwróciły moją uwagę.

  • stringi
    • operator porównania
    • NSMutableString  - obok String
  • typy numeryczne
    • .predecessor(), .successor(), .advancedBy(k), .distanceTo(k)  - czy to nie jest już przerost formy nad treścią?
    • .addWithOverflow(…) - dodawanie z informacją o wystąpieniu przepełnienia, podobne funkcje są także do innych operacji arytmetycznych
  • tablice
    • indekser obsługujący operator zakresu
    • +=, .extend pozwalające wygodnie do jednej tablicy dodać inną
    • .revert(), .filter(…), .map(…), .reduce(…),  sort(…), sorted(…)  - mamy wszystko czego potrzebujemy, nawet możliwość realizacji sumowania w jednej linii dzięki reduce, a także sortowanie w dwóch wariantach - z modyfikacją tablicy i ze zwróceniem posortowanej kopii
  • predefiniowane protokoły Equatable, Comparable i Printable - dużą awangardą jest to, że deklarujemy obsługę protokołu, a w dwóch pierwszych przypadkach definiujemy funkcje globalne o nazwach == i >.  Printable to jakby taki odpowiednik metody ToString() w C#, ale implementujemy propercję, a obiekt możemy wypisać za pomocą println.
  • funkcje globalne - mamy jakby renesans takich funkcji, siłą rzeczy dostajemy je w upowszechniającym się JavaScript czy dostaniemy wizualnie taką postać przez import klasy statycznej w C#6, Swift oferuje kilka predefiniowanych tj. print(), println(), sort(), sorted(). Takie połączenie oldschool-u z nowoczesnością. Przy czym szczególnie te dwie ostatnie wydają mi się trochę umowne. Czemu akurat sortowanie ma być za pomocą funkcji globalnej?  A może to pójście krok dalej, skoro porównywanie robimy globalnymi w sumie funkcjami, to czemu nie robić także sortowania?
  • przekazywanie adresu tablicy przez & do funkcji, która ją zmodyfikuje - jakby krok wstecz do składni C#, dodatkowo pamiętajmy, że tablice są strukturami
  • podpinanie do zdarzenia metody zapisanej jako string - jakby krok wstecz, także do Objective-C
  • bardzo trudna i dziwna implementacja singletonu

 

Wbudowane typy

  • Bool
  • Int32
  • Int16
  • Int8
  • Int
  • UInt64
  • UInt32
  • UInt16
  • UInt8
  • UInt
  • Double
  • Float
  • String
  • Array<T>
  • Dictionary<T>
  • Optional<T>

Operacje na stringach

“ “.isEmpty

str.hasPrefix(“Pro”)

str.hasSuffix(“Forma”)

str.toInt()   //przy sukcesie {Some liczba}, inaczej nil

let str = “a” + “”b”

let a: Character = “a”

let b: Character = “b”

let c = a + b

str += “xxx”

var b = str1 == str2

“amber” < “gold”  // operatory porównania, a jest wcześniej niż g

“C:/Users/Marcin/Desktop”.componentsSeparatedByString(“/”)

let str = NSMutableString(string:  “Pro”)

str.appendString(“ Forma”)

Operacje na typach numerycznych

var n = 41

n.predecessor()   //40

n.successor()  //42

n.advancedBy(10)  //51

n.advancedBy(-10)  //31

n.distanceTo(20)   //-21

var (result, overflowed)  = Int.addWithOverflow(3, 5)   // (8, false),  także: substract*, multiply*, divide*

Int.max,  Int.min

let m = UInt(12)

let pi = 3.14159

sizeofValue(pi)

Double.abs(-10.0)   //10

Operacje na tablicach

let instruments = [“trąbka”, “gitara”, “sampler”]

instruments[1..<3]   // [“gitara”, “sampler”], obsługa operatora zakresu

instruments.append(“klawisze”)

instruments += [“syntetyzator”]

instruments.insert(“puzon”, atIndex: 2)

instruments.removeAtIndex(0)

instruments.removeRange(0…2)

instruments.removeAll(keepCapacity:  true)

instruments.capacity

instruments.extend([“klarnet”, “gitara basowa”, “perkusja”])

instruments.count

instruments.isEmpty //wg. count

instruments.reserveCapacity(9)

let sortedInstruments = instruments.sorted() { $0 < $1 }  //bez zmiany tablicy, zwracana tablica

instruments.sort()  { $0 < $1 }  //zmiana tablicy

instruments.reverse()

let array = instruments.filter() { $0  > “klm” }

let names = items.map()  { $0.name }

let total = array.reduce(0)  { $0 + $1  }  //pierwszy argument: wartość początkowa

Operacje na słownikach

var dictionary = [

         “Piesek”:  “Leszek”,

         “Koń”:  “Rafał”,

         “Miś”:  “Push-Upek”

]

dictionary[“Piesek”]   // {Some  “Leszek”}

dictionary[“Miś”] = “Uszatek”

dictionary.updateValue(“Uszatek”, forKey: “Miś”)  //zwraca starą wartość

dictionary.removeValueForKey(“Koń”)  //zwraca starą wartość

dictionary.count

let keys = dictionary.keys

let values = dictionary.values

Array(keys)

Array(values)

 

Publicznie zdefiniowane protokoły

  • Equatable
  • Comparable
  • Printable

extension Album: Equatable {

}

func == (a1: Album, a2: Album) –> Bool {

        return a1.title == a2.title && a1.artist == a2.artist

}

extension Album: Comparable {  //rozszerza Equatable

}

func < (a1: Album, a2: Album)  -> Bool {

         return a1.title < a2.title

}

extension Album: Printable {

         var description: String {

                 return “\(artist)  \(title)”

         }

}

println(a1)

 

Globalne funkcje

  • print()
  • println()
  • sort()
  • sorted()

sortowanie wymaga Comparable i Equatable

sort(&albums)   //parametr ze słowem kluczowym inout

sort(&albums)  { $0.title < $1.title }   //ze zignorowaniem Comparable

var sortedAlbums = sorted(albums)  //kopiowanie, bez ingerencji w tablicę

 

Objective-C a Swift

Mapowanie typów

  • id –> AnyObject
  • NSString –> String
  • * –> ?

Wywoływanie metod

button.addTarget(self, action: “didPressButton:”, forControlEvents: .AllEditingEvents)

Obsługa błędów

let fm = NSFileManager.defaultManager()

var error: NSError?

if let contents = fm.contentsOfDirectoryAtPath(“/Data”, error: &error)  {

        …

}

else  {

        println(“error: \(error)”)

}

 

Źródła wiedzy

i-book: 

  • The Swift Programming Language
  • Using Swift with Cocoa and Objective-C

Dokumentacja XCode

http://swifter.natecook.com/

forum swift dla deweloperów apple

 

Trudny singleton

class Singleton {

        class var sharedInstance: Singleton  {

                struct Static {

                          static var onceToken:  dispatch_once_t = 0

                          static var instance:  Singleton?  = nil

                }

                dispatch_once(&Static.onceToken)  {

                          Static.instance = Singleton()

                }

                return Static.instance!

       }

}

 

Inne zagadnienia

  • kontrola dostępu
  • kontrola przepływu
  • własne operatory
  • łączenie w łańcuch elementów opcjonalnych
  • interoperabilność z Objective-C

środa, 10 grudnia 2014

iOS, a co to jest - odcinek czwarty: Swift cz.2

Dziś kontynuacja podróży ze Swiftem. Kolejna porcja równie awangardowych rozwiązań, co poprzednio, wśród których chciałbym przede wszystkim zwrócić uwagę na:

  • inicjalizatory - konstruktory, kontrowersyjne moim zdaniem rozróżnienie w składni na inicjalizatory desygnowane (inicjalizujące wszystkie nieopcjonalne propercje) oraz convenience (muszą wywoływać inicjalizator desygnowany lub inny convenience). Powoduje to restrykcje kompilatora na wywoływanie pewnych inicjalizatorów z poziomu innych, są też odpowiednie reguły podczas dziedziczenia. W C# takich ograniczeń nie ma, zastanawiam się na ile uporządkowano w ten sposób tworzenie i używanie inicjalizatorów, a na ile jest to już pewien przerost formy nad treścią
  • właściwości - mówi się o propercjach stored (takie auto) oraz wyliczanych (takie w miarę typowe z getterem i setterem). Propercje związane z typem, nie instancją oznacza się słowem class (klasy) lub static (struktury, wyliczenia). Największą petardą są jednak tutaj obserwatory propercji na poziomie składni języka!  Operując blokami willSet i didSet możemy obserwować moment przed zmianą wartości propercji lub po jej zmianie.  Mi to kojarzy się trochę z handlerem do zmian dependency property we frameworkach XAML czy watchem na elemencie w scopie Angular. Równie interesujące jest słowo lazy, które powoduje, że propercja inicjalizuje się w momencie pierwszego jej użycia. Idea lazy loadingu jest popularna, ale tutaj trafiła wprost do języka!  W temacie inicjalizacji tablic należy wspomnieć, że można je w deklaracji inicjalizować nie tylko zestawem określonych wartości, ale także za pomocą kawałka kodu.
  • nadpisywanie w dziedziczeniu - tylko w klasach, dotyczy metod, inicjalizatorów, deinicjalizatorów oraz propercji wyliczanych, w stylu Javy mamy override i final.
  • polimorfizm - operatory is (podobnie jak w C#,  obecnie wspierane są tylko klasy bez struktur i wyliczeń, w przypadku protokołów muszą być oznaczone @objc, co jest pewnym ograniczeniem i zaburzeniem czystości języka),  as! (konwersja się dokonuje tylko wtedy, kiedy może) i as (wymuszenie konwersji)
  • referencje - zwalnianie obiektów przez automatyczne zliczanie referencji (jak w Objective-C). Na poziomie języka mamy trzy typy referencji:  normalne, weak (musi być typ opcjonalny, po zdealokowaniu obiektu staje się nilem) oraz onowned (typ nieopcjonalny, po zdealokowaniu obiektu wartość niepoprawna)
  • generyki – podobne do tego, co znamy w C#, warte podkreślenia jest wnioskowanie typu tablicy na podstawie zawartości jej elementów oraz możliwość definiowania złożonych warunków na typy generyka (trochę awangardowo warunki są umieszczane także w <>)

 

Wiele inicjalizatorów

inicjator desygnowany – inicjalizacja wszystkich obowiązkowych propercji, może być więcej niż 1

convenience init(name: String, object: AnyObject?)  //każdy convenience musi wywołać albo designated albo inny convenience

 

class Album {  //błąd zgłaszany przez kompilator, nie ma desygnowanego inicjalizatora ustawiającego x i y

        let title: String

        let artist: String

}

class Album {  //kompilator już nie protestuje

        let title: String = “TPN 25”

        let artist: String = “Słabo?!”

}

let a = Album()

class Album {  //kompilator też nie protestuje

        let title: String?

        let artist:  String?

}

class Album {

        let title: String

        let artist: String

        init (title: String, artist: String) {

                self.title = title

                self.artist = artist

        }

        convenience init() {

                self.init(title: “Zakażone piosenki”, artist: “Zuch Kazik”)

        }

}

Można też mieć init z parametrami o domyślnych wartościach

W klasie pochodnej

  • inicjalizator designated może wywołać designated z klasy bazowej
  • każdy convenience może wywołać designated

class SuperAlbum: Album  {

         let discCount: Int

         init (title: String, artist: String, discCount: Int)  {

                  self.discCount = discCount

                  super.init(title:  title, artist: artist)  //jeśli przeniesiemy linijkę wyżej, kompilator zgłosi błąd

         }

         init(discCount: Int)  {

                  self.discCount = discCount

                  super.init()  //błąd kompilatora

                  super.init(title: “Zakażone piosenki”, artist: “Zuch Kazik”)  //najlepiej by w klasie bazowej init() był designated zamiast convenience

         }

}

Aby domyślny bezparametrowy inicjalizator był dostępny wszystkie propercje muszą mieć domyślne wartości.

Dziedziczenie inicjalizatorów

  • Desygnowane inicjalizatory są dziedziczone, jeśli klasa pochodna nie definiuje żadnego swojego
  • Inicjalizatory convenience są dziedziczone, jeśli klasa pochodna ma wszystkie desygnowane inicjalizatory klasy bazowej

 

Deinicjalizatory

  • deinit()
  • wywoływane przed dealokacją
  • niewymagane
  • nieprzeładowywane
  • tylko w klasach

class X: NSObject  {

          deinit {

                  let nc = NSNotificationCenter.defaultCenter()

                  nc.removeObserver(self, name: “Xyz”, object: nil)

          }

          override init()  {

                   super.init()

                   let nc = NSNotificationCenter.defaultCenter()

                   nc.addObserver(self,  selector: “xxx”, name: “Xyz”, object: nil)

          }

          func xxx(notification:  NSNotification) {

          }

}

 

Propercje

class X {

         let a:  Int  //stored

         var b {   //computed

                   get  {  return … }

                   set  {   … = newValue }

         }

}

Typ: 

  • static:  struct, enum
  • class:  class

Obserwatorzy propercji (odpalani nie przez inicjalizatory, jedynie przy zmianach)

class X  {

          var a: String  {

                   willSet  {

                            println(“A changing from \(a)  to \(newValue)”)

                   }

                   didSet  {

                            println(“A changed from \(oldValue) to \(a)”)

                   }

          }

          var b: String  {

                   willSet(newB)  {

                            println(“A changing from \(b)  to \(newB)”)

                   }

                   didSet(oldB)  {

                            println(“A changed from \(oldB) to \(b)”)

                   }

          }

}

Propercje lazy (inicjalizacja przy pierwszym użyciu)

class Datastore  {

         lazy var dbConnection:  Connection = Connection()

}

 

Inicjalizacja tablicy przy pomocy kawałka kodu.

class Calendar  {

       var months: [String] = {

                let df = NSDataFormatter()

               df.locale = NSLocale(localeIdentifier: “pl-PL”)

               return df.monthSymbols.map() { $0 as String }

        }()

}

 

Nadpisywanie

  • tylko w klasach, nie w strukturach i wyliczeniach
  • metody
  • inicjalizatory
  • wyliczane propercje (nie można nadpisać store property, ale obserwator już tak)
  • deinicjalizatory (automatycznie wywoływany łańcuch deinicjalizatorów z klas bazowych)

class Album {

        let title: String

        let artist: String

        var description: String {

                get  {

                        return “\(artist) \(title)”

                }

        }

        init (title: String, artist: String) {

                self.title = title

                self.artist = artist

        }

  }

class SuperAlbum: Album {

        override var description: String {

                get  {

                        return “\(artist) \(title)  - edycja limitowana”

                }

        }

}

final - element nie może zostać nadpisany

 

Polimorfizm

  • is - sprawdzenie zgodności z protokołem lub typem klasy (protokół musi być wtedy poprzedzony @objc, is nie może być stosowany do struktur i wyliczeń, ale można nimi i obiektami o danym protokole zainicjować tablicę)
  • as? - warunkowe rzutowanie
  • as - wymusza rzutowanie

if let guitar = thing as? Guitar {

}

 

Referencje

Automatic Reference Counting  (podobnie jak w Objective-C)

strong:

var a1: Album

weak:

weak a2: Album?  //musi być typ opcjonalny, jest nilem po zdealokowaniu obiektu

unowned:

unowned a3: Album   //typ nieopcjonalny, po zdealokowaniu obiektu wartość nie jest poprawna i przy dostępie do niej wystąpi bład

 

modyfikacja typu referencji

let closure = [unowned self]() –>Void {

         println(“Jestem \(self.name)”)

}

Zwalnianie obiektów wzajemnie wskazujących na siebie

class A {

       var b: B?

}

class B {

       var a: A?

}

Nie działa z automatu, gdy poustawiamy pola a i b zamiast domyślnych nil.

Samo przypisanie głównych zmiennych nil też nic nie da, pomoże jawne wyczyszczenie pól z referencjami.

Lepiej

class B {

        weak var a: A?

}

Unowned zachowuje się przy dealokacji podobnie, ale przy złej kolejności sypnie wyjątkiem

 

Generyki

struct Array<T> { …}

Wnioskowanie typów

let names = [“Gdy naród do boju”, “Czterdzieści lat minęło”, “Dzień zwycięstwa”]

let str = names[1].uppercaseString

Definicje

struct Array<T>:  MutableCollectionType, Sliceable  {

          var first: T? { get }

          …

}

func map<U>(transform: (T) –> U) –> [U]

Warunki

struct Dictionary<Key:  Hashable, Value> {

}

func f<T1: A, T2: A

          where T1.x == T2.x,  T1.x: C>

          (item1: T1, item2: T2)  -> Bool {

           return true

}

poniedziałek, 8 grudnia 2014

iOS, a co to jest - odcinek trzeci: Swift cz.1

Tym razem też uciekniemy w awangardę, ale przyszłości. Przyznam się, że jak usłyszałem “nowy język, z duchem czasu” parę miesięcy temu, to nie zrobiło to na mnie już większego wrażenia. Ot, wyobraziłem sobie mieszankę czegoś pomiędzy TypeScript a C#, no może jeszcze okraszoną jakimiś dziwactwami z Objective-C, którego mało co wtedy kojarzyłem. 

No bo, co jeszcze można wymyśleć na tym świecie?  Po co wymyślać kolejny nowy język, kiedy Javascript przeżywa swój renesans  i pozwala pisać w zasadzie już sporą większość aplikacji?  Apple jednak nie poddał się zwątpieniu i zaproponował na swoją platformę zupełnie nowy język.  Strzał w kolano w erze Web?  Niekoniecznie. Jakby już nikt niczego nie proponował, wszyscy płynęli zgodnie już tylko w mainstreamie JavaScript-owym byłoby zwyczajnie po prostu nudno, a uleganie pewnej zgodności wstecz obniżałoby w pewnych obszarach kreatywność.  Może to kwestia polityki, by nie bratać się z mainstreamem i pokazać, że można wymyśleć coś lepszego swojego?  Taka pewna alienacja, podkreślenie elitarności, unikalności.  Apple przez tyle lat wytrwale trzymało się Objective-C nie ulegając tendencjom światowym. Swift pozwala wywoływać kod Objective-C i vice versa, to z pewnością było istotne dla twórców.   

Pomijając już te z lekka filozoficzne rozważania muszę przyznać, że Swift mnie … zaskoczył. Nie zapoznałem się z nim jeszcze nawet w połowie, a zobaczyłem oprócz adaptacji pewnych idei z TypeScript czy C# całkiem sporo świeżych, nowych pomysłów, by na elementy używane powszechnie w programowaniu spojrzeć w jeszcze bardziej otwarty, elastyczny sposób i uprościć sobie dodatkowo pracę. Swift stara się być maksymalnie nieprzegadany, wymaga na ogół jeszcze mniej pisania niż C# czy JavaScript, a kompilator ma olbrzymią inteligencję, że mysz się nie przeciśnie. Swift jest też zaprzeczeniem dynamiczności, zawsze typ jest znany, nie jest to język skryptowy. Przejdźmy do bardziej konkretnych wrażeń

  • wnioskowanie typów - jak na razie kojarzy mi się bardziej z C# niż TypeScript
  • operatory zakresu - czemu współczesne języki o tym zapomniały?  proponowana forma jest całkiem wygodna i do pętli for jak ulał
  • interpolacja stringów - analogiczna do C# 6, tylko znaczki nieco inne
  • stałe jako stałe i elementy read-only
  • propercje klasy jak definicje stałych i zmiennych
  • brak ;
  • tworzenie nowego obiektu bez słowa new
  • elementy opcjonalne - dość popularny ? na końcu typu, jakby typ nullowalny w C#, ale bez ograniczenia typu,  typ z wykrzyknikiem zawsze wypakowuje prostą wartość z siebie automatycznie
  • warunki if bez nawiasów
  • warunkowe przypisanie do zmiennej i operacje na niej - kojarzy się odrobinę z operatorem ? w C# 6, C# 6 robi to jednak jeszcze oszczędniej
  • funkcje także poza klasami - jakby ukłon w kierunku C/C++/JavaScript, ale i C# 6 poprzez import klasy statycznej jakby też chciał zrobić ukłon w tę stronę
  • słowo func i strzałka w definicji typu zwracanego przez funkcję pewnym udziwnieniem, a może chodziło a nawiązanie formą do modnych lambda expressions?  Ale nadal to są funkcje, nie lambda expressions
  • słowo class przy funkcji w klasie?  To metoda klasowa (czyli statyczna). Podobnie jak w TypeScript mogą istnieć obok siebie niezależnie metoda instancyjna i statyczna o tych samych nazwach.
  • wymyślił ktoś wcześniej definiowanie parametrów funkcji z nazwami widzianymi na zewnątrz i wewnątrz ?
  • klauzule - jakby takie lambda expressions jak dla mnie, tylko to słowo in trochę może niepotrzebne, natomiast warianty skracania składni schodzą do jeszcze większego skrótu niż w C#. Żeby bardziej zamieszać:  jeśli klauzula jest ostatnim parametrem funkcji, to może być wyciągnięta poza jej listę argumentów przy wywoływaniu
  • parametry z domyślną wartością - pomysł z C# i TypeScript
  • struktury kojarzą się ze strukturami C#, ale niespodzianką jest fakt, że Int, Bool, String, Array, Dictionary są strukturami (zwłaszcza te dwie ostatnie pozycje mogą zaskakiwać kopiowaniem)
  • w strukturach jest używane słowo static dla elementów statycznych
  • typy wyliczeniowe - śmiała rewolucja w porównaniu do innych języków, tutaj nie dość, że mają dziwny zapis w definicji kojarzący się z blokiem switch case, to mogą mieć w sobie metody i właściwości!  Zamiast teraz pisać wszędzie bloki switch case możemy niektóre logiki, konwersje, tworzenie zamknąć w implementacji danego typu.  Jeśli chodzi o mapowanie na wartości proste znowu mamy liberalizm, nie musi to być tylko Int, ale także inne typy np. String z podanymi przez nas wartościami.
  • tuples - typowe dla współczesnych języków takich jak C#, a ostatnio także JavaScript / TypeScript, formą bardziej kojarzą mi się z tymi drugimi
  • protokoły - jak interfejsy, mogą zawierać elementy opcjonalne jak TypeScript i Objective-C, słowo optional przy składowej oraz wymóg stosowania wtedy oznaczenia @objc przy całym protokole  burzą trochę spójność i nowoczesność języka, ja dałbym tylko przy składowej ? jak w TypeScript
  • wywołanie metody opcjonalnie zaimplementowanej z ? wywoła ją tylko wtedy, jeśli została zaimplementowana. Czy nie jest to piękne?  Może trochę kojarzyć się z C# 6, gdzie operator ? wywołuje składową obiektu, jeśli ta nie jest nullem. Dodatkowo możemy wymusić wywołanie metody i uśpić kompilator stosując znak !
  • rozszerzenia - są jakby nawiązaniem do extension methods z C#, ale są znacznie czymś więcej, kolejną rewolucją w tej dziedzinie. Otóż możemy rozszerzyć istniejącą już definicję obiektu nie tylko o metody, ale także o wyliczane właściwości,  definicje typów zagnieżdżonych czy implementację wskazanego protokołu (interfejsu). Sami twórcy w swoich definicjach mocno tego używają np. w przypadku String.

Jeśli zainteresowały Cię moje zdania o Swift powyżej, możesz przeczytać notatki o tym języku poniżej lub samemu zacząć go rozpoznawać.  W Swift odnajdziemy z jednej strony pomysły z innych języków z ostatnich lat oraz te najnowsze, z drugiej  strony sporo nowych, czasami wręcz śmiałych propozycji, które mogą z kolei zainspirować twórców C# czy JavaScript.

 

Intro

Swift

  • przyszłość na platformie Apple
  • zerwanie z C
  • połączenie możliwości nowoczesnych języków
  • zwięzły
  • ścisle typowany
  • nie jest językiem skryptowym
  • nie jest dynamicznie typowany
  • nie wszystkie API zostanie przepisane
  • kodu z Objective-C nie można konwertować do Swift, ale można wywoływać z jego poziomu i na odwrót
  • w jednym projekcie można łączyć oba języki

Obietnice Swift

  • bezpieczeństwo
  • elastyczność
  • produktywność
  • nowoczesność
  • szybkość
  • łatwość nauki

Swift REPL

xcrun swift

 

import Foundation

let df = NSDateFormatter()    //jak alloc

df.dateStyle = .MediumStyle   // NSDateFormatterMediumStyle

df.stringFromDate(NSDate())

 

^+P lub strzałka do góry – poprzednia komenda

:quit lub CTRL+D – wyjście

Można też zapisać komendy do pliku *.swift  i go wykonać:

xcrun swift plik.swift  //kompiluje plik i uruchamia plik wykonywalny

println(df.stringFromDate(NSDate()))

 

XCode playground - wyniki po prawej stronie okna

import UIKit

var str = “abc”

var x = 1    // Swift wnioskuje typy tam, gdzie jest to możliwe

for i in 1…10 {    // 1…10 - operator zakresu

        x = i * 2

        println(“x = \(x)”)   //interpolacja stringów, przypomina tą z C# 6 tylko że ma () zamiast {}

}

Można też testować elementy wizualne

let label = UILabel(frame: CGRect(x: 0, y: 0, width: 120, height: 50))

label.backgroundColor = UIColor.redColor()

label.sizeToFit()

 

Typy danych

W każdym momencie typ obiektu jest znany

Podstawowe elementy

  • klasy
  • protokoły
  • rozszerzenia
  • struktury
  • krotki (tuples)
  • funkcje
  • closures
  • wyliczenia

let - stała, bez możliwości ponownego przypisania czy modyfikacji (tablice)

var - zmienna, możliwość zmiany

Objective-C przez typ:  NSArray, NSMutableArray

var array = [“Kapitan Bomba”,  “Piesek Leszek”,  “Koń Rafał”]

array.append(“Miś”)

 

class A {

         let x: String

         var y: String

         init(x: String, y: String)  {

                 self.x = x;

                 self.y = y;

         }

}

var a = A(x: “Lepsza Statystyka”, y: “Układ zamknięty”)

a.x = “Czarny czwartek”

Elementy opcjonalne

var x: String?   //String lub nil, typ jest cały czas widziany jako opcjonalny string   {Some “wartość”} /  Optional(“wartość”), nie jako string

var x: String!    //specjalny przypadek pozwalający na nil, niejawne wypakowanie wartości

 

Opcjonalne bindowanie

Objective-C:

NSFileManager *fm = [NSFileManager defaultManager];

NSData *data = [fm contentsAtPath: @”/Users/marcin/plik.txt” ];

if  (data)  {

}

Swift:

let fm = NSFileManager.defaultManager()

if let data = fm.contentsAtPath(“/Users/marcin/plik.txt”)  {

}

 

if x != nil  {

}

x!  - wymuszenie wypakowania wartości z typu opcjonalnego

let z = (x ?? “brak”)   //wypakowanie zawartości jak inna wartość niż nil

Każdy typ może być opcjonalny

 

Typy wartości i referencji

Typy referencyjne

  • klasy
  • funkcje

func  printAlbum(a: Album)  -> Void  {   //strzałkę z Void można pominąć

       println(“\(a.artist)  \(a.title)”)

}

Typy wartości

  • struktury
  • wyliczenia
  • krotki

 

Klasy i obiekty

  • pojedyncze dziedziczenie
  • polimorfizm
  • klasowe i instancyjne metody i propercje

class A {

         let x: String

         var y: String

         init(x: String, y: String)  {

                 self.x = x;

                 self.y = y;

         }

         func f(message: String) {

         }

         class func f(message: String) {  //metoda klasowa

         }

}

let a = A(x: “Lepsza Statystyka”, y: “Układ zamknięty”)

a.f(“Przepraszam, czy tu biją?”)

A.f(“Miś”)

 

Funkcje i klauzule

let array = [“Kapitan Bomba”,  “Piesek Leszek”,  “Koń Rafał”]

//klauzula

array.filter({(name: String)  -> Bool in

          return name.hasPrefix(“K”)

})

func f(name n: String, age a: Int) - wariant z zewnętrzymi i wewnętrznymi nazwami parametrów, pierwsze są nazwy widziane na zewnątrz

func f(#name: String,  #age: Int)  - zewnętrzne i wewnętrzne nazwy parametrów są takie same, trzeba wtedy w wywołaniu funkcji jawnie użyć nazw parametrów

f(name: “Zuch Kozioł”, age:  32)

Przeładowania funkcji na podstawie listy argumentów

Uproszczenia klauzul

array.filter({(name)  -> Bool in

          return name.hasPrefix(“K”)

}

array.filter({ name  -> Bool in

          return name.hasPrefix(“K”)

}

array.filter({ name  -> Bool in

          return name.hasPrefix(“K”)

}

array.filter({ name  -> Bool in

          name.hasPrefix(“K”)

}

array.filter({ $0.hasPrefix(“K”)}

Jeśli klauzula jest ostatnim parametrem funkcji, to może być wyciągnięta poza jej listę argumentów

array.filter() { (name)  -> Bool in

          return name.hasPrefix(“K”)

}

Taka składnia jest możliwa dla wszystkich czterech wariantów definiowania klauzul.

Zamiast definiowania klauzuli można do filter przekazać nazwę funkcji o odpowiedniej sygnaturze.

Funkcja może zwracać klauzulę:

func nameWithMaxLength(max: Int = 3) –> (String)  -> Bool {

         return {  (name:  String) in

                  return name.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)  <  max

         }

}

Przy argumentach z domyślnymi wartościami, jeśli decydujemy się określić w wywołaniu funkcji ich wartości, należy podać także  ich nazwy

array.filter(nameWithMaxLength(max: 5))

Różnice między metodą a funkcją:

metoda

  • dostępna przez self
  • pierwszy argument - domyślna lokalna nazwa
  • kolejne argumenty - lokalne i zewnętrzne nazwy

 

Struktury

  • właściwości
  • metody
  • dostęp przez indeksy
  • inicjalizatory
  • rozszerzalność
  • zgodność z protokołem

Klasy

  • to, co struktury
  • dziedziczenie
  • polimorfizm
  • deinicjalizatory
  • liczenie referencji

struct Point {

        var x: Int

        var y: Int

}

Domyślny inicjalizator

var p = Point(x: 10, y: 20)

Strukturami są:

  • Array<T>
  • Dictionary<Key: Hashable, Value>
  • Bool
  • String
  • Int

struct X {

       static var x: String { get } //propercja statyczna, tylko do odczytu

}

 

Wyliczenia

Objective-C: makro

typedef NS_ENUM(NSInteger, Xyz) {

         XyzOption1,

         XyzOption2,

         XyzOption3

};

Swift:

enum Xyz: Int {   //: Int można pominąć

         case Option1

         case Option2

         case Option3

}

Wyliczenia mogą zawierać:

  • metody
  • właściwości
  • surowe wartości

UIView.animationCurve = UIViewAnimationCurve.EaseInOut

lub krócej:

UIView.animationCurve = .EaseInOut

 

enum Xyz: String {

         case Option1  = “Option1”

         case Option2  = “Option2”

         case Option3  = “Option3”

 

         private func regularExpression() –> NSRegularExpression  {

                 switch self  {

                         case .Option1:

                                   return NSRegularExpression(pattern: “…”, options: nil, error: nil)

                         case .Option2:

                                   …

                         case .Option3:

                                   …

                 }

         }

         func isValidFor(xyz: String)  -> Bool  {

         }

         static func from(xyz:  String)  -> Xyz {

         }

}

Xyz.from(“Option1”).toRaw()   //konwersja z napisu na enum, a potem enum na wartość

 

0..< 10 - w połowie domknięty operator zakresu, nie zawiera 10

 

Tuples

var x = (code: 500, message: “Error”)    //para (Int, String)

x.code  lub x.0

x.message lub x.1

 

(Int?, String?)

(Int, String) ?

 

Protokoły

protocol A {

          var x: Double { get }

          func f()         

}

class B:  A  {

          var x: Double = 100

          func () {

                 …

          }

}

let array: [A] = [b, c]

 

@objc protocol A {   //@objc - interoperabilność z Objective-C, w przypadku definiowania opcjonalnych wymagane

          var x: Double { get }

          func f()         

          optional func g(h: Double)    //funkcje opcjonalne w protokołach jak w Objective-C

}

a.g?(20)  //wywołanie opcjonalnej metody, jeśli została zaimplementowana

a.g!(20)  //wymuszenie (błąd w run-time)

 

Rozszerzenia

  • wyliczane właściwości
  • nowe metody
  • definicja typów zagnieżdżonych
  • zgodność z protokołem
  • istniejące zachowania nie mogą być nadpisane

extension String  {

        func reverse() –> String {

               let chars = Array(self).reverse()

                …

        }

}

let song = “Karinga”

song.reverse()

 

extension String: CollectionType  {

        struct Index: … {

                 …

        }

        //implementacja metod

}