Access email in EKParticipant/EKAttendee - ios

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
}

Related

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
}

Save multiple values with same keys in NSMutableDictionary then store the dictionary in NSMutableArray

I know this question has been asked many times but not got the solution or i might not understood that's why posting the question.
for (int web=0; web<[web_arr count]; web++) {
AdditionalBookmark *web_book=[[AdditionalBookmark alloc]init];
UITextField *txtNam = (UITextField*)[self.view viewWithTag:1100+web];
NSString *txtName = txtNam.text;
UITextField *txtLin = (UITextField*)[self.view viewWithTag:1200+web];
NSString *txtLink = txtLin.text;
if ((txtName && txtName.length > 0)||(txtLink && txtLink.length > 0))
{
web_book.additionalBookmark_Name = txtName;
web_book.additionalBookmark_Link= txtLink;
web_book.contactDetails_Id=self.contactDetailsinfo.contactDetailsId;
web_book.additionalBookmark_Id=[[[web_arr objectAtIndex:web] valueForKey:#"WeblinkId"] intValue];
[dictWebLinks setObject:txtName forKey:#"WeblinkName"];
[dictWebLinks setObject:txtLink forKey:#"WeblinkUrl"];
[arrForWebLinks addObject:dictWebLinks];
}
[db updateIntoAdditionalBookmarkWithObject:web_book];
}
[dictUpdate setObject:arrForWebLinks forKey:#"Weblink"];
The problem am facing is this suppose there are two items in the array
Printing description of web_arr:
<__NSArrayM 0x170e4db60>(
{
WeblinkId = 9;
WeblinkName = Hdhd;
WeblinkUrl = "ie.comh";
},
{
WeblinkId = 10;
WeblinkName = Hd;
WeblinkUrl = "nd.com";
}
)
what i am getting is below:
Printing description of arrForWebLinks:
<__NSArrayM 0x170e4db30>(
{
WeblinkName = Hd;
WeblinkUrl = "nd.com";
},
{
WeblinkName = Hd;
WeblinkUrl = "nd.com";
}
)
dictionary is replacing the value for same key holding the latest value only, how can i mange to save the values in same format as in web_arr.
Any help is appreciable.
You are adding the same dictionary to the array over and over:
[dictWebLinks setObject:txtName forKey:#"WeblinkName"];
[dictWebLinks setObject:txtLink forKey:#"WeblinkUrl"];
[arrForWebLinks addObject:dictWebLinks];
You need to create a new one each time:
[arrForWebLinks addObject:#{
#"WeblinkName" : txtName,
#"WeblinkUrl" : txtLink
}];
(and remove dictWebLinks).

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);
}];

How to see if NSArray, created with valueForKey is empty

I got an array:
NSArray *itemsArray = [self.tournamentDetails.groups valueForKey:#"Items"];
Where self.tournamentDetails.groups is an NSArray, build with a JSON string from a webRequest.
Items is sometimes empty and sometimes it contains objects. So i need an if statement to check if its empty or not. I've tried some different things like:
if ([itemsArray count]!=0)
if (!itemsArray || !itemsArray.count)
Problem is that if my Items object from valueForKey is empty then the itemsArray still contains an object looking like this
<__NSArrayI 0x178abef0>(
<__NSArrayI 0x16618c30>(
)
)
When theres items inside my Items object it looks like this:
<__NSArrayI 0x18262b70>(
<__NSCFArray 0x181e3a40>(
{
BirthDate = 19601006T000000;
ClubName = "Silkeborg Ry Golfklub";
ClubShortName = "";
CompletedResultSum = {
Actual = {
Text = 36;
Value = 36;
};
ToPar = {
Text = "";
Value = 0;
};
};
}
)
)
Meaning that [itemsArray count] is always equal to 1 or more, and then it jumps into the if statement when it should not.
Anyone know how i can create an if statement where if itemsArray contains "Items:[]" it will be skipped and if itemsArray contains "Items:[lots of objects]" it will run?
EDIT: Solution is to check up on the first index like this if([[itemsArray objectAtIndex:0] count] != 0) then run code.
Try this:
if(itemsArray && itemsArray.count>0) //be sure that it has value){
for(NSArray *item in itemsArray){
if(item.count > 0){
// you have an NSDictionary. Will process it
}else{
//item.count == 0 : this is an empty NSArray.
}
}
}
more simpler
for (id obj in itemsArray)
{
if([obj isKindOfClass:[NSArray class]])
{
if(obj.count > 0)
{
//your if condition here
}
}
}

Adding NSDictionary keys with null values efficiently in ios5

I'm trying to add some values with keys in my dictionary for posting request to server. I have no issues on ios7 but having issue while creating dictionary in ios5, when a null value enters it doesnt insert that key or thereafter in the dictionary. This is a sample of my code
NSLog(#"name:%#, address:%#,city:%#, state:%#, country:%#, postal:%# ,phone:%#,age:%#, gender:%#",self.pop.firstNameTextField.text,self.pop.addressTextField.text, self.pop.cityTextField.text,[NSNumber numberWithInt:stateId],[NSNumber numberWithInt:countryId],self.pop.postalCodeTextField.text,self.pop.phoneNumberTextField.text,self.pop.ageTextField.text,self.pop.genderTextField.text);
NSDictionary *details = [NSDictionary dictionaryWithObjectsAndKeys:
self.pop.firstNameTextField.text,#"name",
self.pop.addressTextField.text,#"address",
self.pop.cityTextField.text,#"city",
[NSNumber numberWithInt:stateId],#"state",
[NSNumber numberWithInt:countryId],#"country",
self.pop.postalCodeTextField.text,#"postalCode",
self.pop.phoneNumberTextField.text,#"phoneNo",
self.pop.ageTextField.text,#"age",
self.pop.genderTextField.text,#"gender",
nil];
NSLog(#"MyDetails:%#",details);
Log result in ios 7:
name: GamerLegend,address:india,city:, state:1, country:1, postal: ,phone:, age:, gender:
MyDetails:
{
address = India;
age = "";
city = "";
country = 1;
gender = "";
name = "GamerLegend";
phoneNo = "";
postalCode = "";
state = 1;
}
Same Log result in ios5:
name: GamerLegend,address:haripad,city:(null), state:1, country:1, postal:(null) ,phone:(null), age:(null), gender:(null)
MyDetails:
{
address = India;
name = "GamerLegend";
}
I want the log of ios5 same as that of ios7. I did some search and I know I can compare the value beforehand to check if its null and then add to dictionary with empty string. But that will be so tedious. I was wondering if there is any other simple workaround for this. Is there any better solution?
This is my current solution
-(void) checkForNull
{
if([self.pop.firstNameTextField.text length] == 0)
self.pop.firstNameTextField.text=#"";
if([self.pop.addressTextField.text length] == 0)
self.pop.addressTextField.text=#"";
if([self.pop.cityTextField.text length] == 0)
self.pop.cityTextField.text=#"";
if([self.pop.postalCodeTextField.text length] == 0)
self.pop.postalCodeTextField.text=#"";
if([self.pop.phoneNumberTextField.text length] == 0)
self.pop.phoneNumberTextField.text=#"";
if([self.pop.ageTextField.text length] == 0)
self.pop.ageTextField.text=#"18";
if([self.pop.genderTextField.text length] == 0)
self.pop.genderTextField.text=#"";
}
This should work if it fits your requirements:
Create an array of EXPECTED keys. Then you loop through this and compare this with the output of the allKeys method in your dictionary. If a key is missing there you could add the missing key with an empty string, [NSNull null], or whatever floats your boat. (You would need to make the dictionary mutable of course).
Something like this:
for (NSString *expectedKey in allExpectedKeys) {
BOOL keyMissing = YES;
for (NSString *existingKey in [dictionary allKeys]) {
if ([expectedKey isEqualToString:existingKey]) {
keyMissing = NO;
break;
}
}
if (keyMissing) {
[dictionary setObject:[NSNull null] forKey:expectedKey];
}
}

Resources