Search code examples
iosobjective-ckey-value-observingcocoalibspotify-2.0

SPSearch key-value-observing and blocks


I'm working on a cross-source solution for playing music (see project on Github). Every so called 'content source' has methods which fetch tracks, albums and artists by a search query (defined in SMKContentSource protocol). The methods work asynchronosly and require a completion handler passed which either accepts a result or a NSError.

At the moment I'm implementing a content source for Spotify. The following lines of code already work well, but in my opinion it is a bit risky solution:

    SPSession *strongSelf = weakSelf;
    dispatch_async([SMKSpotifyContentSource spotifyLocalQueue], ^{
        __block SPSearch *search = [[SPSearch alloc] initWithSearchQuery:predicate inSession:strongSelf];
        [search addObservationKeyPath:@"loaded" options:0 block:^(MAKVONotification *notification) {
            handler(search.albums, nil, nil);
            [search removeAllObservers];
            search = nil;
        }];
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(SMKSpotifyDefaultLoadingTimeout * NSEC_PER_SEC));
        dispatch_after(popTime, [SMKSpotifyContentSource spotifyLocalQueue], ^{
            if (search) {
                handler(nil, nil, [NSError errorWithDomain:SMKSpotifyErrorDomain code:SMKSpotifyLoadingTimeoutErrorCode userInfo:nil]);
                [search removeAllObservers];
                search = nil;
            }
        });
    });

It's a pity but SPSearch doesn't provide any other way then to observe the loaded value. Now I'm asking about what others think about this implementation or tips for a better solution.


EDIT: I also created an issue at CocoaLibSpotify already, because I think the SPSearch interface is bad designed: https://github.com/spotify/cocoalibspotify/issues/148


Solution

  • After issuing that to CocoaLibSpotify they informed me that I've overseen some helper class in their documentation which is designed for exactly this kind of issue.

    The right solution would be to use SPAsyncLoading:

    NSArray *someTracks = …; // Some tracks.
    
    [SPAsyncLoading waitUntilLoaded:someTracks timeout:10.0 then:^(NSArray *loadedTracks, NSArray *notLoadedTracks) {
        NSLog(@"The following tracks are loaded: %@", loadedTracks);
    }];