Can't get key value as String from metadata - swift3

I'm grabbing metadata from an MPMediaItem like this:
let url = item.value(forProperty:MPMediaItemPropertyAssetURL) as? NSURL
let asset = AVURLAsset(url: url! as URL, options: nil)
let metaArray = asset.metadata
for metadata in metaArray{
print("-----metadata:\(metadata)")
print("-----metadata.key:\(String(describing: metadata.key))")
}
However, when I get a block of metadata printed the "key" is printed as a numeric value instead of "pcst" as shown in the printout:
-----metadata:<AVMetadataItem: 0x1740153f0, identifier=itsk/pcst, keySpace=itsk, key class = __NSCFNumber, key=pcst, commonKey=(null), extendedLanguageTag=(null), dataType=com.apple.metadata.datatype.int8, time={INVALID}, duration={INVALID}, startDate=(null), extras={
dataLength = 1;
dataType = 21;
dataTypeNamespace = "com.apple.itunes";
}, value=1>
-----metadata.key:Optional(1885565812)
This is happening for all of the metadata/keys (there are 29 in this particular media item).
Also note that this line of code:
let realString = NSString(string: metadata.key! as! String)
causes this error:
Could not cast value of type '__NSCFNumber' (0x1b80dcdf0) to 'NSString' (0x1b80edae8).
How can I get the string value for the key ("pcst") ?

May be what you are looking for is identifier property of AVMetadataItem.
for metadata in metaArray{
print(metadata.identifier ?? "DefaultValue")
}

In case others want to see the code I'm using:
func returnKeyString(_ inVal: String)->String{
// expecting the metadata for "identifier" as input - returns key value
// eg "itsk/ldes" -> "ldes"
// or "id3/%00WFD" etc. -> "wfd"
var sFinal:String = ""
if (inVal.contains("/")){
sFinal = (inVal.components(separatedBy: "/")[1])
}
if sFinal.contains("%"){
sFinal = sFinal.components(separatedBy: "%")[1]
let index1 = sFinal.index(sFinal.startIndex, offsetBy: 2)
sFinal = sFinal.substring(from: index1)
}
return sFinal.lowercased()
}

Related

How to get the KeyValue from QueryString and return string in specific string format

I am a newbie and need help.
I need to create a baseString from the below QueryString.
This baseString will look something like this in the end:
&ap=裕廊坊 心邻坊&oq=c# nunit mac&q=c# nunit mac
QueryString :
HTTPS://www.sky.com/api/v1/rest/level2/in-in/?q=c%23+nunit+mac&oq=c%23+nunit+mac&ap=裕坊%20邻坊
Problem:
How to get the KeyValue from the above QueryString
1) By getting all the components separated by "&" like below
--Keyvalue from the Query String:
q=c%23+nunit+mac&oq
oq=c%23+nunit+mac
ap=裕坊%20邻坊
I would use struct as I need to call the static func:
struct BaseString {
static func createBaseString(authPrefix,signMethod,urlPath,nonce, timestamp,delimeter="&", bool sort= true, bool quote = false) -> String? {
var dict = [String:String]()
let url = NSURL(string: urlPath)
var keyValues = url.query?.componentsSeparatedByString("&")
//-(1)- adding keyValue into Dictinary
dict.??
//-- how to add the data below?
//- after (1) : Add other key value into same Dictionary
dict Add(authPrefix + "_timestamp", timestamp);
dict.Add(authPrefix + "_nonce", nonce);
dict.Add(authPrefix + "_signature_method", signMethod);
dict.Add(authPrefix + "_version", "1.0");
var return_format:String
if quote == true{
//-- create a baseString
sort the Dictionary
return_format = "&" + url + "&" +Dictionary.ToString()
(format: String = "q ="V1" " the value with double quote)
}else{
//-- create a baseString
sort the Dictionary
return_format = Dictionary.ToString()
(format:Strig = " q=v2")
}
var baseString = return_format
return baseString
}
}
Thanks. your help is much appreciated.
You can get Key-Value dictionary from your URL's query items with the help of URLQueryItem class, like this
let urlString = "https://www.sky.com/api/v1/rest/level2/in-in/?q=c%23+nunit+mac&oq=c%23+nunit+mac&ap=裕坊%20邻坊"
let encodedUrlString = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
let items = URLComponents(string: encodedUrlString)?.queryItems
var keyValues: [String: String] = [:]
items?.forEach{
item in
keyValues[item.name] = item.value
}
print(keyValues)
//["q": "c%23+nunit+mac", "ap": "裕坊%20邻坊", "oq": "c%23+nunit+mac"]
Hope this will help you.
If you need to obtain Query substring from your url string, you need to create URL from it and get query.
guard let url = URL(string: encodedUrlString) else {
fatalError()
}
let queryString = url.query!
print(queryString.removingPercentEncoding)
//q=c%23+nunit+mac&oq=c%23+nunit+mac&ap=裕坊%20邻坊
If you need to add new components to your query,
var components = URLComponents(string: encodedUrlString)
let item = URLQueryItem(name: "NEWVKEY", value: "NEWVALUE")
components?.queryItems?.append(item)
let url = components?.url
let resultString = url?.absoluteString
//or
let resultString2 = url?.absoluteString.removingPercentEncoding
The idea is to use URL processing abilities of Swift standard library. Please check the documentation of URL, URLComponents, URLQueryItem structs. Don't write string processing code, manipulate URLs instead.
https://developer.apple.com/documentation/foundation/url
https://developer.apple.com/documentation/foundation/urlcomponents
https://developer.apple.com/documentation/foundation/urlqueryitem

Getting NSTaggedPointerString but expecting String - how to resolve in Swift 3?

I have a function in Swift 3 that gets metadata from MPMediaItems - in this case Podcasts.
The problem is that for some older Podcasts, when I retrieve a metadata Key instead of getting a String I get an NSTaggedPointerString which is essentially empty for my purposes - I can't compare it to a literal.
Here's an example of what I expect (String):
And here's what is giving me heartburn:
(I know the keys are not the same but that's not the issue)
How can I get the actual String value from the NSTaggedPointerString while still being able to get/use the more common String values that I also get?
Here's my function:
func getMeta(_ item: MPMediaItem)->String{
print("")
print("--------- GETMETA ---------")
let url = item.value(forProperty:MPMediaItemPropertyAssetURL) as? NSURL
let asset = AVURLAsset(url: url as! URL, options: nil)
var sTDES: String = ""
var sUSLT: String = ""
var sCOMM: String = ""
var sTIT3: String = ""
var sFinal: String = ""
for metadata in asset.metadata{
print("sKey:\(metadata.key)")
print("stringValue:\(metadata.stringValue)")
if let sKey = metadata.key{
// All of these comparisons fail if the Key is NSTaggedPointerString
if (sKey as! String == "TDS") || (sKey as! String == "TDES"){
if let xtdes = metadata.stringValue{
sTDES = xtdes
}
}else if (sKey as! String == "COM") || (sKey as! String == "COMM"){
if let xcomm = metadata.stringValue{
sCOMM = xcomm
}
}else if (sKey as! String == "USLT"){
if let xuslt = metadata.stringValue{
sUSLT = xuslt
}
}else if (sKey as! String == "TIT3") || (sKey as! String == "TT3"){
if let xtit3 = metadata.stringValue{
sTIT3 = xtit3
}
}
}
}
//Use the preferred value if available
if sTDES != ""{
sFinal = sTDES
}else if sCOMM != ""{
sFinal = sCOMM
}else if sUSLT != ""{
sFinal = sUSLT
}else if sTIT3 != ""{
sFinal = sTIT3
}else{
sFinal = " "
}
print("sFinal 1:\(sFinal)")
return sFinal
}
Another question is this: Why can I print the value of the key correctly, but not use it as a String for comparison?
//This prints correctly
print("** key string:\(metadata.key!)")
// this yields myKey = ""
myKey = metadata.key as! String
EDIT:
I'm making some small progress but still no solution. I've been tinkering with my code and am now trying this:
let metaArray = asset.metadata(forFormat: "org.id3")
for metadata in metaArray{
print("** metadata key:\(metadata.key!)")
let myKey = String(describing: metadata.key!) // The ! results in empty value, while keeping it results in Optional
print("** myKey:\(myKey)")
if myKey == "TDS"{
print("value:\(metadata.stringValue)")
}
}
It seems so strange that when I can see that myKey contains a value (pic 1) and I can print it, I am not able to remove the Optional() indicator. When I add the ! the value becomes empty and I cannot do a comparison - but I can still print it!! (pic 2)
pic 1
pic 2
EDIT 2:
Finally a solution !
#alDiablo had a good suggestion but only got me part-way. I discovered through more research that part of the problem was a leading Null char "\0" in the returned string. Once I found that out, I could strip it off and do my comparison.
So here is the resulting code to handle the metadata key:
let realString = NSString(string: metadata.key! as! String) //There is probably a cleaner way to do this too
let sKey = realString.replacingOccurrences(of: "\0", with: "")
if (sKey == "TDS") || (sKey == "TDES"){
//Success!
}
Try this:
let realString = NSString(string: nsTaggedPointerString)

Getting ambiguous reference to member subscript

I am updating my code to swift3.0 but getting ambiguous refrence to member? What wrong i might be doing. Here is the method I am getting error in.
open class func parseJsonTenantList(_ list: [NSDictionary]?, strElementName: String, attrName1: String, attrNameValue2: String) -> [TenantRegister]
{
var renantList: [TenantRegister] = []
var key: String?
if let dict : [NSDictionary] = list {
var value: String?
for i in 0..<dict.count {
/// if attribute name doesn't match then it returns nil
if let s1: AnyObject = dict[i].value(forKey: attrName1)
{
key = s1 as? String
}
if let s2: AnyObject = dict[i].value(forKey: attrNameValue2)
{
value = s2 as? String
}
if (!(String.stringIsNilOrEmpty(value) && String.stringIsNilOrEmpty(key)))
{
let t: TenantRegister = TenantRegister()
t.name = key
t.tenantId = Guid(value!)
renantList.append(t)
}
}
}
return renantList
}
The issue is you are using NSDictionary, to solved your problem simply cast the list to Swift's native type [[String:Any]] and then use subscript with it instead of value(forKey:)
if let dict = list as? [[String:Any]] {
var value: String?
for i in 0..<dict.count {
/// if attribute name doesn't match then it returns nil
if let s1 = dict[i][attrName1] as? String
{
key = s1
}
if let s2 = dict[i][attrNameValue2] as? String
{
value = s2
}
if (!(String.stringIsNilOrEmpty(value) && String.stringIsNilOrEmpty(key)))
{
let t: TenantRegister = TenantRegister()
t.name = key
t.tenantId = Guid(value!)
renantList.append(t)
}
}
}
In Swift use native type Dictionary [:] and Array [] instead of NSDictionary and NSArray to overcome this type of issues.

How do I cast an Array<AnyObject> to an empty array variable?

Problem: I can't assign a string array (typed as 'AnyObject') to an empty array.
Steps:
1. Get a dictionary containing strings & arrays.
2. Grab an array from this dictionary via the key 'photos'.
Note: Xcode's warning suggest that I give the explicit type 'AnyObject'.
3. Create an empty array.
4. Attempt to assign it (failed).
let responseDictionary = responseDict as [String : AnyObject]
let ricPhotos:AnyObject = responseDictionary["photos"]!
var thePhotos:Array<AnyObject>?
thePhotos = ricPhotos <--- fails
Compiler Error:
...'AnyObject' is not convertible to 'Array<AnyObject>'
Question: How do I assign 'ricPhotos' to the empty array 'thePhotos' and preferably cast 'AnyObject' to 'String'?
Revision
let responseDictionary = responseDict as [String : AnyObject]
var anyObject: AnyObject? = responseDictionary["photos"]
Okay, 'anyObject' appears to be a Dictionary, and inside it is 'photo' which is an array; as seen in the data.
Here's some of the data(anyObject):
{
page = 1;
pages = 1334;
perpage = 100;
photo = (
{
farm = 3;
"height_m" = 336;
"height_s" = 161;
"height_sq" = 75;
"height_t" = 67;
id = 15166756998;
isfamily = 0;
isfriend = 0;
ispublic = 1;
owner = "127012961#N08";
secret = 053032f300;
server = 2941;
title = "You #NYPL";
"url_m" = "https://farm3.staticflickr.com/2941/15166756998_053032f300.jpg";
"url_s" = "https://farm3.staticflickr.com/2941/15166756998_053032f300_m.jpg";
"url_sq" = "https://farm3.staticflickr.com/2941/15166756998_053032f300_s.jpg";
"url_t" = "https://farm3.staticflickr.com/2941/15166756998_053032f300_t.jpg";
"width_m" = 500;
"width_s" = 240;
"width_sq" = 75;
"width_t" = 100;
},
...etc.
I want to grab the 'photo' array. But I can't downcast 'anyObject' to 'Dictionary' so that I can subscript it. I tried:
var anyObject:Dictionary = responseDictionary["photos"]
But I got:
'(String, AnyObject)' is not convertible to '[String : AnyObject]'
So I'm stuck with:
var anyObject: AnyObject? = responseDictionary["photos"]
So with anyObject, I tried to access 'photos':
let RicPhotos = anyObject["Photo"] as [String : AnyObject]
...I also tried:
let RicPhotos = anyObject["Photo"] as Array<Dictionary<String,String>>
But I got:
'AnyObject?' does not have a member named 'subscript'
I can see the data, but I can't extract into an empty variable.
I attempted to downcast to a specific type (Dictionary) but the compiler refuses.
There must be a strait forward way of getting an embedded array from a dictionary whilst casting to its respective cast (without the 'anyObject').Any ideas?
Edited my answer to fit your edit...
let responseDictionary = [:] as [String : AnyObject]
var photosDic: AnyObject? = responseDictionary["photos"]
if let photosDic = photosDic as? Dictionary<String, AnyObject> {
var photosArray: AnyObject? = photosDic["photo"]
if let photosArray = photosArray as? Array<Dictionary<String, AnyObject>> {
//There you go
}
}

Handling Facebook Graph API result in iOS SDK with Swift

I just want to request data from Facebook's Graph API, e.g. get the current user's basic info.
The Objective-C doc is: https://developers.facebook.com/docs/ios/graph#userinfo
[FBRequestConnection startForMeWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if (!error) {
/* My question: How do I read the contents of "result" in Swift? */
// Success! Include your code to handle the results here
NSLog(#"user info: %#", result);
} else {
// An error occurred, we need to handle the error
// See: https://developers.facebook.com/docs/ios/errors
}
}];
There's no Swift doc yet, and I'm confused about the "result" parameter whose type is "id".
It looks like result contains a dictionary, but it may be nil. In Swift, its type will map to AnyObject?.
So, in Swift, you could do something like:
// Cast result to optional dictionary type
let resultdict = result as? NSDictionary
if resultdict != nil {
// Extract a value from the dictionary
let idval = resultdict!["id"] as? String
if idval != nil {
println("the id is \(idval!)")
}
}
This can be simplified a bit:
let resultdict = result as? NSDictionary
if let idvalue = resultdict?["id"] as? String {
println("the id value is \(idvalue)")
}
Just remember it is not a dictionary all the way down, it is combinations of dictionaries and arrays.
FBRequestConnection.startWithGraphPath("me?fields=feed", completionHandler: { (connection, result, error) -> Void in
if( error == nil){
let fbGraphObject = result as FBGraphObject
let feed = fbGraphObject.objectForKey("feed") as NSMutableDictionary
let data = feed.objectForKey("data") as NSMutableArray
let postDescription = data[0].objectForKey("description") as String
//println( post )
self.fbu.initialUserFeed = feed
self.performSegueWithIdentifier("SelectStreams", sender: self)
}else
{
//TODO Allert to user that something went wrong
println(error)
}
})
I got confused about this in the beginning
This is a simpler way:
let params: [NSObject : AnyObject] = ["redirect": false, "height": 800, "width": 800, "type": "large"]
let pictureRequest = FBSDKGraphRequest(graphPath: "me/picture", parameters: params, HTTPMethod: "GET")
pictureRequest.startWithCompletionHandler({
(connection, result, error: NSError!) -> Void in
if error == nil {
print("\(result)")
let dictionary = result as? NSDictionary
let data = dictionary?.objectForKey("data")
let urlPic = (data?.objectForKey("url"))! as! String
print(urlPic)
} else {
print("\(error)")
}
})
}