I have a simple question but I can't find the right answer. I have a song url saved to my database in a path like this.
aSound.path = [[item valueForProperty: MPMediaItemPropertyAssetURL] absoluteString];
How to convert back to a MPMediaItem object which has songname artist and artwork?
Is it possible?
Saving:
NSNumber* persistentID =
[mediaItem valueForProperty:MPMediaItemPropertyPersistentID];
Loading:
MPMediaPropertyPredicate * predicate =
[MPMediaPropertyPredicate
predicateWithValue:persistentID
forProperty:MPMediaItemPropertyPersistentID];
Another example:
NSNumber for MPMediaItemPropertyPersistentID to NSString and back again
Note song URLs are are unreliable because any DRM song returns a null url
Alternatively, a nasty hack that Works For Me (tm).
- (MPMediaItem *)getMediaItemForURL:(NSURL *)url {
// We're going to assume that the last query value in the URL is the media item's persistent ID and query off that.
NSString *queryString = [url query];
if (queryString == nil) // shouldn't happen
return nil;
NSArray *components = [queryString componentsSeparatedByString:#"="];
if ([components count] < 2) // also shouldn't happen
return nil;
id trackId = [components objectAtIndex:1];
MPMediaQuery *query = [[MPMediaQuery alloc] init];
[query addFilterPredicate:[MPMediaPropertyPredicate predicateWithValue:trackId forProperty:MPMediaItemPropertyPersistentID]];
NSArray *items = [query items];
if ([items count] < 1) // still shouldn't happen
return nil;
return [items objectAtIndex:0];
}
Comments appreciated for where this might go wrong or how to improve it. I prefer to pass the URL around instead of the PersistentID as I have multiple media sources, all of which can use URLs. Using the PersistentID would mean a lot of "if this is a media ID, do this, otherwise, do that with the URL".
Related
I've followed this tutorial - https://www.youtube.com/watch?v=AdV7bCWuDYg
Trying to draw route on map and I do successfully send latitude/longitude of origin/destination. The problem is its generated URL.
I keep getting this
http://maps.googleapis.com/maps/api/directions/json?&origin=37.661519,127.061106&destination=37.664185,127.062994&sensor=false
But as far as I know, the URL should end with |45.566346,18.667512 something like that. I don't know what I'm doing wrong because the tutorial claims it works and its by Google I think.
- (void)setDirectionsQuery:(NSDictionary *)query withSelector:(SEL)selector
withDelegate:(id)delegate{
NSArray *waypoints = [query objectForKey:#"waypoints"];
NSLog(#"***waypoints : %#", waypoints);
NSString *origin = [waypoints objectAtIndex:0];
int waypointCount = [waypoints count];
int destinationPos = waypointCount -1;
NSString *destination = [waypoints objectAtIndex:destinationPos];
NSString *sensor = [query objectForKey:#"sensor"];
NSMutableString *url =
[NSMutableString stringWithFormat:#"%#&origin=%#&destination=%#&sensor=%#",
kMDDirectionsURL,origin,destination, sensor];
if(waypointCount>2) {
[url appendString:#"&waypoints=optimize:true"];
int wpCount = waypointCount-2;
for(int i=1;i<wpCount;i++){
[url appendString: #"|"];
[url appendString:[waypoints objectAtIndex:i]];
}
}
url = [[url
stringByAddingPercentEscapesUsingEncoding: NSASCIIStringEncoding]mutableCopy];
_directionsURL = [NSURL URLWithString:url];
NSLog(#"url : %#", url);
[self retrieveDirections:selector withDelegate:delegate];}
After hours of research I've noticed on Google Directions page that the required URL format is a little different from the one I've been using but still no luck.
[NSMutableString stringWithFormat:#"%#origin=%#&destination=%#&key=[my api key],
kMDDirectionsURL,origin,destination];
Your url should work, I changed the origin to San Francisco and destination to Mountain View and it works:
http://maps.googleapis.com/maps/api/directions/json?&origin=san+francisco&destination=mountain+view&sensor=false
It is possible that Google Maps does not know how to get from 37.661519,127.061106 to 37.664185,127.062994
When I look at this table for a product, I force myself to search by product name search.
Within this list, you will find the name, image, product description and thumb.
See the code below:
-(void) lookinfor
{
NSString *searchText = search_bar.text;
NSMutableArray *searchArray = [[NSMutableArray alloc] init];
dict_produtos = [[NSMutableDictionary alloc] init];
for (NSDictionary *dictionary in array_sections)
{
NSArray *array = [dictionary valueForKey:#"products"];
[searchArray addObjectsFromArray:array];
}
for (NSDictionary *dicts in searchArray)
{
NSString *sTemp = [dicts objectForKey:#"name"];
NSRange titleResultsRange = [sTemp rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (titleResultsRange.length != 0)
{
[dict_produtos setObject:sTemp forKey:#"name"];
[dict_produtos setObject:[dicts objectForKey:#"thumb"] forKey:#"thumb"];
[dict_produtos setObject:[dicts objectForKey:#"image"] forKey:#"image"];
[dict_produtos setObject:[dicts objectForKey:#"description"] forKey:#"description"];
}
}
searchArray = nil;
}
I get the whole list back perfectly, ie, looking for the name and get the thumb to enter the cell of the table, perfect, that's what I need..
So, when counting the lines of the section, only returns 1 line (name, image, thumb and description). So only returns 1 product.
That was the only solution I found to keep all the data together
For this reason I could not keep the sections while im searching and moreover, appears only 1 result (the others do not appear, even having found more than 1 record)
Does anyone have a better solution for this?
Thanks for all help!
I'm attempting to complete the Stanford iPhone Programming (FA10) assignement "Flickr Fetcher" -- so far things are going well, however I have come to an impasse:
I have successfully extracted the location of the "Top 100" pictures, which are formated in a string as "Country, State, City". I would like to create two NSStrings -- one being the country, the other string being the State and City. From where I can then do
cell.textLabel.text = countryString;
cell.detailTextLabel.text = stateCityString;
in my table view datasource methods.
From research on stackoverflow and the Apple Documentaion, NSScanner seems to be my best bet -- here is what I have so far...
- (void)viewDidLoad {
//Get the top 100 photos from Flickr
self.topPlacesArray = [FlickrFetcher topPlaces];
NSString *mainLabelString = [[NSString alloc] init];
NSString *stringFromArray = [[NSString alloc] init];
//This retrieves the string of the location of each photo
stringFromArray = [topPlacesArray valueForKey:#"_content"];
NSScanner *theScanner = [NSScanner scannerWithString:stringFromArray];
NSCharacterSet *commaSet = [[NSCharacterSet alloc] init];
commaSet = [NSCharacterSet characterSetWithCharactersInString:#","];
while ([theScanner isAtEnd] == NO) {
if ([theScanner scanUpToCharactersFromSet:commaSet intoString:&stringFromArray]) {
NSLog(#"%#",stringFromArray);
}
}
I'm just trying to see if the string properly substrings itself -- however I am getting a "SIGBART" at the beggining of the while loop, the error is this:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI length]: unrecognized selector sent to instance 0x8939eb0'
From all the documentation I have seen on NSScanner, it seems I have it set up properly, however, no matter what changes I do, it seems unable to even begin the loop.
What do I have to do to set up NSScanner properly, to avoid the "SIGABRT"? (for the record, i'm assuming "SIGABRT" is a segfault?). Thank you all for your time, you all are the best!
(Btw: I know this is not fully implemented yet for both country and state-city, i just want to get used to NSScanner, I will implement the rest once I get NSScanner under control)
EDIT 1: SosBorn! You are incredible! Thank you so much! So I have implemented this for my viewDidLoad:
- (void)viewDidLoad
{
self.topPlacesArray = [FlickrFetcher topPlaces];
NSArray *ArrayOfStrings = [[NSArray alloc] init];
NSArray *placeElements = [[NSArray alloc] init];
NSString *country = [[NSString alloc] init];
NSString *city = [[NSString alloc] init];
NSString *state = [[NSString alloc] init];
ArrayOfStrings = [topPlacesArray valueForKey:#"_content"];
for (NSString *place in ArrayOfStrings) {
placeElements = [place componentsSeparatedByString:#", "];
if ([placeElements count] == 3 && [placeElements objectAtIndex:0] != nil) {
city = [placeElements objectAtIndex:0];
[self.cityArray addObject:city];
state = [placeElements objectAtIndex:1];
[self.stateArray addObject:state];
country = [placeElements objectAtIndex:2];
[self.countryArray addObject:country];
NSLog(#"%#, %#, %#", city, state, country);
}
else {
NSLog(#"Did this work?");
}
}
[ArrayOfStrings release];
[placeElements release];
[country release];
[city release];
[state release];
[super viewDidLoad];
}
This worked like a complete charm BUT i'm having some bad access going on in the Delegate when trying to access self.window.rootViewController = self.viewController -- this doesn't make any-sense (i actually have a completely empty table, etc...) -- so i'm thinking I played with bad memory management with my substring-ing and now it gets in trouble with this delegate call.
Chuck, I was very interested in your comment as I was taught that the proper way to make variables is to call [myclass alloc] init]; and then release when you are done -- as I have. Of course my objective-C greenness is showing a bit... blush.
You all and this incredible community are such an asset to us Students -- thank you for all your time and dedication. The only path to progress is a path of cooperation!
EDIT 2: Ok -- now it's totally fixed with no terrible leaking problems. Chuck you were right! I had the pricniples of alloc init completely mixed up in my head -- here was my final solution:
NSMutableArray *array1 = [[NSMutableArray alloc] init];
NSMutableArray *array2 = [[NSMutableArray alloc] init];
NSMutableArray *array3 = [[NSMutableArray alloc] init];
self.cityArray = array1;
self.countryArray = array2;
self.stateArray = array3;
[array1 release];
[array2 release];
[array3 release];
NSArray *ArrayOfStrings = [topPlacesArray valueForKey:#"_content"];
NSArray *topPlaces = [NSArray arrayWithArray:ArrayOfStrings];
NSArray *topPlacesSorted = [topPlaces sortedArrayUsingSelector:#selector(compare:)];
ArrayOfStrings = topPlacesSorted;
for (NSString *place in ArrayOfStrings) {
NSArray *placeElements = [place componentsSeparatedByString:#", "];
if ([placeElements count] == 3 && [placeElements objectAtIndex:0] != nil) {
NSString *city = [placeElements objectAtIndex:0];
[self.cityArray addObject:city];
NSString *state = [placeElements objectAtIndex:1];
[self.stateArray addObject:state];
NSString *country = [placeElements objectAtIndex:2];
NSString *stateAndCountry = [NSString stringWithFormat:#"%#, %#", state, country];
[self.countryArray addObject:stateAndCountry];
NSLog(#"%#, %#, %#", city, state, country);
}
else {
NSLog(#"Nil Request");
}
Thank you again SosBorn, i was feeling like I had forgotten the basics of CS ಠ_ಠ.
The only thing that really bothers me is why do we have to initialize instance NSMutableArrays that way -- i found this was the only way to get them to actually work.
Not totally sure why it is crashing, but I think another approach to this would serve you better. You have a topPlacesArray, why not iterate through the array and process each array entry seperately? I am making some assumptions about the topPlacesArray, but it would look something like this:
for (NSString *place in topPlacesArray)
{
//Place is probably in this format: "Country, State, City"
NSArray *placeElements = [place componentsSeperatedByString:#","];
//This should give you an array with three elements. Country State and city.
NSString *country = [placeElements objectAtIndex:0];
NSString *cityState = [NSString stringWithFormat:#"%#, %#", country, cityState];
//Now you have your strings that you need. Do whatever you need to do with them.
//Add them to an array or set the value of a text label, etc.
}
Didn't take the time to handle memory management but you get the idea.
Im working on a simple "point based" app.
under settings the user set´s the number of points needed to get a "goodie" using a slider.
-(IBAction) sliderChanged: (id)sender {
UISlider *slider = (UISlider *) sender;
int progressAsInt =(int)(slider.value +0.5);
NSString *newText = [[NSString alloc] initWithFormat:#"%d",progressAsInt];
sliderLabel.text = newText;
[newText release];
this works fine, but how so i store the slider value in my core data model, and how do make my slider show the stored value when view loads.
hope u can help me out :-D
Hey gerry3 i found my error. i never set my toD-object in my settingsViewController, with:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription
entityForName:#"ToDo" inManagedObjectContext:_context]];
NSError *error = nil;
NSArray *array = [_context executeFetchRequest:request error:&error];
if (array == nil)
{
// Deal with error...
}
if(array.count > 0){
toDo = [array objectAtIndex:0];
} else { // no one to fetch - generate one
toDo = [NSEntityDescription
insertNewObjectForEntityForName:#"ToDo"
inManagedObjectContext:_context];
[toDo retain];
your code works like a charm .....
Thanks
Skov
The key here is that Core Data stores numeric attributes (e.g. integers, floats, etc) as NSNumber objects.
Say that your entity is called Record and it has a integer attribute called 'progress'.
If you create a managed object instance of Record named 'record', then you can set its progress like this:
[record setValue:[NSNumber numberWithInteger:progressAsInt] forKey:#"progress"];
When you want to update your view with the value from your model (usually in viewWillAppear:), you can get its progress like this:
NSNumber *progressNumber = [record valueForKey:#"progress"];
slider.value = [progressNumber floatValue];
Alternatively, if you generate the class files for the Record entity, you can just do:
record.progress = [NSNumber numberWithInteger:progressAsInt];
and:
slider.value = [record.progress floatValue];
I am not a Cocoa developer, but I have been dabbling in it to build some plugins for PhoneGap. This particular plugin method is either 1) crashing the app without saying why or 2) complaining about how I release/don't release an object. I have tried a ton of things on my end, including using an Enumerator instead of the for loop. If anyone can point me in the right direction, that would be awesome. I don't mind legwork:
- (void)getPreferences:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options {
NSUInteger argc = [arguments count];
NSString* jsCallback = nil;
if (argc > 0) {
jsCallback = [arguments objectAtIndex:0];
} else {
NSLog(#"Preferences.getPreferences: Missing 1st parameter.");
return;
}
NSDictionary *defaults = [[NSUserDefaults standardUserDefaults] dictionaryRepresentation];
NSMutableArray *keys = (NSMutableArray *) [options objectForKey:#"keys"];
NSMutableDictionary *values = [[NSMutableDictionary alloc] init];
NSUInteger ky = [keys count];
for (int i = 0; i < ky; i ++) {
#try {
[values setObject:[defaults objectForKey:[keys objectAtIndex:i]] forKey:[keys objectAtIndex:i]];
}
#catch (NSException * err) {
NSLog(#"Error %#", err);
}
}
[keys release];
NSString* jsString = [[NSString alloc] initWithFormat:#"%#(%#);", jsCallback, [values JSONRepresentation]];
[defaults release];
[values release];
[webView stringByEvaluatingJavaScriptFromString:jsString];
[jsString release];
}
Human version:
options contains a dictionary with a single key of "keys"
that key contains an array of strings (that are going to be used as keys for lookup)
I want to loop through that array and
For every value that exists in defaults for that key, copy it to values using the same key
Finally, I want to send that values back as JSON (This part was working when I just passed the entire defaults object in, so I think the JSON method is working)
From your code, it follows that you 'own' objects values and jsString (the ones you created with alloc), so you should release them and not any other.
You can read more on memory management here.
Is this the whole code? Also, what exactly error do you get?
Nikita is right, it looks as though you're overreleasing defaults, which would cause a crash later when the autorelease pool gets released. Also, if I understand what you're trying to do correctly, you could create the values dictionary with a single line of code:
NSDictionary *values = [defaultsDict dictionaryWithValuesForKeys:keys];