EDIT: The culprit was iOS 8, not the simulator (which I didn't realize was already running iOS 8) I've renamed the title to reflect this.
I was happily using the code from this SO question to load album artwork from mp3 files. This was on my iPhone 5 with iOS 7.1.
But then I traced crashing in the iOS simulator to this code. Further investigation revealed that this code also crashed on my iPad. It crashed on my iPad after upgrading it to iOS 8.
It appears the dictionary containing the image is corrupted.
I created a dummy iOS project that only loads album art and got the same result. Below is the code from that viewcontroller.
- (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];
});
}
}];
}
}
I've marked the line with the crash. It expects a file Overkill.mp3
to be in the bundle. I tested with multiple mp3's and m4a's exported from iTunes and Amazon, so I know the files themselves are correctly encoded.
Tested in Xcode 6.0 and 6.1.
Any ideas why it would work on iPhone but not the simulator or iPad?
EDIT / UPDATE:
Logging the item.value reveals differences.
On iPhone 5 (works):
(lldb) po item.value
{
MIME = JPG;
data = <ffd8ffe0 .... several Kb of data ..... 2a17ffd9>;
identifier = "";
picturetype = Other;
}
On Simulator (crashes)
(lldb) po item.value
<ffd8ffe0 .... several Kb of data ..... 2a17ffd9>
So it appears that on the simulator there is no dictionary, just the raw artwork.
Changing the code to not expect a dictionary, but take item.value
as a UIImage works!
for (AVMetadataItem *item in artworks) {
if ([item.keySpace isEqualToString:AVMetadataKeySpaceID3]) {
NSData *newImage = [item.value copyWithZone:nil];
albumArtWork = [UIImage imageWithData:newImage];
}
...
}
See Question&Answers more detail:
os 与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…