ABRecordCopyCompositeName and CFBridgingRelease crash issue - ios

I am developing IOS application using AddressBook.
Here is my code what I used.
I am getting crash issue on substringWithRange function.
What is the crash reason?
Thank you.
NSString * sort_name = CFBridgingRelease(ABRecordCopyCompositeName(person));
if (sort_name != nil) {
[self Make_Sorting_Name:sort_name];
- (NSDictionary *)Make_Sorting_Name:(NSString *)sort_name {
NSString * sort_char = [[NSString stringWithString:[sort_name substringWithRange:NSMakeRange(0, 1)]] uppercaseString];
NSCharacterSet *nonDigits = [NSCharacterSet letterCharacterSet];
BOOL containsNonDigitChars = ([sort_char rangeOfCharacterFromSet:nonDigits].location == NSNotFound);
}

The ABRecordCopyCompositeName function might return nil or empty string sometimes. So the case needs to be checked:
NSString *sort_char = #""; //or another specific character for sorting
if (sort_name != nil && sort_name.length > 0){
sort_char = [[NSString stringWithString:[sort_name substringWithRange:NSMakeRange(0, 1)]] uppercaseString];
}

Related

Calling ViewDidAppear from numerous methods to update NSMutableDictionary data entries

I am fairly new to Objective C and am attempting to develop an app using Xcode5.
I am storing strings (either composed of numbers 1-9 or N/A) in a NSMutableDictionary.
When users get to the "Review your inputed results page" I want them to be able to manually go into a text field, delete the value present and retype their new value if necessary. However, I don't know how to reload this information into the system so that the new values will carry over into the email client, which basically sends the results to whatever email address the user wishes.
Currently, the values are being loaded using ViewDidAppear upon entering the UIView, but I think I need to call it again if, for example, textField1 is updated.
I have methods for all the textFields that are textField(insert correct number here)IsUpdated and inside those I want to store the new value to the NSMutableDictionary (which I believe I can already do).
The issue is I cannot figure out how to get the current version of the dictionary that was loaded upon entering the UIView to update so that the information in ViewDidAppear updates for the email.
Hope that made sense.
As I said, definitely new to Objective C.
Below is the viewDidAppear method.
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:(BOOL)animated];
AppDelegate *app = (AppDelegate *) [[UIApplication sharedApplication] delegate];
NSMutableDictionary *results = [app results];
NSString *firstResult = [results valueForKey:#"first"];
NSString *secondResult = [results valueForKey:#"second"];
NSString *thirdResult = [results valueForKey:#"third"];
if ([firstResult isEqual: #"N/A"]) {
self.Result1.text = results[#"first"];
} else {
int firstResultInt = [firstResult intValue]; firstResultInt++;
[_Result1 setText:[NSString stringWithFormat:#"%i", firstResultInt]];
}
if ([secondResult isEqual: #"N/A"]) {
self.Result2.text = results[#"second"];
} else {
int secondResultInt = [secondResult intValue]; secondResultInt++;
[_Result2 setText:[NSString stringWithFormat:#"%i", secondResultInt]];
}
if ([thirdResult isEqual: #"N/A"]) {
self.Result3.text = results[#"third"];
} else {
int thirdResultInt = [thirdResult intValue]; thirdResultInt++;
[_Result3 setText:[NSString stringWithFormat:#"%i", thirdResultInt]];
}
self.diningResult.text = results[#"dining"];
self.basementResult.text = results[#"basement"];
self.atticResult.text = results[#"attic"];
self.carResult.text = results[#"car"];
self.hallwayResult.text = results[#"hallway"];
self.garageResult.text = results[#"garage"];
self.other1Result.text = results[#"other"];
self.other2Result.text = results[#"other1"];
self.other1Name.text = results[#"other1name"];
self.other2Name.text = results[#"other2name"];
NSMutableString * str = [NSMutableString new];
[str appendString:#"Bedroom: "];
if ([firstResult isEqual: #"N/A"]) {
[str appendString: firstResult];
} else {
int firstResultInt = [firstResult intValue]; firstResultInt++;
NSString *firstResultString = [NSString stringWithFormat:#"%d",firstResultInt];
[str appendString: firstResultString];
}
[str appendString:#"\n"];
[str appendString:#"Living Room: "];
if ([secondResult isEqual: #"N/A"]) {
[str appendString: secondResult];
} else {
int secondResultInt = [secondResult intValue]; secondResultInt++;
NSString *secondResultString = [NSString stringWithFormat:#"%d",secondResultInt];
[str appendString: secondResultString];
}
[str appendString:#"\n"];
[str appendString:#"Kitchen: "];
if ([thirdResult isEqual: #"N/A"]) {
[str appendString: thirdResult];
} else {
int thirdResultInt = [thirdResult intValue]; thirdResultInt++;
NSString *thirdResultString = [NSString stringWithFormat:#"%d",thirdResultInt];
[str appendString: thirdResultString];
}
[str appendString:#"\n"];
[str appendString:#"Dining:"];
[str appendString:self.diningResult.text];
[str appendString:#"\n"];
//Code goes on to do the same with all other fields. all strings led by "str" get transferred over to the email
self.emailString = [NSString stringWithString:str];
}
The code should not call viewDidAppear, it's the responsibility of the framework to call viewDidAppear at the appropriate times. Instead, you should make a separate methods, e.g. UpdateMailContents and UpdateTextFields. Then call those methods from viewDidAppear, and call UpdateMailContents from the textFieldDidEndEditing method of the UITextFieldDelegate protocol.

Split NSString from first whitespace

I have a name textfield in my app, where both the firstname maybe a middle and a lastname is written. Now I want to split these components by the first whitespace, the space between the firstname and the middlename/lastname, so I can put it into my model.
For example:
Textfield Text: John D. Sowers
String 1: John
String 2: D. Sowers.
I have tried using [[self componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] firstObject]; & [[self componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] lastObject];
But these only work if have a name without a middlename. Since it gets the first and the last object, and the middlename is ignored.
So how would I manage to accomplish what I want?
/*fullNameString is an NSString*/
NSRange rangeOfSpace = [fullNameString rangeOfString:#" "];
NSString *first = rangeOfSpace.location == NSNotFound ? fullNameString : [fullNameString substringToIndex:rangeOfSpace.location];
NSString *last = rangeOfSpace.location == NSNotFound ? nil :[fullNameString substringFromIndex:rangeOfSpace.location + 1];
...the conditional assignment (rangeOfSpace.location == NSNotFound ? <<default value>> : <<real first/last name>>) protects against an index out of bounds error.
Well that method is giving you an array with all the words split by white space, so then you can grab the first object as the first name and the rest of the objects as middle/last/etc
NSArray *ar = [self componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSString *firstName = [ar firstObject];
NSMutableString *rest = [[NSMutableString alloc] init];
for(int i = 1; i < ar.count; i++)
{
[rest appendString:[ar objectAtIndex:i]];
[rest appendString:#" "];
}
//now first name has the first name
//rest has the rest
There might be easier way to do this, but this is one way..
Hope it helps
Daniel
I think this example below I did, solves your problem.
Remember you can assign values from the array directly, without transforming into string.
Here is an example:
NSString *textField = #"John D. Sowers";
NSArray *fullName = [textField componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#" "]];
if (fullName.count)
{
if (fullName.count > 2)
{
NSLog(#"Array has more than 2 objects");
NSString *name = fullName[0];
NSLog(#"Name:%#",name);
NSString *middleName = fullName[1];
NSLog(#"Middle Name:%#",middleName);
NSString *lastName = fullName[2];
NSLog(#"Last Name:%#",lastName);
}
else if(fullName.count == 2)
{
NSLog(#"Array has 2 objects");
NSString *name = fullName[0];
NSLog(#"Name:%#",name);
NSString *lastName = fullName[1];
NSLog(#"Last Name:%#",lastName);
}
else
{
NSString *name = fullName[0];
}
}
I found this to be most robust:
NSString *fullNameString = #"\n Barnaby Marmaduke \n \n Aloysius ";
NSMutableArray *nameArray = [[fullNameString componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] mutableCopy];
[nameArray removeObject:#""];
NSString *firstName = [nameArray firstObject];
if(nameArray.count)
{
[nameArray removeObjectAtIndex:0];
}
NSString *nameRemainder = [nameArray componentsJoinedByString:#" "];
Bob's your uncle.

Backward with custom string

I used a string array for emoticons like this:
NSArray *emoticons = #[#"[smile]",#"[cry]",#"[happy]" ...]
then in a UITextView displaying a string like this:
I'm so happy now [happy] now [smile]
When I click a backward or delete button, if the last word is in emoticons, I want a whole emoticon string be deleted, not the last one character only.
Any idea?
Try this,
NSString *string = self.textView.text;
__block NSString *deleteWord = nil;
__block NSRange rangeOfWord;
[string enumerateSubstringsInRange:NSMakeRange(0, self.textView.selectedRange.location + self.textView.selectedRange.length) options:NSStringEnumerationByWords | NSStringEnumerationReverse usingBlock:^(NSString *substring, NSRange subrange, NSRange enclosingRange, BOOL *stop) {
deleteWord = substring;
rangeOfWord = enclosingRange;
*stop = YES;
}];
if ([emoticons containsObject:deleteWord]) {
string = [string stringByReplacingCharactersInRange:rangeOfWord withString:#""];
self.textView.text = string;
self.textView.selectedRange = NSMakeRange(rangeOfWord.location, 0);
}
You might achieve something like this with the UITextViewDelegate method textView:shouldChangeTextInRange:replacementText: checking what is about to be deleted and remove the whole [emoticon] word.
I am giving you the idea that i used.
as you do not mentioned what you used as emoticons.
but for delete logic i think you will get idea from my this code.
if ([string isEqualToString:#""]) {
NSString *lastChar = [txthiddenTextField.text substringFromIndex: [txthiddenTextField.text length] - 1];
NSLog(#"Last char:%#",lastChar);
txthiddenTextField.text = [txthiddenTextField.text substringToIndex:[txthiddenTextField.text length] - 1];
NSString *strPlaceHolder;
strPlaceHolder = txthiddenTextField.text;
if([lastChar isEqualToString:#"]"])
{
int j = 1;
for (int i = [txthiddenTextField.text length]-1; i >=0; --i)
{
NSString *lastChar = [txthiddenTextField.text substringFromIndex: [txthiddenTextField.text length] - 1];
if([lastChar isEqualToString:#"["])
{
NSLog(#"%d",j);
txthiddenTextField.text = [txthiddenTextField.text substringToIndex:[txthiddenTextField.text length] - 1];
// NSLog(#"Processing character %#",strPlaceHolder);
break;
}
txthiddenTextField.text = [txthiddenTextField.text substringToIndex:[txthiddenTextField.text length] - 1];
j = j+1;
}
}
NSLog(#"My text fild value :%#",txthiddenTextField.text);
return YES;
}
So, from here you have to check if the closing bracket is coming or not.
if closing bracket will come then up to opening bracket you have to delete.
then whole emoticon will delete.
hope this helps....

Newbie trying to construct an URL for GET request - my test code and screenshot included

Being an iOS and Objective-C newbie I'm trying to construct an app, where the user can authenticate through Facebook, Google+ or 3 further (Russian) social networks.
For Facebook I know, that I could use the Facebook SDK or Social.framework, but for the others - I have to use OAuth and UIWebView, because there are no good SDKs for them yet.
(I have already succeeded in that once - but that was for an Adobe AIR app and now I'm trying to learn native...)
In Xcode 5.2 I have prepared a very simple Master-Detail app for iPhone and checked it into GitHub:
My question is about constructing a NSString for a GET (or body in POST) request -
Currently I have the following awkward source code in the DetailViewController.m:
- (NSString*)buildUrl
{
NSString *key = _dict[kKey];
NSString *str = _dict[kAuthUrl];
if ([key isEqual: kFB]) {
str = [str stringByAppendingString:#"display=touch"];
str = [str stringByAppendingString:#"&response_time=token"];
str = [str stringByAppendingString:#"&client_id="];
str = [str stringByAppendingString:_dict[kAppId]];
str = [str stringByAppendingString:#"&redirect_uri="];
str = [str stringByAppendingString:_dict[kAppUrl]];
//str = [str stringByAppendingString:#"&state="];
//str = [str stringByAppendingString:rand(1000)];
} else if ([key isEqual: kGG]) {
} else if ([key isEqual: kMR]) {
} else if ([key isEqual: kOK]) {
} else if ([key isEqual: kVK]) {
}
return str;
}
My questions:
Instead of using stringByAppendingString could I use something nicer? Like maybe an NSArray (or even better NSDictionary) and then somehow joining it with ampersands inbetween?
How to escape the HTML entities in the value of redirect_uri= ?
I need to append a random number as the value of state=, but I am not sure what function to use there best...
Here is what my app prints at the moment, for the above code:
MyAuth[9626:70b] request: { URL: https://graph.facebook.com/oauth/authorize?display=touch&response_time=token&client_id=432298283565593&redirect_uri=https://www.facebook.com/connect/login_success.html }
(which is no good: the URL at the end is not escaped and there is no random state number).
stringWithFormat: is your friend.
- (NSString*)buildUrl
{
NSString *key = _dict[kKey];
NSString *str = _dict[kAuthUrl];
if ([key isEqual: kFB]) {
NSString *escapedURI = [_dict[kAppUrl] stringByAddingPercentEscapesUsingEncoding:NSUTF8Encoding];
int state = arc4random_uniform(1000);
str = [NSString stringWithFormat:#"%#display=touch&response_time=token&client_id=%#&redirect_uri=%#&state=%d", _dict[kAuthUrl], _dict[kAppId], escapedURI, state];
} else if ([key isEqual: kGG]) {
} else if ([key isEqual: kMR]) {
} else if ([key isEqual: kOK]) {
} else if ([key isEqual: kVK]) {
}
return str;
}

IOS caseInsensitiveCompare

I have the code below that compares two values and returns if match is found, the problem is that it is case-sensitive, I googled around and found the method caseInsensitiveCompare, please help me run this program with caseInsensitiveCompare method, I am lost.
NSString *listOfnames = #"person, person1, person2, person3";
NSString *name = #"person2";
NSRange match = [listOfnames rangeOfString:name];
if(match.location == NSNotFound ){
NSLog(#"Person not found!");
}else{
NSLog(#"Found you");
}
Use the rangeOfString:options: rendition with the NSCaseInsensitiveSearch option:
NSString *listOfnames = #"person, person1, person2, person3";
NSString *name = #"PERSON2";
NSRange match = [listOfnames rangeOfString:name options:NSCaseInsensitiveSearch];
if(match.location == NSNotFound ){
NSLog(#"Person not found!");
}else{
NSLog(#"Found you");
}

Resources