iOS MKMapItem - how get access to all members? - ios

I am using MapKit to do a local search which returns one or more MKMapItem objects. But there are members of the object I can see in the debugger but I can't access. The one I particularly need is UID.
I have tried item.placemark, but it does not let me access the UID. This seems like it should be really simple. What am I missing here?
This does not work:
NSString *uid = item.placemark.UID
This does not work:
NSDictionary *mapItemDictionary = (NSDictionary *)item;
NSString *uid = [mapItemDictionary objectForKey:#"UID"];
But the debugger command po item shows me all the members of the object:
Name: Shell CurrentLocation: 0 Place: <GEOPlace: 0x17014e650>
{
address = {
business = (
{
**UID = 2478578482074921045**;
URL = "www.shell.com";
canBeCorrectedByBusinessOwner = 1;
name = Shell;
source = (
{
"source_id" = A3H0281540;
"source_name" = "acxiom_us";
},
{
"source_id" = 2276257;
"source_name" = localeze;
}
);
telephone = "+14803968213";
}
);
Any help with this would be appreciated. Here is the code I'm using:
MKLocalSearch *localSearch = [[MKLocalSearch alloc] initWithRequest:request];
[localSearch startWithCompletionHandler:^(MKLocalSearchResponse *response, NSError *error)
{
[response.mapItems enumerateObjectsUsingBlock:^(MKMapItem *item, NSUInteger idx, BOOL *stop)
{
MKPlacemark *placemark = (MKPlacemark *)item.placemark;
NSDictionary *addressDict = placemark.addressDictionary;
NSArray *businessArray = addressDict[#"business"];// businessArray is NIL
NSString *uid=nil;
if (businessArray != nil && businessArray.count >0) {
NSDictionary *businessDict=businessArray[0];
uid=businessDict[#"UID"];
}
NSLog(#"UID is %#",uid);
}];

Ok, so after a lot of digging it seems that the information is in a couple of private objects. The "place" property is a GEOPlace, and this has a property, business, which is an array that contains a GEOBusiness object. Since this is private data you cannot access it directly via properties, but you can get it via key-value encoding. The following code extracts the UID -
[response.mapItems enumerateObjectsUsingBlock:^(MKMapItem *item, NSUInteger idx, BOOL *stop) {
NSValue *place = [item valueForKey:#"place"];
NSArray *businessArray = (NSArray *)[place valueForKey:#"business"];
NSNumber *uid=nil;
if (businessArray != nil && businessArray.count >0) {
id geobusiness=businessArray[0];
uid=[geobusiness valueForKey:#"uID"];
}
NSLog(#"UID is %#",[uid stringValue]);
}];
As this is private data structures there is no guarantee that it won't change. I am also unsure whether the App store validation process will flag this as private api access - Since it is using valueForKey I don't think it will, but there are no guarantees.

Related

NSDictionary get property within list of objects - Objective C

How would I get "Dog" from the following dictionary?
{
Id = "123";
Animal = [{
Id = "456";
Type = "Dog";
Sound = "Bark";
},
{
Id = "789";
Type = "Cat";
Sound = "Meow";
}]
}
I tried
NSString *firstAnimalType = dictionary[#"Animal"][#"Type"];
However since there are multiple animals, it can't recognize what I am trying to find. How would I get the first animal out of the list so that I can access its type? Thanks!
You can use some thing like this, first get animal object and if it exists then find its type from it
NSDictionary *firstAnimal = [dictionary[#"Animal"] firstObject];
if(firstAnimal) //in case your firstAnimal is Empty or it may be nil then may create any unwanted issues in further code so just check it first.
{
NSString *firstAnimalType = firstAnimal[#"Type"];
}
Enumeration method will help and you can stop when and where you want
[myDict[#"Animal"] enumerateObjectsUsingBlock:^(id _Nonnull objList, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(#"%#",objList[#"Type"]);
*stop = YES; //You can stop where you want
}];
You can simply access the first element of the array with castings and get its Type value.
NSString *firstAnimalType = ((NSDictionary *)[((NSArray *)dictionary[#"Animal"]) objectAtIndex: 0])[#"Type"];
for (NSDictionary *animal in dictionary[#"Animal"]) {
NSString *type = animal[#"Type"];
if ([type isKindOfClass:[NSString class]] && [type isEqualToString:#"Dog"]) {
// Dog found
}
}
Here you go:
NSArray *aryFinalAni = [dicMain valueForKey:#"Animal"];
NSArray *aryType = [aryFinalAni valueForKeyPath:#"Type"];
if([aryType containsObject:#"Dog"])
{
int indexOfDog = (int)[aryType indexOfObject:#"Dog"];
NSMutableDictionary *dicDog = [aryFinalAni objectAtIndex:indexOfDog];
NSLog(#"%#",dicDog);
}
else
{
NSLog(#"There is no Dog found.");
}
Try this code:
{
Id = "123";
Animal = [{
Id = "456";
Type = "Dog";
Sound = "Bark";
},
{
Id = "789";
Type = "Cat";
Sound = "Meow";
}]
}
1> NSArray *items = dictionary["Animal"];
2>
NSPredicate *predicate1 = [NSPredicate predicateWithFormat: #"Type CONTAINS[cd] %#", "Dog"];
NSArray *arrData = [items filteredArrayUsingPredicate:predicate1];
if arrData.count > 0 {
dictionary = [arrData objectAtIndex:0];
}
Result:
{
Id = "456";
Type = "Dog";
Sound = "Bark";
}

ABAddressBookCopyArrayOfAllPeople Not returning any People

I am starting to work with the ABAddress Book and using a very simple starting point... I want to get all the entries in my address book and put it into an array. It keeps showing 0 elements.
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
NSArray *allContacts = (__bridge NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);
Doesn't the ABAddressBookCopyArrayOfAllPeople actually get all people from your contact list or did I misunderstand something.
NOTE TO ADMIN's My question was a Objective C related and not for SWIFT so this answer doesn't apply. In the recommended answer it explains why the OP's code migration from Objective C to Swift is flawed and doesn't work. My question is not. I am trying to understand why when I install this on my iPhone, which has hundreds of contacts allContacts has no items. I was trying to understand the correct way to put all my contacts into an array. Basically because of business need I am doing my own contact picker because I don't want to use Apple's built in one.
First of all you are trying to use deprecated apis. This may be why ABAddressBookCreateWithOptions doesn't work.
However, my recommendation based on your edit (where you say that you want to build your own UI) is to use the Contacts framework.
See this github gist:
#import <Contacts/Contacts.h>
#implementation ContactsScan
- (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"];
NSString * 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;
}
#end
Note: This code was not written by me. The source can be found on github.

Can't access event name from Facebook graph api response iOS

I am calling the graph api to retrieve my user's events.And I get back an NSDictionary response. My problem is that I'm trying to access the names of the events but can't figure out the correct way of doing it. Can you please help me with this?
My response :
events = {
data = (
{
id = 16245704637388667;
name = "My event name";
place = {
id = 278379712223737;
location = {
city = Beijing;
country = China;
latitude = "53.598408783333";
....
Mycode to retrieve the vent name:
if ([result objectForKey:#"data"]){
NSArray *events = [result objectForKey:#"data"];
NSString *text=#"You don't have events!";
for (NSDictionary* myevent in events) {
NSString *myeventName = [myevent objectForKey:#"name"];
NSLog(#"%#",myeventName);
}
}
Found a solution:
[[[FBSDKGraphRequest alloc] initWithGraphPath:#"me/events" parameters: #{#"fields": #"name"}]
startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {
if (!error) {
NSMutableArray* events = [result objectForKey:#"data"];
events names = [events valueForKey:#"name"];
}
}];`

See if NSDictionary key is in another NSArray

Database (
{
to = (NSString *)
from = (NSString *)
subject = (NSString *)
uid = int
body = (NSString *)
}, { ...
)
Downloaded (
{
to = (NSString *)
from = (NSString *)
subject = (NSString *)
uid = int
body = (null)
}, { ...
)
I immediately pull and load an NSArray of about 200 NSDictionay objects from my Database into my UITableView, then I download an NSArray of the same structured NSDictionary but without a body.
Q: How do I go through all 200 Downloaded NSDictionary to see if it isn't already in my Database NSArray by matching the key: "uid"?
This should do the trick:
NSArray *arrayOfNew = [arrayDownload filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"NOT (uid IN %#)", [arrayDataBase valueForKey:#"uid"]];
Tested with this sample data, if someone want to test it:
NSDictionary *dictionary0 = #{#"to":#"0",#"from":#"0",#"uid":#(0), #"body":#"0"};
NSDictionary *dictionary1 = #{#"to":#"1",#"from":#"1",#"uid":#(1), #"body":#"0"};
NSDictionary *dictionary2 = #{#"to":#"2",#"from":#"2",#"uid":#(2), #"body":#"0"};
NSDictionary *dictionary3 = #{#"to":#"3",#"from":#"3",#"uid":#(3), #"body":#"0"};
NSDictionary *dictionary4 = #{#"to":#"4",#"from":#"4",#"uid":#(2)};
NSDictionary *dictionary5 = #{#"to":#"5",#"from":#"5",#"uid":#(5)};
NSArray *arrayDataBase = #[dictionary0, dictionary1, dictionary2, dictionary3];
NSArray *arrayDownload = #[dictionary4, dictionary5];
//So the dictionary4 shouldn't be kept, and dictionary5 should be kept.
NSArray *arrayNew = [arrayDownload filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"NOT (uid IN %#)", [arrayDataBase valueForKey:#"uid"]]];
NSLog(#"arrayNew: %#", arrayNew);
Output:
arrayNew: (
{
from = 5;
to = 5;
uid = 5;
}
With this code you can iterate in two arrays called "Downloaded" and "Database" and check if their uid match. I'm not sure if you're looking for a more elegant solution.
for (NSDictionary *dictDownloaded in Downloaded) {
for (NSDictionary *dictDatabase in Database) {
if ([dictDownloaded objectForKey:#"uid"] == [dictDatabase objectForKey:#"uid"]) {
NSLog(#"Object with uid: %d is in database", [[dictDownloaded objectForKey:#"uid"] intValue]);
}
}
}
Beware of nested loops :)
If you use Arturo's example (which works!) and download 1000 messages, you will have a potential of O(n*m) = 1000*200 = 200.000 "calculation steps"
Larme's attempt is pretty elegant (I like predicates!) but it's hard to predict the time it will use for execution, because it's all encapsulated within NSPredicate.
So another attempt, based on Larme's example data, would be to use a dictionary with the uid as the key for fast lookup.
NSMutableDictionary *databaseLookupDictionary = [[NSMutableDictionary alloc]init];
databaseLookupDictionary[#(0)] = #{#"to":#"0",#"from":#"0",#"uid":#(0), #"body":#"0"};
databaseLookupDictionary[#(1)] = #{#"to":#"1",#"from":#"1",#"uid":#(1), #"body":#"0"};
databaseLookupDictionary[#(2)] = #{#"to":#"2",#"from":#"2",#"uid":#(2), #"body":#"0"};
databaseLookupDictionary[#(3)] = #{#"to":#"3",#"from":#"3",#"uid":#(3), #"body":#"0"};
/* your download code */
// example data
NSMutableArray *downloadedData = [[NSMutableArray alloc]init];
[downloadedData addObject: #{#"to":#"0",#"from":#"0",#"uid":#(3)}];
[downloadedData addObject: #{#"to":#"0",#"from":#"0",#"uid":#(4)}];
for(NSDictionary *downloadDataDict in downloadedData)
{
// will be executed for message #4
if(![databaseLookupDictionary.allKeys containsObject:downloadDataDict[#"uid"]])
{
NSLog(#"Unknown message data found: %#", downloadDataDict);
}
}
This runs in linear time (O(n)*O(1)) so you should be fine with the performance. But keep in mind: if your message database count grows, you should think about searching directly in CoreData.

Getting index of an String value within an array of array

I have an unique scenario of getting index value of an element, where the structure is
Array - within Array - within Dictionary
(
{
STS = OPEN;
"STS_ICON" = "LIGHT_GREY";
},
"Headerquarter Planning"
),
(
{
STS = INPR;
"STS_ICON" = "LIGHT_BLUE";
},
"In Process"
),
(
{
STS = COMP;
"STS_ICON" = "LIGHT_GREEN";
},
Released
),
(
{
STS = CANC;
"STS_ICON" = "LIGHT_RED";
},
"ON HOLD - Call Transfer Delay"
)
)
iN THIS Case let's say i want index of #"ON HOLD - Call Transfer Delay" string.
I tried with like this..
NSUInteger index;
if([listOfStatus containsObject: list.statusType])
{
index = [listOfStatus indexOfObject: list.statusType];
}
where list.statusType is #"ON HOLD - Call Transfer Delay". But here i am getting "index" some weird value 15744929.
Try
- (NSInteger)findIndexOfStatus:(NSString *)status
{
NSString *filePath = [[NSBundle mainBundle]pathForResource:#"Status"
ofType:#"json"];
NSData *data = [[NSData alloc]initWithContentsOfFile:filePath];
NSArray *listOfStatus = [NSJSONSerialization JSONObjectWithData:data
options:0
error:nil];
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(NSArray *evaluatedObject, NSDictionary *bindings) {
//NSDictionary *dict = evaluatedObject[0];
//return [dict[#"STS"] isEqualToString:status];
NSString *statusType = evaluatedObject[1];
return [statusType isEqualToString:status];
}];
return [listOfStatus indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
return [predicate evaluateWithObject:obj];
}];
}
You can find the index by calling
NSInteger index = [self findIndexOfStatus:#"ON HOLD - Call Transfer Delay"];
I have also commented out an option to find out if you want to use the status code.
The Status.json file

Resources