How to check contact source in CNContact swift? - ios

In Contact apps there's group like "iCloud", "yahoo", "gmail". In swift, is it possible to fetch contact from gmail source only?

Tested code. Hope it will solve your problem...
func getAppropriateName(for container: CNContainer?) -> String? {
var name = ""
if (container?.name == "Card") || container?.name == nil {
name = "iCloud"
}
else if (container?.name == "Address Book") {
name = "Google"
}
else if (container?.name == "Contacts") {
name = "Yahoo"
}
else {
name = "Facebook"
}
return name
}

iCloud/yahoo/gmail etc are CNContainer. Gmail/iCloud is of type CNContainerTypeCardDAV. So first you need to fetch all contacts, and then filter the array based on the CNContainerType of that contact. But unfortunately, we cannot identify which CardDav is it, i.e iCloud/Gmail.
Please see more details here: How do we know which CNContainer represents iCloud?

You can achieve this by looking at Contacts framework runtime headers here: https://github.com/JaviSoto/iOS10-Runtime-Headers/tree/master/Frameworks/Contacts.framework
You can call them by performSelector message. It's a bit messy, but works.
Generally what you have to do is following:
CNContactStore* store = [CNContactStore new];
// fetch accounts that sync contacts with your device (array of CNAccount)
// since CNAccount class isn't available by default, we treat it as NSObject for our puproses
NSArray* accounts = [store performSelector:#selector(accountsMatchingPredicate:error:) withObject:nil withObject:nil];
// you can iterate through this array, I just use first one for this example
NSObject* account = [accounts firstObject];
// get identifier of the account for NSPredicate we use next
NSString* accountId = [account performSelector:#selector(identifier)];
// Display name of the account (aka Yahoo, Gmail etc.)
NSString* accountName = [account performSelector:#selector(_cnui_displayName)];
// NSPredicate that help us to get corresponding CNContainer
NSPredicate* containerPredicate = [[CNContainer class] performSelector:#selector(predicateForContainersInAccountWithIdentifier:) withObject:accountId];
// Fetching CNContainer
CNContainer* container = [[store containersMatchingPredicate:containerPredicate error:nil] firstObject];
After that it's all about general usage of CNContainers.
Hope it will help.
PS. It works on iOS 10, for future versions you should check for Contacts.framework runtime changes.
PPS. I didn't check on swift, but should work either.
Sorry for my english.
Good luck :)

Related

How to spoof different carriers in iOS?

Is it possible to spoof network providers just like it is possible to spoof locations in iOS?
I have an app that will get a user's ISO country code using Core Location, however I would like a fallback for when the user doesn't authorize location services for my app.
I have a function that is called in order to set a user's country according to their carrier; see below.
- (void)carrierBasedLocationSet {
if (DefaultCountryCode && ![DefaultCountryCode isEqualToString:#"Default"]) {
////NSLog(#"Skip Carrier Based Location set : DefaultCountryCode is [%#]", DefaultCountryCode);
return;
}
/***********************************
* Set country code based on Carrier
***********************************/
CTTelephonyNetworkInfo *networkInfo = [[CTTelephonyNetworkInfo alloc] init];
Carrier = [networkInfo subscriberCellularProvider];
NSString *isoCountryCode = Carrier.isoCountryCode;
if (isoCountryCode == nil || isoCountryCode.length == 0) {
isoCountryCode = [[NSLocale currentLocale] objectForKey:NSLocaleCountryCode];
}
self.ISO_CountryCode = [isoCountryCode uppercaseString];
self.CarrierBased_ISO_Country = self.ISO_CountryCode;
}
This code works and produces US, which is where I am located. However, I want to test this out for different countries. Simply editing the product scheme to spoof a location in Australia, for example, does not give me back the AU country code and still gives me US.
Does anyone know if what I am trying to do is possible? Getting a user's location is essential to my application and I am unsure of another alternative.

How to retrieve an specific child in Firebase?

The childByAutoId would be useful if you want to save in a node multiple children of the same type, that way each children will have its own unique identifier.
pet:{
KJHBJJHB:{
name:fluffy,
owner:John Smith,
},
KhBHJBJjJ:{
name:fluffy,
owner:Jane Foster,
}
}
Therefore, once I have that uID, and I want to retrieve an specific user using the his/her uID. How do I tell Firebase that I want that specific user? Because I create it and store it in Firebase, but then to read it, don't I need to know the value of the uID? Where do I get it from?
What function do I use to retrieve for example the second user using the uID?
Thanks in advance..
in the title u ask how to get rid so:: get the new ref's key property to get the aid created
FIRDatabaseReference *ref = parent.childByAutoID
NSString *uid = ref.key
BUT thats not what you want id say, so:
to filter out all children where owner = XY which you want I think:
FIRDatabaseReference *ref = your pets node
FIRDatabaseQuery *allPets = [ref queryOrderedByChild:#"owner"];
FIRDatabaseQuery *specificPet = [allPets queryEqualToValue:#"XY"];
[specificPet observeEventType:FEventTypeChildAdded withBlock:^(FIRDataSnapshot *snapshot) {
NSDictionary *dict = snapshot.value;
NSString *key = snapshot.key;
NSLog(#"key = %# for child %#", key, dict);
}];
see: Query users by name or email address using Firebase (Swift)

Updating Firebase deletes already existing data

I'm building an app with Firebase, and I recently used this code to update a list/dictionary of "posts" inside a user dictionary. Take a look:
Firebase *baseRef = [[Firebase alloc] initWithUrl:#"https://<MY-APP>.firebaseio.com"];
NSString *userPostsPath = [NSString stringWithFormat:#"users/%#/posts", userId];
NSDictionary *postRef = #{ postId : postDescription };
[baseRef updateChildValues:#{
userPostsPath : postRef
// here I'd have a similar update
}withCompletionBlock:^(NSError *error, Firebase *ref) {
handler(error, ref);
}];
This works on the first update, but the second time I do this, all the existing posts are removed. However, changing the code to this:
Firebase *baseRef = [[Firebase alloc] initWithUrl:#"https://tonightdb.firebaseio.com"];
NSString *usersPostsPath = [NSString stringWithFormat:#"users/%#/posts/%#/", userId, postId];
[baseRef updateChildValues:#{
usersPostsPath : postDescription,
// similar update path
}withCompletionBlock:^(NSError *error, Firebase *ref) {
handler(error, ref);
}];
Updates Firebase correctly.
What is the difference between the two? How come the first didn't work?
Edit
Is it possible to do:
Firebase *baseRef = [[Firebase alloc] initWithUrl:#"https://<MY-APP>.firebaseio.com"];
NSString *newKey = [baseRef childByAutoId];
Then use that key do do an update that looks like this:
[baseRef updateChildValues:#{
[NSString stringWithFormat:#"/posts/%#/", newKey] : // something
[NSString stringWithFormat:#"/posts/%#/members/<something>", newKey] : // something
withCompletionBlock:^(NSError *error, Firebase *ref) {
handler(error, ref, task);
}];
Basically to issue multiple updates to the same path in the same request, that doesn't exist beforehand while avoiding overwrite issues
Your first example translate into an instruction to update:
"users/posts" = { "postid1": postDescription }
The second example translates to:
"users/posts/postid1" = postDescription
Being a computer program, the Firebase server takes a quite literal interpretation of the instructions you give it. It takes each update instruction and replaces the data at the path (the part before the =) with the value (the part after the =).
Knowing that, you can see that in the second example it will write the postDescription at users/posts/postid1. This will replace the existing description for that post, but that is probably what you had in mind.
The second example writes { "postid1": postDescription } at users/posts. This replaces the existing value at that location, so you're essentially replacing all existing posts with the new/updated one. That is probably not what you had in mind.
Update
If you're creating a new object and fanning out the key to multiple locations, you can make use of the fact that childByAutoId is a pure client-side operation:
let newRef = ref.childByAutoId()
let newKey = newRef.key
ref.childByAppendingPath("child1").childByAppendingPath(newKey).setValue("one")
ref.childByAppendingPath("child2").childByAppendingPath(newKey).setValue("two")

Find object by name in NSMutableArray

I have a generic person object with properties personName, lastName, and age. I am storing the user input into an NSMutableArray and I wanted to find a under by his/her name in the array. I have tried finding a bunch of different solutions but none that quite really work.
This is my main.m
#autoreleasepool {
char answer;
char locatePerson[40];
//Create mutable array to add users for retrieval later
NSMutableArray *people = [[NSMutableArray alloc] init];
do{
Person *newPerson = [[Person alloc]init];
[newPerson enterInfo];
[newPerson printInfo];
[people addObject:newPerson];
NSLog(#"Would you like to enter another name?");
scanf("\n%c", &answer);
}while (answer == 'y');
NSLog(#"Are you looking for a specific person?");
scanf("%c", locatePerson);
//This is where I need help
int idx = [people indexOfObject:]
}
This is very basic but I am new to objective-c and I wanted to try and find the user by name. The solutions I've seen have used the indexesOfObjectsPassingTest method. But I was wondering if I can't just use the indexOfObjectmethod the way I did there to locate a person by its name?
Any help is appreciated.
This is one of those hard problems you should avoid with some up-front design. If you know that you are putting things into a collection class and will need to get them out again based on some attribute (rather than by order of insertion) a dictionary is the most efficient collection class.
You can use a NSDictionary keyed with Person's name attribute. You can still iterate over all the objects but you will avoid having to search the whole collection. It can take a surprisingly long time to find a matching attribute in a NSArray! You wouldn't even have to change your Person object, just do
NSDictionary *peopleDictionary = #{ person1.name : person1, person2.name : person2 };
or add them one by one as they are created into a NSMutableArray.
You can try something like this assuming that 'name' is a property for your Person class.
NSUInteger i = 0;
for(Person *person in people) {
if([person.name isEqualToString:locatePerson]) {
break;
}
i++;
}

Postal Code Validation in iOS

I am developing an app for iOS 7 in which I want to retrieve user location details.
Now I also want postal code of user. And I want to check on only that page that postal code entered by the user is valid or not.
I want to validate for US and UK.
How to achieve this?
For India, you can use the below method to validate the pincode
-(BOOL)isValidPinCode:(NSString*)pincode {
NSString *pinRegex = #"^[0-9]{6}$";
NSPredicate *pinTest = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", pinRegex];
BOOL pinValidates = [pinTest evaluateWithObject:pincode];
return pinValidates;
}
For US, you can use
^\d{5}(-\d{4})?$
For UK, use this
^([A-PR-UWYZ]([0-9]{1,2}|([A-HK-Y][0-9]|[A-HK-Y][0-9]([0-9]|[ABEHMNPRV-Y]))|[0-9][A-HJKS-UW])\ [0-9][ABD-HJLNP-UW-Z]{2}|(GIR\ 0AA)|(SAN\ TA1)|(BFPO\ (C\/O\ )?[0-9]{1,4})|((ASCN|BBND|[BFS]IQQ|PCRN|STHL|TDCU|TKCA)\ 1ZZ))$
Swift 3.0
For US, you can use the below method to validate the ZipCode
func validZipCode(postalCode:String)->Bool{
let postalcodeRegex = "^[0-9]{5}(-[0-9]{4})?$"
let pinPredicate = NSPredicate(format: "SELF MATCHES %#", postalcodeRegex)
let bool = pinPredicate.evaluate(with: postalCode) as Bool
return bool
}
You can use this answer to get postal code.
Get the Zip Code of the Current Location - iPhone SDK
As for validation, it will depend on the country the postal code is in, but this may be a good starting point.
how to validate Zipcode for US or Canada in iOS?

Resources