Accessing 'Internet Accounts' programmatically - macos

I'm trying to access the 'accounts' that are stored in the Internet Accounts area on the system settings on OSX. I'm aware of the 'ACAccount' library, but this only really seems to be of any use for Social integration, for example Facebook/Twitter, what i would like is to be able to detect that you have an Exchange account there and open up certain features within an app. But i'm guessing i'll need to get my users to re-enter their details in my app?
I did try and use it, however i get an empty array.
ACAccountStore *store = [[ACAccountStore alloc] init];
NSArray *accounts = [store accounts]; //This is empty
Does anyone know if i'm missing something, or if it's not possible? Thanks!
Edit
I have gained access to the Twitter account for example, but it's not returned in the Accounts list, i had to request permission first. Which makes sense. However, i still see no way of getting access to the Exchange account.
ACAccountStore *account = [[ACAccountStore alloc] init];
ACAccountType *accountType = [account accountTypeWithAccountTypeIdentifier:
ACAccountTypeIdentifierTwitter];
[account requestAccessToAccountsWithType:accountType options:nil
completion:^(BOOL granted, NSError *error)
{
if (granted == YES)
{
NSArray *arrayOfAccounts = [account
accountsWithAccountType:accountType];
}
}];

Related

What API is there for selecting iTunes library location?

How does one programatically set the iTunes library location on macOS to custom locations using e.g. C / Obj-C or Swift API?
Alternatively, environmental settings, such as modifying plists, using the defaults CLI tool, or similar approaches, are also OK for me.
Ordinarily, selecting a custom iTunes library location is done by launching iTunes while holding down the option key. I need to be able to do this in e.g. a unit testing environment / programatically.
You may be able to set it via the prefs.
This is how I access it.
-(void)loadITunesPrefLibraryPath {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSDictionary *userPref = [userDefaults persistentDomainForName:#"com.apple.iTunes"];
id dataBaseLoc = [userPref objectForKey:#"Database Location"];
NSLog(#"%s dataBaseLoc is:%#", __PRETTY_FUNCTION__, dataBaseLoc);
NSLog(#"%s dataBaseLoc class is:%#", __PRETTY_FUNCTION__, [dataBaseLoc class]);
NSData* dataBaseData = (NSData*)dataBaseLoc;
BOOL staleBook = NO;
NSError* bookError = nil;
NSURL* dataBaseURL = [NSURL URLByResolvingBookmarkData:dataBaseData options:NSURLBookmarkResolutionWithoutMounting relativeToURL:nil bookmarkDataIsStale:&staleBook error:&bookError];
self.libExtDBfile = dataBaseURL;
}
Once you get the userPrefs for iTunes.
And create a BookMarkData from URL.
You might be able to set it via
[userPref setObject:newDataBaseLoc forKey:#"Database Location"];
also see next answer for possible ITLibrary framework private API access

PKPaymentAuthorizationViewController present as nil view controller

I am trying to integrate Apple Pay in my demo app following this link & I am facing this issue issue. I have updated my iphone 6+ os to 8.1.1 version but still not able to present PKPaymentAuthorizationViewController properly & I am getting this error "Application tried to present a nil modal view controller on target".Please suggest something since I am stuck on this.Here is the code which I have written :-
PKPaymentRequest *request = [[PKPaymentRequest alloc] init];
request.currencyCode = #"USD";
request.countryCode = #"US";
// This is a test merchant id to demo the capability, this would work with Visa cards only.
request.merchantIdentifier = #"merchant.com.procharge"; // replace with YOUR_APPLE_MERCHANT_ID
request.applicationData = [#"" dataUsingEncoding:NSUTF8StringEncoding];
request.merchantCapabilities = PKMerchantCapability3DS;
request.supportedNetworks = #[PKPaymentNetworkMasterCard, PKPaymentNetworkVisa, PKPaymentNetworkAmex];
request.requiredBillingAddressFields = PKAddressFieldPostalAddress|PKAddressFieldPhone|PKAddressFieldEmail;
request.requiredShippingAddressFields = PKAddressFieldPostalAddress|PKAddressFieldPhone|PKAddressFieldEmail;
///Set amount here
NSString *amountText = #"0.01"; // Get the payment amount
NSDecimalNumber *amountValue = [NSDecimalNumber decimalNumberWithString:amountText];
PKPaymentSummaryItem *item = [[PKPaymentSummaryItem alloc] init];
item.amount = amountValue;
//item.amount = [[NSDecimalNumber alloc] initWithInt:20];
item.label = #"Test Payment Total";
request.paymentSummaryItems = #[item];
PKPaymentAuthorizationViewController *vc = nil;
// need to setup correct entitlement to make the view to show
#try
{
vc = [[PKPaymentAuthorizationViewController alloc] initWithPaymentRequest:request];
}
#catch (NSException *e)
{
NSLog(#"Exception %#", e);
}
if (vc != nil)
{
vc.delegate = self;
[self presentViewController:vc animated:YES completion:CompletionBlock];
}
else
{
//The device cannot make payments. Please make sure Passbook has valid Credit Card added.
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"PassKit Payment Error"
message:NSLocalizedString(#"The device cannot make payment at this time. Please check Passbook has Valid Credit Card and Payment Request has Valid Currency & Apple MerchantID.", #"")
delegate:nil
cancelButtonTitle:NSLocalizedString(#"OK", #"")
otherButtonTitles:nil];
[alert show];
}
Thanks & Regards
Did you implement these methods to check before trying to present the viewcontroller?
// Determine whether this device can process payment requests.
// YES if the device is generally capable of making in-app payments.
// NO if the device cannot make in-app payments or if the user is restricted from authorizing payments.
+ (BOOL)canMakePayments;
// Determine whether this device can process payment requests using specific payment network brands.
// Your application should confirm that the user can make payments before attempting to authorize a payment.
// Your application may also want to alter its appearance or behavior when the user is not allowed
// to make payments.
// YES if the user can authorize payments on this device using one of the payment networks supported
// by the merchant.
// NO if the user cannot authorize payments on these networks or if the user is restricted from
// authorizing payments.
+ (BOOL)canMakePaymentsUsingNetworks:(NSArray *)supportedNetworks;

What is the correct way to handle stale NSURL bookmarks?

When resolving an NSURL from a security scoped bookmark, if the user has renamed or moved that file or folder, the bookmark will be stale. Apple's document says this regarding staleness:
isStale
On return, if YES, the bookmark data is stale. Your app should
create a new bookmark using the returned URL and use it in place of
any stored copies of the existing bookmark.
Unfortunately, this rarely works for me. It may work 5% of the time. Attempting to create a new bookmark using the returned URL results in an error, code 256, and looking in Console reveals a message from sandboxd saying deny file-read-data on the updated URL.
Note If regenerating the bookmark does work, it seems to only work the first time it is regenerated. It seems to never work should the folder/file be moved/renamed again.
How I initially create & store the bookmark
-(IBAction)bookmarkFolder:(id)sender {
_openPanel = [NSOpenPanel openPanel];
_openPanel.canChooseFiles = NO;
_openPanel.canChooseDirectories = YES;
_openPanel.canCreateDirectories = YES;
[_openPanel beginSheetModalForWindow:self.window completionHandler:^(NSInteger result) {
if (_openPanel.URL != nil) {
NSError *error;
NSData *bookmark = [_openPanel.URL bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope
includingResourceValuesForKeys:nil
relativeToURL:nil
error:&error];
if (error != nil) {
NSLog(#"Error bookmarking selected URL: %#", error);
return;
}
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:bookmark forKey:#"bookmark"];
}
}];
}
Code that resolves the bookmark
-(void)resolveStoredBookmark {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSData *bookmark = [userDefaults objectForKey:#"bookmark"];
if (bookmark == nil) {
NSLog(#"No bookmark stored");
return;
}
BOOL isStale;
NSError *error;
NSURL *url = [NSURL URLByResolvingBookmarkData:bookmark
options:NSURLBookmarkResolutionWithSecurityScope
relativeToURL:nil
bookmarkDataIsStale:&isStale
error:&error];
if (error != nil) {
NSLog(#"Error resolving URL from bookmark: %#", error);
return;
} else if (isStale) {
if ([url startAccessingSecurityScopedResource]) {
NSLog(#"Attempting to renew bookmark for %#", url);
// NOTE: This is the bit that fails, a 256 error is
// returned due to a deny file-read-data from sandboxd
bookmark = [url bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope
includingResourceValuesForKeys:nil
relativeToURL:nil
error:&error];
[url stopAccessingSecurityScopedResource];
if (error != nil) {
NSLog(#"Failed to renew bookmark: %#", error);
return;
}
[userDefaults setObject:bookmark forKey:#"bookmark"];
NSLog(#"Bookmark renewed, yay.");
} else {
NSLog(#"Could not start using the bookmarked url");
}
} else {
NSLog(#"Bookmarked url resolved successfully!");
[url startAccessingSecurityScopedResource];
NSArray *contents = [NSFileManager.new contentsOfDirectoryAtPath:url.path error:&error];
[url stopAccessingSecurityScopedResource];
if (error != nil) {
NSLog(#"Error reading contents of bookmarked folder: %#", error);
return;
}
NSLog(#"Contents of bookmarked folder: %#", contents);
}
}
When the bookmark is stale, the resulting resolved URL does point to the correct location, I just can't actually access the file despite the fact that [url startAccessingSecurityScopedResource] returns YES.
Perhaps I'm misinterpreting the documentation regarding stale bookmarks, but I'm hoping I'm just doing something stupid. Popping an NSOpenPanel each time a bookmarked file/folder is renamed or moved, my only other option at this point, seems ridiculous.
I should add that I have com.apple.security.files.bookmarks.app-scope, com.apple.security.files.user-selected.read-write, and com.apple.security.app-sandbox all set to true in my entitlements file.
After a lot of disappointing testing I've come to the following conclusions. Though logical, they're disappointing since the resulting experience for users is far from ideal and a significant pain for developers depending on how far they're willing to go to help users re-establish references to bookmarked resources.
When I say "renew" below, I mean "generate a new bookmark to replace a stale bookmark using the URL resolved from the stale bookmark."
Renewal always works as long as the bookmarked resource is moved or renamed within a directory that your app already has permission to access. So, by default, it always works inside your application's container folder.
Renewal fails if a bookmarked resource is moved into a folder your application does not have permission to access. e.g. User drags a folder from your container folder to some folder outside the container folder. You will be able to resolve the URL, but not access nor renew the bookmark.
Renewal fails if a bookmarked resource lives in a folder your application doesn't have access to and is then renamed. This means a user can explicitly grant your application access to a resource, then inadvertently revoke that access just by renaming it.
Resolution fails if a resource is moved to another volume. Not sure if this is a limitation of bookmarks in general or just when used in a sandboxed application.
For issues 2 & 3 you're in a decent position as the developer since resolution of the bookmarked URL does work. You can at least lead the user by telling them exactly which resources they need to grant your app access to and where they are. The experience could be improved by having them select a folder that contains (directly or indirectly) all resources that you need to renew a bookmark for. This could even be the volume, which solves the problem completely if they're willing to give your application this much access.
For issue 4, resolution doesn't work at all. The user will have to relocate the file without any hints since you can't resolve the new location. One thing I've done in my current app that has reduced the pain of this issue is to add an extended attribute to any resource I store a bookmark for. Doing this at least lets me have the user choose a folder to search for previously associated resources.
Frustrating limitations, but bookmarks still win over storing static paths.

Fetching user data from Facebook on Xcode

I am trying to develop a simple app, which, retrieves data from Facebook, when the user connects to it.
After reading Facebook's example about how to retrieve User's photos and User's names, I just want to get information such as gender, city, e-mail, and date of birth, for example.
The following part, is where I got stuck:
- (void)populateUserDetails
{
if (FBSession.activeSession.isOpen) {
[[FBRequest requestForMe] startWithCompletionHandler:
^(FBRequestConnection *connection,
NSDictionary<FBGraphUser> *user,
NSError *error) {
if (!error) {
self.userNameLabel.text = user.name;
self.userProfileImage.profileID = user.id;
}
}];
}
}
My questions are:
Should I make a dictionary with all this data? (gender, city, e-mail, etc.)
-Also, I'm using the storyboard, can I use labels to all those data as Facebook's tutorial states, for the username?
I read in a couple of places that the method requestForMe isn't the appropriate one for the other type of data I am looking for. What would be the method for my requests?
First you must ask user for permissions to access his gender, email, city ...
You make a array with required permissions and add it to the openActiveSessionWithReadPermissions: method
NSArray *permissions = [[NSArray alloc] initWithObjects:#"user_birthday",#"user_hometown",#"user_location",#"email",#"basic_info", nil];
[FBSession openActiveSessionWithReadPermissions:permissions
allowLoginUI:YES
completionHandler:^(FBSession *session,
FBSessionState status,
NSError *error) {
}];
Then make a request like this to get informations you wanted
[FBRequestConnection startForMeWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
NSLog(#"%#", [result objectForKey:#"gender"]);
NSLog(#"%#", [result objectForKey:#"hometown"]);
NSLog(#"%#", [result objectForKey:#"birthday"]);
NSLog(#"%#", [result objectForKey:#"email"]);
}];
I hope i resolved your problem.

Can I find out the GameCenter user name that's actively logged in?

I'd like my iphone app to pre-populate some fields once a user has logged into gamecenter. Specifically, the username!
I have full logging in working - as well as a leaderboard and acheivements all working - I just can't seem to find out how to access the users name property anywhere.
Any advice / help would be greatly appreciated.
Thanks,
Kolya
PS - This is my first question. Please be nice :-)
Here is the code I'm trying:
if ([GameCenterManager isGameCenterAvailable]) {
self.gameCenterManager = [[[GameCenterManager alloc] init] autorelease];
[self.gameCenterManager setDelegate:self];
[self.gameCenterManager authenticateLocalUser];
NSLog(#"User alias: %#",[[GKLocalPlayer localPlayer]alias]);
But the NSLog outputs "User alias: (null)"?
As the local player is the subclass of GKPlayer, you could use this:
[[GKLocalPlayer localPlayer]alias]
Oh.. you have not authenticate the user... It is the first step..
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
[localPlayer authenticateWithCompletionHandler:^(NSError *error) {
if (localPlayer.isAuthenticated)
{
// Perform additional tasks for the authenticated player.
}
}];
I think you can just replace the 3 lines of codes with the code above.. Should work..

Resources