I don't understand how Objective-C loop system works. I have function (hope names are right, rather check in code) which executes query from Health Kit. I got my mind blown when I realised that function pass return value before query finishes.
__block bool allBeckuped = true;
HKSampleQuery *mySampleQuery = [[HKSampleQuery alloc] initWithSampleType:mySampleType
predicate:myPredicate
limit:HKObjectQueryNoLimit
sortDescriptors:#[mySortDescriptor]
resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) {
if(!error && results)
{
for(HKQuantitySample *samples in results)///main hk loop
{
allBeckuped = false;
NSLog(#"1");
}
}
}];//end of query
[healthStore executeQuery:mySampleQuery];
NSLog(#"2");
return allBeckuped;
I'm trying to check if there are any new data, but I don't know where to put condition for that, because nslog2 is called before nslog 1.
Any words I should Google up?
Any words I should google up?
You can start with: asynchronous design, blocks, GCD/Grand Central Dispatch should help as well - you're not using it but asynchronous designs often do.
Look at the initWithSampleType: method you are calling, it is an example of a method following the asynchronous model. Rather than return a result immediately, which is the synchronous model you are probably used to, its last argument, resultsHandler:, is a block which the method calls at some future time passing the result of its operation to it.
This is the pattern you will need to learn and follow.
Your method which contains the call to initWithSampleType: cannot return a result (e.g. your allBeckuped) synchronously. So it needs to take a "results handler" block argument, and the block you pass to initWithSampleType: should call the block passed to your method - and so the asynchronous flow of control is weaved.
HTH
Related
I have a unit test class with multiple test cases. Some test cases have common XCTest Assertions. These test cases implements an API call. This API call can have different input request parameters but the response is same. I have put test assertions on response. So, is it good by design to extract out this common assertions code on response in a separate method (not a test method) and call that method in the test methods?
Below is the code for reference:
- (void)testRequest {
AppType app = #"A";
NSDictionary *requestMessage = #{requestMessage};
__block BOOL hasReceivedResponse = NO;
[Class handleRequestMessage:requestMessage
appType:app
managedObjectContext:self.fixtures.managedObjectContext
completionBlock:^(NSDictionary *responseMessage, NSError *error) {
XCTAssertNil(error);
NSString *name = responseMessage[#"name"];
XCTAssert([name isEqualToString:#"Search"]);
NSString *response = responseMessage[#"response"];
XCTAssert([response isEqualToString:#"1"]);
hasReceivedResponse = YES;
}];
[[NSRunLoop currentRunLoop] runUntilCompletionIndicator:&hasReceivedResponse];
}
Here appType can be A, B, C. Response as present in completion block remains the same. Can I extract completion block code to a separate method?
Refactored Code:
- (BOOL)receivedResponseForRequestMessage:(NSDictionary *)responseMessage error:(NSError *)error {
XCTAssertNil(error);
NSString *name = responseMessage[#"name"];
XCTAssert([name isEqualToString:#"Search"]);
NSString *response = responseMessage[#"response"];
XCTAssert([response isEqualToString:#"1"]);
}
-(void)testRequestForA {
AppType app = #"A";
NSDictionary *requestMessage = #{requestMessage};
__block BOOL hasReceivedResponse = NO;
[Class handleRequestMessage:requestMessage
appType:app
managedObjectContext:self.fixtures.managedObjectContext
completionBlock:^(NSDictionary *responseMessage, NSError *error) {
hasReceivedResponse = [self receivedResponseForRequestMessage:responseMessage error:error
}];
[[NSRunLoop currentRunLoop] runUntilCompletionIndicator:&hasReceivedResponse];
}
-(void)testRequestForB {
AppType app = #"B";
NSDictionary *requestMessage = #{requestMessage};
__block BOOL hasReceivedResponse = NO;
[Class handleRequestMessage:requestMessage
appType:app
managedObjectContext:self.fixtures.managedObjectContext
completionBlock:^(NSDictionary *responseMessage, NSError *error) {
hasReceivedResponse = [self receivedResponseForRequestMessage:responseMessage error:error
}];
[[NSRunLoop currentRunLoop] runUntilCompletionIndicator:&hasReceivedResponse];
}
Is this kind of refactoring correct by design?
Having a common XCTAssert does have at least one drawback. XCTAssert reports its line number, which enables you to find the failing test and quickly fix it. When your method testRequestForA fails, the failing line will be in receivedResponseForRequestMessage:error:.
One thing you can do in Objective-C is pass in a message after the expression being asserted. According to the docs, this is "An optional description of the failure. A literal NSString, optionally with string format specifiers." Your test results will contain this information, so if you identified the assertion well it will be easy to find the source line.
XCTAssert([response isEqualToString:#"1"], #"%# response failed", name); // Replace name with another string if appropriate
As a side note, Swift also allows you to also pass in the line number and/or file where the test failed, which makes it a lot easier to use assertions in helper methods.
In general, it's helpful to refactor test code to make it more expressive. This includes extracting assertion helpers.
In practice, this isn't easy to do in the Objective-C because assertions like XCTAssert are actually macros. They need to be macros at the call site in order to pick up __FILE__ and __LINE__. Unfortunately Apple chose to implement them entirely as macros, instead of as thin macros which call methods taking file name and line number arguments.
But in How to Structure Tests that Do Real Networking, I recommend not putting assertions within a block. Instead, have the block capture its arguments, and trigger the exit. This avoids issues with timeouts, and moves assertions to the end of the test where they are easier to read.
This would change the first part of your example to this:
- (void)testRequest {
AppType app = #"A";
NSDictionary *requestMessage = #{requestMessage};
__block BOOL hasReceivedResponse = NO;
__block NSDictionary *capturedResponseMessage = nil;
__block NSError *capturedError = nil;
[Class handleRequestMessage:requestMessage
appType:app
managedObjectContext:self.fixtures.managedObjectContext
completionBlock:^(NSDictionary *responseMessage, NSError *error) {
capturedResponseMessage = responseMessage;
capturedError = error;
hasReceivedResponse = YES;
}];
[[NSRunLoop currentRunLoop] runUntilCompletionIndicator:&hasReceivedResponse];
The hasReceivedResponse trigger can be replaced by an XCTestExpectation. This would let you use a timeout.
Now we can perform assertions against the captured arguments. But I'm going to change the assertions:
XCTAssertNil(error, #"error");
XCTAssertEqualObjects(responseMessage[#"name"], #"Search", #"name");
XCTAssertEqualObjects(responseMessage[#"response"], #"1", #"response");
}
First, I replaced the XCTAssert … isEqualToString: with XCTAssertEqualObjects. This is important, because when the assertion fails, it will report the 2 values that weren't equal instead of just "Failed". You can immediately see what the actual value was without using the debugger.
I also added a message to each assertion. This can be important when you have multiple assertions in a single test. It may not matter when sitting in front of your IDE, because you can click on a failure message to see which assertion triggered it. But when tests are running on a CI server, we want to log more information so that any failures can be diagnosed without hunting down line numbers.
If you still want to extract common assertions, I'd recommend either:
Write them entirely as macros so you get __FILE__ and __LINE__. Look at the definition of XCTAssert to get started.
Use OCHamcrest and write a custom matcher. Because OCHamcrest takes care of capturing file name and line number, the matcher is easier to write.
However, pulling the assertions out of the block made them so short they're hardly worth extracting. I'd focus instead on extracting a helper for the first part, capturing the block arguments.
I have a method that calls a request with a response block inside. I want to stub my response and return fake data. How can this be done?
-(void)method:(NSString*)arg1{
NSURLRequest *myRequest = ...........
[self request:myRequest withCompletion:^(NSDictionary* responseDictionary){
//Do something with responseDictionary <--- I want to fake my responseDictionary
}];
}
- (void)request:(NSURLRequest*)request withCompletion:(void(^)(NSDictionary* responseDictionary))completion{
//make a request and passing a dictionary to completion block
completion(dictionary);
}
If I understand you correctly, you want to mock request:withCompletion: and call the passed completion block.
Here is how I have done this in the past. I've adapted this code to your call, but I cannot check it for compilation errors, but it should show you how to do it.
id mockMyObj = OCClassMock(...);
OCMStub([mockMyObj request:[OCMArg any] completion:[OCMArg any]])).andDo(^(NSInvocation *invocation) {
/// Generate the results
NSDictionary *results = ....
// Get the block from the call.
void (^__unsafe_unretained completionBlock)(NSDictionary* responseDictionary);
[invocation getArgument:&callback atIndex:3];
// Call the block.
completionBlock(results);
});
You will need the __unsafe_unretained or things will go wrong. Can't remember what right now. You could also combine this with argument captures as well if you wanted to verify the passed arguments such as the setup of the request.
I'm using OAuthSwift to access the goodreads api. First I have an api call to get the user id. Then, using that id, I get the books the user has in a shelf. Then I check if there are any books with a certain title, and if there aren't, I call the api again to create that book. And then, being sure the book is there, I post a status update on its progress.
This is the code to get the user id:
oAuth.client.get(
"https://www.goodreads.com/api/auth_user",
success: {
data, response in
// Here I use the returned ID to get the books.
},
failure: {
error in
//…
}
)
Once I got the ID, I need to use it for the next api call, which gets the books. The thing is, I can't just add the next call after that first one, because they're asynchronous, so the second call would start before I got the ID from the first one.
What I'm doing, then, is to add the second api call inside that success closure (I pointed it out in the code with a comment). So I have two very similar pieces of code one inside the other; when the first api call succeeds it calls the next. But then I have to use the result of that second call to decide if I'll add a new book or not. So I have a third api call (to add the book) inside the success block of the second call, which is inside the success block of the first call… and you can see things are starting to get pretty confusing. And it doesn't even stop there: I have a fourth api call to post a status update, and it's inside the completion handler of the third call, which is inside that of the second, which is inside that of the first.
So… what should I do instead of that? I'm sorry if it's a silly question, but I'm new at swift and at programming overall.
I think Apple has provided best way of handling this scenario using GCD, GCD provides serial queue to execute blocks/API in serial manner. That is in FIFO Order
let serialQueue : dispatch_queue_t = dispatch_queue_create("com.yourSerialQueue.queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQueue) { () -> Void in
//Call First API
print("First API call succeed")
};
dispatch_async(serialQueue) { () -> Void in
//Call Second API
print("Second API call succeed")
};
dispatch_async(serialQueue) { () -> Void in
//Call Third API
print("Third API call succeed")
};
dispatch_async(serialQueue) { () -> Void in
//Call Fourth API
print("Fourth API call succeed")
};
OutPut:
First API call succeed
Second API call succeed
Third API call succeed
Fourth API call succeed
I do not have deep knowledge of GCD yet, for your scenario what i can suggest you in real quick is call each API with completion block as below, (this is what i can help you as of now)
dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(backgroundQueue, ^{
[self callGetUserIdAPIAndCompletion:^(NSDictionary *dictResponse) {
[self callGetBooksAPIForUserId:dictResponse[#"userId"] AndCompletion:^(NSDictionary *dictResponse) {
[self callAddBookAPIForUserId:dictResponse[#"userId"] bookId:dictResponse[#"bookId"] AndCompletion:^(NSDictionary *dictResponse) {
[self callPostStatusUpdateAPIForUserId:dictResponse[#"userId"] status:#"status" AndCompletion:^(NSDictionary *dictResponse) {
}];
}];
}];
}];
});
And write your API calls functions as below
-(void)callGetUserIdAPIAndCompletion:(void(^)(NSDictionary *dictResponse))completion {
NSDictionary *dictResponse;
//Call First API
NSLog(#"API 1 completes");
completion(dictResponse);
}
-(void)callGetBooksAPIForUserId:(NSString*)userId AndCompletion:(void(^)(NSDictionary *dictResponse))completion {
NSDictionary *dictResponse;
//Call Second API with userId
NSLog(#"API 2 completes");
completion(dictResponse);
}
-(void)callAddBookAPIForUserId:(NSString*)userId bookId:(NSString*)bookId AndCompletion:(void(^)(NSDictionary *dictResponse))completion {
NSDictionary *dictResponse;
//Call Third API with userId and bookId
NSLog(#"API 3 completes");
completion(dictResponse);
}
-(void)callPostStatusUpdateAPIForUserId:(NSString*) userId status:(NSString*) status AndCompletion:(void(^)(NSDictionary *dictResponse))completion {
NSDictionary *dictResponse;
//Call Fourth API with userId and status
NSLog(#"API 4 completes");
completion(dictResponse);
}
I have a class with this code which gets called a few times per minute on average, and only runs on the main thread:
PFObject* eventObj = [PFObject objectWithClassName:#"AdminConsoleEvent"];
eventObj[kACParseEventName] = event;
eventObj[kACParseEventUrgency] = urgency;
if( param1 )
eventObj[kACParseEventParam1] = param1;
eventObj[kACParseEventPointerToAdminConsole] = self.adminConsole;
=== [eventObj saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
+++ if( !succeeded ) {
//here
}
}];
If I put a breakpoint where === is, I see that every time eventObj is how I expect... a non-nil object with valid information on it.
If I put a breakpoint where +++ is, then I see that it gets hit exactly only once -- the first time this code is called. If I look on the Parse data browser (online), sure enough, only the first object gets saved (immediately)! The rest never show up.
Why the heck isn't the block (+++) ever running for subsequent calls? Why aren't the other objects being saved?
OK this fixed it...
[PFObject saveAllInBackground:#[eventObj, self.adminConsole] block:^(BOOL succeeded, NSError *error) {
I assume that this is because there was a circular reference: self.adminConsole had a reference being added to it for eventObj, and eventObj had a reference being added to it for self.adminConsole. For whatever reason, that breaks Parse for me if I use saveInBackground directly on the objects.
This question already has answers here:
Is it okay to pass in NULL to a block parameter?
(2 answers)
Closed 9 years ago.
I have a method that does an asynchronous network call, and then passes the success or failure results back via blocks:
- (void) loginWithSuccess:(void (^)(id responseObject))success failure:(void (^)(NSError* error))failure {
...
if(error) {
failure(error);
}
else {
success(responseObject);
}
}
I noticed that if I call this method and pass in nil as my blocks, my application will crash with EXEC_BAD_ACCESS:
[manager loginWithWithSuccess:nil failure:nil];
But, if I pass in empty blocks, it runs fine:
[manager loginWithWithSuccess:^(id responseObject){} failure:^(NSError *error){}];
I assume this is because at runtime you can't pass parameters to nil? So, when defining my methods that take blocks, should I always check that the blocks are not nil before calling them?
Just looking at Apple's Frameworks, some methods with block parameters accept NULL/nil as the block argument (e.g. animateWithDuration:animations:completion:), others don't (e.g. enumerateObjectsUsingBlock:).
If you're designing an API, you have to make that decision. If it makes sense to not have a block, you should accept nil and check before executing the block, otherwise you should throw an assertion like [#[] enumerateObjectsUsingBlock:nil] does:
'NSInvalidArgumentException', reason: '*** -[NSArray enumerateObjectsUsingBlock:]: block cannot be nil'
So why are you getting EXEC_BAD_ACCESS?
When invoking a block you are dereferencing the address, which you obviously can't do if it's not pointing to an actual block. There is a great explanation in this answer.
Please try the following example to understand call block logic:
void (^printString)(NSString*) = ^(NSString* arg) {
NSLog(#"%#", arg);
};
//(1) printString = ^(NSString* arg){};
//(2) printString = NULL;
printString(#"1");
In the console you will see "1". Then uncomment (1) and console reveals "Called" but no errors!
And at last, uncomment (2) and get EXEC_BAD_ACCESS. It is your situation exactly.
Called block must be not NULL or nil. You need to check existence of passing blocks in loginWithSuccess before call.