Dealing with multiple BOOL combinations - objective-c

Here is the scenario: In my app I'm syncing some data, whenever there is some error when syncing I flag this in a BOOL. When all syncing is complete I want to display sync feedback (errors) for the user.
If there is ie a calendar sync error and a contact sync error I first display a UIAlertView with information about the calendar sync error, when the user has tapped "OK" I then display a UIAlertView with information about the contact sync error. To be able to know when the user has tapped "OK" I use completion blocks. So my code looks something like this:
if (calendarSyncFailed && contactSyncFailed && facebookSyncFailed && contactSyncConflicts) {
[self displayCalendarSyncAlertCompletionBlock:^{
[self displayContactsSyncAlertCompletionBlock:^{
[self displayFacebookSyncAlertCompletionBlock:^{
[self displayContactSyncConflictsAlertCompletionBlock:^{
}];
}];
}];
}];
} else if (calendarSyncFailed && contactSyncFailed && facebookSyncFailed) {
[self displayCalendarSyncAlertCompletionBlock:^{
[self displayContactsSyncAlertCompletionBlock:^{
[self displayFacebookSyncAlertCompletionBlock:^{
}];
}];
}];
} else if (contactSyncFailed && facebookSyncFailed && contactSyncConflicts) {
[self displayContactsSyncAlertCompletionBlock:^{
[self displayFacebookSyncAlertCompletionBlock:^{
[self displayContactSyncConflictsAlertCompletionBlock:^{
}];
}];
}];
} else if (you get the idea…) {
}
As you can see there will be alot of different combinations for dealing with these 4 boolean values and I was wondering if there is a more smarter/elegant way of coding this?

While I do agree with demosten that it would be better to have only one message, this is how I would do this with less code:
Use a mutable array as a property where you store your alertviews.
In the method where you test your conditions, create an alert view for every failure that evaluates to true, and put them in your array in the desired order. (This is the key part as you are only doing 4 tests, instead of 2^4 - 1 tests).
Implement the UIAlertViewDelegate method alertView: didDismissWithButtonIndex: something like this:
-(void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
NSInteger nextIndex = [self.alertViews indexOfObject:alertView] + 1;
if (nextIndex < [self.alertViews count]){
UIAlertView *next = [self.alertViews objectAtIndex: nextIndex];
[next show];
}
}

Related

Is it ok to perform a segue inside a for loop?

Is it ok to perform a segue in a for loop like below or I am setting myself up for trouble?
Or will it simply perform the segue and the rest of the code is never executed?
for(symbol in results) {
if ([symbol.data hasPrefix:#"--"]) {
actualBarCodeStr = [symbol.data substringFromIndex:2];
[self performSegueWithIdentifier:#"trListViewToTrSearchView" sender:self];
} else {
createTransactionResult = [NWBarCodeHelper createTransactionRowFromBarCode:symbol.data];
if ([NWTillHelper isDebug] == 1) {
NSLog(#"Zbar delegate holds barcode: %#", symbol.data);
if(createTransactionResult != 0) {
NSLog(#"TransactionListView:ZBarDelegate:createTransactionFrombarCode failed with errorCode %i", createTransactionResult);
}
}
}
}
As the code is in a method, they will all run in one runloop even if you have performed the first segue. So all the segue will be performed. If it's just a push, I think there will be a bunch of view controller be pushed. For cases like modal present, I think it may crash but I have not tested it.

Program/Method Flow with Threads

I have a UIAlertView that initiates an update process.
The UIAlertView asks the user whether they'd like to update or not.
Here's my code:
- (void)reachabilityChanged:(NSNotification *)notification {
if ([connection isReachable]){
[updateLabel setText:#"Connection Active. Checking Update Status"];
[[[UIAlertView alloc] initWithTitle:#"Update Available" message:#"Your File Database is Out of Date. Would you like to Update?\nNote: Updates can take a long time depending on the required files." delegate:self cancelButtonTitle:#"Later" otherButtonTitles:#"Update Now", nil] show];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (buttonIndex == 1) {
[self updateFiles:[UpdateManager getUpdateFiles]];
}
}
The above code runs fine, however, within my updateFiles: method, I require some UI adjustments.
- (void)updateFiles:(NSArray *)filesList {
for (NSDictionary *file in filesList) {
[updateLabel setText:[NSString stringWithFormat:#"Downloading File: %#", [file objectForKey:#"Name"]]];
[UpdateManager updateFile:[file objectForKey:#"File Path"]];
}
[updateIndicator stopAnimating];
[updateLabel setText:#"Update Completed"];
}
The UIAlertView doesn't dismiss until after the for statement in the updateFiles method is run.
I can't get the updateLabel to display the files it's currently downloading, though at the end of the update process, we do get a 'Update Completed' in the label.
Can anybody help?
UPDATE
I'm starting to suspect this is more of a process that's being delayed by some heavy synchronous processes. For example, my [UpdateManager getUpdateFiles] method is heavy and involves getting resources from the web. Likewise with my [UpdateManager updateFile:[file objectForKey:#"File Path"]]; method.
Is there any way I can force the UI updates to take priority over these methods?
I'm just trying to give the user some feedback on what's happening.
I found the solution.
I couldn't update the UI and process some heavy methods on the same thread.
Since I can only update the UI on the main thread, I had to do some re-organising to ensure the processes were on a background thread, but then promote the UI changes to the main.
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (buttonIndex == 1) {
[self performSelectorInBackground:#selector(updateFiles:) withObject:[UpdateManager getUpdateFiles]];
}
}
- (void)updateFiles:(NSArray *)filesList {
for (NSDictionary *file in filesList) {
[updateLabel performSelectorOnMainThread:#selector(setText:) withObject:[NSString stringWithFormat:#"Downloading File: %#", [file objectForKey:#"Name"]]];
[UpdateManager updateFile:[file objectForKey:#"File Path"]];
}
[updateIndicator stopAnimating];
[updateLabel setText:#"Update Completed"];
}
So, I send updateFiles: to the background and promote setText: and any other UI changes to the Main Thread.

How to Check the string is same or changed in Objective C

I check the frame of any subclass, if it is change or not with the following code.
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
if(imageFrameSize == CGRectZero) {
imageFrameSize = self.recycleBin.frame;
NSLog(#"Frame: %#", NSStringFromCGRect(imageFrameSize));
}
}
Now I want to check the string is same or changed. Basically I am storing history in history.plist. And I run the function in below delegate to save document url in history.plist file.
- (void)webViewDidFinishLoad:(UIWebView *)webView {
//when push to reload is done it will disappear pulltorefresh
[(PullToRefreshView *)[self.view viewWithTag:998] finishedLoading];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[self actualizeButtons];
urlPage =[webView stringByEvaluatingJavaScriptFromString:#"document.title"]; //[[[webView request] URL]absoluteString];
[self addHistoryFunction];
NSLog(#"WebView Finish Load %#",[[[webView request] URL]absoluteString] );
}
This function works good. But the problem is that webViewDidiFinishLoad run almost two times for google and three or many times for other websites. So addHistoryFuncation save the history two or more times with same page url.
I want to check the urlPage string before adding to history, if it is updated new string then run the addhistory function, if it is same then skip addhistory function. Just like above CGRectZero code.
Thats my idea to save history in history.plist.
If there is a better then mine please help me or solve my issue.
Thanks in advance.
- (void)webViewDidFinishLoad:(UIWebView *)webView called when ever awebview finish loading an object with in the web page so the best approch to prevent this method to be called more than once to add this line :
if ([webView isLoading]){return;)
this line will make sure that your webview will do the commands when its only finishedl loading.
here is your function after edit try it .
- (void)webViewDidFinishLoad:(UIWebView *)webView {
if ([webView isLoading]){return;)
//when push to reload is done it will disappear pulltorefresh
[(PullToRefreshView *)[self.view viewWithTag:998] finishedLoading];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[self actualizeButtons];
urlPage =[webView stringByEvaluatingJavaScriptFromString:#"document.title"]; //[[[webView request] URL]absoluteString];
[self addHistoryFunction];
NSLog(#"WebView Finish Load %#",[[[webView request] URL]absoluteString] );
}
if you want to compare to string this is how
BOOL isTheSameStrings=[mystring isEqualToString:string2];
if (isTheSameStrings){
NSLog(#"the are the same);
}else{
NSLog(#"the are NOT the same);
}
or you want to check if the string found in an array
BOOL myArrayDoesContainMyString=[myArray containsObject:myString];
if (myArrayDoesContainMyString){
NSLog(#"myArray contain myString);
}else{
NSLog(#"myArray does not contain myString);
}

How to reload a tableView i.e call the viewDidLoad method if a condition is met

The problem is this i need a way to basically erase all the entry data a user placed into my arrays if a condition is met. Im new to Objective-C and iOS programming, but i believed the solution might be in calling the viewDidLoad method, thus it would virtually refresh the applications with the values of the array reset to default. If there is any other logical way of doing this i would appreciate the help.
In short i need to refresh the arrays as they were when the application first launched and the user did not select anything.
This is the part where i need it to refresh.
if ([gradeRecieved objectAtIndex:i]==nil) {
break; // if this condition is met the program must begin anew.
Edit* I need to recall the - (void)viewDidLoad method
here is more of the code.
-(IBAction)button:(id)sender{
double sum = 0;
int i = 0;
double gradeEarned=0;
double creditHours = 3;
while (i<8){
// [credits removeObjectIdenticalTo:[NSNull null]];
if ([credits count ] ==0) {
break;
}
if ([credits objectAtIndex:i] == radioButtonA) {
// [gradeRecieved replaceObjectAtIndex:i withObject:GradeA];
[defArray addObject:GradeA];
gradeEarned+=GradeA.intValue;
i++;
continue;
}
if ([credits objectAtIndex:i] == radioButtonB) {
// [gradeRecieved replaceObjectAtIndex:i withObject:GradeB];
[defArray addObject:GradeB];
gradeEarned+=GradeB.intValue;
i++;
continue;
}
if ([credits objectAtIndex:i]== radioButtonC){
// [gradeRecieved replaceObjectAtIndex:i withObject:GradeC];
[defArray addObject:GradeC];
gradeEarned+=GradeC.intValue;
i++;
continue;
}
if ([credits objectAtIndex: i] == defaulter) {
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Custom button pressed" message:[NSString stringWithFormat:#"You pressed the custom button C"] delegate:self cancelButtonTitle:#"great"otherButtonTitles:nil];
[alert show];
[alert release];
[self viewDidLoad];
break;
}
}
if ([defArray count]>0) {
sum= ([defArray count])/(creditHours*gradeEarned);
NSLog(#"%f",sum);}
this new code however results in the app freezing if the user clicks the button and then try's to redo some selections made
If you want to call that code again, wrap that code into a method and call that method in viewDidLoad and wherever else you want. Here is some example code:
- (void)viewDidLoad {
if ([gradeRecieved objectAtIndex:i]==nil) {
[self refreshArray];
}
}
- (void)refreshArray {
// refresh here
}
Then call [self refreshArray] wherever you need!
I see you are new! If this answer was correct you can up vote or tick!
The viewDidLoad method loads only once - when the view is first loaded. To reload data, the easiest way is to create another method and call that method everytime you need to load data.
Example,
-(void)loadingData{
//code to load the data
}
-(void)viewDidLoad{
//this will call the loadingData method to load the data
[self loadingData];
}

Pausing iteration of a for loop to wait for user input

I wrote a for loop which is iterating through an array of objects.
Now I am asking myself if it's possible to break the iteration of the loop until the user clicks on a button which calls a IBAction?
for (int i = 0; i < [array count]; i++) {
// do something with the object
// wait for action method called
// user clicked action so go on
}
You can adapt the code to fit your case. It basically "unrolls" the loop into multiple messages. Start the sequence with [self doItForIndex:[NSNumber numberWithInt:0]];
- (BOOL)canDoitForIndex:(NSNumber *)i {
// return YES if you want to go ahead
// (e.g. test a BOOL you set in response to the user tapping a button
}
- (void)waitForIndex:(NSNumber *)i {
if ([self canDoItForIndex:i]) {
// do anything to clean up for i
// then repeat for i+1:
[self doItForIndex:[NSNumber numberWithInt:[i intValue]+1]];
} else {
[self performSelector:_cmd withObject:i afterDelay:0.01f;
}
}
- (void)doItForIndex:(NSNumber *)i {
if ([i intValue] < lastIndex) {
// do what you have to do
[self waitForIndex:i];
}
// else you're done
}
Apple's NSRunLoop concept expects you to complete processing pretty quickly. If you tie up the main thread by waiting for something, nothing else in your app can happen. The above code breaks the "wait" into multiple message sends, and keeps your app responsive.
ODRM algorithm works very well.
I just changed this line :
[self performSelector:_cmd withObject:i afterDelay:0.01f];
with this :
[NSThread sleepForTimeInterval:0.25];
[NSThread detachNewThreadSelector:_cmd toTarget:self withObject:i];
As I had UI elements to be updated, it was better for we to force waiting to be in a background thread.