convert from CGImageMetadata to nsdictionary - ios

i tried to convert my 360° metadata image to nsdictionary. every time the app crash when i tried to print a value for an attribute. i wrote this code
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL);
NSArray *metadataArray = nil;
if (source) {
_metaData = CGImageSourceCopyMetadataAtIndex(source, 0, NULL);
if (_metaData) {
metadataArray = CFBridgingRelease(CGImageMetadataCopyTags(_metaData));
CFRelease(_metaData);
}
CFRelease(source);
}
NSLog(#"%#",metadataArray[12]);//this is the problem
In the output, i find that the type of immutableMetadata is CGImageMetadata not a NSdictionary. How i can convert to nsdictionary please??

Swift
extension CGImageSource {
func metadata() -> Dictionary<String, Any>? {
guard let imageMetadata = CGImageSourceCopyMetadataAtIndex(self, 0, .none) else {
return .none
}
guard let tags = CGImageMetadataCopyTags(imageMetadata) else {
return .none
}
var result = [String: Any]()
for tag in tags as NSArray {
let tagMetadata = tag as! CGImageMetadataTag
if let cfName = CGImageMetadataTagCopyName(tagMetadata) {
let name = String(cfName)
let value = CGImageMetadataTagCopyValue(tagMetadata)
result[name] = value
}
}
return result
}
}

I don't know much about ImageIO. It's full of CF/CG stuff, which may lead to __bridge uses.
You are only interested in a value, but if someone is looking for another, it could use that sample code/research:
I looked into CGImageMetadata.h for each use of functions related to CGImageMetadataTagRef that may be useful.
CGImageSourceRef source = CGImageSourceCreateWithData((CFDataRef)imagedata, NULL);
CGImageMetadataRef imageMetadataRef = CGImageSourceCopyMetadataAtIndex(source, 0, NULL);
NSArray *metadata = CFBridgingRelease(CGImageMetadataCopyTags(imageMetadataRef));
NSLog(#"metadata = %#", metadata);
for (id aRef in metadata)
{
CGImageMetadataTagRef currentRef = (__bridge CGImageMetadataTagRef)(aRef);
NSLog(#"Ref: %#", currentRef);
CGImageMetadataType type = CGImageMetadataTagGetType(currentRef);
NSLog(#"Type: %d", type);
CFStringRef nameSpace = CGImageMetadataTagCopyNamespace(currentRef);
NSLog(#"NameSpace: %#", nameSpace);
CFStringRef prefix = CGImageMetadataTagCopyPrefix(currentRef);
NSLog(#"prefix: %#", prefix);
CFStringRef name = CGImageMetadataTagCopyName(currentRef);
NSLog(#"name: %#", name);
CFTypeRef value = CGImageMetadataTagCopyValue(currentRef);
NSLog(#"Value: %#", value);
CFStringRef valueTypeStr = CFCopyTypeIDDescription(CFGetTypeID(value));
NSLog(#"valueTypeStr: %#", valueTypeStr);
//Test related to question
if ([#"GPano" isEqualToString:(__bridge NSString *)prefix] && [#"PoseHeadingDegrees" isEqualToString:(__bridge NSString *)name])
{
NSString *str = (__bridge NSString *)value;
NSLog(#"Str: %#", str);
NSLog(#"Int: %d", [str intValue]);
}
}
To know if the value is convertible to a more "known" object, like NSString (<=> CFString), NSArray (<=> CFArray), NSDictionary (<=> CFDictionary), I looked at CFStringRef valueTypeStr = CFCopyTypeIDDescription(CFGetTypeID(value)); (or you can also do a "quicker test" with related question: Determining what a CFTypeRef is?).
It's all "manually" done, I'm quite surprised that there is no conversion from CGImageMetadataTagRef to a more "known" object (and not struct), maybe there is, I didn't read the whole headers of CGImageMetadata.h, so there may be a quicker way, but I thought that even using "old school" approach, by digging could be useful for another user with a related question, helping him/her to find quickly what's he/she is looking for.
To answer your initial question: CGImageMetadata to NSDictionary, if there is no "easy conversion" tool, and with the "digging" approach, it's won't make a lot of sense, because the interesting method CGImageMetadataCopyTags returns a NSArray (or CFArray).
But since a lot of CGImageMetadataTagRef prefix are the same, you may be interested, by doing manually the following dictionary:
#{prefix1:#[stuff10, stuff11, etc.], prefix2:#[stuff20, stuff21, etc.]}

Related

ARC lead to crash CoreFoundation object

I have a part of code which sometimes crashes on OSX system on client side but I can't reproduce the crash. I have only few assumptions but I don't want to make blind fix. I just want to be convinced with the fix:
+ (NSArray *)loginItems:(NSError *__autoreleasing *)error {
LSSharedFileListRef list = [self loginItemsFileListRef];
NSMutableArray *result = [NSMutableArray array];
NSArray *items = [self loginItemsForFileListRef:list];
CFRelease(list);
for (id item in items) {
LSSharedFileListItemRef itemRef = (__bridge LSSharedFileListItemRef)item;
[result addObject:[self loginItemFromItemRef:itemRef]];
}
return result;
}
+ (NSDictionary *)loginItemFromItemRef:(LSSharedFileListItemRef)itemRef {
NSMutableDictionary *itemDict = [NSMutableDictionary dictionary];
// Name
CFStringRef nameRef = LSSharedFileListItemCopyDisplayName(itemRef);
if (nameRef) {
NSString *name = (__bridge NSString *)nameRef;
itemDict[RESLoginItemsNameKey] = name.stringByDeletingPathExtension;
CFRelease(nameRef);
}
// Path
CFURLRef URLRef = NULL;
if (LSSharedFileListItemResolve(itemRef, 0, &URLRef, NULL) == noErr) {
if (URLRef) {
NSURL *URL = (__bridge NSURL *)URLRef;
NSString *path = URL.path;
if (path) {
itemDict[RESLoginItemsPathKey] = path;
}
CFRelease(URLRef);
}
}
// Hidden
CFBooleanRef hiddenRef = LSSharedFileListItemCopyProperty(itemRef,
kLSSharedFileListLoginItemHidden);
if (hiddenRef) {
if (hiddenRef == kCFBooleanTrue) {
itemDict[RESLoginItemsHiddenKey] = #YES;
}
CFRelease(hiddenRef);
}
return itemDict;
}
The crash inside loginItems: namely SharedFileListItemDeallocate cause the crash. When NSArray's items auto-released. I suppose that:
SSharedFileListItemRef itemRef = (__bridge LSSharedFileListItemRef)item;
breaks ARC behavior and when loop finished it lead to release concrete item of the array. And second release will be called on function return which will lead to the crash.
Has anyone can prove or disprove my idea? Thanks.
I think the problem is that you are releasing list before using the items returned by
[self loginItemsForFileListRef:list]
If the items returned depend on list being valid, because the release of list might also trigger the release of the items, then you might crash when you try to iterate the returned items. Try to move the release of list after the for-loop that iterates the items. Sometimes you get bitten if you try to be overzealous about memory usage.

ios/c/addressbook: Parse kABPersonAddressProperty crash

I am using the following code to retrieve different address fields from the Addressbook. It works okay for name and some properties but crashes with address. I suspect that the address is an int rather than string but can't find anything in the docs to explain why it should be different. Would appreciate any suggestions:
//This works:
CFStringRef jobtitleRef = nil;
jobtitleRef = ABRecordCopyValue(addressBookRecord, kABPersonJobTitleProperty);
NSString *jobtitle = #"";
if (jobtitleRef!=nil) {
jobtitle =[jobtitle stringByAppendingString:(__bridge NSString *)jobtitleRef];
}
//But this crashes
CFStringRef addr1Ref = nil;
addr1Ref = ABRecordCopyValue(addressBookRecord, kABPersonAddressProperty);
NSString *addr1 = #"";
if (addr1Ref!=nil) {
addr1 = [addr1 stringByAppendingString:(__bridge NSString *)addr1Ref]; //crashes on this line
}
Edit:
Found the answer in another question:
ABMultiValueRef st = ABRecordCopyValue(person, kABPersonAddressProperty);
if (ABMultiValueGetCount(st) > 0) {
CFDictionaryRef dict = ABMultiValueCopyValueAtIndex(st, 0);
self.street.text = CFDictionaryGetValue(dict, kABPersonAddressStreetKey);
}

How to set a textview's text to a string created by receiving text from an app action extension

I am playing around with action extensions and looked at Apple's documents and found this code.
NSExtensionContext *myExtensionContext = self.extensionContext;
NSArray *inputItems = myExtensionContext.inputItems;
Then I change the array to a string.
NSString * resultString = [inputItems componentsJoinedByString:#""];
Then, I set the text view to the resultString string.
textView.text = resultString;
What I have been getting is
<NSExtensionItem: 0x174002840> - userInfo: {NSExtensionItemAttachmentsKey = ("<NSItemProvider: 0x17424c900> {types = (\n \"public.plain-text\"\n)}");}
that appears in my text view.
Code snippet from viewDidLoad:
[super viewDidLoad];
NSExtensionContext *myExtensionContext = self.extensionContext;
NSArray *inputItems = myExtensionContext.inputItems;
NSString * resultString = [inputItems componentsJoinedByString:#""];
textView.text = resultString;
Actually following code will return array of NSExtensionItem not a NSString type so you can not parse directly using
NSString * resultString = [inputItems componentsJoinedByString:#""];
To Parse NSArray of NSExtensionItems, You need to do following things. Here I assume that 'NSDictionary' as input type.
for (NSExtensionItem *item in self.extensionContext.inputItems) {
for (NSItemProvider *itemProvider in item.attachments) {
if ([itemProvider hasItemConformingToTypeIdentifier:#"typeIdentifier"]) {
// This is an image. We'll load it, then place it in our image view.
[itemProvider loadItemForTypeIdentifier:#"typeIdentifier" options:nil completionHandler:^(id<NSSecureCoding> item, NSError *error) {
NSDictionary* tempDict = (NSDictionary*)item;
NSLog(#"Dectionary : %#",item);
}];
}
}
}
For More details Action Extension tutorial may help you.

iOS How To Convert NSDictionary To URL QueryString In Simplest Way [duplicate]

With all the URL-handling objects lying around in the standard Cocoa libraries (NSURL, NSMutableURL, NSMutableURLRequest, etc), I know I must be overlooking an easy way to programmatically compose a GET request.
Currently I'm manually appending "?" followed by name value pairs joined by "&", but all of my name and value pairs need to be manually encoded so NSMutableURLRequest doesn't fail entirely when it tries to connect to the URL.
This feels like something I should be able to use a pre-baked API for.... is there anything out of the box to append an NSDictionary of query parameters to an NSURL? Is there another way I should approach this?
Introduced in iOS8 and OS X 10.10 is NSURLQueryItem, which can be used to build queries. From the docs on NSURLQueryItem:
An NSURLQueryItem object represents a single name/value pair for an item in the query portion of a URL. You use query items with the queryItems property of an NSURLComponents object.
To create one use the designated initializer queryItemWithName:value: and then add them to NSURLComponents to generate an NSURL. For example:
NSURLComponents *components = [NSURLComponents componentsWithString:#"http://stackoverflow.com"];
NSURLQueryItem *search = [NSURLQueryItem queryItemWithName:#"q" value:#"ios"];
NSURLQueryItem *count = [NSURLQueryItem queryItemWithName:#"count" value:#"10"];
components.queryItems = #[ search, count ];
NSURL *url = components.URL; // http://stackoverflow.com?q=ios&count=10
Notice that the question mark and ampersand are automatically handled. Creating an NSURL from a dictionary of parameters is as simple as:
NSDictionary *queryDictionary = #{ #"q": #"ios", #"count": #"10" };
NSMutableArray *queryItems = [NSMutableArray array];
for (NSString *key in queryDictionary) {
[queryItems addObject:[NSURLQueryItem queryItemWithName:key value:queryDictionary[key]]];
}
components.queryItems = queryItems;
I've also written a blog post on how to build URLs with NSURLComponents and NSURLQueryItems.
You can create a category for NSDictionary to do this -- there isn't a standard way in the Cocoa library that I could find either. The code that I use looks like this:
// file "NSDictionary+UrlEncoding.h"
#import <cocoa/cocoa.h>
#interface NSDictionary (UrlEncoding)
-(NSString*) urlEncodedString;
#end
with this implementation:
// file "NSDictionary+UrlEncoding.m"
#import "NSDictionary+UrlEncoding.h"
// helper function: get the string form of any object
static NSString *toString(id object) {
return [NSString stringWithFormat: #"%#", object];
}
// helper function: get the url encoded string form of any object
static NSString *urlEncode(id object) {
NSString *string = toString(object);
return [string stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
}
#implementation NSDictionary (UrlEncoding)
-(NSString*) urlEncodedString {
NSMutableArray *parts = [NSMutableArray array];
for (id key in self) {
id value = [self objectForKey: key];
NSString *part = [NSString stringWithFormat: #"%#=%#", urlEncode(key), urlEncode(value)];
[parts addObject: part];
}
return [parts componentsJoinedByString: #"&"];
}
#end
I think the code's pretty straightforward, but I discuss it in some more detail at http://blog.ablepear.com/2008/12/urlencoding-category-for-nsdictionary.html.
I wanted to use Chris's answer, but it wasn't written for Automatic Reference Counting (ARC) so I updated it. I thought I'd paste my solution in case anyone else has this same issue. Note: replace self with the instance or class name where appropriate.
+(NSString*)urlEscapeString:(NSString *)unencodedString
{
CFStringRef originalStringRef = (__bridge_retained CFStringRef)unencodedString;
NSString *s = (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,originalStringRef, NULL, (CFStringRef)#"!*'\"();:#&=+$,/?%#[]% ", kCFStringEncodingUTF8);
CFRelease(originalStringRef);
return s;
}
+(NSString*)addQueryStringToUrlString:(NSString *)urlString withDictionary:(NSDictionary *)dictionary
{
NSMutableString *urlWithQuerystring = [[NSMutableString alloc] initWithString:urlString];
for (id key in dictionary) {
NSString *keyString = [key description];
NSString *valueString = [[dictionary objectForKey:key] description];
if ([urlWithQuerystring rangeOfString:#"?"].location == NSNotFound) {
[urlWithQuerystring appendFormat:#"?%#=%#", [self urlEscapeString:keyString], [self urlEscapeString:valueString]];
} else {
[urlWithQuerystring appendFormat:#"&%#=%#", [self urlEscapeString:keyString], [self urlEscapeString:valueString]];
}
}
return urlWithQuerystring;
}
The other answers work great if the values are strings, however if the values are dictionaries or arrays then this code will handle that.
Its important to note that there is no standard way of passing an array/dictionary via the query string but PHP handles this output just fine
-(NSString *)serializeParams:(NSDictionary *)params {
/*
Convert an NSDictionary to a query string
*/
NSMutableArray* pairs = [NSMutableArray array];
for (NSString* key in [params keyEnumerator]) {
id value = [params objectForKey:key];
if ([value isKindOfClass:[NSDictionary class]]) {
for (NSString *subKey in value) {
NSString* escaped_value = (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,
(CFStringRef)[value objectForKey:subKey],
NULL,
(CFStringRef)#"!*'();:#&=+$,/?%#[]",
kCFStringEncodingUTF8);
[pairs addObject:[NSString stringWithFormat:#"%#[%#]=%#", key, subKey, escaped_value]];
}
} else if ([value isKindOfClass:[NSArray class]]) {
for (NSString *subValue in value) {
NSString* escaped_value = (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,
(CFStringRef)subValue,
NULL,
(CFStringRef)#"!*'();:#&=+$,/?%#[]",
kCFStringEncodingUTF8);
[pairs addObject:[NSString stringWithFormat:#"%#[]=%#", key, escaped_value]];
}
} else {
NSString* escaped_value = (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,
(CFStringRef)[params objectForKey:key],
NULL,
(CFStringRef)#"!*'();:#&=+$,/?%#[]",
kCFStringEncodingUTF8);
[pairs addObject:[NSString stringWithFormat:#"%#=%#", key, escaped_value]];
[escaped_value release];
}
}
return [pairs componentsJoinedByString:#"&"];
}
Examples
[foo] => bar
[translations] =>
{
[one] => uno
[two] => dos
[three] => tres
}
foo=bar&translations[one]=uno&translations[two]=dos&translations[three]=tres
[foo] => bar
[translations] =>
{
uno
dos
tres
}
foo=bar&translations[]=uno&translations[]=dos&translations[]=tres
I refactored and converted to ARC answer by AlBeebe
- (NSString *)serializeParams:(NSDictionary *)params {
NSMutableArray *pairs = NSMutableArray.array;
for (NSString *key in params.keyEnumerator) {
id value = params[key];
if ([value isKindOfClass:[NSDictionary class]])
for (NSString *subKey in value)
[pairs addObject:[NSString stringWithFormat:#"%#[%#]=%#", key, subKey, [self escapeValueForURLParameter:[value objectForKey:subKey]]]];
else if ([value isKindOfClass:[NSArray class]])
for (NSString *subValue in value)
[pairs addObject:[NSString stringWithFormat:#"%#[]=%#", key, [self escapeValueForURLParameter:subValue]]];
else
[pairs addObject:[NSString stringWithFormat:#"%#=%#", key, [self escapeValueForURLParameter:value]]];
}
return [pairs componentsJoinedByString:#"&"];
}
- (NSString *)escapeValueForURLParameter:(NSString *)valueToEscape {
return (__bridge_transfer NSString *) CFURLCreateStringByAddingPercentEscapes(NULL, (__bridge CFStringRef) valueToEscape,
NULL, (CFStringRef) #"!*'();:#&=+$,/?%#[]", kCFStringEncodingUTF8);
}
If you are already using AFNetworking (as was the case with me), you can use it's class AFHTTPRequestSerializer to create the required NSURLRequest.
[[AFHTTPRequestSerializer serializer] requestWithMethod:#"GET" URLString:#"YOUR_URL" parameters:#{PARAMS} error:nil];
In case you only require the URL for your work, use NSURLRequest.URL.
Here is a simple example in Swift (iOS8+):
private let kSNStockInfoFetchRequestPath: String = "http://dev.markitondemand.com/Api/v2/Quote/json"
private func SNStockInfoFetchRequestURL(symbol:String) -> NSURL? {
if let components = NSURLComponents(string:kSNStockInfoFetchRequestPath) {
components.queryItems = [NSURLQueryItem(name:"symbol", value:symbol)]
return components.URL
}
return nil
}
I took Joel's recommendation of using URLQueryItems and turned into a Swift Extension (Swift 3)
extension URL
{
/// Creates an NSURL with url-encoded parameters.
init?(string : String, parameters : [String : String])
{
guard var components = URLComponents(string: string) else { return nil }
components.queryItems = parameters.map { return URLQueryItem(name: $0, value: $1) }
guard let url = components.url else { return nil }
// Kinda redundant, but we need to call init.
self.init(string: url.absoluteString)
}
}
(The self.init method is kinda cheesy, but there was no NSURL init with components)
Can be used as
URL(string: "http://www.google.com/", parameters: ["q" : "search me"])
I've got another solution:
http://splinter.com.au/build-a-url-query-string-in-obj-c-from-a-dict
+(NSString*)urlEscape:(NSString *)unencodedString {
NSString *s = (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,
(CFStringRef)unencodedString,
NULL,
(CFStringRef)#"!*'\"();:#&=+$,/?%#[]% ",
kCFStringEncodingUTF8);
return [s autorelease]; // Due to the 'create rule' we own the above and must autorelease it
}
// Put a query string onto the end of a url
+(NSString*)addQueryStringToUrl:(NSString *)url params:(NSDictionary *)params {
NSMutableString *urlWithQuerystring = [[[NSMutableString alloc] initWithString:url] autorelease];
// Convert the params into a query string
if (params) {
for(id key in params) {
NSString *sKey = [key description];
NSString *sVal = [[params objectForKey:key] description];
// Do we need to add ?k=v or &k=v ?
if ([urlWithQuerystring rangeOfString:#"?"].location==NSNotFound) {
[urlWithQuerystring appendFormat:#"?%#=%#", [Http urlEscape:sKey], [Http urlEscape:sVal]];
} else {
[urlWithQuerystring appendFormat:#"&%#=%#", [Http urlEscape:sKey], [Http urlEscape:sVal]];
}
}
}
return urlWithQuerystring;
}
You can then use it like so:
NSDictionary *params = #{#"username":#"jim", #"password":#"abc123"};
NSString *urlWithQuerystring = [self addQueryStringToUrl:#"https://myapp.com/login" params:params];
-(NSString*)encodeDictionary:(NSDictionary*)dictionary{
NSMutableString *bodyData = [[NSMutableString alloc]init];
int i = 0;
for (NSString *key in dictionary.allKeys) {
i++;
[bodyData appendFormat:#"%#=",key];
NSString *value = [dictionary valueForKey:key];
NSString *newString = [value stringByReplacingOccurrencesOfString:#" " withString:#"+"];
[bodyData appendString:newString];
if (i < dictionary.allKeys.count) {
[bodyData appendString:#"&"];
}
}
return bodyData;
}
Yet another solution, if you use RestKit there's a function in RKURLEncodedSerialization called RKURLEncodedStringFromDictionaryWithEncoding that does exactly what you want.
Simple way of converting NSDictionary to url query string in Objective-c
Ex: first_name=Steve&middle_name=Gates&last_name=Jobs&address=Palo Alto, California
NSDictionary *sampleDictionary = #{#"first_name" : #"Steve",
#"middle_name" : #"Gates",
#"last_name" : #"Jobs",
#"address" : #"Palo Alto, California"};
NSMutableString *resultString = [NSMutableString string];
for (NSString* key in [sampleDictionary allKeys]){
if ([resultString length]>0)
[resultString appendString:#"&"];
[resultString appendFormat:#"%#=%#", key, [sampleDictionary objectForKey:key]];
}
NSLog(#"QueryString: %#", resultString);
Hope will help :)
If you are already using AFNetwork, you can use their built in serializer to to produce an encoded URL;
NSString *baseURL = #"https://api.app.com/parse";
NSDictionary *mutableParameters = [[NSMutableDictionary alloc] initWithObjectsAndKeys:#"true",#"option1", data, #"option2", token, #"token", #"3.0", #"app", nil];
NSURLRequest *request = [[AFHTTPRequestSerializer serializer] requestWithMethod:#"GET" URLString:baseURL parameters:mutableParameters error:nil];
NSString *urlPath = request.URL.absoluteString;
NSLog(#"%#", urlPath); // https://api.app.com/parse?option1=true&option2=datavalue&token=200%3ATEST%3AENCODE ....
Note; this is an extension to an above answer. The edit queue is full so cannot be added to the existing answer.

Avmetadataitem Getting the trackNumber from an iTunes or ID3 metadata on iOS

I'm trying to get the trackNumber of a aac, mp3 or mp4 file. It's not in the commonMetadata so I started to spelunk in the other metadata keys. I found something that looks like it, but I'm yet unable to read it, and make sense of it. Even the raw data makes no sense to me.
At the moment, I'm just trying to get it using this basic code:
NSArray *meta = [asset metadataForFormat:AVMetadataFormatiTunesMetadata];
for ( AVMetadataItem* item in meta ) {
id key = [item key];
NSString *value = [item stringValue];
NSLog(#"key = %#, value = %#", key, value);
}
Knowing I'm looking for AVMetadataiTunesMetadataKeyTrackNumber.
I realize this thread is quite old but I recently came across this issue myself. I needed ALL the metadata I could gather and came up with the following solution. It's not the most elegant solution but it works well enough. Written in Swift.
func processFile(url:NSURL) {
let yearKey = -1453039239
let genreKey = -1452841618
let encoderKey = -1451987089
let trackKey = "com.apple.iTunes.iTunes_CDDB_TrackNumber"
let CDDBKey = "com.apple.iTunes.iTunes_CDDB_1"
let path:String = url.absoluteString
let asset = AVURLAsset(URL: url, options: nil)
let format = AVMetadataFormatiTunesMetadata
for item:AVMetadataItem in asset.metadataForFormat(format) as Array<AVMetadataItem> {
if let key = item.commonKey { if key == "title" { println(item.value()) } }
if let key = item.commonKey { if key == "artist" { println(item.value()) } }
if let key = item.commonKey { if key == "albumName" { println(item.value()) } }
if let key = item.commonKey { if key == "creationDate" { println(item.value()) } }
if let key = item.commonKey { if key == "artwork" { println( "art" ) } }
if item.key().isKindOfClass(NSNumber) {
if item.key() as NSNumber == yearKey { println("year: \(item.numberValue)") }
if item.key() as NSNumber == genreKey { println("genre: \(item.stringValue)") }
if item.key() as NSNumber == encoderKey { println("encoder: \(item.stringValue)") }
}
if item.key().isKindOfClass(NSString) {
if item.key() as String == trackKey { println("track: \(item.stringValue)") }
if item.key() as String == CDDBKey { println("CDDB: \(item.stringValue)") }
}
}
}
If your track has ID3 metadata, you can easily get the numberValue for the track number. If your track has iTunesMetadata, the dataValue is all you get. You have to guess the intValue yourself.
So far, I'm here. I'm pretty sure I need to work more on the bytes portion.
NSArray *meta = [asset metadataForFormat:AVMetadataFormatiTunesMetadata];
NSArray *itfilteredKeys = [AVMetadataItem metadataItemsFromArray:meta withKey:AVMetadataiTunesMetadataKeyTrackNumber keySpace:nil];
for ( AVMetadataItem* item in itfilteredKeys ) {
NSData *value = [item dataValue];
unsigned char aBuffer[4];
[value getBytes:aBuffer length:4];
unsigned char *n = [value bytes];
int value1 = aBuffer[3];
NSLog(#"trackNumber from iTunes = %i", value1);
}
This question is rather old, but I came upon it as I had a similar problem, so for anyone who still needs a solution. I managed to figure out a way of getting the total tracks number and the track number using the following code:
NSString *value = #"<0000000a 00140000>" //[item stringValue] (in this case 10/20)
NSString *track_no_hex = [[value componentsSeparatedByString:#" "][0] stringByReplacingOccurrencesOfString:#"<" withString:#""];
NSString *total_track_no_hex = [[value componentsSeparatedByString:#" "][1] stringByReplacingOccurrencesOfString:#">" withString:#""];
NSString *track_no = #"";
for(int k=0;k<=4;k+=4){
unsigned result = 0;
NSScanner *scanner = [NSScanner scannerWithString:[track_no_hex substringWithRange:NSMakeRange(k, 4)]];
[scanner scanHexInt:&result];
if(result == 00){
}
else{
track_no = [NSString stringWithFormat:#"%#%u",track_no,result];
}
}
NSString *total_track_no = #"";
for(int k=0;k<=4;k+=4){
unsigned result = 0;
NSScanner *scanner;
if(k+4<=[total_track_no_hex length]){
scanner = [NSScanner scannerWithString:[total_track_no_hex substringWithRange:NSMakeRange(k, 4)]];
}
else{
}
[scanner scanHexInt:&result];
if(result == 00){
}
else{
total_track_no = [NSString stringWithFormat:#"%#%u",total_track_no,result];
}
}
NSLog(#"%#/%#",track_no, total_track_no); // Output 10/20
This will work fine for track numbers under 14461 which should be large enough considering iTunes max track number is 999.

Resources