One To Many With Convenience Backpointer - objective-c

I'd like to model a one-to-many relationship between users and checkIns on Parse.com. Additionally, I'd like a user to have a convenience pointer to it's lastCheckin, to avoid having to query checkIns to get a user's most recent checkIn. Using the following code, the user's lastCheckin column is always empty in the Data Browser:
PFUser *currentUser = [PFUser currentUser];
PFObject *checkIn = [PFObject objectWithClassName:#"CheckIn"];
checkIn[#"forUser"] = currentUser;
checkIn[#"foursquareID"] = selectedVenue[#"id"];
checkIn[#"name"] = selectedVenue[#"name"];
checkIn[#"location"] = [PFGeoPoint geoPointWithLatitude:[selectedVenue[#"location"][#"lat"] doubleValue]
longitude:[selectedVenue[#"location"][#"lng"] doubleValue]];
[checkIn saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
currentUser[#"lastCheckin"] = checkIn;
[currentUser saveInBackground];
}];
Removing the following line causes the User.lastCheckin to correctly point to the desired CheckIn in the Data Browser, but obviously this breaks the one-to-many linkage:
checkIn[#"forUser"] = currentUser;
Is there some way for me to get both the one-to-may relationship between Users and CheckIns, and a User.lastCheckIn pointer?

If you are setting the lastCheckin on the User each time you create a new CheckIn you can access that checkin easily by fetching the current user and including lastCheckin with includeKey.
http://blog.parse.com/2011/12/06/queries-for-relational-data/
To get all checkins for a user you can simply query the CheckIn class where the current user is equal to forUser. That is your 1 to many relationship. You do not need to maintain a relation of checkins on the User class since you have a pointer from each CheckIn instance back to the user as a pointer. When you set lastCheckin on the user instance you are not changing the property on older checkins.
Perhaps you are thinking this is like SQL with primary and foreign keys. I get confused by NoSQL since I have a SQL background. I am always learning to think like Parse and NoSQL. What I see above is all you need to query all checkins for a user and the last checkin for a user. You can also simply query all checkins for a user, limit result to 1 and sort it so the newest checkin is the first result. It should be quite fast on Parse.
You may also want to look into using Cloud Code. You could call a Cloud Code function and simply pass the parameters you need to create a new checkin and have all of this work done in Cloud Code. An After Save function could be used to update lastCheckin on the user.
http://www.parse.com/docs/js/symbols/Parse.Cloud.AfterSaveRequest.html
If you user on Cloud Code you can update how the data is managed without updating the iOS app.

Related

How do I sort an active record collection by a value held by one of its associations?

I have an Active Record Model called Owner. It is associated with both a House and a CoOwner. An Owner also has a column called debt_amount. In my case, every Owner will always be attached to both a House and a CoOwner
A house has a location field
I am looking to run a query to find all Owners attached to a specific co-owner, with nil for debt_amount. This is working fine.
Where I am running into trouble is when I attempt to sort this the associated Houses.location.
Below is what I am currently using.
Owner.includes(:house).where(co_owner_id: 10)
.where.not(debt_amount: nil)
.order([house.location])
Any ideas on how to make this work, or pointers to resources I can use to do some reading?
Owner.includes(:house)
.where(co_owner_id: 10)
.where.not(debt_amount: nil)
.order("houses.location ASC")
Here are more examples, https://apidock.com/rails/ActiveRecord/QueryMethods/order

LOCATION type in QB CUSTOM OBJECT

With the last release of Quickblox SDK there is a new LOCATION field type, meaning we can save gps coordinates.
My question is: are or will be there specific functions/methods to query on this kind of field?
For instance, can I get all records in a CUSTOM OBJECT class that are located with 10 kms of my current location? - (based on a location field in the class)
Can I order the result by distance from my location?
These kind of queries are available in the LOCATION module.
Until now we were queering LOCATION to get something like "all users within 10 kms of my location" and then using those users ID's to query CUSTOM OBJECT and get specific information about those users.
With the new LOCATION field can we do this using only CUSTOM OBJECTS?
Try something like this:
NSMutableDictionary *params = [NSMutableDictionary new];
params[#"field_name[near]"] = #"34.0013123, 15.014915; 40000";
To avoid name conflicts, try to give a plural name to location field.

Show specific data to a user after log in

I am using simple membership provider and mvc. After a user registers he/she will be redirected to a view called registerDepartments. Incase time is an issue the user can login at a later stage. How do I retrieve the data registered by the user previously. In my previous workings it shows all the data from the table for all users who registered a branch.
I didn't get your question clearly, because you didn't show any code of your work and any example.
However I think you want to retrieve branches created by a user for him/her right? If So, you can use something like the following:
var currentUserId = (int)Membership.GetUser().ProviderUserKey;
var branches = dbCtx.Branches
.Where(b => b.CreatorUserId == currentUserId).ToList();

Dealing with duplicate contacts due to linked cards in iOS' Address Book API

Some beta-users of my upcoming app are reporting that the list of contacts contain a lot of duplicate records. I'm using the result from ABAddressBookCopyArrayOfAllPeople as the data source for my customized table view of contacts, and it baffles me that the results are different from the iPhone's 'Contacts' app.
When looking more closely at the Contacts app, it seems that the duplicates originate from entries with "Linked Cards". The screenshots below have been obfuscated a bit, but as you see in my app on the far right, "Celine" shows up twice, while in the Contacts app on the left there's only one "Celine". If you click the row of that single contact, you get a "Unified Info" card with two "Linked Cards" (as shown in the center, I didn't use Celine's contact details because they didn't fit on one screenshot):
The issues around "Linked Cards" have quite a few topics on Apple's forums for end users, but apart from the fact that many point to a 404 support page, I can't realistically go around fixing all of my app's users' address books. I would much rather like to deal with it elegantly and without bothering the user. To make matters worse, it seems I'm not the only one with this issue, since WhatsApp is showing the same list containing duplicate contacts.
Just to be clear about the origins of the duplicate contacts, I'm not storing, caching or otherwise trying to be smart about the array ABAddressBookCopyArrayOfAllPeople returns. So the duplicate records come directly from the API call.
Does anyone know how to deal with or detect these linked cards, preventing duplicate records from showing up? Apple's Contacts app does it, how can the rest of us do so too?
UPDATE: I wrote a library and put it on Cocoapods to solve the issue at hand. See my answer below
One method would be to only retrieve the contacts from the default address book source:
ABAddressBookRef addressBook = ABAddressBookCreate();
NSArray *people = (__bridge NSArray *)ABAddressBookCopyArrayOfAllPeopleInSource(addressBook, ABAddressBookCopyDefaultSource(addressBook));
But that is lame, right? It targets the on-device address book, but not extra contacts that might be in Exchange or other fancy syncing address books.
So here's the solution you're looking for:
Iterate through the ABRecord references
Grab each respective "linked references" (using ABPersonCopyArrayOfAllLinkedPeople)
Bundle them in an NSSet (so that the grouping can be uniquely identified)
Add that NSSet to another NSSet
Profit?
You now have an NSSet containing NSSets of linked ABRecord objects. The overarching NSSet will have the same count as the number of contacts in your "Contacts" app.
Example code:
NSMutableSet *unifiedRecordsSet = [NSMutableSet set];
ABAddressBookRef addressBook = ABAddressBookCreate();
CFArrayRef records = ABAddressBookCopyArrayOfAllPeople(addressBook);
for (CFIndex i = 0; i < CFArrayGetCount(records); i++)
{
NSMutableSet *contactSet = [NSMutableSet set];
ABRecordRef record = CFArrayGetValueAtIndex(records, i);
[contactSet addObject:(__bridge id)record];
NSArray *linkedRecordsArray = (__bridge NSArray *)ABPersonCopyArrayOfAllLinkedPeople(record);
[contactSet addObjectsFromArray:linkedRecordsArray];
// Your own custom "unified record" class (or just an NSSet!)
DAUnifiedRecord *unifiedRecord = [[DAUnifiedRecord alloc] initWithRecords:contactSet];
[unifiedRecordsSet addObject:unifiedRecord];
CFRelease(record);
}
CFRelease(records);
CFRelease(addressBook);
_unifiedRecords = [unifiedRecordsSet allObjects];
I've been using ABPersonCopyArrayOfAllLinkedPeople() in my app for some time now. Unfortunately, I've just discovered that it doesn't always do the right thing. For example, if you have two contacts that have the same name but one has the "isPerson" flag set and the other does not, the above function won't consider them "linked". Why is this an issue? Because Gmail(exchange) sources don't support this boolean flag. If you try to save it as false, it will fail, and the contact you saved in it will come back on the next run of your app as unlinked from the contact you saved in iCload (CardDAV).
Similar situation with social services: Gmail doesn't support them and the function above will see two contacts with the same names as different if one has a facebook account and one does not.
I'm switching over to my own name-and-source-recordID-only algorithm for determining whether two contact records should be displayed as a single contact. More work but there's a silver lining: ABPersonCopyArrayOfAllLinkedPeople() is butt-slow.
The approach that #Daniel Amitay provided contained nuggets of great value, but unfortunately the code is not ready for use. Having a good search on the contacts is crucial to my and many apps, so I spent quite a bit of time getting this right, while on the side also addressing the issue of iOS 5 and 6 compatible address book access (handling user access via blocks). It solves both the many linked cards due to incorrectly synched sources and the cards from the newly added Facebook integration.
The library I wrote uses an in-memory (optionally on-disk) Core Data store to cache the address book record ID's, providing an easy background-threaded search algorithm that returns unified address book cards.
The source is available on a github repository of mine, which is a CocoaPods pod:
pod 'EEEUnifiedAddressBook'
With the new iOS 9 Contacts Framework you can finally have your unified contacts.
I show you two examples:
1) Using fast enumeration
//Initializing the contact store:
CNContactStore* contactStore = [CNContactStore new];
if (!contactStore) {
NSLog(#"Contact store is nil. Maybe you don't have the permission?");
return;
}
//Which contact keys (properties) do you want? I want them all!
NSArray* contactKeys = #[
CNContactNamePrefixKey, CNContactGivenNameKey, CNContactMiddleNameKey, CNContactFamilyNameKey, CNContactPreviousFamilyNameKey, CNContactNameSuffixKey, CNContactNicknameKey, CNContactPhoneticGivenNameKey, CNContactPhoneticMiddleNameKey, CNContactPhoneticFamilyNameKey, CNContactOrganizationNameKey, CNContactDepartmentNameKey, CNContactJobTitleKey, CNContactBirthdayKey, CNContactNonGregorianBirthdayKey, CNContactNoteKey, CNContactImageDataKey, CNContactThumbnailImageDataKey, CNContactImageDataAvailableKey, CNContactTypeKey, CNContactPhoneNumbersKey, CNContactEmailAddressesKey, CNContactPostalAddressesKey, CNContactDatesKey, CNContactUrlAddressesKey, CNContactRelationsKey, CNContactSocialProfilesKey, CNContactInstantMessageAddressesKey
];
CNContactFetchRequest* fetchRequest = [[CNContactFetchRequest alloc] initWithKeysToFetch:contactKeys];
[fetchRequest setUnifyResults:YES]; //It seems that YES is the default value
NSError* error = nil;
__block NSInteger counter = 0;
And here i loop through all unified contacts using fast enumeration:
BOOL success = [contactStore enumerateContactsWithFetchRequest:fetchRequest
error:&error
usingBlock:^(CNContact* __nonnull contact, BOOL* __nonnull stop) {
NSLog(#"Unified contact: %#", contact);
counter++;
}];
if (success) {
NSLog(#"Successfully fetched %ld contacts", counter);
}
else {
NSLog(#"Error while fetching contacts: %#", error);
}
2) Using unifiedContactsMatchingPredicate API:
// Contacts store initialized ...
NSArray * unifiedContacts = [contactStore unifiedContactsMatchingPredicate:nil keysToFetch:contactKeys error:&error]; // Replace the predicate with your filter.
P.S You maybe also be interested at this new API of CNContact.h:
/*! Returns YES if the receiver was fetched as a unified contact and includes the contact having contactIdentifier in its unification */
- (BOOL)isUnifiedWithContactWithIdentifier:(NSString*)contactIdentifier;
I'm getting all sources ABAddressBookCopyArrayOfAllSources, moving the default one ABAddressBookCopyDefaultSource to the first position, then iterate through them and getting all people from source ABAddressBookCopyArrayOfAllPeopleInSource skipping ones I've seen linked before, then getting linked people on each ABPersonCopyArrayOfAllLinkedPeople.

How to check the user's CRUD permissions for an object in Salesforce?

According to a requirement, i have to change the owner of an account if the user does not have read access to a third object.
I need a functionality similar to the isAccessible() method of Describe Field Result, but it is only available for the current logged in user.
Is there any other way to check the user's CRUD permissions for an object in Apex code?
I wrote an article about this on my blog. There is a feature that was just released in version 24.0 of the API (Spring Release) that will let you do just this on a record by record basis for the current user.
Here is the link to that blog entry that goes into details: How to tell if a user has access to a record
Don't confuse record level access with CRUD - the latter is the ability for a user to Create, Read, Update or Delete an object in general, regardless of sharing rules etc. that might affect the user's access to a particular record.
To check whether a user can create (e.g. Contacts) in general, just use
Schema.sObjectType.Contact.isCreateable()
(returns true or false)
From the documentation. it sounds like you want to use execute anonymously.
Apex generally runs in system context; that is, the current user's permissions, field-level security, and sharing rules aren’t taken into account during code execution.​ The only exceptions to this rule are Apex code that is executed with the executeAnonymous call. executeAnonymous always executes using the full permissions of the current user. For more information on executeAnonymous, see Anonymous Blocks.
Although Apex doesn't enforce object-level and field-level permissions by default, you can enforce these permissions in your code by explicitly calling the sObject describe result methods (of Schema.DescribeSObjectResult) and the field describe result methods (of Schema.DescribeFieldResult) that check the current user's access permission levels. In this way, you can verify if the current user has the necessary permissions, and only if he or she has sufficient permissions, you can then perform a specific DML operation or a query.
For example, you can call the isAccessible, isCreateable, or isUpdateable methods of Schema.DescribeSObjectResult to verify whether the current user has read, create, or update access to an sObject, respectively. Similarly, Schema.DescribeFieldResult exposes these access control methods that you can call to check the current user's read, create, or update access for a field. In addition, you can call the isDeletable method provided by Schema.DescribeSObjectResult to check if the current user has permission to delete a specific sObject.
http://www.salesforce.com/us/developer/docs/apexcode/index_Left.htm#StartTopic=Content/apex_classes_perms_enforcing.htm#kanchor431
Have you tried the runAs() method?
Something like (not verified):
User u = [SELECT Id FROM User WHERE Name='John Doe'];
System.runAs(u) {
if (Schema.sObjectType.Contact.fields.Email.isAccessible()) {
// do something
}
}
The DescribeSObjectResult class has methods for checking CRUD.
E.g. this allows you to test whether or not the current user can update the account object in general.
Schema.DescribeSObjectResult drSObj = Schema.sObjectType.Account;
Boolean thisUserMayUpdate = drSObj.isUpdateable();
#John De Santiago: your article covers record level access rather than object CRUD (= object level access)
Very old post. Since then SF add option to query object permission:
Select SobjectType ,ParentId, PermissionsEdit, PermissionsRead
From ObjectPermissions
Order by ParentID, SobjectType ASC
Basically you will need to get the profile and permissionset of the user that you want to check and the relevant object. So it will be something like:
Select SobjectType ,ParentId, PermissionsEdit, PermissionsRead
From ObjectPermissions
where parentId IN :UserProfileIdAndPermission
AND sObjectType=:objectType
Order by ParentID, SobjectType ASC