I've been playing with Accounts framework and during my tests I've denied the app access to Accounts. Now the following code returns Denied each time I run it.
[[self accountStore] requestAccessToAccountsWithType:twitterType options:nil completion:^(BOOL granted, NSError *error) {
if (granted) {
accounts = [[self accountStore] accountsWithAccountType:twitterType];
} else if (error != nil) {
NSLog(#"Failed to list Twitter accounts %#", error);
} else {
NSLog(#"Access denied");
}
}];
What sucks is I have no clue where to reset it and googling doesn't help. I've seen how to fix it on iOS but I'm running a Mac app and the solution doesn't apply.
The Account Framework API documentation is not helpful at all.
Palm face.
Figured it out, I've found a file with my app id called "~/Library//Application Support/com.apple.TCC/TCC.db"
It's a sqlite3 file, so run sqlite3 "~/Library//Application Support/com.apple.TCC/TCC.db" then search for your app in "access" table, delete the row and should you have access again.
If that doesn't help there's also a file called ~/Library/Accounts/Accounts3.sqlite, there ZAUTHORIZATION table which includes your app.
The proposed solution no longer works, because of high-profile hacks made by big companies and others. This solution has never been supported and is an invitation to hackers. The right way to do this is using the tool provided by Apple: tccutil.
Thus, do the following:
Open Terminal.
Run tccutil reset <service> [bundle_id], in which <service> can be AddressBook or Calendar or All with the optional ID of the bundle you want to reset.
Example:
tccutil reset All com.apple.Terminal
Related
from OS X 10.11 and iOS 9.0 Apple provided this framework Contacts:
https://developer.apple.com/library/prerelease/mac/documentation/Contacts/Reference/Contacts_Framework/index.html#//apple_ref/doc/uid/TP40015328
and I would like to access contacts from Mac, but I cannot do anything, because the app does not have access to contacts:
switch CNContactStore.authorizationStatusForEntityType(.Contacts) {
case .Authorized:
print("Authorized")
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "contactsAllowed")
case .Denied:
print("Denied")
NSUserDefaults.standardUserDefaults().setBool(false, forKey: "contactsAllowed")
case .NotDetermined:
print("Not Determined")
case .Restricted:
print("Restricted")
}
The app never prompts to allow access to contacts, neither can I select it in System Preferences (it does not show up).
Has anyone any idea how to acccess them on Mac (on iOS it works well)?
Any call to instance of CNContactStore should trigger request. If not try
Reboot (actually this helped me; I was granted access)
Turn on sandboxing
Resetprivacy settings "sudo tccutil reset AddressBook"
Call it on main thread
Instance of CNContactStore call:
[[CNContactStore alloc] init];
Privacy
Users can grant or deny access to contact data on a per-application
basis. Any call to CNContactStore will block the application while the
user is being asked to grant or deny access. Note that the user is
prompted only the first time access is requested; any subsequent
CNContactStore calls use the existing permissions. To avoid having
your app’s UI main thread block for this access, you can use either
the asynchronous method [CNContactStore
requestAccessForEntityType:CNEntityTypeContacts completionHandler:] or
dispatch your CNContactStore usage to a background thread.
Are you trying this from the playground? This won't work.
Try this method in your project:
func getContactImage(name:String) -> NSImage?
{
let store = CNContactStore()
do
{
let contacts = try store.unifiedContactsMatchingPredicate(CNContact.predicateForContactsMatchingName(name), keysToFetch:[CNContactImageDataKey])
if contacts.count > 0
{
if let image = contact[0].imageData
{
return NSImage.init(data: image)
}
}
}
catch
{
}
return nil
}
Anyway your question led me on the path to find a way to get the contacts image, so thanks for that.
In my app, I use the following code in the view controller in my sprite kit project to authenticate a player using GameKit:
-(void)autheticateLocalPlayer{
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
localPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error){
if (error != nil){
NSLog(#"%#", [error localizedDescription]);
}
if (viewController != nil){
[self presentViewController:viewController animated:YES completion:nil];
}else{
if ([GKLocalPlayer localPlayer].authenticated){
_gameCenterEnabled = YES;
[[GKLocalPlayer localPlayer]loadDefaultLeaderboardIdentifierWithCompletionHandler:^(NSString *leaderboardIdentifier, NSError *error) {
if (error != nil){
NSLog(#"%#", [error localizedDescription]);
}
else{
_leaderboardIdentifier = leaderboardIdentifier;
}
}];
}
else{
_gameCenterEnabled = NO;
NSLog(#"game center not available");
}
}
};
}
It works fine on my phone, but after building an Ad-Hoc build with a provisioning profile including a friend's phone to see how it would work, they were not asked to log in when they opened the app, whereas my phone was (and all simulator phones were).
On top of that, attempting to open the leaderboards would simply show a message "game center is not available, user is not logged in" or something like that.
I went to this friend's phone's Game Center settings, and verified that sandbox mode was ON. After that, I manually logged in to a Sandbox tester account I made in iTunes connect, and once I opened my app again, (on their phone), it displayed the "welcome back" message. However, the leaderboards showed "no scores", when I believe that my score should be on there.
My questions are:
1. Why were they not asked to log in? Is it a problem with my code, or is that how it's supposed to work when beta testing with sandbox mode on?
2. Why doesn't it show the other scores? My leaderboard is in iTunes connect, and the leaderboard name is properly displayed in the app, but no score are visible.
Thanks for any help!
Apparently, it takes about 10 hours before the other user's scores will be updated. Today, all of my test users' scores are on game center. Would be nice if this was made more obvious, so developers didn't think Game Center wasn't working.
I have3 Estimote beacons that can be seen with the App store Estimate App.
Now I am trying to run the Apple demo app AirLocation AirLocate
I have changed the UUID in the APLDefaults.m file to the default Estimote UUID _supportedProximityUUIDs = #[[[NSUUID alloc] initWithUUIDString:#"B9407F30-F5F8-466E-AFF9-25556B57FE6D"]];
I have enabled the Region to start startMonitoringForRegion as this stackoverflow says.
But they are not showing up, have you seen this ? Or am I missing some Estimate specific.
Regards
The problem is that AirLocate was written for iOS7, and in iOS8, the permissions model for iBeacons and other location operations has changed. In order to get the program to work on iOS 8 when compiled from XCode 6, you need to add code that requests permission in your AppDelegate. Like this:
if([self.locationManager respondsToSelector:#selector(requestAlwaysAuthorization)]) {
[self.locationManager requestAlwaysAuthorization];
}
This will prompt the user to authorize location operations including beacons. You also need to edit the info.plist for the app, and add a new string key called NSLocationAlwaysUsageDescription with a value like "This app needs access to location services" so the OS can prompt the user for this permission.
After you run your app, you can check in settings to see if this permission has been granted properly.
Another problem I have noticed in iOS 9 is that the calibration sometimes does not work. Seems to be an NSNumber conversion issue. The following edit in APLCalibrationCalculator.m fixed it:-
//measuredPower = [[sample valueForKeyPath:#"#avg.rssi"] integerValue];
measuredPower = [[sample valueForKeyPath:#"#avg.rssi"] intValue];
We have an app with IAP coin purchases. We've recently received mails from a few of our users, saying that they couldn't purchase any coins. I looked at their session logs and saw Failed IAP events, each with no fail reason logged. Here's the related code:
- (void)_purchaseRequestFailed:(SKPaymentTransaction *)transaction state:(StoreTransactionState)state error:(NSError *)error
{
IAPProduct *product = [self getProductWithId:transaction.payment.productIdentifier];
if (error.code==SKErrorPaymentCancelled) {
[_metricsManager logFailIAP:product failReason:#"Payment canceled"];
} else {
[_metricsManager logFailIAP:product failReason:error.localizedDescription];
}
if ([_delegate respondsToSelector:#selector(didSucceedPurchasingProduct:)]) {
[_delegate didFailPurchasingProduct:product];
}
}
inside logFailIAP, I log things like time, UDID, event name, and the error.localizedDescription.
if (failReason != nil && failReason.length > 0) {
[metricsDictionary setObject:failReason forKey:MetricsEventParameterFailReason];
}
In the logs, I'm getting Failed IAP events, but no logged reason for failing. Is there any case where, you get "SKPaymentTransactionStateFailed", but, have an empty error.localizedDescription? I can confirm that the logs work, and have seen errors like "Payment canceled", and "Cannot connect to iTunes" being logged in other devices. The issue is not specific to a device or iOS.
I have encountered a nil error in the following scenario:
User updates iOS on their device.
Immediately after the update, the user goes into your app and tries to make a purchase.
Instead of the usual purchase dialog, they get an "iTunes terms of service have changed. You need to accept new T&C's" (or something along the lines), which redirects them to the iTunes app and shows them the new T&C's. The payment is then cancelled and you get a nil error, and, obviously, a nil error.localizedDescription.
For this scenario to happen, your app needs to be the user's first point of interaction with the iTunes Store after the system update.
As part of the process of creating a VPN connection programatically in OSX, using Cocoa, I need to store the PPP password in the System keychain. When I try to do this using the keychain API, I get the following error as a result of calling SecKeychainAddGenericPassword:
"Could not write to the file. It may have been opened with insufficient access privileges."
Here is the code I am using:
- (void)storePasswordInKeychain
{
SecKeychainRef keychain = nil;
err = SecKeychainCopyDomainDefault(kSecPreferencesDomainSystem, &keychain);
if (err != errSecSuccess) {
NSLog(#"Error getting system keychain: %#", SecCopyErrorMessageString(err, NULL));
} else {
NSLog(#"Succeeded opening keychain: %#", SecCopyErrorMessageString(err, NULL));
SecKeychainItemRef item = nil;
err = SecKeychainUnlock(keychain, 0, NULL, FALSE);
NSLog(#"Keychain unlocked: %#", SecCopyErrorMessageString(err, NULL));
err = SecKeychainAddGenericPassword (keychain,
3, "VPN",
8, "username",
8, "password",
&item);
NSLog(#"Result of storing password: %#", SecCopyErrorMessageString(err, NULL));
}
}
The discussion How to write to the System.keychain? makes it seem like I need to make a command line call to /usr/bin/security from within my program, but the point of the Keychain API seems to be to avoid that kind of hackery.
Can anybody point me in the right direction for storing a new password in the System keychain? Thanks.
You need root privilege when you write something to system keychain.
For xcode debugging, you just "EditScheme"(From Menu by "Product->EditScheme...->Run->Info->Debug process as->root"). Well,my xcode version is 6.1, maybe there is some difference in different xcode version.
Or just use command line by sudo your app.
Hope this helps.
It is true that the credentials need to go into the system keychain and not the user keychain. You will not need the SMJobBless to do that.
After you unlocked the keychain, create an SecAccessRef like so:
SecAccessRef access = nil;
status = SecAccessCreate(CFSTR("Some VPN Test"), (__bridge CFArrayRef)(self.trustedApps), &access);
Then build your keychain item
SecKeychainAttribute attrs[] = {
{kSecLabelItemAttr, (int)strlen(labelUTF8), (char *)labelUTF8},
{kSecAccountItemAttr, (int)strlen(accountUTF8), (char *)accountUTF8},
{kSecServiceItemAttr, (int)strlen(serviceUTF8), (char *)serviceUTF8},
{kSecDescriptionItemAttr, (int)strlen(descriptionUTF8), (char *)descriptionUTF8},
};
And finally store it to the keychain:
SecKeychainAttributeList attributes = {sizeof(attrs) / sizeof(attrs[0]), attrs};
status = SecKeychainItemCreateFromContent(kSecGenericPasswordItemClass, &attributes, (int)strlen(passwordUTF8), passwordUTF8, keychain, access, &item);
There is a project on Github which does just that. Have a look at the VPNKeychain.m file to see the whole implementation. https://github.com/halo/macosvpn
Surely a VPN username & password belong to a particular user, do you really mean the system keychain and not the user keychain?
Try dropping the calls to SecKeychainCopyDomainDefault & SecKeychainUnlock and just pass NULL as the first argument to SecKeychainAddGenericPassword - this should add the item to the default keychain.
Apple's current recommendation for privileged access is to use the Service Management API. They have a sample project: SMJobBless. I'm not sure if the sandbox will allow system keychain access if you are targeting the Mac App Store.
The other question you point to seems to be recommending the Authorization Services API which Service Management replaced (in 10.6) and which is explicitly stated as not being allowed at all when sandboxed.