Search code examples
iosobjective-creactive-cocoa

-subscribeNext: works, but RAC() doesn't


This works as expected:

        // Return a sequence for photos
    [[[[[[RACObserve(self, event.photos) filter:^BOOL(id value) { return value != nil ; }] flattenMap:^RACStream *(NSDictionary *photos)
        {
        NSLog(@"Got photos: %@" , photos) ;
        return photos.rac_sequence.signal ;
        }]

    // Consider each photo
    filter:^BOOL(NSDictionary *photoDescriptor)
        {
        NSLog(@"Descriptor: %@" , photoDescriptor) ;
        return ((NSNumber *)photoDescriptor[@"primary"]).boolValue ;
        }]

    // Load the selected photo
    map:^id(NSDictionary *selectedPhotoDescriptor)
        {
        NSLog(@"Photo URL: %@" , selectedPhotoDescriptor[@"url"]) ;
        return [[AsyncImageFetcher imageAtURL:[NSURL URLWithString:selectedPhotoDescriptor[@"url"]] cache:YES] firstOrDefault:[UIImage imageNamed:@"detail_placeholder"]] ;
        }]

    // Deliver on main thread
    deliverOn:RACScheduler.mainThreadScheduler]

    subscribeNext:^(id x)
        {
        ((UIImageView *)self.headerView).image = x ;
        }] ;

This does not; the image is never set:

    RAC( ((UIImageView *)self.headerView), image ) =

    // Return a sequence for photos
    [[[[[RACObserve(self, event.photos) filter:^BOOL(id value) { return value != nil ; }] flattenMap:^RACStream *(NSDictionary *photos)
        {
        NSLog(@"Got photos: %@" , photos) ;
        return photos.rac_sequence.signal ;
        }]

    // Consider each photo
    filter:^BOOL(NSDictionary *photoDescriptor)
        {
        NSLog(@"Descriptor: %@" , photoDescriptor) ;
        return ((NSNumber *)photoDescriptor[@"primary"]).boolValue ;
        }]

    // Load the selected photo
    map:^id(NSDictionary *selectedPhotoDescriptor)
        {
        NSLog(@"Photo URL: %@" , selectedPhotoDescriptor[@"url"]) ;
        return [[AsyncImageFetcher imageAtURL:[NSURL URLWithString:selectedPhotoDescriptor[@"url"]] cache:YES] firstOrDefault:[UIImage imageNamed:@"detail_placeholder"]] ;
        }]

    // Deliver on main thread
    deliverOn:RACScheduler.mainThreadScheduler] ;

Why?


Solution

  • Here is the version that works:

        // When there's a new image, fetch it, and set the headerView (which by default is an UIImageView)
    RAC( self, imageView.image ) =
    
        // Return a sequence for photos
        [[[[RACObserve(self, event.photos) ignore:nil] flattenMap:^RACStream *(NSDictionary *photos)
            {
            NSLog(@"Got photos: %@" , photos) ;
            return photos.rac_sequence.signal ;
            }]
    
        // Consider each photo
        filter:^BOOL(NSDictionary *photoDescriptor)
            {
            NSLog(@"Descriptor: %@" , photoDescriptor) ;
            return ((NSNumber *)photoDescriptor[@"primary"]).boolValue ;
            }]
    
        // Load the selected photo
        flattenMap:^RACStream *(NSDictionary *selectedPhotoDescriptor)
            {
            NSLog(@"selected photo desc: %@" , selectedPhotoDescriptor) ;
            return [AsyncImageFetcher imageAtURL:[NSURL URLWithString:selectedPhotoDescriptor[@"url"]] cache:YES] ; // This will -deliverOn: the main thread
            }] ;
    

    Note that AsyncImageFetcher's imageAtURL:cache: returns a signal.

    A few notes about this solution:

    First I had to create a new private property self.imageView that does nothing but return self.headerView. The reason for this relates to the arguments that the RAC() macro can accept. I became suspicious that the parameters I was passing to RAC() was causing my problem, so I simplified it by doing the cast in the aforementioned private property:

    @synthesize imageView ;
    - (UIImageView *) imageView
    {
    return (id) self.headerView ;
    }
    

    Then I tried:

    RAC( self.imageView, image ) = …
    

    But this didn't work. I then tried

    RAC( self, imageView.image ) = …
    

    This worked! So then I thought, let's get rid of self.imageView and see if it works with the casting right in the macro:

    RAC( self, (((UIImageView *)headerView).image) ) = …
    

    Unfortunately, this yields a syntax error.

    So, the solution that utilizes the casting private property works, but it feels like a workaround. I'm accepting the answer since it works for me, but I'd still like to know if there's a way I can do this without creating the (redundant) imageView property.