Contacts Framework. Access to phones ' values - ios

- (void)viewDidLoad {
[super viewDidLoad];
self.givenName = [[NSMutableArray alloc]init];
NSArray *KeysToFetch = #[CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey ];
NSString *containerId = [[[CNContactStore alloc]init]defaultContainerIdentifier];
NSPredicate *predicate = [CNContact predicateForContactsInContainerWithIdentifier:containerId];
_contacts = [[[CNContactStore alloc]init]unifiedContactsMatchingPredicate:predicate keysToFetch:KeysToFetch error:nil];
for (CNContact *c in _contacts) {
[_givenName addObject:c.givenName];
NSLog(#"%#", c.phoneNumbers);
}
}
Hi, how can I get access to the 'digits' value? when i do a NSLog of 'phoneNumbers' i get this on console:
2015-10-19 12:33:41.423 testContacts[7064:2001002] (
"<CNLabeledValue: 0x7b72eed0: identifier=A73ABAA0-7698-47D7-A2BD-630E04C0C811, label=_$!<Mobile>!$_, value=<CNPhoneNumber: 0x7b734db0: countryCode=us, digits=8885555512>>",
"<CNLabeledValue: 0x7b72eb80: identifier=BC927A1D-AA98-4E67-82A9-BB5AD09A6CAE, label=_$!<Home>!$_, value=<CNPhoneNumber: 0x7b72ecd0: countryCode=us, digits=8885551212>>"

If the phone number is localized with phone characters like 1 (234) 456-78, stringValue returns exactly that string.
To obtain the digits, the correct code (in Swift) is
(contact.phoneNumbers[0].value as! CNPhoneNumber).valueForKey("digits") as! String

The digits are a private variable.
For getting a phone number:
CNLabeledValue<CNPhoneNumber*>* labeledValue = contact.phoneNumbers[0];
NSString *phone = labeledValue.value.stringValue;
E.g. to get a mobile number:
// get mobile
NSString *phone = nil;
for (CNLabeledValue<CNPhoneNumber*>* labeledValue in contact.phoneNumbers)
{
if ([labeledValue.label isEqualToString:CNLabelPhoneNumberMobile])
{
phoneNumber = labeledValue.value.stringValue;
break;
}
}

This worked for me in Swift 2.1. / Xcode 7.2. and iOS 9.2.1.
import ContactsUI
Import the contacts framework at the bottom of your file
Get your contacts from your addressBook
This is my example:
let contacts = XXXContactsAccessManagerM.sharedContacts.getAll()
Add this code to the class Model or ViewController in which you are working
for var ctcCtr = 0 ; ctcCtr < contacts.count ; ctcCtr++
{
let contact = contacts[ctcCtr]
let phoneNumbers = contact.phoneNumbers
for var phnCtr = 0 ; phnCtr < phoneNumbers.count ; phnCtr++
{
let phoneNumberLabel = phoneNumbers[phnCtr]
let phoneNumber = (phoneNumberLabel.value as! CNPhoneNumber).valueForKey("digits") as! String
print(phoneNumber)
}
}
I got a normal phone number, the exact way I wrote them in my phone book 097XXXXXXX and so on... (with out hte (XXX) and XXX-XXX)

Using KPKContacts package was simple as calling
contact.firstNumberAvailable() which returns a String value
or you can do contact.numbers.first!.number

This code very helpful for getting both Phone number value and phone number label in swift 3.1
let ContactNumber: NSString = (phoneNumber.value).value(forKey:"digits") as! NSString
print(ContactNumber)
let localizedLabel = CNLabeledValue<NSString>.localizedString(forLabel: phoneNumber.label!)
print(localizedLabel)

Related

Split array object "1;item1" into 1 and item1

country = (
"3;India",
"4;US",
"5;UK",
)
state = (
"9;3;Uttar Pradesh",
"10;3;Maharashtra",
"11;3;Andaman and Nicobar Islands"
)
city = (
"110;3;10;pune",
"111;3;10;mumbai",
"112;3;10;nasik",
)
I want to show just the country name in dropdown list and want to pass it's Id to web service. If user selects India, I have to pass 3. How can I do this?
As I understand you have an array with strings in it.
If so, you just need to split your strings.
objective-c code
NSArray *country = #[#"3;India", #"4;US", #"5;UK"];
NSString *countryStr = country[0]; // 3; India
NSArray *countryArr = [countryStr componentsSeparatedByString:#";"];
NSString *countryCode = countryArr.firstObject; //3
NSString *countryName = countryArr.lastObject; //India
NSLog(#"%#", countryCode); //3
NSLog(#"%#", countryName); //India
swift code
let country : NSArray = ["3;India", "4;US", "5;UK"]
let countryStr = country[0]
let countryArr : NSArray = countryStr.componentsSeparatedByString(";")
let countryCode = countryArr.firstObject
let countryName = countryArr.lastObject
if let countryCodePr = countryCode {
print(countryCodePr)
}
if let countryNamePr = countryName {
print(countryNamePr)
}

Alexa: productId, code challenge: what should they be?

I am following closely this document to authorize a hardware from my iOS app:
In iOS section, at step 5):
- (IBAction)onLogInButtonClicked:(id)sender {
NSArray *requestScopes = [NSArray arrayWithObjects:#"alexa:all", nil];
NSMutableDictionary *options = [[NSMutableDictionary alloc] init];
NSString* scopeData = [NSString stringWithFormat:#"{\"alexa:all\":{\"productID\":\"%#\","
"\"productInstanceAttributes\":{\"deviceSerialNumber\":\"%#\"}}}",
productId, deviceSerialNumber];
options[kAIOptionScopeData] = scopeData;
options[kAIOptionReturnAuthCode] = #YES;
options[kAIOptionCodeChallenge] = #"CODE_CHALLENGE_GOES_HERE";
options[kAIOptionCodeChallengeMethod] = #"S256";
[AIMobileLib authorizeUserForScopes:requestScopes delegate:delegate options:options];
}
what should productId in scopeData be? As I have read some other posts, it is said that productId is taken from ID column of an app created in AVS Developer Portal, which is not linked with the document as it mentions to App Console in Getting Started guide for iOS. So I am confused at how/where to take the productId.
deviceSerialNumber can be any unique string?
I implemented a code-challenge method in ObjC based on item 1) and 2) described in "Transferring an Authorization Code from a Mobile App to an Alexa-enabled Product" section. Is it correct? (since I have no reference example)
- (NSString *)codeChallenge {
verifier = [NSString randomStringWithLength:128]; // generate 128-char string containing [A-Z], [a-z], [0-9], "-", "_", ".", "~"
NSData *sha256 = [[verifier dataUsingEncoding:NSUTF8StringEncoding] SHA256]; // SHA256 that string
NSString *base64Enc = [sha256 base64EncodedStringWithOptions:0]; // base64 encode SHA256 result
NSLog(#"base64Enc: %#", base64Enc);
NSMutableString *ret = [NSMutableString string];
for (NSInteger i=0; i<base64Enc.length; i++) { // remove "="; replace "+" with "-"; replace "/" with "_" as referenced from: http://tools.ietf.org/html/draft-ietf-oauth-spop-10#appendix-A
unichar c = [base64Enc characterAtIndex:i];
if (c == '=') {
continue;
}
else if (c == '+') {
[ret appendString:#"-"];
}
else if (c == '/') {
[ret appendString:#"_"];
}
else {
[ret appendFormat:#"%C", c];
}
}
return ret;
}
Regards,
So, it turned out that the App ID is the productID. You should able able find yours on Developer Console under Application Type Info tab. Your code challenge seems fine to me, but I'm not sure why you'd want to strip off =+-_. And yeah, deviceSerailNumber could be anything unique, I'm assuming that it should also be unique per installation.
The following in the Swift example for the same,
#IBAction func loginWithAmazon() {
let scopes = ["alexa:all"];
let codeChallenge = sha256("CODE_CHALLENGE_GOES_HERE".dataUsingEncoding(NSUTF8StringEncoding)!).base64EncodedStringWithOptions(NSDataBase64EncodingOptions.Encoding64CharacterLineLength)
var options: [String: AnyObject] = [:]
let scopeData = String(format: "{\"alexa:all\":{\"productID\":\"%#\",\"productInstanceAttributes\":{\"deviceSerialNumber\":\"%#\"}}}", "yourAppIDHere", "anyUniqueID")
options[kAIOptionScopeData] = scopeData;
options[kAIOptionReturnAuthCode] = true;
options[kAIOptionCodeChallenge] = codeChallenge;
options[kAIOptionCodeChallengeMethod] = "S256";
AIMobileLib.authorizeUserForScopes(scopes, delegate: self, options: options);
}
func requestDidSucceed(apiResult: APIResult!) {
accessToken = apiResult.result
print(apiResult.result)
}
func requestDidFail(errorResponse: APIError!) {
print(errorResponse.error)
}
=================================================
func sha256(data : NSData) -> NSData {
var hash = [UInt8](count: Int(CC_SHA256_DIGEST_LENGTH), repeatedValue: 0)
CC_SHA256(data.bytes, CC_LONG(data.length), &hash)
let res = NSData(bytes: hash, length: Int(CC_SHA256_DIGEST_LENGTH))
return res
}

DynamoDB iOS : AWSDynamoDBScanExpression Provide multiple parameters for scanFilter property

Firstly, what I am trying to achieve with DynamoDB:
-> (I am trying to include username and password in scan filter for login system. So if for email/user and password combination, scan method of AWSDynamoDBObjectMapper returns an object, login is successful otherwise no.)
What's going wrong?
I have been trying to read documentation and sample code on Github for this but none seems to have example of proper use of it. From documentation I only found that scanFilter is of type NSDictionary. So I tried to include multiple parameters for filtering (UserName/UserEmail and Password) and it is failing to scan results.
scanExpression.scanFilter = #{#"Password": passwordCondition, UserNameOrUserEmail : usernameOrEmailCondition};
However if I pass only one parameter it works and returns an object, or lets say a row of a database.
scanExpression.scanFilter = #{#"Password": passwordCondition};
Full code for reference (Similar code for Android works fine, when I referred to colleague's, who is doing Android.):
// Check if email or username and decide DynamoDB Attribute accordingly.
NSString *UserNameOrUserEmail;
if ([usernameOrEmail rangeOfString:#"#"].location == NSNotFound)
UserNameOrUserEmail = #"UserName";
else
UserNameOrUserEmail = #"UserEmail";
AWSDynamoDBScanExpression *scanExpression = [AWSDynamoDBScanExpression new];
AWSDynamoDBCondition *passwordCondition = [AWSDynamoDBCondition new];
AWSDynamoDBAttributeValue *passwordAttribute = [AWSDynamoDBAttributeValue new];
passwordAttribute.S = password;
passwordCondition.attributeValueList = #[passwordAttribute];
passwordCondition.comparisonOperator = AWSDynamoDBComparisonOperatorEQ;
AWSDynamoDBCondition *usernameOrEmailCondition = [AWSDynamoDBCondition new];
AWSDynamoDBAttributeValue *usernameOrEmailAttribute = [AWSDynamoDBAttributeValue new];
usernameOrEmailAttribute.S = #"sammy#hhd.com";//usernameOrEmail;
usernameOrEmailCondition.attributeValueList = #[usernameOrEmailAttribute];
usernameOrEmailCondition.comparisonOperator = AWSDynamoDBComparisonOperatorEQ;
//Apply filter condtions: http://stackoverflow.com/a/27856299/818506
scanExpression.scanFilter = #{#"Password": passwordCondition, UserNameOrUserEmail : usernameOrEmailCondition}; //#{#"UserEmail": usernameOrEmailCondition};//
//^^^^^^^^^^^^^^^^^^^^^^^^^^^Is this correct?^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//Perform a scan:
[[dynamoDBObjectMapper scan:[User class] expression:scanExpression]continueWithBlock: ^id (AWSTask *task){
if (task.error) {
NSLog(#"The request failed. Error: [%#]", task.error);
}
if (task.exception) {
NSLog(#"The request failed. Exception: [%#]", task.exception);
}
if (task.result) {
AWSDynamoDBPaginatedOutput *paginatedOutput = task.result;
for (User *user in paginatedOutput.items) {
isLoginSuccessful = YES;
}
completionHandler(isLoginSuccessful);
}
return nil;
}];
Any suggestion/idea/comment would be appreciate.
Thanks,
Rohan.
Have you considered using AWSDynamoDBScanInput and a NSMutableDictionary? I have the following example in Swift that should be fairly easy to translate to Objective-C:
// create password condition
let passwordCondition = AWSDynamoDBCondition()
let v1 = AWSDynamoDBAttributeValue(); v1.S = password
passwordCondition.comparisonOperator = AWSDynamoDBComparisonOperator.EQ
passwordCondition.attributeValueList = [ v1 ]
// create username condition
let usernameCondition = AWSDynamoDBCondition()
let v2 = AWSDynamoDBAttributeValue(); v2.S = "sammy#hhd.com"
usernameCondition.comparisonOperator = AWSDynamoDBComparisonOperator.EQ
usernameCondition.attributeValueList = [ v2 ]
// add these conditions to a NSMutableDictionary
var scanFilterDict : NSMutableDictionary! = NSMutableDictionary()
scanFilterDict.addEntriesFromDictionary(["password": passwordCondition])
scanFilterDict.addEntriesFromDictionary(["UserNameOrUserEmail": usernameCondition])
// use the mutable dictionary as scan filter
scanInput.scanFilter = scanFilterDict as Dictionary<NSObject, AnyObject>
scanInput.exclusiveStartKey = self.lastEvaluatedKey
scanInput.limit = 20
scanInput.tableName = "yourDynamoDBTable"
// apply AND operator on both conditions
scanInput.conditionalOperator = AWSDynamoDBConditionalOperator.And
AWSDynamoDB.defaultDynamoDB().scan(input)
I stubbled on this thread while working on a similar problem. Here is how I solved it:
let objectMapper = AWSDynamoDBObjectMapper.default()
let scanExpression = AWSDynamoDBScanExpression()
scanExpression.filterExpression = "#UserNameOrUserEmail = :UserNameOrUserEmail AND #password = :follows"
scanExpression.expressionAttributeNames = ["#UserNameOrUserEmail": "UserNameOrUserEmail" , "#password": "password" ,]
scanExpression.expressionAttributeValues = [":UserNameOrUserEmail": "sammy#hhd.com", ":password": password ,]
objectMapper.scan(User.self, expression: scanExpression) { (response: AWSDynamoDBPaginatedOutput?, error: Error?) in
// handle data or error here...
}
Just it case it helps someone else.

Retrieve 1 specific key from several NSArrays and add them together

In my app, I am needing to loop through each NSArray, get the NSInteger associated with the key 'people' in the NSArray, and then add them all together. What would be a good starting point for first retrieving each specific NSInteger from each NSArray?
The array in question returns like this in console.
(
"<Prayers:DDomBXIONY:(null)> {\n Anonymous = 1;\n DeviceID = 123;\n FirstName = Name;\n LastName = Name;\n Location = HI;\n PrayerWarriors = 8;\n Request = Hi;\n Title = Hi;\n UserId = RtXN6QZsgaIiPw4SjFWGtkxXx;\n dateMade = \"Jan_09_2015\";\n}"
)
Basically just need to retrieve NSInteger from each PrayerWarriors key, and add them all together.
(
"<Prayers:DDomBXIONY:(null)> {\n Anonymous = 1;\n DeviceID = 123;\n FirstName = Name;\n LastName = Name;\n Location = HI;\n PrayerWarriors = 8;\n Request = Hi;\n Title = Hi;\n UserId = RtXN6QZsgaIiPw4SjFWGtkxXx;\n dateMade = \"Jan_09_2015\";\n}",
"<Prayers:yG7GC4bCIH:(null)> {\n Anonymous = 1;\n DeviceID = 123;\n FirstName = Name;\n LastName = Name;\n Location = bye;\n PrayerWarriors = 0;\n Request = bye;\n Title = bye;\n UserId = RtXN6QZsgaIiPw4SjFWGtkxXx;\n dateMade = \"Jan_09_2015\";\n}"
)
So without understanding exactly how your PFObject works, I'm going to assume it's like a dictionary.
Add each of your objects to a single array:
NSMutableArray *arrayHoldingPrayersObjects = [NSMutableArray alloc] init];
arrayHoldingPrayersObjects[0] = prayerObject1;
arrayHoldingPrayersObjects[1] = prayerObject2;
arrayHoldingPrayersObjects[2] = prayerObject3;
etc...
Then create a integer variable outside of a for loop and iterate through your objects, adding the value for PrayerWarrior at each iteration.
int totalPrayerWarriors = 0;
for (int i = 0; i < arrayHoldingPrayersObjects.count; i++)
{
NSMutableDictionary *prayerObject = arrayHoldingPrayersObjects[i];
totalPrayerWarriors += [prayerObject objectForKey:#"PrayerWarriors"] intValue];
}
You should end up with a correct total from all arrays. Do some tests to make sure it's accurate for you. Hope this helps.
*EDIT
The error you're getting indicates that it actually is a NSMutableArray, which you can't access using methods like objectForKey, so... there must be a method provided by PFObject that allows you to do that. OR, if PrayerWarriors is reliably the [5]th value (including 0) in the array, then you might be able to access it by index.
replace the lines:
NSMutableDictionary *prayerObject = arrayHoldingPrayersObjects[i];
totalPrayerWarriors += [prayerObject objectForKey:#"PrayerWarriors"] intValue];
with
NSMutableArray *prayerObject = arrayHoldingPrayersObjects[i];
totalPrayerWarriors += prayerObject[5];
Not sure where the mutable array is coming from. Parse.com will always produce immutable arrays as far as I know. So lets say you've retrieved Prayers with:
PFQuery *query = [PFQuery queryWithClassName:#"Prayers"];
[query findObjectsInBackgroundWithBlock:^(NSArray *prayers, NSError *error) {
// see how it's an NSArray, not mutable
}];
Now you want the total of the retrieved PrayerWarrior attributes, so...
[query findObjectsInBackgroundWithBlock:^(NSArray *prayers, NSError *error) {
NSInteger total = 0;
for (PFObject *p in prayers) {
NSNumber *warriorCount = p[#"PrayerWarriors"];
total += [warriorCount intValue]; // notice we cannot add nsnumbers directly (we need intValue)
}
NSLog(#"total warriors = %d", total);
}];

Access email in EKParticipant/EKAttendee

I want to get the email address of the attendee of an event in the EKEventKit.
I have the following code:
if ( event.attendees.count > 0)
{
NSArray *people = event.attendees;
for(EKParticipant *person in people)
{
if ( person.participantType == EKParticipantTypePerson && person.URL.resourceSpecifier.length > 0)
{
NSString *dataString = [NSString stringWithFormat:#"event_id=%ld&name=%#&is_me=%d&email=%#&role=%#",event_id,person.name, person.isCurrentUser,person.URL.resourceSpecifier, #"attendee"];
//<DO SOMETHING USEFUL WITH dataString>;
}
}
}
When I run the code person populates with the following data:
EKAttendee <0x17809acc0> {UUID = 4F657EA4-452A-412B-A9AA-FEC5551DC096; name = A. Real Person; email = realperson#therightdomain.com; status = 0; role = 0; type = 1}
How to I access the email field?
I tried (as above) to use URL.resourceSpecifier, but that frequently is some strange string that is definitely NOT an email address.
The "Description" of the EKParticipant object is a property list of sorts. I tried several different methods of parsing that list into something containing key:value pairs unsuccessfully. So I wrote the following:
// This is re-useable code that converts any class description field into a dictionary that can be parsed for info
NSMutableDictionary *descriptionData = [NSMutableDictionary dictionary];
for (NSString *pairString in [person.description componentsSeparatedByString:#";"])
{
NSArray *pair = [pairString componentsSeparatedByString:#"="];
if ( [pair count] != 2)
continue;
[descriptionData setObject:[[pair objectAtIndex:1] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] forKey:[[pair objectAtIndex:0]stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
}
With this I simply get the email address with
[descriptionData valueForKey:#"email"]
I tried to answer this same question in "how to get ekevent EKparticipant email?" thread:
What you need to do is use EKPrincipal:ABRecordWithAddressBook and then extract email from there. Like this:
NSString *email = nil;
ABAddressBookRef book = ABAddressBookCreateWithOptions(nil, nil);
ABRecordRef record = [self.appleParticipant ABRecordWithAddressBook:book];
if (record) {
ABMultiValueRef value = ABRecordCopyValue(record, kABPersonEmailProperty);
if (value
&& ABMultiValueGetCount(value) > 0) {
email = (__bridge id)ABMultiValueCopyValueAtIndex(value, 0);
}
}
Note that calling ABAddressBookCreateWithOptions is expensive so you might want to do that only once per session.
If you can't access the record, then fall back on URL.resourceSpecifier.
In Swift 4:
private static func getParticipantDescriptionData(_ participant: EKParticipant) -> [String:String] {
var descriptionData = [String: String]()
for pairString in participant.description.components(separatedBy: ";") {
let pair = pairString.components(separatedBy: "=")
if pair.count != 2 {
continue
}
descriptionData[pair[0].trimmingCharacters(in: .whitespacesAndNewlines)] =
pair[1].trimmingCharacters(in: .whitespacesAndNewlines)
}
return descriptionData
}

Resources