I'm creating an IOS App based on cocoaLibSpotify.
In some point, I'm creating a Spotify playlist from an array of songs. I have previously obtained all the Spotify URIs and then I create the playlist and add all the tracks one by one.
The code below is in the logic class of the app and then I have a controller to show the results.
The problem is that I call this logic from the controller but I dont know the way to get back when the adding process has finished. I have tried to implement a delegate but im not sure how to do it...
Which is the right way to add tracks to a playlist? I have been searching on the documentation and in the GitHub repository but I have only found an example with two nested track addings... :S
Thanks in advance! (and sorry for my english)
- (void) createPlaylist:(NSArray*)spotifyURIs withName:(NSString*)name {
int songsRead = 0;
[container createPlaylistWithName:name callback:^(SPPlaylist *createdPlaylist) {
[SPAsyncLoading waitUntilLoaded:createdPlaylist timeout:kSPAsyncLoadingDefaultTimeout then:^(NSArray *loadedPlaylist, NSArray *notLoadedPlaylist) {
for (int i=[spotifyURIs count]-1; i>=0; i--) {
NSString *trackURI = spotifyURIs[i];
if (trackURI != nil){
[[SPSession sharedSession] trackForURL:[NSURL URLWithString:trackURI] callback:^(SPTrack *track) {
if (track != nil) {
[createdPlaylist addItems:[NSArray arrayWithObject: track] atIndex:[[createdPlaylist items] count] callback:nil];
}
}];
}
songsRead++;
// If I have read the whole tracklist, end of the process, returning to controller...
if (songsRead == [spotifyURIs count]){
// ...
}
}
}];
}];
}
What you need is to use the KVO system on your new playlist or using BLOCKS
you can implement something like this
typedef void (^spotifycompletionWithData)(id data);
-(void)addTrack:(NSString *)trackURI withCompletionBlock:(spotifycompletionWithData)CompletionBlock;
then whenever you call the function you should return the loaded SPTRACK within the completion block i hope its helping
Related
I am using Quickblox Api, for chat and video chat. iOS. And I am using the latest version of the API
When I try to Make a video call,
most of the times i don't get video, only audio.
i get video on both ends 1 out of 15 times.
3 out of 10 times video on one end.
very weird. I have good internet connection. connecting to chat users are receiving the call. Can seem to find out the issue.
After spending sometime to find the issue, I received and help from Quickblox Help Center.
If your face such Behavior on the API
1.Make Sure that you set Delegate Methods in viewDidLod, not view did appear or etc. For Ex:
- (void)viewDidLoad {
[super viewDidLoad];
[[QBChat instance] addDelegate:self];
[QBRTCClient.instance addDelegate:self];
[QBSettings setCarbonsEnabled:YES];
}
Use Breakpoints to find out if they are getting called, once you make or receive calls.
2.Make Sure that your Calling methods are correct. An array containing Users must not equal to currentUser.ID.
NSInteger currentUserID = [QBSession currentSession].currentUser.ID;
int count = 0;
NSNumber *currentUserIndex = nil;
for (NSNumber *opponentID in opponentsIDs) {
if ([opponentID integerValue] == currentUserID) {
currentUserIndex = #(count);
break;
}
count++;
}
if (currentUserIndex) [opponentsIDs removeObjectAtIndex:[currentUserIndex intValue]];
QBRTCSession *session = [QBRTCClient.instance createNewSessionWithOpponents:opponentsIDs
withConferenceType:QBRTCConferenceTypeVideo];
NSDictionary *userInfo = #{ #"key" : #"value" };
[session startCall:userInfo];
if (session) {
self.currentSession = session;
[self performSegueWithIdentifier:#"openDialogSeg" sender:self];
}
else {
[SVProgressHUD showErrorWithStatus:#"You should login to use chat API. Session hasn’t been created. Please try to relogin the chat."];
}
}
Check View Layout, size and width. make sure they are set correctly.
How can I check if a method is or isn't running, in an if statement? For example-
if ([(UIButton *)sender isEqual:blueButton] && **showBlueText method is running** )
{
Keep playing.
}
else if ([(UIButton *)sender isEqual:blueButton] && **showBlueText method is NOT running** )
{
Game over.
}
-(void)showBlueText
{
blueText.hidden = NO;
[self performSelector:#selector(hideText) withObject:nil afterDelay:textDelay];
[self performSelector:#selector(showGreenText) withObject:nil afterDelay:hideDelay];
}
Just to clarify, 'showBlueText' is a part of its own loop that runs independently of this if statement. I'm just trying to check if showBlueText is currently running.
You want to record state here. Make a new instance variable in this class.
// new iVar
BOOL textIsShowing;
// method
-(void)showBlueText {
textIsShowing = YES;
blueText.hidden = NO;
[self performSelector:#selector(hideText) withObject:nil afterDelay:textDelay];
}
// method
- (void)hideText {
textIsShowing = NO;
blueText.hidden = YES;
}
// button press
- (void)buttonPressed {
if (textIsShowing) {
NSLog(#"Keep playing");
} else {
NSLog(#"Game over");
}
}
Between the time you call this method, and the animation stops, don't think of it as "running". It schedules code to be executed later. Instead you want to be notified after it has finally run.
And in this case it's easiest to keep track of the state yourself. Use a new variable to track the state of things, and change it's value when that state changes.
But can't you just check if (blueText.hidden)? Yeah, you could. But it's bad practice to store state about your program in some obscure property of a random unimportant object.
Examine your state to figure out what you show. Don't examine what's showing to figure out your state.
I suggest replace Keep playing or Game over or Doing stuff with NSLog() statements. I always use it to keep a track of the changes in program if I am getting unexpected result.
So your statement may look like:
NSLog(#"Keep playing");
I hope this helps.
Just check if the text is hidden. No need to store parallel state in your controller - all that does is create the possibility that they'll be out of sync.
Im a noob in game center # games generally. Im making my second game now and implemented the game center.
If the internet is available, there is no problem, everything works well.
But just now I purposely make the internet unreachable, and when I get an achievement, obviously it does not register to the Game Center's Achievement.
How and what's the best way to handle this issue?
Thank you....
You could add the GKAchievement objects that fail to register to an array and then resend them when you get back connectivity. You should also consider committing that array to persistent storage, in case your app terminates before it has a chance to send those achievements. Try this in your completion handler:
// Load or create array of leftover achievements
if (achievementsToReport == nil) {
achievementsToReport = [[NSKeyedUnarchiver unarchiveObjectWithFile:pathForFile(kAchievementsToReportFilename)] retain];
if (achievementsToReport == nil) {
achievementsToReport = [[NSMutableArray array] retain];
}
}
#synchronized(achievementsToReport) {
if(error == nil)
{
// Achievement reporting succeded
// Resend any leftover achievements
BOOL leftoverAchievementReported = NO;
while ([achievementsToReport count] != 0) {
[self resendAchievement:[achievementsToReport lastObject]];
[achievementsToReport removeLastObject];
leftoverAchievementReported = YES;
}
// Commit leftover achievements to persistent storage
if (leftoverAchievementReported == YES) {
[NSKeyedArchiver archiveRootObject:achievementsToReport toFile:pathForFile(kAchievementsToReportFilename)];
}
}
else
{
// Achievement reporting failed
[achievementsToReport addObject:theAchievement];
[NSKeyedArchiver archiveRootObject:achievementsToReport toFile:pathForFile(kAchievementsToReportFilename)];
}
}
Hope this helps.
I see that the two answers here focus on the specific mechanisms for archiving the undelivered achievement messages. For a higher-level description of the overall approach, you can see my answer to this related question: Robust Game Center Achievement code
Achievements (and all of the gamecenter stuff like leaderboard updates) conform to NSCoding. You can store them if you get an error submitting them and submit them later. This is what apple recommends in their docs.
Your application must handle errors when it fails to report progress to Game Center. For example, the device may not have had a network when you attempted to report the progress. The proper way for your application to handle network errors is to retain the achievement object (possibly adding it to an array). Then, your application needs to periodically attempt to report the progress until it is successfully reported. The GKAchievement class supports the NSCoding protocol to allow your application to archive an achievement object when it moves into the background.
from: http://developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/GameKit_Guide/Achievements/Achievements.html#//apple_ref/doc/uid/TP40008304-CH7-SW13
// Submit an achievement to the server and store if submission fails
- (void)submitAchievement:(GKAchievement *)achievement
{
if (achievement)
{
// Submit the achievement.
[achievement reportAchievementWithCompletionHandler: ^(NSError *error)
{
if (error)
{
// Store achievement to be submitted at a later time.
[self storeAchievement:achievement];
}
else
{
NSLog(#"Achievement %# Submitted..",achievement);
if ([storedAchievements objectForKey:achievement.identifier]) {
// Achievement is reported, remove from store.
[storedAchievements removeObjectForKey:achievement.identifier];
}
[self resubmitStoredAchievements];
}
}];
}
}
In case anyone stumbles upon this question in the future, Apple now has sample code for submitting achievements that includes a way to archive achievements that failed to submit (due to no network connection, etc). You'll find it in the Game Center programming guide.
I have my app give a notification when a Game Center achievement is achieved/reaches 100%, however it shows the notification every times the user completes it, but I only want it to notify the first time in actually completed.
I found this in the Apple docs:
http://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/GameKit_Guide/Achievements/Achievements.html#//apple_ref/doc/uid/TP40008304-CH7-SW11
However didn't quite understand how it help fix my issue.
I only want to call this notification once, when the achievement is first achieved. So only show it if its not already achieved.
Edit
I have this to unlock the achievement:
- (void) reportAchievementIdentifier: (NSString*) identifier percentComplete: (float) percent
{
GKAchievement *achievement = [[[GKAchievement alloc] initWithIdentifier: identifier] autorelease];
if (achievement)
{
achievement.percentComplete = percent;
[achievement reportAchievementWithCompletionHandler:^(NSError *error)
{
if (error != nil)
{
// Retain the achievement object and try again later (not shown).
}
}];
}
}
And unlock with this:
[self reportAchievementIdentifier:identifier percentComplete:percent];
Then show the notification with this line:
[[GKAchievementHandler defaultHandler] notifyAchievementTitle:#"Title" andMessage:#"Message"];
So do I simply need something like this in that code chunk?
if (achievement.completed != TRUE) {
[[GKAchievementHandler defaultHandler] notifyAchievementTitle:#"Title" andMessage:#"Message"];
}
There's a "completed"-property in GKAchievement... Try to make a new GKAchievement and check if it's not completed, then unlock it.
EDIT: The problem i am having is that i am using the TapKu calendar, so i am relying on the provided delegates. Here is the problm:
- (NSArray*) calendarMonthView:(TKCalendarMonthView*)monthView marksFromDate:(NSDate*)startDate toDate:(NSDate*)lastDate{
//SOAP Request has NSURLConnection which runs asychonrous delegate methods.
//SOAP Request will return before the data array has been populated
[self SOAPRequest:startDate];
//i need to do something like this, however the SOAPRequest will return and get stuck into the loop instead of the asychronos delegates firing
//therefore i can never set bGettingData to NO.
int iWait;
while (bGettingData) {
iWait++;
}
return dataArray;
}
Hello,
In the app i am creating, i rely on SOAP requests to retrieve data, parse the XML and populate an array.
The problem i have, is that when i check the array it is empty, because the SOAP request has not completed. How do i stop my code from executing until the SOAP request is complete and resume the code? Can this be done through a callback or threading?
Thanks
Don't temporarily stop, sleep or wait, instead simply exit/quit/return from the current routine/function/method.
Break your current "stuff" into multiple fragments of code, each fragment in its own method.
Use subsequent method(s) to do whatever comes next, and have that method called by the completion routine of your async network/SOAP request.
Basically, your problem is that you are still thinking in terms of procedural coding. The proper paradigm is to use event driven coding: have the OS call your code, rather than having your code call the OS and waiting.
Yes,
start here: http://ondotnet.com/pub/a/dotnet/2005/08/01/async_webservices.html
You want indeed to wait for the answer to be complete - a callback is usually easiest. Exactly how depends on the programming library/language you are using (is above in javascript, objectiveC, did you hand code or start with an example).
Check out the answers to Iphone SOAP request step by step tutorial - such as http://macresearch.org/interacting-soap-based-web-services-cocoa-part-1 and http://brismith66.blogspot.com/2010/05/iphone-development-accesing-soap.html. Or follow https://developer.omniture.com/node/321 - which simply waits until the answer has fully arrived.
Unfortunately when using the TapKu calendar, you can not asynchronously load from a database via SOAP. You must synchronously load the calendar, because their is not way to refresh the calendar view once the data has finished loading. If you have 40+ records per month, this will create a huge 5-6 second delay.
This is indeed possible, as an example for the day calendar view, modify _refreshDataPageWithAtIndex to be like this:
- (void) _refreshDataWithPageAtIndex:(NSInteger)index{
UIScrollView *sv = self.pages[index];
TKTimelineView *timeline = [self _timelineAtIndex:index];
CGRect r = CGRectInset(self.horizontalScrollView.bounds, HORIZONTAL_PAD, 0);
r.origin.x = self.horizontalScrollView.frame.size.width * index + HORIZONTAL_PAD;
sv.frame = r;
timeline.startY = VERTICAL_INSET;
for (UIView* view in sv.subviews) {
if ([view isKindOfClass:[TKCalendarDayEventView class]]){
[self.eventGraveYard addObject:view];
[view removeFromSuperview];
}
}
if(self.nowLineView.superview == sv) [self.nowLineView removeFromSuperview];
if([timeline.date isTodayWithTimeZone:self.timeZone]){
NSDate *date = [NSDate date];
NSDateComponents *comp = [date dateComponentsWithTimeZone:self.timeZone];
NSInteger hourStart = comp.hour;
CGFloat hourStartPosition = hourStart * VERTICAL_DIFF + VERTICAL_INSET;
NSInteger minuteStart = round(comp.minute / 5.0) * 5;
CGFloat minuteStartPosition = roundf((CGFloat)minuteStart / 60.0f * VERTICAL_DIFF);
CGRect eventFrame = CGRectMake(self.nowLineView.frame.origin.x, hourStartPosition + minuteStartPosition - 5, NOB_SIZE + self.frame.size.width - LEFT_INSET, NOB_SIZE);
self.nowLineView.frame = eventFrame;
[sv addSubview:self.nowLineView];
}
if(!self.dataSource) return;
timeline.events = [NSMutableArray new];
[self.dataSource calendarDayTimelineView:self eventsForDate:timeline.date andEvents:timeline.events success:^{
[timeline.events sortUsingComparator:^NSComparisonResult(TKCalendarDayEventView *obj1, TKCalendarDayEventView *obj2){
return [obj1.startDate compare:obj2.startDate];
}];
[self _realignEventsAtIndex:index];
if(self.nowLineView.superview == sv)
[sv bringSubviewToFront:self.nowLineView];
}];
}
and then change your eventsForDate function to look like this:
- (void) calendarDayTimelineView:(TKCalendarDayView*)calendarDayTimeline eventsForDate:(NSDate *)eventDate andEvents:(NSMutableArray *)events success:(void (^)())success {
[Model doSomethingAsync andSuccess:^(NSArray *classes) {
// .. Add stuff to events..
success();
}];
}
I'm assuming the pattern for the other controls is very similar. The premise is you're waiting to continue the formatting/layout flow til you get your data.