Design pattern for handling a server response - objective-c

I've an observer pattern on the UI that checks what's the status of an object that handles a server connection that's trying to update a certain field on a database.
The UI's update method receives an object containing data pairs containing the information of what's happening with the connection. The problem is that I'm getting tangled with a lot of ifs checking for different possibilities.
- (void) update:(Bundle *)arg
{
if ([[arg getData:#"updatee"] isEqualToString:#"email"]){
UITableViewCell *emailCell = [[self tableView] cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
if ([[arg getData:#"connecting"] isEqualToString:#"true"]) {
//Email is being posted
[_emailLabel_email setText:#"Connecting..."];
[_emailLabel_set setHidden:YES];
emailCell.accessoryType = UITableViewCellAccessoryNone;
[_emailActivityIndicator startAnimating];
}else{
if ([[arg getData:#"succesfull"] isEqualToString: #"false"])
//Email was posted unsuccesfully
[[[UIAlertView alloc] initWithTitle:#"Taken Email Address"
message:#"The email address that you entered is already in use, please double check it"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil] show];
else{
//Email was posted succesfully.
[_emailLabel_set setText:#"Change"];
}
[_emailActivityIndicator stopAnimating];
[_emailLabel_email setText:[mng getEmail]];
[_emailLabel_set setHidden:NO];
emailCell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
}
//Password cases
}
}
As the server responds with a string I'm finding difficult to avoid this spaggetti of code.
Which would be the smarter object to send on the update method?

You could keep the data how it is and put all of the values you expect for [arg getData:#"updatee"] as the keys in a dictionary. The values would be the string representation of method selectors, where each of the methods handles one of the cases in your current if statement. Now your update: method just gets the selector string from the dictionary, converts it to a real selector (NSSelectorFromString) and calls the method (passing the arg as a parameter, all methods used here must have matching parameter listing). If you receive an unexpected update type nothing would happen so you should log / assert that. If you get an exception while translating the selector you probably have a typo (good candidate for a test which executes all of the methods in the configuration).
Of course you could basically do the same thing with all the methods and a dirty big if / else statement.

Related

How to use OCMock to verify static methods

I am trying to use OCMock library. I am trying to create mock of class object, but it is failing to verify the method. I am unable to understand why the tests are failing.
#interface MyClass:NSObject
+(void) someMethod;
#end
#implementation MyClass
+(void) someMethod
{
NSError* error = nil;
if (![Utility isValidPropWithError:&error])
{
[Logger log:LoggerLevelWarning message:[error localizedDescription] className:className];
}
}
#end
Test :
-(void)testIfLoggerIsset{
id partialMockLogger = OCMClassMock([Logger class]);
id partialMockUtility = OCMClassMock([Utility class]);
id partialMockClass = OCMClassMock([MyClass class]);
NSError *error = nil;
OCMExpect([partialMockUtility isValidPropWithError:&error]);
[MyClass someMethod];
//This works fine.
OCMVerifyAll(partialMockClass);
NSString *className = #"classname";
//This is failing...
OCMVerify([partialMockUtility isValidPropWithError:&error]);
OCMVerifyAll(partialMockUtility);
//This is failing...
OCMVerify([partialMockLogger log:LoggerLevelWarning message:[error localizedDescription] className:className]);
[partialMockUtility stopMocking];
[partialMockLogger stopMocking];
}
In the above code, although [Utility isValidPropWithError:&error]; is called OCMVerify([partialMockUtility isValidPropWithError:&error]);is failing.
Several things here:
First, OCMVerify([partialMockUtility isValidPropWithError:&error] is failing because you are expecting the address of the NSError object you created in the test to be passed to isValidPropWithError:, but in MyClass +someMethod you are creating a different NSError object. The addresses of two different objects will not be the same.
To fix this, change your expectation and verification to:
OCMExpect([partialMockUtility isValidPropWithError:(NSError __autoreleasing**)[OCMArg anyPointer]]);
OCMVerify([partialMockUtility isValidPropWithError:(NSError __autoreleasing**)[OCMArg
and just ignore the actual value of the parameter and expect that it's going to be an NSError pointer (since you're creating it inside of someMethod, there's no way to know what it's going to be before you call the method).
Second, since you are already explicitly verifying +isValidPropWithError, OCMVerifyAll(partialMockUtility) isn't going to verify anything. You should either explicitly verify all of your expectations, or simply use OCMVerifyAll(partialMockUtility) and let it verify all your expectations and don't bother with expecting the specific call. OCMVerifyAll will verify everything you expect on the mock object you give it. This isn't going to cause a test failure - both calls will pass, since you've already verified the call the first time, the call to OCMVerifyAll() isn't going to have anything to verify, so it will pass.
Last, OCMVerify([partialMockLogger log:LoggerLevelWarning message:[error localizedDescription] className:className]); is failing because you didn't set an expectation for it.

Crash on second address book (contacts) lookup

Update:
I believe the problem lies somewhere in how I am storing the reference to ABRecordRef. I am currently just hanging onto the value as delivered to peoplePickerNavigationController:shouldContinueAfterSelectingPerson:property:identifier: and not CFRetaining it or anything. It's unclear from the documentation if it needs to be retained.
I'm working on an iPhone app and it interfaces with the address book using the AddressBook and AddressBookUI frameworks. I'm using the ABPeoplePickerNavigationController to present a contact list to the user to choose, and am capturing the resultant ABRecordRef as an instance variable on a custom class.
This is all working fine on the first use. However, the second time I pick someone from the contacts (even a different person), my app blows up with EXC_BAD_ACCESS on a call to ABRecordCopyValue. I am logging the pointers and they are definitely different each time a contact is selected (even if the same contact twice).
I fail to understand how this reference could be deallocated. A memory leak sure, but why does it work fine the first time and not the second?
Here's the actual call it's dying on:
- (NSString*)displayName {
return CFBridgingRelease( ABRecordCopyValue( self.contact, kABPersonFirstNameProperty ) );
}
Here's some debug output if it's helpful at all:
Printing description of self->_contact:
(ABRecordRef) _contact = 0x1f582dc0
(lldb) expr (CFTypeRef)ABRecordCopyValue(self.contact, kABPersonFirstNameProperty)
error: Execution was interrupted, reason: EXC_BAD_ACCESS (code=1, address=0xd1f57cc1).
The process has been returned to the state before execution.
Turns out all I needed was to CFRetain( person ) and everything's happy go lucky again. I also added a dealloc to my class to clean up the pointer when the object goes away:
- (void)dealloc {
CFRelease( _contact );
}
My code now runs smoothly and the static analyser is happy (not that it caught the leak anyway).
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker
shouldContinueAfterSelectingPerson:(ABRecordRef)person
{
[self displayPerson:person];
[self dismissViewControllerAnimated:YES completion:nil];
return NO;
}
you returning NO ?
Try checking to see if the value exists maybe
ie
- (void)displayPerson:(ABRecordRef)person
{
NSString* companyName = (__bridge_transfer NSString*)ABRecordCopyValue(person, kABPersonOrganizationProperty);
NSString* name = (__bridge_transfer NSString*)ABRecordCopyValue(person, kABPersonFirstNameProperty);
NSString* display = #"";
if (companyName) {
display = companyName;
} else if (name) {
display = name;
} else {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"No Details For Contact"
message:#"Please update contact with company and/or first name"
delegate:nil
cancelButtonTitle:#"Dismiss"
otherButtonTitles:nil];
[alertView show];
}
}

Preferred method of ceasing execution based on conditional within a method

I am new to iOS, and still learning the proper ways of how to do things. Another question that just reared its head, how can I cease execution of a method, based on a conditional and return back to calling code? Normally in PHP, I simply return true/false, or throw an exception, rather than tucking huge blocks of code within nested conditionals, but in iOS, I am not allowed to return from a method with a IBAction return signature.
What would be the preferred way of handling this sitch?
- (IBAction)submitCode:(id)sender
{
if ([codeEntry.text length] == 0) {
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"Form Validation" message:#"No code entered, please try again."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[message show];
// IOS not allowing this
return NO;
}
// Prefer not to wrap the rest of the logic in an else, rather just cease
// execution and return back to calling code
NSLog(#"I would have submitted!");
}
You need to call return;, not return NO;.
The IBAction is really void. This means the method has no return value.
Just use
return;
This will stop execution of the current method.
Since IBAction is secretly typdef'ed to void, you can't return anything. Nothing is nothing is nothing, so just use return;.

How to: Implement server forcing disconnect of client in GKSession

I am implementing the GKSession server/client mode operation in my application on iOS. I found one question related to mine but with no answer. I am trying to allow the server to disconnect any client that is currently connected to the session. I thought that calling disconnectPeerFromAllPeers:(NSString *)peerID would allow me to do this, but is seems to have no effect.
Any suggestions?
Thanks
Actually answered via question update ion 01/03/2012, but moved this text to the answer section
I wanted to share how I implemented a disconnect request sent from server to client. All of the code presented below is contained within a class I created to completely encapsulate all the interfacing with a GKSession instance (also implements the GKSessionDelegate methods).
First I have the server send a disconnect request to the client that shall be disconneted. Any data that is sent from client to server or vice versa is contained within a dictionary that also has a key-value pair to specify the type of data that is sent (in this case the data is a disconnect request).
- (void)sendDisconnectRequestToPeer:(NSString *)peer {
//create the data dictionary that includes the disconnect value for the data type key
NSMutableDictionary *dictPrvw = [[NSMutableDictionary alloc] initWithObjectsAndKeys:[NSNumber numberWithInt:GKSessionDataTypeDisconnect], kDictKeyDataType, nil];
NSData *dataChunk = [[NSKeyedArchiver archivedDataWithRootObject:dictPrvw] retain];
//[self printDict:dictPrvw];
NSArray *peers = [[NSArray alloc] initWithObjects:peer, nil];
[self sendData:dataChunk toPeers:peers];
[dataChunk release];
[dictPrvw release];
}
The client receives the data, casts it into a dictionary and examines the key-value pair that specifies what type of data was sent. If it's a disconnect request, my "GKSessionManager" class then implements a disconnect.
- (void)recievedAllDataChunksInSession:(GKSession *)session fromPeer:(NSString *)peer context:(void *)context {
//The chunk was packaged by the other user using an NSKeyedArchiver,
//so unpackage it here with our NSKeyedUnArchiver
NSMutableDictionary *responseDictionary = (NSMutableDictionary *)[[NSKeyedUnarchiver unarchiveObjectWithData:self.recievedPackets] mutableCopyWithZone:NULL];
//[self printDict:responseDictionary];
//get the enumerator value for the data type
NSNumber *gkSessDataType = [responseDictionary objectForKey:kDictKeyDataType];
int intDataType = [gkSessDataType intValue];
UIAlertView *anAlrtVw;
switch (intDataType) {
case GKSessionDataTypeMessageData:
[self sessionManager:self recievedDataDictionary:responseDictionary];
break;
case GKSessionDataTypePreviewRequest:
if (sess.sessionMode == GKSessionModeServer) {
[self sendMsgPreviewToPeer:peer];
}
break;
case GKSessionDataTypePreviewSend:
//[self sessionManager:self recievedDataDictionary:responseDictionary];
[self sessionManager:self connectedWithPrelimData:responseDictionary];
break;
case GKSessionDataTypeDisconnect:
anAlrtVw = [[UIAlertView alloc]
initWithTitle:nil
message:#"The server has disconnect you."
delegate:self cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[anAlrtVw show];
[anAlrtVw release];
[self closeSession];
[self disconnectedByServer];
default:
break;
}
}
- (void)closeSession {
[sess disconnectFromAllPeers];
[sess setDataReceiveHandler: nil withContext: NULL];
sess.available = NO;
sess.delegate = nil;
self.sess = nil;
self.serverId = nil;
self.rqstPeerId = nil;
serverIsConnecting = NO;
}
The user never sees the disconnect request and so has no control over whether or not to deny it.
Hope this information helps. I realize what I wrote my not be entirely clear and I have left a lot of other code out (on purpose) so feel free to comment or ask questions.

My timer isn't stopping my parsing

I want to put in a timeout in case it takes too long to find my location, send out the relevant url, and parse the xml. It worked when I used performSelector:withObject:afterDelay in the locationManager (just to test getting the xml), but when I put similar code around my parser it doesn't actually abort the parsing. I am testing this by dropping the delay to 0.01.
My problem is: even with the delay set to 0.01, it still waits for all the parsing to complete first, and only then does it put up the alertView that is coded in the parsingDidTimeout method.
I did try this with a timer, and that wasn't working as well as performSelector: does in the other parts of my code. Either way, it doesn't put up the alertView, and stop the parsing, until after the parsing has finished, no matter how long that takes.
I create a url which requires a radius. First I try a small radius, but if I don't get the data I need, I expand the radius and send the url again and parse again. Here is part of my StartParsing method.
xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
XMLParser *parser = [[XMLParser alloc] initXMLParser];
[xmlParser setDelegate:parser];
if (!hadToExpandRadius){//meaning, only do this the first time I send out the url and parse
[self performSelector:#selector(parsingDidTimeout:) withObject:nil afterDelay:0.01];
}
//Start parsing the XML file.
BOOL success = [xmlParser parse];
if(success){
if((didNotGetTheDataYet) && (radius < 500)){
hadToExpandRadius = YES;
radius = radius + 35;
[self startParsing];//do this same method, with larger radius
}
else {
NSLog(#"No Errors");
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(parsingDidTimeout:) object:nil];}
[parser release];
}
-(void)parsingDidTimeout{
[xmlParser abortParsing];
UIAlertView *servicesDisabledAlert = [[UIAlertView alloc] initWithTitle:#"Try Later" message:#"We need a better connection. We can get the data later." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[servicesDisabledAlert show];
[servicesDisabledAlert release];
[myActivityView stopAnimating];
}
Thank you for your help.
Calling performSelector:withObject:afterDelay: you ask the run loop to call the selector later. But [xmlParser parse] blocks the run loop, so it doesn't have a chance to call you selector.
abortParsing is designed to be called inside parsers' delegate methods.
The workaround can be to parse in a separate thread.
Found it -- just extra ":" in my performSelector:#selector(parsingDidTimeout:)!
I thought it was something fancy having to do with the second thread. Just syntax.
Thanks for explaining about the parse blocking the run loop. I was hoping not to need another thread, but your suggestion fixed my problem. Thanks.