2013-10-09 5 views
3

Мое приложение анализирует информацию из приложения Rails с помощью JSON. Я ищу способ загрузки JSON асинхронно, но мне не удается заставить мой код работать с примерами, которые я нашел из-за сложности моего кода. Что мне нужно сделать для асинхронности загрузки JSON? Благодарю.Как загрузить JSON асинхронно (iOS)

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 

    NSURL *upcomingReleaseURL = [NSURL URLWithString:@"http://obscure-lake-7450.herokuapp.com/upcoming.json"]; 

    NSData *jsonData = [NSData dataWithContentsOfURL:upcomingReleaseURL]; 

    NSError *error = nil; 

    NSDictionary *dataDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error]; 

    NSArray *upcomingReleasesArray = [dataDictionary objectForKey:@"upcoming_releases"]; 

    //This is the dateFormatter we'll need to parse the release dates 
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; 
    [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"]; 
    NSTimeZone *est = [NSTimeZone timeZoneWithAbbreviation:@"EST"]; 
    [dateFormatter setTimeZone:est]; 
    [dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]]; //A bit of an overkill to avoid bugs on different locales 

    //Temp array where we'll store the unsorted bucket dates 
    NSMutableArray *unsortedReleaseWeek = [[NSMutableArray alloc] init]; 
    NSMutableDictionary *tmpDict = [[NSMutableDictionary alloc] init]; 

    for (NSDictionary *upcomingReleaseDictionary in upcomingReleasesArray) { 

     //We find the release date from the string 
     NSDate *releaseDate = [dateFormatter dateFromString:[upcomingReleaseDictionary objectForKey:@"release_date"]]; 

     //We create a new date that ignores everything that is not the actual day (ignoring stuff like the time of the day) 
     NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; 
     NSDateComponents *components = 
     [gregorian components:(NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit) fromDate:releaseDate]; 

     //This will represent our releases "bucket" 
     NSDate *bucket = [gregorian dateFromComponents:components]; 

     //We get the existing objects in the bucket and update it with the latest addition 
     NSMutableArray *releasesInBucket = [tmpDict objectForKey:bucket]; 
     if (!releasesInBucket){ 
      releasesInBucket = [NSMutableArray array]; 
      [unsortedReleaseWeek addObject:bucket]; 
     } 

     UpcomingRelease *upcomingRelease = [UpcomingRelease upcomingReleaseWithName:[upcomingReleaseDictionary objectForKey:@"release_name"]]; 
     upcomingRelease.release_date = [upcomingReleaseDictionary objectForKey:@"release_date"]; 
     upcomingRelease.release_price = [upcomingReleaseDictionary objectForKey:@"release_price"]; 
     upcomingRelease.release_colorway = [upcomingReleaseDictionary objectForKey:@"release_colorway"]; 
     upcomingRelease.release_date = [upcomingReleaseDictionary objectForKey:@"release_date"]; 
     upcomingRelease.thumb = [upcomingReleaseDictionary valueForKeyPath:@"thumb"]; 
     upcomingRelease.images = [upcomingReleaseDictionary objectForKey:@"images"]; 
     [releasesInBucket addObject:upcomingRelease]; 
     [tmpDict setObject:releasesInBucket forKey:bucket]; 
    } 

    [unsortedReleaseWeek sortUsingComparator:^NSComparisonResult(id obj1, id obj2) { 
     NSDate* date1 = obj1; 
     NSDate* date2 = obj2; 
     //This will sort the dates in ascending order (earlier dates first) 
     return [date1 compare:date2]; 
     //Use [date2 compare:date1] if you want an descending order 
    }]; 

    self.releaseWeekDictionary = [NSDictionary dictionaryWithDictionary:tmpDict]; 
    self.releaseWeek = [NSArray arrayWithArray:unsortedReleaseWeek]; 

} 
+0

Обратите внимание, что если viewDidLoad инициирует загрузку, вам нужно будет каким-то образом привести к концу загрузки, чтобы «обновить» ваш взгляд, так как загрузка закончится после viewDidLoad уже вернулся. –

ответ

0

Скачать инструкцию асинхров данных, как в этом ответе: Object-c/iOS :How to use ASynchronous to get a data from URL?

Затем запустите его через JSon анализатор.

+0

Кроме того, я бы рекомендовал сделать все разбор JSON и создание объектов в другом потоке. Было бы лучше всего запустить «gregorian» только один раз (не в цикле), так как «NSCalendar' медленно запускаются. –

1

Если вы хотите получить только данные json, вам не нужно настраивать множество вещей.

используйте приведенный ниже код. Создайте метод jsonParse, который получает объект NSData.

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul); 
dispatch_async(queue, ^{ 

    NSData *data = [[NSData alloc]initWithContentsOfURL:[NSURL URLWithString:@"http://obscure-lake-7450.herokuapp.com/upcoming.json"]]; 
    dispatch_sync(dispatch_get_main_queue(), ^{ 

     [self jsonParse:data]; 

    }); 
}); 
+0

Почему бы не использовать 'удобный метод класса NSURLConnection' sendAsynchronousRequest: queue: completionHandler': [NSURLConnection Class Reference] (https://developer.apple.ком/библиотека/IOS/документация/какао/справка/основание/Классы/NSURLConnection_Class/Справка/reference.html # // apple_ref/ОКК/CLM/NSURLConnection/sendAsynchronousRequest: очереди: completionHandler :)? Хотя это будет по-прежнему наивным подходом, есть, по крайней мере, более эффективная обработка ошибок. – CouchDeveloper

0

родовым выполнения кода в фоновом потоке вы можете использовать этот метод:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
    // Code here is run on a background thread 

    dispatch_async(dispatch_get_main_queue(), ^{ 
      // Code here is run on the main thread (the UI thread) after your code above has completed so you can update UI after the JSON call has completed if you need to. 
    }); 
}); 

Но помните, что компания Apple не позволяет обновить элементы пользовательского интерфейса в фоновом потоке. Кроме того, они не позволяют вам создавать больше потоков из фонового потока, это должно быть сделано из основного потока.

7

Один простой подход заключается в использовании метода класса NSURLConnectionsendAsynchronousRequest:queue:error.

Следующий фрагмент кода представляет собой пример загрузки JSON с сервера и выполнения обработчика завершения в фоновом потоке, который анализирует JSON. Он также выполняет все рекомендуемые проверки ошибок:

NSURL* url = [NSURL URLWithString:@"http://example.com"]; 
NSMutableURLRequest* urlRequest = [NSMutableURLRequest requestWithURL:url]; 
[urlRequest addValue:@"application/json" forHTTPHeaderField:@"Accept"]; 
NSOperationQueue* queue = [[NSOperationQueue alloc] init]; 

[NSURLConnection sendAsynchronousRequest:urlRequest 
            queue:queue 
         completionHandler:^(NSURLResponse* response, 
              NSData* data, 
              NSError* error) 
{ 
    if (data) { 
     NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response; 
     // check status code and possibly MIME type (which shall start with "application/json"): 
     NSRange range = [response.MIMEType rangeOfString:@"application/json"]; 

     if (httpResponse.statusCode == 200 /* OK */ && range.length != 0) { 
      NSError* error; 
      id jsonObject = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; 
      if (jsonObject) { 
       dispatch_async(dispatch_get_main_queue(), ^{ 
        // self.model = jsonObject; 
        NSLog(@"jsonObject: %@", jsonObject); 
       }); 
      } else { 
       dispatch_async(dispatch_get_main_queue(), ^{ 
        //[self handleError:error]; 
        NSLog(@"ERROR: %@", error); 
       }); 
      } 
     } 
     else { 
      // status code indicates error, or didn't receive type of data requested 
      NSString* desc = [[NSString alloc] initWithFormat:@"HTTP Request failed with status code: %d (%@)", 
           (int)(httpResponse.statusCode), 
           [NSHTTPURLResponse localizedStringForStatusCode:httpResponse.statusCode]]; 
      NSError* error = [NSError errorWithDomain:@"HTTP Request" 
               code:-1000 
              userInfo:@{NSLocalizedDescriptionKey: desc}]; 
      dispatch_async(dispatch_get_main_queue(), ^{ 
       //[self handleError:error]; // execute on main thread! 
       NSLog(@"ERROR: %@", error); 
      }); 
     } 
    } 
    else { 
     // request failed - error contains info about the failure 
     dispatch_async(dispatch_get_main_queue(), ^{ 
      //[self handleError:error]; // execute on main thread! 
      NSLog(@"ERROR: %@", error); 
     }); 
    } 
}]; 

Несмотря на то, что кажется несколько сложным, ИМО это минималистичный и еще наивный подход. Среди других недостатков, основными вопросами являются:

  • ему не хватает возможности отменить запрос и
  • нет никакого способа, чтобы справиться с более сложной аутентификации.

Более сложный подход должен использовать NSURLConnectionделегатов. Обычно библиотеки сторонних разработчиков реализуют его таким образом, инкапсулируя запрос NSURLConnection и другую соответствующую информацию о состоянии в подкласс NSOperation. Вы можете начать с своей собственной реализации, например, используя this code в качестве шаблона.

+0

Можете ли вы объяснить назначение своих вызовов 'dispatch_async' здесь? Если я правильно понимаю, этот блок помещается в очередь, которая была сделана как часть основного потока, поэтому она должна выполняться в том же потоке, что и main_queue, так или иначе? –

+1

@CodeCommander Блок завершения метода удобного класса 'sendAsynchronousRequest: queue: completionHandler:' будет выполнен по указанному _queue_. _queue_ является «NSOperationQueue», который был создан с использованием '[[NSOperationQueue alloc] init]', который использует «частный контекст выполнения», то есть - это НЕ основной поток. Вы можете прочитать больше в официальной документации для NSOperationQueue и NSURLConnection. ;) – CouchDeveloper

+0

Я вижу, и если вы передаете результат 'dispatch_get_main_queue()' в качестве очереди на 'sendAsynchronousRequest: queue: completionHandler:' это приведет к блокировке основного потока в ожидании запроса? (побеждая цель асинхронного вызова) –

-1

Попробуйте этот код:

 NSURL * inkURL = [NSURL URLWithString:@"your url"]; 
     NSURLRequest * request = [[NSURLRequest alloc]initWithURL:inkURL cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:10.0]; 
     NSOperationQueue * queue = [[NSOperationQueue alloc]init]; 
     [NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse * response, NSData * data, NSError * error) { 
      NSData * jsonData = [NSData dataWithContentsOfURL:inkURL]; 
      NSDictionary * dataDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error]; 
      self.inkArray = [dataDictionary objectForKey:@"users"]; 


     }]; 
0
NSString *[email protected]"http://itunes.apple.com/in/rss/topsongs/limit=25/json"; 
NSMutableURLRequest *request=[[NSMutableURLRequest alloc]initWithURL:[NSURL URLWithString:urlstr]]; 

[NSURLConnection sendAsynchronousRequest:request 
            queue:[[NSOperationQueue alloc] init] 
         completionHandler:^(NSURLResponse* response, 
              NSData* data, NSError* error) 
{ 
    NSError *myError = nil; 
    NSDictionary *dic1 = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&myError]; 

    if (myError ==nil) { 
     NSDictionary*feed =[dic1 objectForKey:@"feed"]; 
     NSArray*arrayofentry =[feed objectForKey:@"entry"]; 

     for(NSDictionary *dic2 in arrayofentry) { 
      requestReply=[dic2 objectForKey:@"title"]; 
      [arr1 addObject:requestReply]; 
     } 
     [self.table reloadData]; 
    } 
}]; 
+0

Возможно, вы можете вкратце объяснить, что вы делаете? –

+0

Этот подход имеет две проблемы: 1) Проверка параметра out 'myError' не является правильным способом проверить, не сработал ли метод' JSONObjectWithData'. Вместо этого вам нужно проверить значение _return_, то есть проверить, соответствует ли 'dic1'' nil', затем _and only then_ вы можете получить доступ к параметру out 'myError'. В противном случае метод был успешным. 2) Обработчик завершения выполняется в частной очереди (из-за 'queue: [[NSOperationQueue alloc] init') - то есть код не выполняется в основном потоке - и' [self.table reloadData] 'будет аварийно завершен - так как методы UIKit не должны запускаться по не-основным потокам. ;) – CouchDeveloper

Смежные вопросы