Checking the value of a Swift variable inside the completion block of the method that initializes it - objective-c

Take the creation of NSURLSessionDownloadTask in Objective-C:
NSURLSessionDownloadTask *task = [[NSURLSession sharedSession] downloadTaskWithURL:[NSURL URLWithString:#"google.com"] completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
if (task.state == NSURLSessionTaskStateCompleted) {
// Do things
}
}];
[task resume];
I can access the very task I'm creating, task, within the completion block as well without issue.
However, in Swift, if I try the same thing:
let URL = NSURL(string: "google.com")
let task = NSURLSession.sharedSession().downloadTaskWithURL(URL, completionHandler: { location, response, error in
if task.state == .Completed {
// Do things
}
}
task.resume()
I can an error that "variable is being used within its own initial value".
How do I circumvent this?

Updated answer
I realize now that I didn't read your code carefully. You are not passing the closure to an initializer, but to a method. What I originally wrote is still valid when passing a closure to an initializer, but not in your case.
Your problem is similar though.
You have a task variable, which is initialized with the return value of a function. You pass a closure to the function, and inside the closure you are referencing the task variable.
The compiler doesn't know when the closure is executed (at least I don't think it checks for that, it's a downloadTaskWithURL() internal implementation detail) - and it's possible that it is executed in the function body (as opposed to have it stored in a property and executed at a later time). If the closure is executed in the function body, then it would access to the task variable when it has not been assigned a value yet (because the function is still executing).
If there were a way to let the compiler know the closure is not executed in the function body, then it would be possible for the compiler to handle that case. But swift doesn't implement anything like that.
Conclusion: I appreciate the compiler throwing an error for that, because otherwise I'd expect a runtime exception - although maybe not in your specific case (because the closure is executed later).
Original answer
As you probably know, in swift self is unavailable until all class/struct properties have been properly initialized, and a base initializer has been called (for inherited classes)
You are passing the closure to the class initializer - the compiler can't determine when the closure will be executed, so the closure itself cannot contain any (direct or indirect) reference to self.
In your case, task is the variable being instantiated, so when you are using it in the closure, it's like you are using self. That's not allowed in swift, so you have that error.
The same doesn't happen in Objective C because there's no such constraint in initializers.
Note however that conceptually what you are doing doesn't look a good practice. You are reading properties of a class instance before it's been properly initialized. To determine the status of the call, you should rely on the parameters passed to the closure, which ideally should provide all info you need.

Your task.state has not been initiazed yet whith some value. You are trying to read properties of a class instance before it's been initialized.

Right now the only way I have been able to get around this is to declare the variable before the initializer and then access it inside the closure:
let URL = NSURL(string: "google.com")
var task: NSURLSessionTask?
task = NSURLSession.sharedSession().downloadTaskWithURL(URL, completionHandler: { location, response, error in
if task?.state == .Completed {
// Do things
}
}
task?.resume()

Related

How can I stub a function's block with OCMock to return an incrementing value?

The documentation for OCMock is great and clear. I have my particular class level function stubbed. It's for a request, and looks something like this:
OCMStub([self.mockApiManager
requestToURL:[OCMArg any]
method:[OCMArg any]
keypath:[OCMArg any]
parameters:[OCMArg any]
objectClass:[OCMArg any]
shouldRetry:NO
retryBlock:nil
withCompletionBlock:([OCMArg invokeBlockWithArgs:#(1), #[#(apiRequestCount)], requestOperation, [[NSError alloc] init], #(0), nil])])._andDo(^(NSInvocation *invocation) {
apiRequestCount++;
});
The part that I'm interested in is [OCMArg invokeBlockWithArgs:#(1), #[#(apiRequestCount)], requestOperation, [[NSError alloc] init], #(0), nil]. Particularly where I pass in apiRequestCount.
apiRequestCount is a static int on my test class. What I desire is to pass back an incremented value everytime the -requestToURL:metho... function is called/stubbed. So call it once, and it returns 0. Call it a second time, it returns 1. Etc.
However, despite this function getting stubbed 3 times, I get a 0 for the invokeBlockWithArgs. Can anyone think of a way around this?
At the point when the stub is created and the invokeBlockWithArgs method is called on OCMArg, all the arguments (including the apiRequestCount) are taken and kept in an object behind the scenes. Further updates to apiRequestCount do not update that internal state and therefore the block is invoked with the same value that was originally provided.
That said, the invokeBlock methods are just for convenience. In your case you could pass [OCMArg any] for the block parameter to make sure the stub matches. Then, in the block you pass to andDo, you can pull out the completion handler block from the NSInvocation, and invoke it with the right value.

__block for method parameters in Objective C?

So thanks to this post, I'm familiar with the __block keyword.
It basically means to NOT copy the instance, but rather just passing its original reference.
The benefits I see for doing that are:
Any modification made to the instance inside the block will reflect in the original instance.
Avoiding the "waste" of copying the instance we're gonna use inside the block.
I am curious, though, how much should we really bother with this declaration, for example, if I have this method that receives a callback block as a parameter:
-(void)doSomethingWithCallback:(MyTypeOfCallback)callback;
and let's say this method calls another method with a callback as a parameter. Is it then worthwhile to __block the original callback parameter if we want to call it inside the next method:
-(void)doSomethingWithCallback:(MyTypeOfCallback)callback
{
__block MyTypeOfCallback blockCallback = callback;
[self doAnotherThingWithBlock:^(BOOL result) {
if (result)
blockCallback();
}];
}
or should I simply call the original block parameter inside the next method?
-(void)doSomethingWithCallback:(MyTypeOfCallback)callback
{
[self doAnotherThingWithBlock:^(BOOL result) {
if (result)
callback();
}];
}
I'm asking because it makes sense to include the __block option, but then again I find myself doing it in too many places and it's starting to take many code lines.
BTW, this also goes for every any other type of parameter, not only blocks.
It's basically telling the compiler to NOT copy the instance
No. __block has nothing to do with "instances". __block is a storage qualifier for variables.
__block on a variable means that the same copy of the variable will be shared between the original scope any any blocks that capture it (as opposed to each block getting a separate copy of the variable when it captures non-__block variables).
In your case, you have a variable of type MyTypeOfCallback, a (I'm guessing) pointer-to-block type. In the first piece of code, you make it __block, so there is a single pointer variable whose state is shared between the function scope and the block (which captures it). If either scope assigns to the pointer variable, the change would be visible in the other scope.
In the second piece of code, you make it non-__block, and when the block literal is executed, it copies the value of that pointer at that moment into a separate pointer variable in its own structure, so that you have two copies of the pointer. If you afterwards assign to the pointer variable in the function scope, the change would not be visible to the block, since it has its own copy.
In this case, there is no difference between the two, because you never assign to the pointer variable in question after initialization. It is basically a constant variable, and one copy or two copies makes no difference.
-(void)doSomethingWithCallback:(MyTypeOfCallback)callback
{
__block MyTypeOfCallback blockCallback = callback;
[self doAnotherThingWithBlock:^(BOOL result) {
if (result)
blockCallback();
}];
}
You can call callback from in block so
-(void)doSomethingWithCallback:(void(^)(void))callback
{
__block typeof(callback)blockCallback = callback;
[self doAnotherThingWithBlock:^(BOOL result) {
if (result)
blockCallback();
}];
}

How to partially mock an object inside legacy code with OCMock?

I would like to accomplish what also is described here, i.e create mocks inside legacy code. However I require partial instead of nice or strict mocks.
For example, consider leaderboards that behave exactly like GKLeaderbaord except for implementing a stubbed version of loadScoresWithCompletionHandler:.
I've tried this code inside an XCTestCase but it currently fails at runtime in the indicated line: OCMInvocationMatcher raises an EXC_BAD_ACCESS error. Perhaps there is some infinite recursion going on.
id leaderboardMock = OCMClassMock(GKLeaderboard.class);
OCMStub([leaderboardMock alloc])
.andReturn(OCMPartialMock([GKLeaderboard alloc]));
OCMStub([leaderboardMock loadScoresWithCompletionHandler: [OCMArg any]])
.andDo(^(NSInvocation *invocation) { /* ... */ });
// these parts normally nested inside legacy code
GKLeaderboard *leaderboard = /* raises EXC_BAD_ACCESS */
[[GKLeaderboard alloc] initWithPlayers: #[ GKLocalPlayer.localPlayer ]];
leaderboard.identifier = #"Test";
[leaderboard loadScoresWithCompletionHandler: nil /* ... */ ];
What am I doing wrong and is this even possible for partial mockups?
UPDATE I can by now see how the indicated line might (quite obviously) cause an infinite recursion, but don't yet know how to avoid (or break) it.
UPDATE I've also had no success with an attempt of bringing in an dedicated class with OCMStub([leaderboardMock alloc]).andReturn([LeaderboardMock alloc]) (nor with OCMStub([leaderboardMock initWithPlayers: [OCMArg any]]).andReturn([[LeaderboardMock alloc] initWithPlayers:nil])). Perhaps OCMock does its magic at the level of init (the documentation says: "it is not possible to stub the init method, because that is implemented by the mock itself") hence such an attempt the level of alloc (or initWithPlayers:) cannot have its desired effect.
Not sure I follow what you are trying to do. It seems like a misunderstanding. Does the following not work for you?
GKLeaderboard *leaderboard = [[GKLeaderboard alloc] initWithPlayers: ... ];
id leaderboardMock = OCMPartialMock(leaderboard);
OCMStub([leaderboarMock loadScoresWithCompletionHandler: ...]);
You can use the normal object without restrictions. You can use the partial mock created for the object to manipulate the actual instance in leaderboard. That's the beauty of partial mocks.
UPDATE: If the object creation is not under your control, you can try the following:
GKLeaderboard *leaderboard = [[GKLeaderboard alloc] initWithPlayers: ... ];
id leaderboardMock = OCMPartialMock(leaderboard);
OCMStub([leaderboardMock alloc]).andReturn(leaderboardMock);
OCMStub([leaderboardMock initWithPlayers:[OCMArg any]).andReturn(leaderboard);
OCMStub([leaderboarMock loadScoresWithCompletionHandler: ...]);
I have by now concluded that method swizzling would be a possible choice.
A replacement method could e.g. generate a partial mockup from within the context of legacy code and hence introduce a partial mock in that context without requiring changes to legacy APIs.
you should not use following line, it will mock your entire class and none of real object will get called.
OCMClassMock(GKLeaderboard.class)

How to convert a delegate-based callback system into block-based?

I have a class, which has a delegate based system for sending different type of requests. it uses delegate to tell the object when the request is complete and also if it was a success o an error.
Now, I also have to check what type of request was it in response to take appropriate action.
I have wrapper class that should give me a block based interface for the same.
I pass a completion-block and an error-block to a request method which should internally use this delegate based class.
And when the response comes, should automatically call the appropriate handler for that request type and depending on success and error as well.
I saw a similar question on SO, but it was a little unclear to me, So please give a general idea of how to go about it instead of marking it as duplicate straight away.
Here is one way to do it. Use this RAExpendable class to dynamically build a delegate with a block based implementation.
Let's say your delegate is:
#protocol XDelegate
-(void) foo:(id)response;
#end
Add RAExpendable.h, RAExpendable.m from https://github.com/evadne/RAExpendable to your project. Dynamically add the delegate method:
RAExpendable *expendable = [RAExpendable new];
[expendable addMethodForSelector:#selector(foo:) types:"v#:#" block:^(id x, SEL sel, id response){
NSLog(#"response is %#", response);
}];
And set the expendable class as your delegate:
someObject.delegate = expendable;
Now, if you do this:
[expendable performSelector:#selector(foo:) withObject:#"OK"];
You get the string response is OK. Replace NSLog with whatever success/failure implementation you see fit. From now on, when you call foo:, the block executes instead.
If you want to modify this for your use case, note that the parameters for this example were v#:#, which according to the Type Encoding guide of the runtime means: void return, self, SEL, object. self and SEL are the two hidden parameters present on every Objective-C methods, the third parameter is the first non hidden parameter of the method. The signature of the block has to match the signature of the method.
With REKit, you can make a delegate dynamically like below:
id dynamicDelegate;
dynamicDelegate = [[NSObject alloc] init];
[dynamicDelegate respondsToSelector:#selector(foo:) withKey:nil usingBlock:^(id receiver, id response) {
NSLog(#"response is %#", response);
}];
someObject.delegate = dynamicDelegate;

Chaining Completion Blocks

I have two instances of different classes who both need to add a completion block to a particular operation. I'll try to explain the problem generically rather than having to explain everything my app is attempting to do.
A view controller is calling into an instance of a resource manager class for it to save a resource. The resource manager then calls into the class of the resource to be saved to get a network operation for the save.
The instance of the resource creates the operation and gives it a completion block that will affect the state of the resource when it fires.
This is where my problem is - the resource class also needs to add a completion block to this operation in order for the view controller to be informed of the success or failure of the save.
Here's a snippit of the save method on the manager:
-(void)save:resource withCompletion:completion
{
.
.
.
NSOperation *operation = [resource operationForSave];
NSOperation __weak *weakOperation = operation;
void(^__weak resourceCompletion)(void)= operation.completionBlock;
[operation setCompletionBlock:^{
if (resourceCompletion) {
resourceCompletion();
}
if (completion) {
if (weakOperation.error) {
completion(NO, operation.error);
}
else {
completion(YES, nil);
}
}
}];
.
.
.
// add the operation to a network operation queue
}
While I think this will technically work, I'm not crazy about it. It feels pretty funky. I would prefer to have one block encapsulating the second block, but this isn't possible because the view controller and the resource are creating their own completion blocks, and the manager class is the one that has to smash them together.
Is there a more elegant way to chain these two completion blocks together in this situation, or is my current method of creating a block to contain the original two blocks the best I'm going to get?
Any input would be great appreciated.
The code you posted will probably not work. When you replace the operation's completion block with your own block, you're probably removing the only strong reference to the original completion block (set by the resource). So your resourceCompletion variable, being weak, will become nil by the time setCompletionBlock: returns.
Just making resourceCompletion strong should fix the problem. But if you want to do it in a cleaner way, modify the operationForSave message (on the resource) to take a completion block itself:
__block NSNetworkOperation *operation = [resource operationForSaveWithCompletion:^{
NSError *error = operation.error;
completion(error == nil, error);
// Break the retain cycle between this block and the operation object.
operation = nil;
}];
And make it the job of the resource's own internal completion block to call the completion block you provide.
If you don't want to or can't modify the resource's API, you can still simplify your code by eliminating the weak references:
__block NSNetworkOperation *operation = [resource operationForSave];
__block void (^priorCompletion)(void) = operation.completionBlock;
operation.completionBlock = ^{
if (priorCompletion) {
priorCompletion);
// Break possible retain cycle.
priorCompletion = nil;
}
NSError *error = operation.error;
completion(error == nil, error);
// Break the retain cycle between this block and the operation object.
operation = nil;
};
Also, I sincerely hope you don't really have a class named NSNetworkOperation, because Apple reserves the NS prefix (and all other two-letter prefixes) for its own use.