application crash on ios 11 when working with AddressBook framework? - ios

I'm working on voip app. fetch contact work but when I want to make call, app crash.
[ABSAddressBook contacts]: message sent to deallocated instance
0x1c1478180 warning: could not execute support code to read
Objective-C class data in the process. This may reduce the quality of
type information available.
crash happen in this line.
NSArray *lContacts = (NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);
- (void) checkContactListForJogvoiceList {
// if (![BundleLocalData isLoadingJogvoiceContactList]) {
// [BundleLocalData setLoadingJogvoiceContactList:true];
int maxPhoneNumberSubmit = 200;
NSArray *lContacts = (NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);
NSMutableDictionary *phoneNumberContactsDictionary = [[NSMutableDictionary alloc] init];
NSMutableArray *allPhoneNumberList = [[NSMutableArray alloc] init];
for (id lPerson in lContacts) {
ABRecordRef person = (ABRecordRef)lPerson;
NSArray *phoneList = [AppUtil getContactPhoneList:person];
for (NSString* phoneNumber in phoneList) {
NSMutableArray* contactList = phoneNumberContactsDictionary[phoneNumber];
if (!contactList) {
contactList = [[NSMutableArray alloc] init];
}
[contactList addObject:(__bridge ABRecordRef)person];
phoneNumberContactsDictionary[phoneNumber] = contactList;
}
[allPhoneNumberList addObjectsFromArray:phoneList];
if (allPhoneNumberList.count >= maxPhoneNumberSubmit) {
[self checkContactList:allPhoneNumberList phoneNumberContactsDictionary:phoneNumberContactsDictionary];
}
}
if (allPhoneNumberList.count > 0) {
[self checkContactList:allPhoneNumberList phoneNumberContactsDictionary:phoneNumberContactsDictionary];
}
// ABAddressBookUnregisterExternalChangeCallback(addressBook, sync_address_book, self);
// [BundleLocalData setLoadingJogvoiceContactList:false];
// }
}
probably because AddressBook framework deprecate in ios9? am I right?
I don’t want to use Contacts framework.

According to Apple doc ABAddressBookCreateWithOptions using address book function ABAddressBookCreateWithOptions returns NULL if no permission granted from user.
On iOS 6.0 and later, if the caller does not have access to the Address Book database:
For apps linked against iOS 6.0 and later, this function returns NULL.
For apps linked against previous version of iOS, this function returns an empty read-only database.
You should follow this article How do I correctly use ABAddressBookCreateWithOptions method in iOS 6?

Related

Address Book UI Framework deprecated methods

I am working on an existing objective c project , While reading Address Book UI Framework Reference for iOS i found the below classes have deprecated in iOS 9 .
( ABUnknownPersonViewController , ABPersonViewController , ABPeoplePickerNavigationController, ABNewPersonViewController )
What is the replacement of this .? Where i can find some document related this . any help appreciated . Thanks in advance .
The AddressBookUI framework has been deprecated in iOS 9, so better you should use ContactsUI Framework.
It has many new features including all the features of AddressBookUI framework.
So, in case if you are targeting the iOS 9 specifically then you should go for ContactsUI Framework.
To check that AddressBookUI framework is available for specific iOS version you can do the following:
if ([CNContactStore class]) {
CNContactStore *store = [CNContactStore new];
//...
} else {
// Fallback to old framework
}
Here is the complete code for that:
- (void) contactScan
{
if ([CNContactStore class]) {
//ios9 or later
CNEntityType entityType = CNEntityTypeContacts;
if( [CNContactStore authorizationStatusForEntityType:entityType] == CNAuthorizationStatusNotDetermined)
{
CNContactStore * contactStore = [[CNContactStore alloc] init];
[contactStore requestAccessForEntityType:entityType completionHandler:^(BOOL granted, NSError * _Nullable error) {
if(granted){
[self getAllContact];
}
}];
}
else if( [CNContactStore authorizationStatusForEntityType:entityType]== CNAuthorizationStatusAuthorized)
{
[self getAllContact];
}
}
}
-(void)getAllContact
{
if([CNContactStore class])
{
//iOS 9 or later
NSError* contactError;
CNContactStore* addressBook = [[CNContactStore alloc]init];
[addressBook containersMatchingPredicate:[CNContainer predicateForContainersWithIdentifiers: #[addressBook.defaultContainerIdentifier]] error:&contactError];
NSArray * keysToFetch =#[CNContactEmailAddressesKey, CNContactPhoneNumbersKey, CNContactFamilyNameKey, CNContactGivenNameKey, CNContactPostalAddressesKey];
CNContactFetchRequest * request = [[CNContactFetchRequest alloc]initWithKeysToFetch:keysToFetch];
BOOL success = [addressBook enumerateContactsWithFetchRequest:request error:&contactError usingBlock:^(CNContact * __nonnull contact, BOOL * __nonnull stop){
[self parseContactWithContact:contact];
}];
}
}
- (void)parseContactWithContact :(CNContact* )contact
{
NSString * firstName = contact.givenName;
NSString * lastName = contact.familyName;
NSString * phone = [[contact.phoneNumbers valueForKey:#"value"] valueForKey:#"digits"];
NSStrubg * email = [contact.emailAddresses valueForKey:#"value"];
NSArray * addrArr = [self parseAddressWithContac:contact];
}
- (NSMutableArray *)parseAddressWithContac: (CNContact *)contact
{
NSMutableArray * addrArr = [[NSMutableArray alloc]init];
CNPostalAddressFormatter * formatter = [[CNPostalAddressFormatter alloc]init];
NSArray * addresses = (NSArray*)[contact.postalAddresses valueForKey:#"value"];
if (addresses.count > 0) {
for (CNPostalAddress* address in addresses) {
[addrArr addObject:[formatter stringFromPostalAddress:address]];
}
}
return addrArr;
}
Just make sure that you ask the permission to read the contacts from device.
Reference link: https://gist.github.com/willthink/024f1394474e70904728
Updated:
For replacement for AddressBookUI you need to use CNContactPickerViewController. You can check the delegate methods which can be used to pickup the one or multiple contacts at a time.
This will present a inbuilt UIViewController with all the contacts and you need to implement the delegate methods of it!
To select one contact:
contactPicker:didSelectContact:
To select multiple (New Feature):
contactPicker:didSelectContacts:
CNContactPickerDelegate reference: https://developer.apple.com/library/ios/documentation/ContactsUI/Reference/CNContactPickerDelegate_Protocol/
Apple has introduced new framework for this for iOS9 and above please fine below link Link
Edit:
one more link :Link2

Pass message to Apple Watch using MMWormhole

I'm developing an Apple Watch App, and I need to notify the watch when certain changes occur in the parent application. I'm using the MMWormhole library found on GitHub, but I'm having trouble passing messages from the phone to the watch. Here is my code, do you have any ideas on why this is happening?
My main viewController code looks like this
ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
self.wormhole = [[MMWormhole alloc] initWithApplicationGroupIdentifier:#"com.mycompany.myapp"
optionalDirectory:#"wormhole"];
NSString *myString = [[NSString alloc] initWithFormat:#"Test String"];
[self.wormhole passMessageObject:#{#"string" : myString}
identifier:#"messageIdentifier"];
My InterfaceController from my WatchkitExtension looks like this:
InterfaceController.m
- (void)awakeWithContext:(id)context {
[super awakeWithContext:context];
// Initialize the wormhole
self.wormhole = [[MMWormhole alloc] initWithApplicationGroupIdentifier:#"com.mycompany.myapp"
optionalDirectory:#"wormhole"];
// Obtain an initial value for the selection message from the wormhole
id messageObject = [self.wormhole messageWithIdentifier:#"messageIdentifier"];
NSString *string = [messageObject valueForKey:#"string"];
if (string != nil) {
NSLog(string);
[myLabel setText:string];
}
// Listen for changes to the selection message. The selection message contains a string value
// identified by the selectionString key. Note that the type of the key is included in the
// name of the key.
[self.wormhole listenForMessageWithIdentifier:#"messageIdentifier" listener:^(id messageObject) {
NSString *string = [messageObject valueForKey:#"string"];
if (string != nil) {
[self.myLabel setText:string];
}
}];
}
Thank you!
Is "com.mycompany.myapp" the real value you use in the app? Because group identifiers have to start with group..
If you use a wrong group identifier everything fails because the containerURLForSecurityApplicationGroupIdentifier call inside MMWormhole returns nil. Unfortunately the developers of MMWormhole didn't do any checks or asserts to make sure that the shared group identifier is correct.
So I would recommend to stop concentrating on MMWormhole for a minute. Instead add this code early in your code (e.g. applicationDidFinishLaunching) to verify that your container identifier is correct:
NSFileManager *fileManager = [[NSFileManager alloc] init];
NSURL *appGroupContainer = [fileManager containerURLForSecurityApplicationGroupIdentifier:#"group.com.mycompany.myapp"];
if (!appGroupContainer) {
NSLog(#"group identifier incorrect, or app groups not setup correctly");
}
This will tell you if your app group setup is incorrect.
I'm not sure how far you are into setting up app groups, but you have to use the group identifier you used in the App Groups capabilities section of your project.

Trouble Using NSKeyedUnarchiver during init

I have created a FacebookManager singleton that gets called on a background thread when my app launches. Everything is working just fine with the facebook manager the singleton, the app etc. However, when the app first launches, it is quite a few seconds before it is useful because the facebook manager has not finished doing its thing yet. So what I want to do, is use NSKeyedArchiver to save the facebookManager and all its dictionaries so that upon launch, the app has a navigable interface while the facebook data is being updated in the background. Make sense?
All within the FacebookManager.m, first, when the manager is done updating the friends dictionaries, etc, I call the method that saves the data:
- (BOOL)saveFacebookData
{
// returns success or failure
NSString *path = [self archivePath]; // just a helper method
return [NSKeyedArchiver archiveRootObject:self toFile:path];
}
Then in init, I am trying this, which doesn't seem to work. :
-(id)init
{
self = [super init];
NSString *path = [self archivePath];
self = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
// If the manager hadn't been saved previously, create a new new one
if (!self) {
if (_idsByNameDict == nil) {
_idsByNameDict = [[NSMutableDictionary alloc] init];
}
if (_photosByNameDict == nil) {
_photosByNameDict = [[NSMutableDictionary alloc] init];
}
if (_installedByNameDict == nil) {
_installedByNameDict = [[NSMutableDictionary alloc] init];
}
if (_allFriendsArray == nil) {
_allFriendsArray = [[NSArray alloc] init];
}
basicPermissions = NO;
extendedPermissions = NO;
// Create synchronous dispatch queue for all facebook activity
if (_facebookUpdateQueue == nil) {
_facebookUpdateQueue = dispatch_queue_create("com.facebookUpdateQueue", NULL);
}
}
I think my general strategy is sound but I am tripping over how to actually grab the archived version of the manager during init! Any advice?
Your class needs to implement <NSCoding> and both of its methods encodeWithCoder: to archive all of your property values and initWithCoder: to in archive them. Make sure to call super in the implementations. Generally, the class using the archived class would know about the archiving but you could hide that knowledge in init by using initForReadingWithData: to create your NSKeyedUnarchiver and then calling [self initWithCoder:...];.

Bug after app store deployment, unable to repro in Ad Hoc deployment

My application is involves users saving data which I store using NSCoding/NSKeyedArchiver. I supply the user with sample data objects on the first run of the app.
The expected behavior happens during regular testing, as well as through ad hoc deployment. Unfortunately, a significant bug happens when the app is downloaded through the app store.
What other environmental considerations might there be so I can reproduce (and then fix) the issue in my regular testing?
Expected behavior:
A new user may add/edit data objects in addition to the current ones. (A classic CRUD scenario).
Actual behavior:
If the user's first action is to save a new object, all of the previously loaded sample objects disappear (the elusive bug).
However, If the user's first action is to edit, then all of objects persist as expected, and the user can add additional ones without issue.
Thanks for the help.
EDIT
In my most recent testing, I switched the Build Configuration to release in the "Run " scheme.
http://i.imgur.com/XNyV6.png
App Delegate, which correctly initializes app
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.dataArray = nil;
self.dataArray = [AppDelegate getArray];
if (self.dataArray == nil) {
self.dataArray = [[NSMutableArray alloc] init];
}
//First run of the app
if (dataArray.count == 0) {
//Add sample data to array
//Save array
NSString *path = [AppDelegate getDocPath];
[NSKeyedArchiver archiveRootObject:self.dataArray toFile:path];
}
}
+(NSString *) getDocPath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
NSString *tempDocPath = [documentsPath stringByAppendingPathComponent:#"FilePath.dat"];
return tempDocPath;
}
+(NSMutableArray *)getArray {
return [[NSKeyedUnarchiver unarchiveObjectWithFile:[AppDelegate getDocPath]] mutableCopy];
}
Object creation, which deletes preloaded data if data hasn't been edited
-(void)viewDidLoad {
tempArray = nil;
tempArray = [NSKeyedUnarchiver unarchiveObjectWithFile:[AppDelegate getDocPath]];
if (tempArray == nil) {
tempArray = [[NSMutableArray alloc] init];
}
}
-(void)saveObject {
[tempArray addObject:createdData];
[tempArray sortUsingSelector:#selector(compare:)];
NSString *path = [AppDelegate getDocPath];
[NSKeyedArchiver archiveRootObject:tempArray toFile:path];
AppDelegate *dg = (AppDelegate *)[[UIApplication sharedApplication] delegate];
dg.dataArray = tempArray;
}
I am not sure how to solve your current problem (without looking at the code), but here's how you can avoid it in the future:
Make sure that the build you submit to the app store is the ad-hoc build you have QA'd, but signed with an app store provisioning profile.
Two advantages:
1) You should be able to repro the same bug on the adhoc and appstore build
2) dSym for both these are the same. So, you dont have to wait to get the AppStore crash logs before you can dig in and see what's happening.
I guess while saving the new object, you are not appending it to the existing data. You might be over-writing the previously created file. You should access the previous file and append the new data to the previous file. Sharing code would help to point out where you are going wrong.
EDIT: Replace the following code and check if its still showing the same behaviour
-(void)viewDidLoad {
tempArray = nil;
tempArray = [NSKeyedUnarchiver unarchiveObjectWithFile:[AppDelegate getDocPath]mutableCopy];
if (tempArray == nil) {
NSLog(#"tempArray is nil"); //if tempArray doesn't get initialized by the file contents
tempArray = [[NSMutableArray alloc] init];
}
}

How do I fix this leak?

Analyzer keeps saying that I have a leak in the line with the * at the beginning and end, how would I fix this leak so it gets rid of the warning?
+ (void)flushOfflineQueue
{
// TODO - if an item fails, after all items are shared, it should present a summary view and allow them to see which items failed/succeeded
// Check for a connection
if (![self connected])
return;
// Open list
NSMutableArray *queueList = [self getOfflineQueueList];
// Run through each item in the quietly in the background
// TODO - Is this the best behavior? Instead, should the user confirm sending these again? Maybe only if it has been X days since they were saved?
// - want to avoid a user being suprised by a post to Twitter if that happens long after they forgot they even shared it.
if (queueList != nil)
{
SHK *helper = [self currentHelper];
if (helper.offlineQueue == nil)
***helper.offlineQueue = [[NSOperationQueue alloc] init];***
SHKItem *item;
NSString *sharerId, *uid;
for (NSDictionary *entry in queueList)
{
item = [SHKItem itemFromDictionary:[entry objectForKey:#"item"]];
sharerId = [entry objectForKey:#"sharer"];
uid = [entry objectForKey:#"uid"];
if (item != nil && sharerId != nil)
[helper.offlineQueue addOperation:[[[SHKOfflineSharer alloc] initWithItem:item forSharer:sharerId uid:uid] autorelease]];
}
// Remove offline queue - TODO: only do this if everything was successful?
[[NSFileManager defaultManager] removeItemAtPath:[self offlineQueueListPath] error:nil];
}
}
Thanks!
When you use properties they will often perform the proper memory management. In your situation you need to autorelease the class you set.
helper.offlineQueue = [[[NSOperationQueue alloc] init] autorelease];

Resources