Разработка приложений для iOS




          Лекция 4
 Работа с данными



                            Глеб Тарасов
Насчет ДЗ
•   для кнопки сделать базовый класс

•   насчет иконок в tabBar
                                       http://glyphish.com/
•   стрелки в таблице
Стрелки в ячейках
Варианты хранения данных?
Яндекс.Карты
Аудиокнига
Аудиокнига с
возможностью
   покупки
Новости
Новости с
 возможностью
оффлайн чтения
Лекции и тесты
10 000 магазинов
   сразу после
    установки
   программы
•   Файлы (загружаются из сети или поставляются
    вместе с приложением)
•   Данные загружаются из сети в память и нигде
    не сохраняются
•   Данные загружаются из сети, кешируются на
    какое-то время
•   Данные загружаются из сети, сохраняются в
    базу данных
•   База данных поставляется вместе с
    приложением
Работа с потоками
Главный и фоновый потоки

background
  threads                   copy file



                                                                         unzip folder
           download file




                                            presentModalViewController
    self.view.frame = ...
                               addSubview                                main thread
Через селекторы
- (void)updateUI
{
    NSLog(@"update");
}

- (void)doSomething
{
    NSLog(@"test");
    [self performSelectorOnMainThread:@selector(updateUI)
                           withObject:nil
                        waitUntilDone:NO];
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self performSelectorInBackground:@selector(doSomething)
                           withObject:nil];
}
- (void)updateWithString:(NSString *)str
{
    NSLog(@"update %@", str);
}

- (void)doSomething:(NSString *)str
{
    NSLog(@"test %@", str);
    [self performSelectorOnMainThread:@selector(updateUI:)
                            withObject:str
                        waitUntilDone:NO];
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self performSelectorInBackground:@selector(doSomething:)
                           withObject:@"blabla"];
}
Grand Central Dispatch
- (void)viewDidLoad
{
    [super viewDidLoad];

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // ...
        NSLog(@"blabla");
        dispatch_async(dispatch_get_main_queue(), ^{
            // ...
            NSLog(@"blabla");
        });
    });
}
NSOperationQueue
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 2;

[queue addOperationWithBlock:^{
    // do something 1
}];

[queue addOperationWithBlock:^{
    // do something 2
}];

[queue addOperationWithBlock:^{
    // do something 3
}];

[queue waitUntilAllOperationsAreFinished];
NSOperation
@interface DownloadFileOperation : NSOperation

- (id)initWithURL:(NSString *)url;
                                     @interface DownloadFileOperation()
@end
                                     @property(strong, nonatomic) NSString *url;

                                     @end

                                     @implementation DownloadFileOperation

                                     @synthesize url = _url;

                                     - (id)initWithURL:(NSString *)url
                                     {
                                         self = [super init];
                                         if (self)
                                         {
                                             self.url= url;
                                         }
                                         return self;
                                     }

                                     - (void)main
                                     {
                                         // ... download
                                     }

                                     @end
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 2;

DownloadFileOperation *o1 = [[DownloadFileOperation alloc]
                             initWithURL:@"http://test.ru/text1.txt"];
DownloadFileOperation *o2 = [[DownloadFileOperation alloc]
                             initWithURL:@"http://test.ru/text2.txt"];
DownloadFileOperation *o3 = [[DownloadFileOperation alloc]
                             initWithURL:@"http://test.ru/text3.txt"];

[queue addOperation:o1];
[queue addOperation:o2];
[queue addOperation:o3];

[queue waitUntilAllOperationsAreFinished];
Блокировка
- (void)doSomething1
{
    // ...
    @synchronized(self)
    {
        // ...
    }
}

- (void)doSomething2
{
    // ...
    @synchronized(self)
    {
        // ...
    }
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self performSelectorInBackground:@selector(doSomething1)
                           withObject:nil];
    [self performSelectorInBackground:@selector(doSomething2)
                           withObject:nil];
}
Работа с файлами
NSFileManager - удалять, копировать файлы и т.д.

- (BOOL)removeItemAtPath:(NSString *)path
                              error:(NSError **)error;

- (BOOL)fileExistsAtPath:(NSString *)path;


                    NSData - бинарные данные
- (BOOL)writeToFile:(NSString *)path
                 atomically:(BOOL)useAuxiliaryFile;




                   NSString - текстовые данные

+ (id)stringWithContentsOfFile:(NSString *)path
            encoding:(NSStringEncoding)enc error:(NSError **)error;
Папки

             Файл поставляется вместе с
                   приложением
NSString *path = [[NSBundle mainBundle]
                pathForResource:@"data" ofType:@"txt"];

                   Корневая папка
                       bundle
NSString *path = [[NSBundle mainBundle] bundlePath];
Папки
         Папка Documents (для долгого хранения)
NSString *path = [NSSearchPathForDirectoriesInDomains(
                                                         NSDocumentDirectory,
                                                         NSUserDomainMask, YES)
                  lastObject];




            Папка Caches (для хранения кеша)

NSString *path = [NSSearchPathForDirectoriesInDomains(
                                                         NSCachesDirectory,
                                                         NSUserDomainMask, YES)
                  lastObject];
NSData *data = ...

NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
                                                      NSUserDomainMask, YES)
                  lastObject];

NSString *dataPath = [path stringByAppendingPathComponent:@"data"];

[NSFileManager.defaultManager createDirectoryAtPath:path
                        withIntermediateDirectories:YES
                                         attributes:nil
                                              error:nil];

NSString *filePath = [dataPath stringByAppendingPathComponent:@"file.txt"];
[data writeToFile:filePath atomically:NO];

NSString *filePath2 = [dataPath stringByAppendingPathComponent:@"file2.txt"];
[NSFileManager.defaultManager copyItemAtPath:filePath
                                      toPath:filePath2
                                       error:nil];

[NSFileManager.defaultManager removeItemAtPath:filePath error:nil];
Работа с сетью
http://site.ru/app/request.php?cmd=get_news



                 Запрос



                    Ответ
GET
http://site.ru/request.php?param1=test&param2=123

          POST
http://site.ru/request.php
                             Тело запроса:
                  <request query="load-courses">
                     <courses query="create">
                         <course id="100"/>	

                         <course id="312"/>	

                     </courses >
                  < /request >
XML
        http://ru.wikipedia.org/wiki/XML
<?xml version="1.0" encoding="UTF-8"?>
<recipe name="хлеб" preptime="5" cooktime="180">
  <title>Простой хлеб</title>
  <ingredient amount="3" unit="стакан">Мука</ingredient>
  <ingredient amount="0.25" unit="грамм">Дрожжи</ingredient>
  <ingredient amount="1.5" unit="стакан">Тёплая вода</ingredient>
  <ingredient amount="1" unit="чайная ложка">Соль</ingredient>
  <instructions>
   <step>Смешать все ингредиенты и тщательно замесить.</step>
   <step>Закрыть тканью и оставить на один час в тёплом помещении.</
step>
   <!-- <step>Почитать вчерашнюю газету.</step> - это сомнительный
шаг... -->
   <step>Замесить ещё раз, положить на противень и поставить в
духовку.</step>
  </instructions>
</recipe>
Парсеры
  SAX                                                                DOM
  libxml2                                                             libxml2

NSXMLParser                                                         KissXML

                                                                  TouchXML

                                                         GData DOM Parser
  http://www.raywenderlich.com/553/how-to-chose-the-best-xml-parser-for-your-iphone-project
JSON
http://ru.wikipedia.org/wiki/
            JSON
{
   "firstName": "Иван",
   "lastName": "Иванов",
   "address": {
       "streetAddress": "Московское ш.,
101, кв.101",
       "city": "Ленинград",
       "postalCode": 101101
   },
   "phoneNumbers": [
       "812 123-1234",
       "916 123-4567"
   ]
}
Парсеры
                                                 JSONKit

                                                 SBJSON

                                             TouchJSON

                            NSJSONSerialization (iOS5)


http://stackoverflow.com/questions/2256625/comparison-of-json-parser-for-objective-c-json-framework-yajl-touchjson-etc
Get-запрос
NSURL *url = [NSURL URLWithString:@"http://site.ru/request.php?param=1"];

NSString *result = [NSString stringWithContentsOfURL:url
                                            encoding:NSUTF8StringEncoding
                                               error:nil];
POST-запрос
NSURL *url = [NSURL URLWithString:@"http://site.ru/request.php"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

request.HTTPMethod = @"POST";
NSString *request = @"текст запроса";
request.HTTPBody = [request dataUsingEncoding:NSUTF8StringEncoding];

NSData *resultData = [NSURLConnection sendSynchronousRequest:request
                              returningResponse:nil error:nil];

NSString *resultString = [[NSString alloc] initWithData:resultData
                              encoding:NSUTF8StringEncoding];
Загрузка файлов



     ASIHTTPRequest
https://github.com/pokeb/asi-http-request
Серверная часть

• Xостинг + скрипты на Python, PHP, Ruby.
• Облако + скрипты (Google App Engine,
  Amazon EC, Microsoft Azure и т.д.)
• Облачная NoSQL база без скриптов
  (Amazon SimpleDB, MongoDB и т.д.)
• Сторонние сервисы (Parse, ...)
REST
         http://ru.wikipedia.org/wiki/REST


• Сервер не держит сессию с клиентом, не хранит
  состояние.
• Каждый запрос от клиента содержит всю нужную
  информацию для ответа.
• Запросы кешируемы
•…
Parse   http://parse.com
Работа с сетью
(демонстрация)
Внутренняя база данных
NSUserDefaults

[NSUserDefaults.standardUserDefaults setObject:@"Value" forKey:@"Key"];
[NSUserDefaults.standardUserDefaults setInteger:10 forKey:@"Integer"];
[NSUserDefaults.standardUserDefaults synchronize];

NSInteger integer = [NSUserDefaults.standardUserDefaults integerForKey:@"Integer"];
NSString *value = [NSUserDefaults.standardUserDefaults objectForKey:@"Key"];
SQLite
Встраиваемая база данных с поддержкой SQL-синтаксиса.




 • Работа напрямую через API на Си
 • Использование сторонних ORM на
    Objective-C
 • CoreData (ORM от Apple)
CoreData
(демонстрация)
Вспоминаем
Как запустить фоновый поток?
Как запустить фоновый поток?

   - через performSelectorInBackground
   - через GCD
   - можно добавить задачу в очередь
Как из фонового потока изменить
          интерфейс?
Как из фонового потока изменить
          интерфейс?

    - через performSelectorOnMainThread
    - через GCD запустить в главном потоке
Как загрузить большой файл,
отображая прогресс на экране?
Как загрузить большой файл,
отображая прогресс на экране?


 Проще всего воспользоваться сторонней
 библиотекой, например, ASIHTTPRequest
Сколько нужно создавать
       экземпляров
NSManagedObjectContext при
    работе с CoreData?
Сколько нужно создавать
       экземпляров
NSManagedObjectContext при
    работе с CoreData?

    Столько же, сколько потоков.
Спасибо
     Глеб Тарасов
     gleb34@gmail.com
     twitter.com/pilot34

Школа-студия разработки для iOS. Лекция 4. Работа с данными

  • 1.
    Разработка приложений дляiOS Лекция 4 Работа с данными Глеб Тарасов
  • 2.
    Насчет ДЗ • для кнопки сделать базовый класс • насчет иконок в tabBar http://glyphish.com/ • стрелки в таблице
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
    10 000 магазинов сразу после установки программы
  • 12.
    Файлы (загружаются из сети или поставляются вместе с приложением) • Данные загружаются из сети в память и нигде не сохраняются • Данные загружаются из сети, кешируются на какое-то время • Данные загружаются из сети, сохраняются в базу данных • База данных поставляется вместе с приложением
  • 13.
  • 14.
    Главный и фоновыйпотоки background threads copy file unzip folder download file presentModalViewController self.view.frame = ... addSubview main thread
  • 15.
    Через селекторы - (void)updateUI { NSLog(@"update"); } - (void)doSomething { NSLog(@"test"); [self performSelectorOnMainThread:@selector(updateUI) withObject:nil waitUntilDone:NO]; } - (void)viewDidLoad { [super viewDidLoad]; [self performSelectorInBackground:@selector(doSomething) withObject:nil]; }
  • 16.
    - (void)updateWithString:(NSString *)str { NSLog(@"update %@", str); } - (void)doSomething:(NSString *)str { NSLog(@"test %@", str); [self performSelectorOnMainThread:@selector(updateUI:) withObject:str waitUntilDone:NO]; } - (void)viewDidLoad { [super viewDidLoad]; [self performSelectorInBackground:@selector(doSomething:) withObject:@"blabla"]; }
  • 17.
    Grand Central Dispatch -(void)viewDidLoad { [super viewDidLoad]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // ... NSLog(@"blabla"); dispatch_async(dispatch_get_main_queue(), ^{ // ... NSLog(@"blabla"); }); }); }
  • 18.
    NSOperationQueue NSOperationQueue *queue =[[NSOperationQueue alloc] init]; queue.maxConcurrentOperationCount = 2; [queue addOperationWithBlock:^{ // do something 1 }]; [queue addOperationWithBlock:^{ // do something 2 }]; [queue addOperationWithBlock:^{ // do something 3 }]; [queue waitUntilAllOperationsAreFinished];
  • 19.
    NSOperation @interface DownloadFileOperation :NSOperation - (id)initWithURL:(NSString *)url; @interface DownloadFileOperation() @end @property(strong, nonatomic) NSString *url; @end @implementation DownloadFileOperation @synthesize url = _url; - (id)initWithURL:(NSString *)url { self = [super init]; if (self) { self.url= url; } return self; } - (void)main { // ... download } @end
  • 20.
    NSOperationQueue *queue =[[NSOperationQueue alloc] init]; queue.maxConcurrentOperationCount = 2; DownloadFileOperation *o1 = [[DownloadFileOperation alloc] initWithURL:@"http://test.ru/text1.txt"]; DownloadFileOperation *o2 = [[DownloadFileOperation alloc] initWithURL:@"http://test.ru/text2.txt"]; DownloadFileOperation *o3 = [[DownloadFileOperation alloc] initWithURL:@"http://test.ru/text3.txt"]; [queue addOperation:o1]; [queue addOperation:o2]; [queue addOperation:o3]; [queue waitUntilAllOperationsAreFinished];
  • 21.
    Блокировка - (void)doSomething1 { // ... @synchronized(self) { // ... } } - (void)doSomething2 { // ... @synchronized(self) { // ... } } - (void)viewDidLoad { [super viewDidLoad]; [self performSelectorInBackground:@selector(doSomething1) withObject:nil]; [self performSelectorInBackground:@selector(doSomething2) withObject:nil]; }
  • 22.
  • 23.
    NSFileManager - удалять,копировать файлы и т.д. - (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error; - (BOOL)fileExistsAtPath:(NSString *)path; NSData - бинарные данные - (BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile; NSString - текстовые данные + (id)stringWithContentsOfFile:(NSString *)path encoding:(NSStringEncoding)enc error:(NSError **)error;
  • 24.
    Папки Файл поставляется вместе с приложением NSString *path = [[NSBundle mainBundle] pathForResource:@"data" ofType:@"txt"]; Корневая папка bundle NSString *path = [[NSBundle mainBundle] bundlePath];
  • 25.
    Папки Папка Documents (для долгого хранения) NSString *path = [NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; Папка Caches (для хранения кеша) NSString *path = [NSSearchPathForDirectoriesInDomains( NSCachesDirectory, NSUserDomainMask, YES) lastObject];
  • 26.
    NSData *data =... NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; NSString *dataPath = [path stringByAppendingPathComponent:@"data"]; [NSFileManager.defaultManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil]; NSString *filePath = [dataPath stringByAppendingPathComponent:@"file.txt"]; [data writeToFile:filePath atomically:NO]; NSString *filePath2 = [dataPath stringByAppendingPathComponent:@"file2.txt"]; [NSFileManager.defaultManager copyItemAtPath:filePath toPath:filePath2 error:nil]; [NSFileManager.defaultManager removeItemAtPath:filePath error:nil];
  • 27.
  • 28.
  • 29.
    GET http://site.ru/request.php?param1=test&param2=123 POST http://site.ru/request.php Тело запроса: <request query="load-courses"> <courses query="create"> <course id="100"/> <course id="312"/> </courses > < /request >
  • 30.
    XML http://ru.wikipedia.org/wiki/XML <?xml version="1.0" encoding="UTF-8"?> <recipe name="хлеб" preptime="5" cooktime="180"> <title>Простой хлеб</title> <ingredient amount="3" unit="стакан">Мука</ingredient> <ingredient amount="0.25" unit="грамм">Дрожжи</ingredient> <ingredient amount="1.5" unit="стакан">Тёплая вода</ingredient> <ingredient amount="1" unit="чайная ложка">Соль</ingredient> <instructions> <step>Смешать все ингредиенты и тщательно замесить.</step> <step>Закрыть тканью и оставить на один час в тёплом помещении.</ step> <!-- <step>Почитать вчерашнюю газету.</step> - это сомнительный шаг... --> <step>Замесить ещё раз, положить на противень и поставить в духовку.</step> </instructions> </recipe>
  • 31.
    Парсеры SAX DOM libxml2 libxml2 NSXMLParser KissXML TouchXML GData DOM Parser http://www.raywenderlich.com/553/how-to-chose-the-best-xml-parser-for-your-iphone-project
  • 32.
    JSON http://ru.wikipedia.org/wiki/ JSON { "firstName": "Иван", "lastName": "Иванов", "address": { "streetAddress": "Московское ш., 101, кв.101", "city": "Ленинград", "postalCode": 101101 }, "phoneNumbers": [ "812 123-1234", "916 123-4567" ] }
  • 33.
    Парсеры JSONKit SBJSON TouchJSON NSJSONSerialization (iOS5) http://stackoverflow.com/questions/2256625/comparison-of-json-parser-for-objective-c-json-framework-yajl-touchjson-etc
  • 34.
    Get-запрос NSURL *url =[NSURL URLWithString:@"http://site.ru/request.php?param=1"]; NSString *result = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];
  • 35.
    POST-запрос NSURL *url =[NSURL URLWithString:@"http://site.ru/request.php"]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; request.HTTPMethod = @"POST"; NSString *request = @"текст запроса"; request.HTTPBody = [request dataUsingEncoding:NSUTF8StringEncoding]; NSData *resultData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil]; NSString *resultString = [[NSString alloc] initWithData:resultData encoding:NSUTF8StringEncoding];
  • 36.
    Загрузка файлов ASIHTTPRequest https://github.com/pokeb/asi-http-request
  • 37.
    Серверная часть • Xостинг+ скрипты на Python, PHP, Ruby. • Облако + скрипты (Google App Engine, Amazon EC, Microsoft Azure и т.д.) • Облачная NoSQL база без скриптов (Amazon SimpleDB, MongoDB и т.д.) • Сторонние сервисы (Parse, ...)
  • 38.
    REST http://ru.wikipedia.org/wiki/REST • Сервер не держит сессию с клиентом, не хранит состояние. • Каждый запрос от клиента содержит всю нужную информацию для ответа. • Запросы кешируемы •…
  • 39.
    Parse http://parse.com
  • 40.
  • 41.
  • 42.
    NSUserDefaults [NSUserDefaults.standardUserDefaults setObject:@"Value" forKey:@"Key"]; [NSUserDefaults.standardUserDefaultssetInteger:10 forKey:@"Integer"]; [NSUserDefaults.standardUserDefaults synchronize]; NSInteger integer = [NSUserDefaults.standardUserDefaults integerForKey:@"Integer"]; NSString *value = [NSUserDefaults.standardUserDefaults objectForKey:@"Key"];
  • 43.
    SQLite Встраиваемая база данныхс поддержкой SQL-синтаксиса. • Работа напрямую через API на Си • Использование сторонних ORM на Objective-C • CoreData (ORM от Apple)
  • 44.
  • 45.
  • 46.
  • 47.
    Как запустить фоновыйпоток? - через performSelectorInBackground - через GCD - можно добавить задачу в очередь
  • 48.
    Как из фоновогопотока изменить интерфейс?
  • 49.
    Как из фоновогопотока изменить интерфейс? - через performSelectorOnMainThread - через GCD запустить в главном потоке
  • 50.
    Как загрузить большойфайл, отображая прогресс на экране?
  • 51.
    Как загрузить большойфайл, отображая прогресс на экране? Проще всего воспользоваться сторонней библиотекой, например, ASIHTTPRequest
  • 52.
    Сколько нужно создавать экземпляров NSManagedObjectContext при работе с CoreData?
  • 53.
    Сколько нужно создавать экземпляров NSManagedObjectContext при работе с CoreData? Столько же, сколько потоков.
  • 54.
    Спасибо Глеб Тарасов gleb34@gmail.com twitter.com/pilot34