Moving playlist to folder doesn't work - spotify

I'm having issues moving a newly created SPPlaylist to a (possibly newly created) SPPlaylistFolder.
The idea is to create a folder in the user's Spotify account, where I can add playlists generated from my application. If no such folder has been created, I'm creating a new SPPlaylistFolder and save the folder id for later use.
This is what I'm doing (I've omitted parts of the code that aren't interesting to this subject):
If a folderId has been previously saved (i.e. a folder created), use that ID to load the folder instance:
...
NSError *error = nil;
if (folderId > 0) {
// try to fetch folder
folder = [[SPSession sharedSession] playlistFolderForFolderId:folderId inContainer:container];
}
if (folder == nil) {
// create folder
folder = [container createFolderWithName:#"My Folder" error:&error];
// save a reference to the folder in an instance var
_appFolder = [folder retain];
// (also saving folder.folderId in NSUserDefaults)
}
...
Create an SPPlaylist: [[[SPSession sharedSession] userPlaylists] createPlaylistWithName:#"My Playlist"].
Use KVO to observe the container's playlists property and get notified when the playlist has been created: [[[SPSession sharedSession] userPlaylists] addObserver:self forKeyPath:#"playlists" options:0 context:nil].
Observe the playlists property and move the created playlist to my SPPlaylistFolder (containerPlaylist is the playlist I've identified as the one to move):
...
// identify the index of the containerPlaylist
NSInteger playlistIndex = [[[[SPSession sharedSession] userPlaylists] playlists] indexOfObject:containerPlaylist];
// move playlist
NSError * error = nil;
BOOL success = [container movePlaylistOrFolderAtIndex:playlistIndex ofParent:nil toIndex:0 ofNewParent:_appFolder error:&error];
if (success) {
// This should be a great success. But the playlist hasn't been moved, although the error variable is nil.
}
...
After these steps, both the playlist and folder have been created, but the playlist hasn't been moved. And I'm not getting any errors indicating any invalid input to the movePlaylistOrFolderAtIndex method.
Am I missing something obvious here? Or is the move functionality flawed somehow?
Note: I have also tried to use this code to move playlists that have been created previously (i.e. move all playlists named "My Playlist" to the folder).
EDIT 1: I've investigated this a bit further and actually got some moving action going on. But I had to rewrite some of the code and perform the move several times (or at a later stage). It seems like this is related to the data in SPSession not being entirely synced/up-to-date (?) since it's possible to move playlists when logging later with new session.
Is it possible that it's a syncing issue, i.e. libspotify believes that the SPPlaylistFolder is created and moves SPPlaylists to it, without it actually being created yet?

After having updated my code with reference to this issue on cocoalibspotify, it's working better. What I didn't realize at first was how the syncing with the Spotify service worked. It can easily take several minutes for the changes to be reflected in the Spotify desktop client, for example.

Related

UIDocumentPickerViewController NewBox App Hangs

I am referring WWDC 2014 sample app NewBox for document provider extension.
I am using following code from NeBox app, to import a document from Document Provider to my app.
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url {
BOOL startAccessingWorked = [url startAccessingSecurityScopedResource];
NSURL *ubiquityURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
NSLog(#"ubiquityURL %#",ubiquityURL);
NSLog(#"start %d",startAccessingWorked);
NSFileCoordinator *fileCoordinator = [[NSFileCoordinator alloc] init];
NSError *error;
[fileCoordinator coordinateReadingItemAtURL:url options:0 error:&error byAccessor:^(NSURL *newURL) {
NSData *data = [NSData dataWithContentsOfURL:newURL];
NSLog(#"error %#",error);
NSLog(#"data %#",data);
}];
[url stopAccessingSecurityScopedResource];
}
App totally hangs for coordinateReadingItemAtURL method.
Any inputs will be helpful.
I noticed this problem in NewBox app as well, and decided to trace it. So, there are two extensions in this app: Document Picker and File Provider. To make long story short, there is a race condition between the two when they try to access files within app's document storage folder.
In my opinion, the easiest method to trace down a problem is to put NSLog() in a bunch of locations. The problem is, however, that the debugging output generated by extension won't be visible in Xcode console. The good news is that you can open console in iOS Simulator app by clicking to Debug -> Open System Log menu. This will show all kinds of debugging messages, including those generated by extensions. You can find more about extension debugging here.
By using this method one can easily realize that execution gets stuck in File Provider's startProvidingItemAtURL method. More specifically, the following line causes a deadlock:
[self.fileCoordinator coordinateWritingItemAtURL:url options:0 error:&error byAccessor:^(NSURL *newURL) {
Why is that? Take a look at documentation for coordinateWritingItemAtURL:
If the url parameter specifies a file:
This method waits for other readers and writers of the exact same file to finish in-progress actions.
Function documentPicker that you mentioned calls a read operation, which in its turn triggers a write operation. This is a deadlock. I guess the easiest way to fix it would be to avoid using coordinateWritingItemAtURL in File Provider.
As per documentation:
Each of these methods wait synchronously on the same thread they were invoked on before invoking the passed-in accessor block on the same thread, instead of waiting asynchronously and scheduling invocation of the block on a specific queue.
Apple recommends that you not use file coordination inside this method. The system already guarantees that no other process can access the file while this method is executing. That's the sole reason for this deadlock.
Please refer to this documentation for more details.
You can use block also. Block works too fast, hang problem will get resolve.
Step 1: Take global variable of
UIDocumentPickerViewController *documentPicker;
also decalre
typedef void(^myCompletion)(BOOL);
Step 2: Write a method where allocation takes place and can send callback on completion
-(void) allocateDocumentPicker:(myCompletion) compblock{
//do stuff
documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:#[#"public.content"]
inMode:UIDocumentPickerModeImport];
documentPicker.delegate = self;
documentPicker.modalPresentationStyle = UIModalPresentationFormSheet;
compblock(YES);
}
Step 3: Call the method where allocation is taking place every time you want to open the composer but present it on receiving completion as YES.
-(IBAction)attachmentButtonClicked:(id)sender{
[self allocateDocumentPicker:^(BOOL finished) {
if(finished){
[self.parentScreen presentViewController:documentPicker animated:YES completion:nil];
}
}];
}
Simple Syntax to create own block, take reference from this link
Custom completion block for my own method

New project New Model NSPersistentDocument This NSPersistentStoreCoordinator has no persistent stores

I have been searching stackoverflow and Googling for hours. I made a simple project to mess around with Core Data and bindings. It added an entity to the model and it wouldn't work any more. I was getting "This NSPersistentStoreCoordinator has no persistent stores It cannot perform a save operation" whenever I tried to add data to a new document. I followed every piece of advice I could find with no luck.
Finally, I made a new project (NSPersistentDocument based) and I made a new model from scratch. I made sure the model was perfect before I ran the project for the first time.
In WindowControllerDidLoadNib: The project calls a method to add data. Before addData routine, I log the ManagedObjectContext and the ManagedObjectModel. Neither of them are nil.
I am still getting this %$&##! error.
Does anyone have any new ideas about this?
EDIT: Could this be because the new untitled document has never been saved? If so, how do you get around that? Can you save an untitled document? Do you really want to?
I had a similar problem a while back on a file import. Since I had full control, I named and saved the document and then I was able to save the context.
As I indicated in the comment above, at least in Mountain Lion you have to save the document at least once before you can save the context. I did some experiments and the small amount of data that I changed was preserved by autosave without saving the context. I have changed my saveContext method to the following:
- (void)saveContext {
if (![self fileURL]) {
NSLog(#"Can't save context. No file name has been set.");
return;
}
NSError *error = nil;
if (![self.managedObjectContext save:&error]) {
[NSApp presentError:error];
NSLog(#"Error userInfo: %#",[error userInfo]);
abort();
}
}

SPPlaylistCallbackProxy playlist: message sent to deallocated instance

I'm using cocoalibspotify for a Spotify iOS app.
At one point in my app, I'm creating a new playlist and add a number of tracks to the playlist. When doing so, I'm getting a crash since the created playlist instance seem to have been deallocated.
This is what the code looks like:
[[[SPSession sharedSession] userPlaylists]
createPlaylistWithName:playlistName
callback:^(SPPlaylist *createdPlaylist) {
if (createdPlaylist) {
[SPAsyncLoading waitUntilLoaded:createdPlaylist
then:^(NSArray *playlists) {
// Load all tracks using the URI's and add them to the playlist
SPPlaylist *playlist = [playlists objectAtIndex:0];
for (NSString *trackUri in trackUris) {
[[SPSession sharedSession]
trackForURL:[NSURL URLWithString:trackUri]
callback:^(SPTrack *track) {
if (track != nil) {
[SPAsyncLoading
waitUntilLoaded:track
then:^(NSArray *tracks) {
[playlist addItems:tracks atIndex:0 callback:NULL];
}];
}
}];
}
}];
}}];
This is the log message:
*** -[SPPlaylistCallbackProxy playlist]: message sent to deallocated instance 0x100e0120
I've tried retaining the playlist in my class, but I'm still getting the same problem. Am I missing something obvious here?
Bonus question: After having created a playlist or loaded a track (i.e. using -trackForURL:callback), do I have to use SPAsyncLoading, or is the object always already loaded?
(Note: I'm using ARC in my project.)
EDIT: I ran Zombies in instruments to see what was going on and got the following result when it crashed:
If you have an object (SPTrack, etc), you can add it to a playlist without waiting for it to load since loading only deals with metadata. You might want to wait for the playlist to load so you know the indexes you're using are correct (although, in this case 0 will always be valid).
Looking into the meat of your question now. It's most likely a bug in CocoaLibSpotify - your code looks fine.

How do you handle a UIManagedDocument?

First off, I should mention that this is my first post on this site. I am trying to teach myself to program iOS and in my google searches for answers I find that I'm constantly directed here. So thank you to all who have contribute here. You have help me a ton already.
I have been going through the Stanford CS193P class and LOVE it. But I'm stuck right now and not sure where to turn.
My problem has been with the UIManagedDocument.
I tired to make a simple app to test my new skills. This is what it does:
A simple accounting app that tracks individual contributions to a fundraising event.
I have a UITabBar that on each tab allows you to:
1. Track the participants (Players) - This will connect to the address book and allow you to add them or just keep them in this app.
2. Manage the events (Events) - You can add, edit or delete events that you will then add participants to and then be able to add what they brought (Bank) in on that event.
3. Settings. - I've added some buttons just to help me figure stuff out now including a reset button that clears all data and a "dummy data" button.
I have three coreData Entities. Players, Events and Bank each with relationships with the other two.
When I first tried to make this app (pre iOS5) I used the appDelegate to create my ManagedObjectContext and pass it around to my viewControllers. That worked. But now I'm supposed to use the UIManagedObjectDocument and not use the AppDelegate. I believe I understand the principle and the integration to iCloud. (I could be wrong)
Using the examples from the class and what I could find online I made a helper class that will provide the first of each of my ViewControllers within my UINavigationControllers the ManagedDocument.
+ (UIManagedDocument *)sharedManagedDocument
{
static UIManagedDocument *sharedDocument = nil;
NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
url = [url URLByAppendingPathComponent:#"DefaultAppDatabase"];
// url is "<Documents Directory>/<DefaultAppDatabase>"
// Create the shared instance lazily upon the first request.
if (sharedDocument == nil) {
sharedDocument = [[UIManagedDocument alloc] initWithFileURL:url];
}
if (sharedDocument.fileURL != url) {
UIManagedDocument *newDocument = [[UIManagedDocument alloc] initWithFileURL:url];
sharedDocument = newDocument;
}
NSLog(#"SharedDocument: %#", sharedDocument);
return sharedDocument;
}
I then had that first ViewController open the document and perform the fetch.
From there I pass whatever NSManagedObject is selected to the next ViewController through the segue.
The problems are when I get to adding or reseting the data. (I'm assuming that if I can get it to work with the "dummy data" button I can get it to work on an individual entry) When I press the "Reset" or "Dummy" button my logs tell me that it was pushed but I don't see any change in the data until I restart the app. Then it shows up perfectly. My guess is I'm not saving the file correctly or I'm not refreshing the tableViews correctly. I made a small attempt at using NSNotification but didn't go to far into it since I couldn't get it to respond to anything. I'm happy to go back down that road if I need to.
This is my save method... pretty much just copied from the default coreData appDelegate.
- (void)saveContext
{
NSError *error = nil;
NSManagedObjectContext *managedObjectContext = self.appDatabase.managedObjectContext;
if (managedObjectContext != nil)
{
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error])
{
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
[self.appDatabase saveToURL:self.appDatabase.fileURL
forSaveOperation:UIDocumentSaveForOverwriting
completionHandler:^(BOOL success){
if(!success) NSLog(#"failed to save document %#", self.appDatabase.localizedName);
if(success) NSLog(#"Success: save document %#", self.appDatabase.localizedName);
}];
}
Paul, the instructor from CS193P, suggested in Assignment 6 to use a helper method to pass around the UIManagedDocument through a block. I get the theory behind blocks but haven't completely wrapped my head around them so I haven't ruled out that my answer may lie there as well.
Thank you so much for any help or pointing me in the right direction to do more research. Sorry this post is so long, I was trying to be as clear as I can.
I was over thinking the whole thing. Alan asked my question perfectly in this post:
How do I create a global UIManagedDocument instance per document-on-disk shared by my whole application using blocks?
The question and the answers cleared everything up.
Thanks

Core Data code and multithreading

The following code is fetch data (fill data for the first time) part of my tableViewController. I am using an NSManagedDocument's managedObjectContext to fill (pre populate) my database. The source is an array that I clean up from my TXT file which rests directly in the Xcode's resources folder. After this creation, I have document cases like closed / open and normal.
The following code inputs and fetches my data onto the table correctly with a fetched results controller request. However, when the data is loading in the thread that is meant to free the UI from this one time data creation (26854 object names) into managedObject.name attribute heavy operation, the tableview and my UI is frozen (for 1-15 seconds that is I think while populating in document.managedObjectContext for the first time for my database).
After 10-15 seconds data is loaded and shows correctly. However, when I stop the simulator and restart the app in simulator, although I save the document as seen in below code, and I use the same fetch results controller setup (and request) the table view shows empty, it is movable in this case (The document state shows open and normal at this stage and file path is same, I checked... It seems like neither autosave nor explicit saveForOverwriting I use work... Or is it something else? I tried a lot of things and I'll go crazy soon. I think it has something to do with my multithreading.
self.managedObjectNames is the array property in the table view and I set it from the TXT file during my table view's loadView:
Is there anybody out there who can show the mistake here? Is it that I give self.managedObjectNames in the method of entity creation category.
Thanks!
- (void)fetchDataIntoDocument:(UIManagedDocument *)document {
dispatch_queue_t fetchQ = dispatch_queue_create("Data fetcher", NULL);
dispatch_async(fetchQ, ^{
[document.managedObjectContext performBlock:^{
for (int i = 0; i < 26854; i++) {
[managedObject managedObjectWithId:[NSNumber numberWithInt:i] andArray:self.managedObjectNames inManagedObjectContext:document.managedObjectContext];
}
// NSLog(#"Save baby!!?");
[document saveToURL:document.fileURL forSaveOperation:UIDocumentSaveForOverwriting completionHandler:nil];
}];
});
dispatch_release(fetchQ);
}
The reason why your UI is blocked for 10-15 seconds is because the document.managedObjectContext has been created with NSMainQueueConcurrencyType. That means that the performBlock: method will be executed on the main queue.
Creating the fetchQ in your code does not have any reason. It would have a reason if fetching of data would take some considerable amount of time but adding them would be fast (e.g. creating/modifying only few objects):
dispatch_async(fetchQ, ^{
// fetch data here (e.g. fetchAttribute may take few seconds)
NSString *attribute = fetchAttribute();
[document.managedObjectContext performBlock:^{
MyObject *o;
o = [NSEntityDescription insertNewObjectForEntityForName:#"MyObject"
inManagedObjectContext:document.managedObjectContext];
o.myAttribute = attribute;
}];
});
However I don't know answer to your main question.

Resources