Question

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?

Was it helpful?

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.

OTHER TIPS

I have encountered similar problems, and here are some of my thoughts.

  1. RAC(self.imageView, image) binds the image key path on self.imageView, while RAC(self, imageView.image) binds the imageView.image key path on self. This basically means that the signal will only bind to the property image of self.imageView in the first case, but imageView.image of self in the second case. The difference is negligible in most scenario but according to what you have described the difference is likely crucial. If the binding happens before the creation of imageView only RAC(self, imageView.image) will work since self.imageView does not exist yet. (this most likely happens when imageView is an IBOutlet and the binding happens before viewDidLoad).
  2. The reason subscribeNext: works is probably because when the Async signal sends next (which takes time), imageView is already created and so you can successfully assign image to it.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top