I am using ABAddressBookRegisterExternalChangeCallback to get the external changes in AddressbookBook of the user. I am using the following code to register the callback:
ABAddressBookRef ntificationaddressbook = ABAddressBookCreate();
ABAddressBookRegisterExternalChangeCallback(ntificationaddressbook, MyAddressBookExternalChangeCallback, self);
and when this callback is called then MyAddressBookExternalChangeCallback is called successfully
void MyAddressBookExternalChangeCallback (ABAddressBookRef ntificationaddressbook,CFDictionaryRef info,void *context)
{
NSLog(#"Changed Detected......");
}
I have the following questions:
How can i detect which contacts was changed, and which action (ADD,Update,Delete) was performed on that Contact. I need to get the recordID of that contact. Is it Possible ?
Unfortunately if thats not possible then how the apps like viber, tango, watsapp gets the change information ?
I gets the callback method called only when the app is in background if the app is terminated is there any way to get the changes notification.
Please help. Thanks in advance.
I found the solution to find what was changed in the addressbook. I am not sure if its a precise one, But some how that works.
Create a Callback Notification for Addressbook:-
ABAddressBookRef ntificationaddressbook = ABAddressBookCreate();
ABAddressBookRegisterExternalChangeCallback(ntificationaddressbook, MyAddressBookExternalChangeCallback, self);
Than Add the following code when notification Occurs:-
void MyAddressBookExternalChangeCallback (ABAddressBookRef ntificationaddressbook,CFDictionaryRef info,void *context)
{
NSTimeInterval timeStampone = [[NSDate date] timeIntervalSince1970];
NSNumber *timeStamponeobj= [NSNumber numberWithDouble: timeStampone];
NSLog(#"Start Process");
NSUserDefaults *userdefs = [[NSUserDefaults alloc]init];
NSMutableArray *arraytemp = [[NSMutableArray alloc]init];
CFArrayRef peopleRefs = ABAddressBookCopyArrayOfAllPeopleInSource(ntificationaddressbook, kABSourceTypeLocal);
NSMutableArray *changedrecords = [[NSMutableArray alloc]init];
ABAddressBookRevert(ntificationaddressbook);
CFIndex count = CFArrayGetCount(peopleRefs);
for (CFIndex i=0; i < count; i++) {
ABRecordRef ref = CFArrayGetValueAtIndex(peopleRefs, i);
NSDate* datemod = ( NSDate *)(ABRecordCopyValue(ref, kABPersonModificationDateProperty));
NSDate *lastopened = [userdefs valueForKey:#"LastOpenedDate"];
NSTimeInterval datemodifiedtime =[datemod timeIntervalSince1970];
NSNumber *modifieddatenumber= [NSNumber numberWithDouble: datemodifiedtime];
NSTimeInterval lastopeddate = [lastopened timeIntervalSince1970];
NSNumber *lastopenednumber= [NSNumber numberWithDouble: lastopeddate];
if ([modifieddatenumber intValue]>[lastopenednumber intValue]) {
ABRecordRef aSource = CFArrayGetValueAtIndex(peopleRefs,i);
int recordid = ABRecordGetRecordID(aSource);
[changedrecords addObject:[NSString stringWithFormat:#"%d",recordid]];
}
}
NSString *arraystring= [changedrecords componentsJoinedByString:#","];
[userdefs setValue:arraystring forKey:#"ArrayOfChangedPeopleString"];
[userdefs setValue:changedrecords forKey:#"ArrayOfChangedPeople"];
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
if ([arraystring isEqualToString:#"(null)"]) {
arraystring = [arraystring stringByReplacingOccurrencesOfString:#"(null)" withString:#""];
}
NSLog(#"arraychangedstring= %# and length = %d",arraystring,arraystring.length);
NSLog(#"Must send data to the server here for this changed people array= %#",changedrecords);
if (arraystring.length>2) {
NSLog(#"inside if");
ABAddressBookRef addressBook = ABAddressBookCreate();
CFArrayRef allSources = ABAddressBookCopyArrayOfAllPeople( addressBook );
[userdefs setValue:#"" forKey:#"ArrayOfChangedPeopleString"];
[userdefs setValue:#"" forKey:#"ArrayOfChangedPeople"];
for (int i = 0; i<[changedrecords count]; i++) {
NSString *recordid= [changedrecords objectAtIndex:i];
int nPeople = [recordid intValue];
[IntoochUtil current_function_name:#"Inside getcontactdetails - Start"];
if(nPeople != 0){
// for (CFIndex i = nPeople_start; i < (nPeople_end); i++)
// {
// NSString *reocrdid = [NSString stringWithFormat: #"Record %ld",i];
// NSLog(#"Recordid= %#",reocrdid);
NSMutableDictionary *dict = [[NSMutableDictionary alloc]init];
//common field
NSString *first_name =nil;
NSString *mid_name = nil ;
NSString *last_name =nil ;
NSString *basic_email =nil ;
NSString *basic_mobile =nil ;
NSString *other_phone=nil;
NSString *pager_phone= nil;
//personal filed
NSString *Home_email =nil ;
NSString*home_mobile =nil ;
NSString*home_address =nil;
//business filed
NSString *work_email =nil ;
NSString *company_name =nil ;
NSString *job_title =nil ;
NSString*work_mobile =nil ;
NSString *iphone = nil;
NSString *main = nil;
NSString *work_address =nil ;
NSString *home_fax, *work_fax, *other_fax =nil;
NSString *birthday = nil;
// NSString *blogs = nil;
NSString *record_id;
ABRecordRef aSource = ABAddressBookGetPersonWithRecordID(addressBook, nPeople);
// Fetch all groups included in the current source
// Getting Record id from address book
int recordid = ABRecordGetRecordID(aSource);
record_id = [NSString stringWithFormat:#"%d",recordid];
// Getting Names from address book
// CFStringRef firstName = ABRecordCopyValue(aSource, kABPersonFirstNameProperty);
first_name= ABRecordCopyValue(aSource, kABPersonFirstNameProperty);
// first_name=[NSString stringWithFormat:#"%#",firstName];
// CFRelease(firstName);
// NSLog(#"First name= %#",first_name);
NSMutableDictionary *dict_name = [[NSMutableDictionary alloc]init];
if (!([first_name isEqualToString:#"(null)"]|| first_name == nil || first_name.length ==0))
{
[dict_name setValue:first_name forKey:#"FirstName"];
}
[first_name release];
mid_name= ABRecordCopyValue(aSource, kABPersonMiddleNameProperty);
// NSLog(#"mid name = %#",mid_name);
// CFRelease(midname);
if (!([mid_name isEqualToString:#"(null)"]|| mid_name == nil || mid_name.length ==0))
{
[dict_name setValue:mid_name forKey:#"MiddleName"];
}
[mid_name release];
last_name = ABRecordCopyValue(aSource, kABPersonLastNameProperty);
// NSLog(#"Last Name= %#",last_name);
// CFRelease(lastName);
if (!([last_name isEqualToString:#"(null)"]|| last_name == nil || last_name.length ==0))
{
[dict_name setValue:last_name forKey:#"LastName"];
}
[last_name release];
NSString *dictNamestring = [NSString stringWithFormat:#"%#",dict_name];
if (!([dictNamestring isEqualToString:#"(null)"]|| dictNamestring == nil || dictNamestring.length <4))
{
[dict setValue:dict_name forKey:#"Names"];
}
// [dict_name release];
// Getting Phone numbers from address book
NSMutableDictionary *dict_phone = [[NSMutableDictionary alloc]init];
ABMultiValueRef phones =(NSString*)ABRecordCopyValue(aSource, kABPersonPhoneProperty);
NSString* mobileLabel;
for(CFIndex i = 0; i < ABMultiValueGetCount(phones); i++) {
mobileLabel = (NSString*)ABMultiValueCopyLabelAtIndex(phones, i);
if([mobileLabel isEqualToString:(NSString *)kABPersonPhoneMobileLabel])
{
home_mobile = (NSString*)ABMultiValueCopyValueAtIndex(phones, i) ;
[dict_phone setValue:home_mobile forKey:#"Mobile"];
}
else if ([mobileLabel isEqualToString:(NSString*)kABHomeLabel])
{
basic_mobile = (NSString*)ABMultiValueCopyValueAtIndex(phones, i);
[dict_phone setValue:basic_mobile forKey:#"Home"];
}
else if([mobileLabel isEqualToString:(NSString *)kABWorkLabel])
{
work_mobile = (NSString*)ABMultiValueCopyValueAtIndex(phones, i);
[dict_phone setValue:work_mobile forKey:#"Work"];
}
else if([mobileLabel isEqualToString:(NSString *)kABPersonPhoneIPhoneLabel])
{
iphone = (NSString*)ABMultiValueCopyValueAtIndex(phones, i);
[dict_phone setValue:iphone forKey:#"iPhone"];
}
else if([mobileLabel isEqualToString:(NSString *)kABPersonPhoneMainLabel])
{
main = (NSString*)ABMultiValueCopyValueAtIndex(phones, i);
[dict_phone setValue:main forKey:#"Main"];
}
else if ([mobileLabel isEqualToString:(NSString*)kABOtherLabel])
{
other_phone = (NSString*)ABMultiValueCopyValueAtIndex(phones, i);
[dict_phone setValue:other_phone forKey:#"OtherPhone"];
}
else if ([mobileLabel isEqualToString:(NSString*)kABPersonPhonePagerLabel])
{
pager_phone = (NSString*)ABMultiValueCopyValueAtIndex(phones, i);
[dict_phone setValue:pager_phone forKey:#"Pager"];
}
else
{
NSLog(#"%# label is not processed",mobileLabel);
}
}
CFRelease(phones);
NSString *dictphonestring = [NSString stringWithFormat:#"%#",dict_phone];
if (!([dictphonestring isEqualToString:#"(null)"]|| dictphonestring == nil || dictphonestring.length <4))
{
[dict setValue:dict_phone forKey:#"PhoneNos"];
}
// [dict_phone release];
// Getting Fax numbers from address book
NSMutableDictionary *dict_fax = [[NSMutableDictionary alloc]init];
ABMultiValueRef faxes =(NSString*)ABRecordCopyValue(aSource, kABPersonPhoneProperty);
NSString* FaxLabel;
for(CFIndex i = 0; i < ABMultiValueGetCount(faxes); i++) {
FaxLabel = (NSString*)ABMultiValueCopyLabelAtIndex(faxes, i);
if([FaxLabel isEqualToString:(NSString *)kABPersonPhoneHomeFAXLabel])
{
home_fax = (NSString*)ABMultiValueCopyValueAtIndex(faxes, i) ;
[dict_fax setValue:home_fax forKey:#"Home"];
}
if ([FaxLabel isEqualToString:(NSString*)kABPersonPhoneWorkFAXLabel])
{
work_fax = (NSString*)ABMultiValueCopyValueAtIndex(faxes, i);
[dict_fax setValue:work_fax forKey:#"Work"];
}
if([FaxLabel isEqualToString:(NSString *)kABPersonPhoneOtherFAXLabel])
{
other_fax = (NSString*)ABMultiValueCopyValueAtIndex(faxes, i);
[dict_fax setValue:other_fax forKey:#"Other"];
}
}
CFRelease(faxes);
NSString *dictfaxstring = [NSString stringWithFormat:#"%#",dict_fax];
if (!([dictfaxstring isEqualToString:#"(null)"]|| dictfaxstring == nil || dictfaxstring.length <4))
{
[dict setValue:dict_fax forKey:#"FaxNos"];
}
[dict_fax release];
// Getting emails from address book
ABMultiValueRef email = ABRecordCopyValue(aSource, kABPersonEmailProperty);
NSMutableDictionary *dict_email = [[NSMutableDictionary alloc]init];
NSString* email_lbl;
for(CFIndex i = 0; i < ABMultiValueGetCount(email); i++) {
email_lbl = (NSString*)ABMultiValueCopyLabelAtIndex(email, i);
if([email_lbl isEqualToString:(NSString *)kABHomeLabel])
{
Home_email = [(NSString*)ABMultiValueCopyValueAtIndex(email, i)retain];
[dict_email setValue:Home_email forKey:#"Home"];
}
else if([email_lbl isEqualToString:(NSString *)kABWorkLabel])
{
work_email = [(NSString*)ABMultiValueCopyValueAtIndex(email, i)retain];
[dict_email setValue:work_email forKey:#"Work"];
}
else if([email_lbl isEqualToString:(NSString *)kABOtherLabel])
{
basic_email = [(NSString*)ABMultiValueCopyValueAtIndex(email, i)retain];
[dict_email setValue:basic_email forKey:#"Other"];
}
else
{
NSLog(#"%# label is processed as other",email_lbl);
basic_email = [(NSString*)ABMultiValueCopyValueAtIndex(email, i)retain];
[dict_email setValue:basic_email forKey:#"Other"];
}
}
CFRelease(email);
NSString *dictemailstring = [NSString stringWithFormat:#"%#",dict_email];
if (!([dictemailstring isEqualToString:#"(null)"]|| dictemailstring == nil || dictemailstring.length <4))
{
[dict setValue:dict_email forKey:#"Emails"];
}
// [dict_email release];
company_name = ABRecordCopyValue(aSource, kABPersonOrganizationProperty);
// NSLog(#"Company name= %#",company_name);
job_title = ABRecordCopyValue(aSource, kABPersonJobTitleProperty);
// NSLog(#"Job title= %#",job_title);
NSDate *birthdate = ABRecordCopyValue(aSource, kABPersonBirthdayProperty);
NSString *birthdate_string = [NSString stringWithFormat:#"%#",birthdate];
if (!([birthdate_string isEqualToString:#"(null)"]|| birthdate_string == nil || birthdate_string.length ==0)) {
NSDateComponents *components = [[NSCalendar currentCalendar] components:NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit fromDate:birthdate];
NSInteger day = [components day];
NSInteger month = [components month];
NSInteger year = [components year];
birthday = [NSString stringWithFormat:#"%d-%d-%d",day,month,year];
NSLog(#"birthday= %#",birthday);
}
[birthdate release];
// Getting addressess from address book
ABMultiValueRef address =(NSString*)ABRecordCopyValue(aSource, kABPersonAddressProperty);
NSDictionary *dict_address = [[NSMutableDictionary alloc]init];
NSString* addLabel;
for(CFIndex i = 0; i < ABMultiValueGetCount(address); i++) {
addLabel=(NSString*)ABMultiValueCopyLabelAtIndex(address, i);
if ([addLabel isEqualToString:(NSString*)kABWorkLabel]) {
NSMutableDictionary *dict_work_add = [(NSMutableDictionary *)ABMultiValueCopyValueAtIndex(address, i)retain];
work_address=[NSString stringWithFormat:#"%# %# %# %# %#",[dict_work_add objectForKey:#"Street"],[dict_work_add objectForKey:#"City"],[dict_work_add objectForKey:#"State"],[dict_work_add objectForKey:#"Country"],[dict_work_add objectForKey:#"ZIP"]] ;
work_address = [work_address stringByReplacingOccurrencesOfString:#"(null)" withString:#""];
work_address = [work_address stringByReplacingOccurrencesOfString:#"\n" withString:#""];
// NSLog(#"work address = [%#] and length= %d",work_address,work_address.length);
if (!([work_address isEqualToString:#"(null)"]|| work_address == nil || work_address.length ==0))
{
[dict_address setValue:work_address forKey:#"Work"];
}
}
if ([addLabel isEqualToString:(NSString*)kABHomeLabel])
{
NSMutableDictionary *dict_home_add = [(NSMutableDictionary *)ABMultiValueCopyValueAtIndex(address, i)retain];
home_address = [NSString stringWithFormat:#"%# %# %# %# %#",[dict_home_add objectForKey:#"Street"],[dict_home_add objectForKey:#"City"],[dict_home_add objectForKey:#"State"],[dict_home_add objectForKey:#"Country"],[dict_home_add objectForKey:#"ZIP"]];
home_address = [home_address stringByReplacingOccurrencesOfString:#"(null)" withString:#""];
home_address = [home_address stringByReplacingOccurrencesOfString:#"\n" withString:#""];
// NSLog(#"home address = [%#] and length= %d",home_address,home_address.length);
if (!([home_address isEqualToString:#"(null)"]|| home_address == nil || home_address.length ==0))
{
[dict_address setValue:home_address forKey:#"Home"];
}
}
}
CFRelease(address);
NSString *dictaddrssstring = [NSString stringWithFormat:#"%#",dict_address];
if (!([dictaddrssstring isEqualToString:#"(null)"]|| dictaddrssstring == nil || dictaddrssstring.length <4))
{
[dict setValue:dict_address forKey:#"Addresses"];
}
[dict_address release];
// Getting Social Networks from address book
ABMultiValueRef social =(NSString*)ABRecordCopyValue(aSource, kABPersonSocialProfileProperty);
NSMutableDictionary *dict_social = [[NSMutableDictionary alloc]init];
if (social) {
for (int i = 0 ; i < ABMultiValueGetCount(social); i++) {
NSDictionary *socialDict = (NSDictionary *)ABMultiValueCopyValueAtIndex(social, i);
NSString *twitterid;
if ([socialDict[#"service"] isEqualToString:(NSString*)kABPersonSocialProfileServiceTwitter]) {
twitterid = (NSString*)socialDict[#"username"];
[dict_social setValue:twitterid forKey:#"Twitter"];
}
NSString *facebookid;
if ([socialDict[#"service"] isEqualToString:(NSString*)kABPersonSocialProfileServiceFacebook]) {
facebookid = (NSString*)socialDict[#"username"];
[dict_social setValue:facebookid forKey:#"Facebook"];
}
NSString *linkedinid;
if ([socialDict[#"service"] isEqualToString:(NSString*)kABPersonSocialProfileServiceLinkedIn]) {
linkedinid = (NSString*)socialDict[#"username"];
[dict_social setValue:linkedinid forKey:#"LinkedIn"];
}
NSString *gamecenterid;
if ([socialDict[#"service"] isEqualToString:(NSString*)kABPersonSocialProfileServiceGameCenter]) {
gamecenterid = (NSString*)socialDict[#"username"];
[dict_social setValue:gamecenterid forKey:#"GameCenter"];
}
NSString *myspaceid;
if ([socialDict[#"service"] isEqualToString:(NSString*)kABPersonSocialProfileServiceMyspace]) {
myspaceid = (NSString*)socialDict[#"username"];
[dict_social setValue:myspaceid forKey:#"MySpace"];
}
NSString *flickerid;
if ([socialDict[#"service"] isEqualToString:(NSString*)kABPersonSocialProfileServiceFlickr]) {
flickerid = (NSString*)socialDict[#"username"];
[dict_social setValue:flickerid forKey:#"Flicker"];
}
[socialDict release];
}
CFRelease(social);
}
NSString *dictsocialstring = [NSString stringWithFormat:#"%#",dict_social];
if (!([dictsocialstring isEqualToString:#"(null)"]|| dictsocialstring == nil || dictsocialstring.length <4))
{
[dict setValue:dict_social forKey:#"SocialNetworks"];
}
[dict_social release];
// Getting Other Fields from address book
NSMutableDictionary *dict_others = [[NSMutableDictionary alloc]init];
if (!([company_name isEqualToString:#"(null)"]||company_name == nil || company_name.length ==0)) {
[dict_others setValue:company_name forKey:#"CompanyName"];
}
if (!([job_title isEqualToString:#"(null)"]||job_title == nil || job_title.length ==0)) {
[dict_others setValue:job_title forKey:#"JobTitle"];
}
if (!([birthday isEqualToString:#"1-1-2001"]||[birthday isEqualToString:#"(null)"]||birthday == nil || birthday.length ==0)) {
[dict_others setValue:birthday forKey:#"BirthDay"];
}
NSString *dictothersstring = [NSString stringWithFormat:#"%#",dict_others];
if (!([dictothersstring isEqualToString:#"(null)"]|| dictothersstring == nil || dictothersstring.length <4))
{
[dict setValue:dict_others forKey:#"OtherInfo"];
}
[dict_others release];
[dict setValue:record_id forKey:#"RecordID"];
//This NSLog(#"Value for Dict is========= %# =========",dict);
NSString *namedictionstring = [NSString stringWithFormat:#"%#",dict_name];
NSString *emaildictstring = [NSString stringWithFormat:#"%#",dict_email];
NSString *phonedictstring = [NSString stringWithFormat:#"%#",dict_phone];
// NSLog(#"name dict length= %d email dict length= %d phone dict length = %d",namedictionstring.length,emaildictstring.length,phonedictstring.length);
// [array_addressbook addObject:dict];
if (phonedictstring.length >3) {
[arraytemp addObject:dict];
}
else
{
if (emaildictstring.length>3 && namedictionstring.length) {
[arraytemp addObject:dict] ;
}
else
{
NSLog(#"Contact is not valid= %#",dict_name);
}
}
[dict_name release];
[dict_phone release];
[dict_email release];
[dict release];
[company_name release];
[job_title release];
/* CFRelease(firstName);
CFRelease(midname);
CFRelease(lastName);
CFRelease(jobtitle);
CFRelease(birthday_c);
CFRelease(companyname);*/
// }
}
}
NSString *json_array_string = [arraytemp JSONRepresentation];
[IntoochUtil current_function_name:#"Inside getcontactdetails - End"];
NSLog(#"Json String To send= %#",json_array_string);
if (allSources) {
CFRelease(allSources);
}
if (addressBook) {
CFRelease(addressBook);
}
}
else
{
NSLog(#"This is empty.... nothing to perform");
NSMutableArray *arrayofdeletedpeoples= [[NSMutableArray alloc]init];
ABAddressBookRef addressbook = ABAddressBookCreate();
CFArrayRef allPeoples= ABAddressBookCopyArrayOfAllPeople(addressbook);
NSMutableArray *arrayofrecordIDCallback = [[NSMutableArray alloc]init];
int count = CFArrayGetCount(allPeoples);
for (int i =0; i<count; i++) {
ABRecordRef aSource = CFArrayGetValueAtIndex(allPeoples,i);
NSString * recordid = [NSString stringWithFormat:#"%d",ABRecordGetRecordID(aSource)];
[arrayofrecordIDCallback addObject:recordid];
}
NSLog(#"arrayofrecordIDCallback = %# and count= %d",arrayofrecordIDCallback,[arrayofrecordIDCallback count]);
NSMutableArray *arrayofallrecordids = [userdefs valueForKey:#"AllRecordIDSBackGround"];
NSLog(#"arrayofallrecordids= %# and count= %d",arrayofallrecordids,[arrayofallrecordids count]);
for (int i=0 ; i< [arrayofallrecordids count]; i++) {
if ([arrayofrecordIDCallback containsObject:[arrayofallrecordids objectAtIndex:i]]) {
}
else
{
[arrayofdeletedpeoples addObject:[arrayofallrecordids objectAtIndex:i]];
}
}
NSLog(#"Array of deleted peoples= %#",arrayofdeletedpeoples);
[arrayofdeletedpeoples release];
[arrayofallrecordids release];
[arrayofrecordIDCallback release];
if (allPeoples) {
CFRelease(allPeoples);
}
if (addressbook) {
CFRelease(addressbook);
}
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
[changedrecords release];
[userdefs release];
[arraytemp release];
NSLog(#"Ends Process");
NSTimeInterval timeStamptwo = [[NSDate date] timeIntervalSince1970];
NSNumber *timeStamptwoObj = [NSNumber numberWithDouble: timeStamptwo];
NSLog(#"Time Difference= %d",[timeStamptwoObj intValue]-[timeStamponeobj intValue]);
}
Yes thats true that you can not get specific notification on Phonebook changes. You will need to synch your data when callback notification fire.
void MyAddressBookExternalChangeCallback (ABAddressBookRef ntificationaddressbook,CFDictionaryRef info,void *context)
{
NSLog(#"Changed Detected......");
// synch data
}
The apps like viber, tango, watsapp also does the same thing but in Background thread.
If the app is terminated, then you will need to synch data when app gets started like in didFinishLaunchingWithOptions.
As far I know, you can't tell what are the difference - I bumped to this problem myself.
My solution was:
I had a local Core Data based address book, and every time I got this notification, I ran a background thread that synchronised the data, and notify in the end all the changes.
During the first time, I am dumping all the data in local database using sqlite, then on external change I am dumping new data from contacts database in dummy db in sqlite, then firing big long compare query based on what has changed. In our case, main focus was on phone numbers and name. Once you get change update that in your contacts db in sqlite.
I think even whatsapp is doing something similar. I checked the time taken to reflect the changes by whatsapp and our app almost are similar. Actually, my app is 2 secs faster than whatsapp in terms of reflecting changes.
Hope this will help.
NSMutableArray *nameArray = [[NSMutableArray alloc] init];
[nameArray addObject:#"abc"];
[nameArray addObject:#"cdf"];
[nameArray addObject:#"jkl"];
//Use a for each loop to iterate through the array
for (NSString *s in nameArray) {
NSLog(#"value is %#", s);
}
The above code shows all the values of nameArray. But I want to assign all those values to these NSString:
NSString *a;
NSString *b;
NSString *cd;
An aray can have 1 or more elements but not more than 5.
Actually,I have 5 buttons, each button on click will add a NSString value(values are: f1,f2,f3,f4 and f5) to NSMutableArray. Now its upto the user if he clicks 2 buttons or 3 or 5 in a day. Now all these values will be saved in NSMutableArray (which can be 1 or 2 but not more than 5). That NSMutableArray will be saved in NSUserDefaults. This NSMutableArray than will be used in another view where I have some UIImageView (1,2,3,4 and 5). Now when I will get the string values from that Array(f1,f2,f3). If it is f1 then an image will be assigned to UIImage 1 if it is f3 then to image 3 and so on.
How to achieve this?
I would do something like that:
NSArray *array = [NSArray arrayWithObjects:#"A", #"B", #"C", #"D", #"E", nil];
NSString *a = nil, *b = nil, *c = nil, *d = nil, *e = nil;
NSUInteger idx = 0;
for ( NSString *string in array )
{
switch ( idx++ ) {
case 0: a = string; break;
case 1: b = string; break;
case 2: c = string; break;
case 3: d = string; break;
case 4: e = string; break;
}
}
As at least one element will be there in your array:
NSString *a = (NSString *)[nameArray objectAtIndex:0];
As maximum will be five elements:
for(int i = 1;i<[array count];i++)
{
if(i == 1)
{
NSString *b = (NSString *)[nameArray objectAtIndex:1];
}
else if(i == 2)
{
NSString *c = (NSString *)[nameArray objectAtIndex:2];
}
else if(i == 3)
{
NSString *d = (NSString *)[nameArray objectAtIndex:3];
}
else if(i == 4)
{
NSString *e = (NSString *)[nameArray objectAtIndex:4];
}
}
a = [nameArray objectAtIndex:0];
b = [nameArray objectAtIndex:1];
cd = [nameArray objectAtIndex:2];
If you want to put your array elements into separate variables with distinct names, there is no automation in objective c (unlike say, in JavaScript) since it is a compiled and not a interpreted language. Something similar you can achieve with NSDictionary, i.e. to "index" objects with strings or whatever type you want.
You could go on with a simple C array of 5 unsigned chars, where the index of the array would point to your data. Something like this:
unsigned char nameArray[5] = {0, 0, 0, 0, 0};
// if you want to set the 3rd variable, use:
nameArray[2] = 1;
// to query:
if (nameArray[2]) { ... }
// When you need to save it to NSUserDefaults, wrap it into an NSData:
NSData* nameData = [NSData dataWithBytes:nameArray length:sizeof(nameArray)];
[[NSUserDefaults standardUserDefaults] setObject:nameData forKey:#"myKey"];
// To query it:
NSData* nameData = [[NSUserDefaults standardUserDefaults] dataForKey:#"myKey"];
const unsigned char* nameArray2 = [nameData bytes];
unsigned char second = nameArray2[2];
EDITED: corrected array access
If there is at most five options the easiest way to do it is with an if-else-if chain.
NSString *label1 = nil, *label2 = nil, *label3 = nil, *label4 = nil, *label5 = nil;
for (NSString *s in nameArray) {
if (label1 == nil)
label1 = s;
else if (label2 == nil)
label2 = s;
else if (label3 == nil)
label3 = s;
else if (label4 == nil)
label4 = s;
else if (label5 == nil)
label5 = s;
}
NSString *str1=nil;
NSString *str2=nil;
NSString *str3=nil;
NSString *str4=nil;
for (LocationObject *objLoc in arrLocListFirstView)
{
if (objLoc.isLocSelected)
{
for (LocationObject *obj in arrLocListFirstView)
{
if (str1 == nil)
str1 = objLoc.strLoc;
else if (str2 == nil)
str2 = obj.strLoc;
else if (str3 == nil)
str3 = obj.strLoc;
else if (str4 == nil)
str4 = obj.strLoc;
}
NSString *combined = [NSString stringWithFormat:#"%#,%#,%#,%#", str1,str2,str3,str4];
lblLocation.text=combined; (displaying text in UILabel)
}
}
I am having a memory leak, and can't figure it out. Basically, what I am doing here is pulling in images to a Mutable Dictionary and returning the dictionary.
- (NSMutableDictionary *)loadImageDataGroup:(NSUInteger)index {
int lim = 5;
int sta = 0;
if (index > lim) {
sta = index-lim;
}
int fin = (((lim*2)+1)+sta);
if (fin > [self imageCount]) {
sta = ([self imageCount]-((lim*2)-1));
fin = [self imageCount];
}
BOOL firstTime = NO;
if ([imagesLoaded count] == 0) {
firstTime = YES;
}
NSMutableDictionary *tempDict = [[[NSMutableDictionary alloc] init] autorelease];
for (int i = sta; i < fin; i++) {
NSString *imageName = [self imageNameAtIndex:i];
if ([imagesLoaded valueForKey:imageName] == nil) {
// This is the Memory Leak...at least that is where Leaks is pointing me.
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageName]];
[tempDict setObject:imageData forKey:imageName];
} else {
[tempDict setObject:[imagesLoaded valueForKey:imageName] forKey:imageName];
}
}
return tempDict;
}
The leak is in the NSData = *imageData spot, and when I pull it up in leaks, it points to NSConcreteData.
Looking at your code, I'm not seeing anything obviously wrong. Be aware that Instruments can produce false positives.
See the following:
Instruments showing false leak?
i'm trying to build a function that will tell me the range of a string at an occurrence.
For example if I had the string "hello, hello, hello", I want to know the range of hello at it's, lets say, third occurrence.
I've tried building this simple function, but it doesn't work.
Note - the top functions were constructed at an earlier date and work fine.
Any help appreciated.
- (NSString *)stringByTrimmingString:(NSString *)stringToTrim toChar:(NSUInteger)toCharacterIndex {
if (toCharacterIndex > [stringToTrim length]) return #"";
NSString *devString = [[[NSString alloc] init] autorelease];
for (int i = 0; i <= toCharacterIndex; i++) {
devString = [NSString stringWithFormat:#"%#%#", devString, [NSString stringWithFormat:#"%c", [stringToTrim characterAtIndex:(i-1)]]];
}
return devString;
[devString release];
}
- (NSString *)stringByTrimmingString:(NSString *)stringToTrim fromChar:(NSUInteger)fromCharacterIndex {
if (fromCharacterIndex > [stringToTrim length]) return #"";
NSString *devString = [[[NSString alloc] init] autorelease];
for (int i = (fromCharacterIndex+1); i <= [stringToTrim length]; i++) {
devString = [NSString stringWithFormat:#"%#%#", devString, [NSString stringWithFormat:#"%c", [stringToTrim characterAtIndex:(i-1)]]];
}
return devString;
[devString release];
}
- (NSRange)rangeOfString:(NSString *)substring inString:(NSString *)string atOccurence:(int)occurence {
NSString *trimmedString = [inString copy]; //We start with the whole string.
NSUInteger len, loc, oldLength;
len = 0;
loc = 0;
NSRange tempRange = [string rangeOfString:substring];
len = tempRange.length;
loc = tempRange.location;
for (int i = 0; i != occurence; i++) {
NSUInteger endOfWord = len+loc;
trimmedString = [self stringByTrimmingString:trimmedString fromChar:endOfWord];
oldLength += [[self stringByTrimmingString:trimmedString toChar:endOfWord] length];
NSRange tmp = [trimmedString rangeOfString:substring];
len = tmp.length;
loc = tmp.location + oldLength;
}
NSRange returnRange = NSMakeRange(loc, len);
return returnRange;
}
Instead of trimming the string a bunch of times (slow), just use rangeOfString:options:range:, which searches only within the range passed as its third argument. See Apple's documentation.
So try:
- (NSRange)rangeOfString:(NSString *)substring
inString:(NSString *)string
atOccurence:(int)occurence
{
int currentOccurence = 0;
NSRange rangeToSearchWithin = NSMakeRange(0, string.length);
while (YES)
{
currentOccurence++;
NSRange searchResult = [string rangeOfString: substring
options: NULL
range: rangeToSearchWithin];
if (searchResult.location == NSNotFound)
{
return searchResult;
}
if (currentOccurence == occurence)
{
return searchResult;
}
int newLocationToStartAt = searchResult.location + searchResult.length;
rangeToSearchWithin = NSMakeRange(newLocationToStartAt, string.length - newLocationToStartAt);
}
}
You need to rework the whole code. While it may seem to work, it's poor coding and plain wrong, like permanently reassigning the same variable, initializing but reassigning one line later, releasing after returning (which will never work).
For your question: Just use rangeOfString:options:range:, and do this the appropriate number of times while just incrementing the starting point.
Been running instruments on my app. Its says i am leaking 864bytes & 624bytes from 2 NSCFString and the library responsible is Foundation.
So that leads me to believe thats its not a leak caused by me? Or is it?
Here is the offending method according to instruments. It seems to be a
substringWithRange
that is leaking.
-(void) loadDeckData
{
deckArray =[[NSMutableArray alloc] init];
NSString* path = [[NSBundle mainBundle] pathForResource:#"rugby" ofType:#"txt"
inDirectory:#""];
NSString* data = [NSString stringWithContentsOfFile:path encoding:
NSUTF8StringEncoding error: NULL];
NSString *newString = #"";
NSString *newline = #"\n";
NSString *comma = #",";
int commaCount = 0;
int rangeCount = 0;
NSString *nameHolder = #"";
NSString *infoHolder = #"";
NSMutableArray *statsHolder = [[NSMutableArray alloc] init];
for (int i=0; i<data.length; i++)
{
newString = [data substringWithRange:NSMakeRange(i, 1)];
if ([newString isEqualToString: comma]) //if we find a comma
{
if (commaCount == 0)// if it was the first comma we are parsing the
NAME
{
nameHolder = [data substringWithRange:NSMakeRange(i-
rangeCount, rangeCount)];
}
else if (commaCount == 1)//
{
infoHolder = [data substringWithRange:NSMakeRange(i-
rangeCount, rangeCount)];
//NSLog(infoHolder);
}
else // if we are on to 2nd,3rd,nth comma we are parsing stats
{
NSInteger theValue = [[data
substringWithRange:NSMakeRange(i-rangeCount,rangeCount)]
integerValue];
NSNumber* boxedValue = [NSNumber
numberWithInteger:theValue];
[statsHolder addObject:boxedValue];
}
rangeCount=0;
commaCount++;
}
else if ([newString isEqualToString: newline])
{
NSInteger theValue = [[data substringWithRange:NSMakeRange(i-
rangeCount,rangeCount)] integerValue];
NSNumber* boxedValue = [NSNumber numberWithInteger:theValue];
[statsHolder addObject:boxedValue];
commaCount=0;
rangeCount=0;
Card *myCard = [[Card alloc] init];
myCard.name = nameHolder;
myCard.information = infoHolder;
for (int x = 0; x < [statsHolder count]; x++)
{
[myCard.statsArray addObject:[statsHolder
objectAtIndex:x]];
}
[deckArray addObject:myCard];
[myCard autorelease];
[statsHolder removeAllObjects];
}
else
{
rangeCount++;
}
}
[statsHolder autorelease];
}
Thanks for your advice.
-Code
As Gary's comment suggests this is very difficult to diagnose based on your question.
It's almost certainly a leak caused by you however, I'm afraid.
If you go to the View menu you can open the Extended Detail. This should allow you to view a stack trace of exactly where the leak occurred. This should help diagnose the problem.
When to release deckArray? If deckArray is a class member variable and not nil, should it be released before allocate and initialize memory space?