Objective-C Block Property with Xcode code completion

18,118

Solution 1

You can definitely have your cake and eat it too, if you are willing to add one extra line of code to your class interface.

First, define block with a typedef and create a property like you did in your question:

typedef void (^CompletionBlock)(MyObject *myObj);

...

@property (nonatomic, copy) CompletionBlock completionBlock;

Next, as MobileOverload pointed out in his answer, we know that Xcode provides correct code completion for typedef'd blocks if used in a standalone method declaration. So, let's add an explicit declaration for the setter of completionBlock:

- (void)setCompletionBlock:(CompletionBlock)completionBlock;

When called, this method resolves to the setter method declared by the property. However, because we explicitly defined it in the class interface, Xcode sees it and applies full code completion.

So, if you include all three of those lines you should get the desired result. This behavior is clearly a shortcoming of Xcode, as there is no reason why a setter defined in a @property statement should have different code completion than the same method defined on its own.

Solution 2

You can get some fancy looking code completion when passing your blocks as an argument to a method in your class. In the header file I typedef'd the block like this

typedef void (^MyCompletionBlock)(id obj1, id obj2);

Then I was able to use it as an argument to my method that I have also declared in this class header.

-(void)doThisWithBlock:(MyCompletionBlock)block;

In the m file I declared the method

-(void)doThisWithBlock:(MyCompletionBlock)block {
    NSLog(@"Something");
}

and when I went to call it I got fancy code completion like this. CodeCompletion1

CodeCompletion2

Hopefully this answers your question.

Solution 3

Ok so I figured out a stopgap way of doing this that does't result in warnings / errors... and actually makes things easier to read / shorter to type, etc.

define a macro with our "abbreviation", and then use the full format in the property declaration like...

#define TINP NSString*(^)(NSString *typed, const char *raw)
@interface ....
@property (copy) NSString*(^termDidReadString)(NSString *typed, const char *raw);

subsequently.. you can then reference that "kind" of argument, etc like..

+ (void)addInputBlock:(TINP)termDidReadString;

and voilá... not only will your code be TINIER!! but code completion will work, like a charm...

enter image description here

Share:
18,118
Andrew
Author by

Andrew

Updated on June 02, 2022

Comments

  • Andrew
    Andrew almost 2 years

    Is it possible to define an Objective-C block property but still have full-code completion in Xcode 4?

    If I use a typedef to define the block:

    typedef void (^CompletionBlock)(MyObject *myObj);

    and then define the property:

    @property (nonatomic, copy) CompletionBlock completionBlock;

    and then @synthesize the property I don't get full code completion when calling the setter. Xcode will use the typedef and because of this, the code completion doesn't use the full block syntax complete with block parameters, it uses the typedef.

    If I define a method prototype in the header that uses the full block syntax instead of the typedef:

    @property (nonatomic, copy) void (^completionBlock)(MyObject *myObj);

    and then I use @synthesize, the provided setter comes close to using the full code completion syntax but crucially it leaves out the parameter names:

    [self setCompletionBlock:(void (^)(MyObject *)) { ... }

    Finally, if I try to @synthesize and then override the setter implementation or put the prototype in the header:

    - (void)setCompletionBlock:(void (^)(MyObject *myObj))completionBlock {...}

    A warning is raised stating that the property type does not match the accessor type. No matter how I try to finagle the syntax, I'm not able to both define a block property and a setter that has the full syntax for code completion. Can I have my cake and eat it too?

    Thanks!

  • Andrew
    Andrew over 12 years
    Unfortunately the behavior is different and Xcode does not use the full block syntax when you define a @property with a typedef block type and use a manually defined setter method or generated @synthesize'd setter.
  • Klaas
    Klaas about 12 years
    I ran into the same issue. As of Xcode 4.3.2 (4E2002) this is correct. You have to use a typedef and a explicit method declaration in you interface section. Neither using a property nor defining a method w/o a typedef'd block is possible when you want to get full code completion.
  • Andrew
    Andrew over 11 years
    This works, thanks! I'll submit a bug report for this so hopefully we won't need the explicit method declaration in the future.
  • djibouti33
    djibouti33 over 11 years
    If I add the explicit setter declaration to my interface file, I get a project warning stating that I have an incomplete implementation. If I declare this setter in the interface, then am I required to implement it in the .m?
  • Alex Gray
    Alex Gray almost 11 years
    It's STILL like this in Xcode (the number after 4, redact me, if you dare). UGH!
  • Andrew
    Andrew almost 11 years
    The accepted solution still appears to be the easiest way to achieve the desired behavior as it only requires declaring the setter method in the interface.
  • Matt
    Matt almost 11 years
    You should only be getting that warning if you are not declaring the property or if the setter the property defines is has a different definition than the one you explicitly declared.
  • nsuinteger
    nsuinteger over 8 years
    adding setter explicitly for a property with block does the trick.
  • James Bush
    James Bush about 6 years
    I don't think this answered his question at all. The highlights should appear over each, individual property—not the entire parameter set. He wants to replace, for example, id obj1, with a real value. Your code does not allow you to do that; rather, it returns the values you would have already passed to the completion block so that you can use them within the block. To get values into obj1 and obj2, you have to call block(<some value>, <some value>); That defeats the purpose.