2014-09-11 5 views
9

EDIT: виновником был iOS 8, а не симулятор (который я не понимал, что уже работает iOS 8). Я переименовал заголовок, чтобы отразить это.Получение сбоев в работе с альбомами на iOS 8, но работает на iOS 7

Я с радостью воспользовался кодом this SO question, чтобы загрузить обложку альбома из mp3-файлов. Это было на моем iPhone 5 с iOS 7.1.

Но потом я проследил сбой в iOS-симуляторе этого кода. Дальнейшее исследование показало, что этот код также разбился на моем iPad. Он разбился на моем iPad после обновления его до iOS 8.

Похоже, что словарь, содержащий изображение, поврежден.

Я создал фиктивный проект iOS, который только загружает обложку альбома и получил тот же результат. Ниже приведен код этого диспетчера представлений.

- (void) viewDidAppear:(BOOL)animated 
{ 
    self.titleText = @"Overkill"; // Set song filename here 
    NSString *filePath = [[NSBundle mainBundle] pathForResource:self.titleText ofType:@"mp3"]; 
    if (!filePath) { 
     return; 
    } 
    NSURL *fileURL = [NSURL fileURLWithPath:filePath]; 

    NSLog(@"Getting song metadata for %@", self.titleText); 
    AVAsset *asset = [AVURLAsset URLAssetWithURL:fileURL options:nil]; 
    if (asset != nil) { 
     NSArray *keys = [NSArray arrayWithObjects:@"commonMetadata", nil]; 
     [asset loadValuesAsynchronouslyForKeys:keys completionHandler:^{ 
      NSArray *artworks = [AVMetadataItem metadataItemsFromArray:asset.commonMetadata 
                   withKey:AVMetadataCommonKeyArtwork 
                   keySpace:AVMetadataKeySpaceCommon]; 
      UIImage *albumArtWork; 

      for (AVMetadataItem *item in artworks) { 
       if ([item.keySpace isEqualToString:AVMetadataKeySpaceID3]) { 
        NSDictionary *dict = [item.value copyWithZone:nil]; 

        // ********** 
        // Crashes here with SIGABRT. dict is not a valid dictionary. 
        // ********** 

        if ([dict objectForKey:@"data"]) { 
         albumArtWork = [UIImage imageWithData:[dict objectForKey:@"data"]]; 
        } 
       } 
       else if ([item.keySpace isEqualToString:AVMetadataKeySpaceiTunes]) { 
        // This doesn't appear to get called for images set (ironically) in iTunes 
        albumArtWork = [UIImage imageWithData:[item.value copyWithZone:nil]]; 
       } 
      } 

      if (albumArtWork != nil) { 
       dispatch_sync(dispatch_get_main_queue(), ^{ 
        [self.albumArtImageView setImage:albumArtWork]; 
       }); 
      } 

     }]; 
    } 

} 

Я отметил линию аварии. Он ожидает, что файл Overkill.mp3 будет в комплекте. Я тестировал с несколькими mp3 и m4a, экспортированными из iTunes и Amazon, поэтому я знаю, что сами файлы правильно закодированы.

Протестировано в Xcode 6.0 и 6.1.

Любые идеи, почему это будет работать на iPhone, но не на симуляторе или iPad?

EDIT/UPDATE:

Протоколирование item.value показывает различия.

На iPhone 5 (работа):

(lldb) po item.value 
{ 
    MIME = JPG; 
    data = <ffd8ffe0 .... several Kb of data ..... 2a17ffd9>; 
    identifier = ""; 
    picturetype = Other; 
} 

На Simulator (сбои)

(lldb) po item.value 
<ffd8ffe0 .... several Kb of data ..... 2a17ffd9> 

Выходит, что на тренажере нет словаря, только сырая работа.

Изменение кода не ожидать словаря, но взять item.value как UIImage работает!

 for (AVMetadataItem *item in artworks) { 
      if ([item.keySpace isEqualToString:AVMetadataKeySpaceID3]) { 
       NSData *newImage = [item.value copyWithZone:nil]; 
       albumArtWork = [UIImage imageWithData:newImage]; 
      } 
      ... 
     } 
+0

Ah ... Xcode 6.0. Должен сказать, я написал этот код, когда был еще новичком. С тех пор многое изменилось. Попробуйте logging 'item.value',' item' и словарь, созданные путем копирования 'item.value'. Кроме того, не могли бы вы разместить сообщение об ошибке и журнал сбоев? – duci9y

+0

@ duci9y Спасибо за руководство посмотреть! См. Мое обновление выше. Кажется, что item.value - это сырые данные UIImage (а не NSDictionary) при использовании имитатора или iPad. Очень странно. Вы знаете способ определения UIImage vs NSDictionary без сбоев приложения? (Я сейчас изучаю это.) –

+0

Используйте 'AVMetadataID3MetadataKeyAttachedPicture' для MP3 и' AVMetadataiTunesMetadataKeyCoverArt' для iTunes вместо общего ключа и посмотрите, получаете ли вы более предсказуемые результаты. – duci9y

ответ

17

Кажется, возвращаемая структура данных изменилась прошивкой 8. value из AVMetadataItem объекта больше не является словарь, но фактические исходные данные UIImage.

Добавление теста для NSFoundationVersionNumber решает проблему. Возможно, есть более чистое решение.

- (void) viewDidAppear:(BOOL)animated 
{ 
    self.titleText = @"Overkill"; 
    NSString *filePath = [[NSBundle mainBundle] pathForResource:self.titleText ofType:@"mp3"]; 
    if (!filePath) { 
     return; 
    } 
    NSURL *fileURL = [NSURL fileURLWithPath:filePath]; 

    NSLog(@"Getting song metadata for %@", self.titleText); 
    AVAsset *asset = [AVURLAsset URLAssetWithURL:fileURL options:nil]; 
    if (asset != nil) { 
     NSArray *keys = [NSArray arrayWithObjects:@"commonMetadata", nil]; 
     [asset loadValuesAsynchronouslyForKeys:keys completionHandler:^{ 
      NSArray *artworks = [AVMetadataItem metadataItemsFromArray:asset.commonMetadata 
                   withKey:AVMetadataCommonKeyArtwork 
                   keySpace:AVMetadataKeySpaceCommon]; 
      UIImage *albumArtWork; 

      for (AVMetadataItem *item in artworks) { 
       if ([item.keySpace isEqualToString:AVMetadataKeySpaceID3]) { 

        // *** WE TEST THE IOS VERSION HERE *** 

        if (TARGET_OS_IPHONE && NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_7_1) { 
         NSData *newImage = [item.value copyWithZone:nil]; 
         albumArtWork = [UIImage imageWithData:newImage]; 
        } 
        else { 
         NSDictionary *dict = [item.value copyWithZone:nil]; 
         if ([dict objectForKey:@"data"]) { 
          albumArtWork = [UIImage imageWithData:[dict objectForKey:@"data"]]; 
         } 
        } 
       } 
       else if ([item.keySpace isEqualToString:AVMetadataKeySpaceiTunes]) { 
        // This doesn't appear to get called for images set (ironically) in iTunes 
        albumArtWork = [UIImage imageWithData:[item.value copyWithZone:nil]]; 
       } 
      } 

      if (albumArtWork != nil) { 
       dispatch_sync(dispatch_get_main_queue(), ^{ 
        [self.albumArtImageView setImage:albumArtWork]; 
       }); 
      } 

     }]; 
    } 

} 
+1

Большое спасибо Стэну! Иногда мне кажется, что я прыгаю на самолете до SF и дросселирую нескольких сотрудников Apple. Почему они чувствуют необходимость тратить свое время на такую ​​глупость, я никогда не пойму. – amergin

+1

Я думаю, вы можете понять их, когда разработали свою собственную ОС и выпустили несколько ее версий. :) –

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