iOS 8 requestWhenInUseAuthorization no Popup - objective-c

I tried to make my AppProject iOS 8 ready. I had read a lot about
[_locationManager requestWhenInUseAuthorization];
and the entry in plist
NSLocationWhenInUseUsageDescription
So I changed all the necessary code lines.
It works fine, but now I have copied my project again from my iOS 7 base to include new features. But when I make the changes for the iOS8 Location Privacy the Popup doesn't appear anymore.
My code worked until I copied.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSLocationWhenInUseUsageDescription</key>
<string>tolle sache </string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>fapporite.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
and here is my call
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
_UserLocation = [[CLLocation alloc]init];
_locationManager = [[CLLocationManager alloc]init]; // initializing locationManager
_locationManager.delegate = self;
_locationManager.desiredAccuracy = kCLLocationAccuracyBest; // setting the accuracy
[_locationManager requestWhenInUseAuthorization]; // iOS 8 MUST
[_locationManager startUpdatingLocation]; //requesting location updates
NSLog(#"passed initwithcode");
}
return self;
}
How can I fix this?

From the documentation
NSLocationWhenInUseUsageDescription (String - iOS) describes the
reason why the app accesses the user’s location normally while running
in the foreground. Include this key when your app uses location
services to track the user’s current location directly. This key does
not support using location services to monitor regions or monitor the
user’s location using the significant location change service. The
system includes the value of this key in the alert panel displayed to
the user when requesting permission to use location services.
This key is required when you use the requestWhenInUseAuthorization
method of the CLLocationManager class to request authorization for
location services. If the key is not present when you call the
requestWhenInUseAuthorization method without including this key, the
system ignores your request.
This key is supported in iOS 8.0 and later. If your Info.plist file
includes both this key and the NSLocationUsageDescription key, the
system uses this key and ignores the NSLocationUsageDescription key.
Read about it here.
I find that the easiest way to add this key to your info.plist is to right click you info.plist and choose
Open As->Source Code
and then add the following in the end before </dict></plist>
<key>NSLocationWhenInUseUsageDescription</key>
<string></string>
If you want you can add a text in between <string></string> that describes to the user why you want to use his/hers location. This text will show up under the default text in the alert.

Try writing a NSLocationWhenInUseUsageDescription in Info.plist

iOS 8.3, Xcode 6.3.1, ARC enabled
The question has been resolved, but I have (2) notes to add from my recent involvement with CLLocationManager.
1) You must have the following keys entered into your *.plist file:
Most commonly used keys have generic more descriptive names, such as "Privacy - Location Usage Description", which really is the "NSLocationUsageDescription" key.
To see the "raw" key names go to "*-Info.plist" and right click in the Navigator area where the keys are listed, see below:
And you get the following results:
The three keys that are related to this article are:
2) Make certain that you allocate and initialize your implementation of CLLocationManager before you try to request authorization or update location.
*.h file:
#interface SomeController : UIViewController <CLLocationManagerDelegate>
#property (strong, nonatomic) CLLocationManager *locationManager;
*.m file:
- (IBAction)locationButton:(UIButton *)sender
{
if (self.locationManager == nil)
{
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
}
else
{
nil;
}
if ([self.locationManager respondsToSelector:#selector(requestWhenInUseAuthorization)])
{
[self.locationManager requestWhenInUseAuthorization];
}
else
{
nil;
}
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[self.locationManager startUpdatingLocation];
}
Hope this saves someone time! Thanks.

Here's a little gotcha. Make sure you're adding the NSLocationAlwaysUsageDescription or NSLocationWhenInUseUsageDescription keys to the main bundle plist and not one of your test target plists!

Had the same problem caused because I was instantiating CLLocationManager in a local var inside a method, solved it making the CLLocationManager a class property.
After a while I found the solution here, but I'm leaving it here since this is the first result in google, hope I save you some time:
requestWhenInUseAuthorization() not Work in iOS 8 With NSLocationWhenInUseUsageDescription Key in Info.plist

Pertaining to the Apple Watch:
You need to put the requestWhenInUseAuthorization key in the iPhone's Info.plist, not the WatchKit App's.
This has bitten me twice now.

Related

Text Replace Service with Xcode - Not replacing selected text

I am trying to build a standalone system service (app with .service extension, saved to ~/Library/Services/) to replace user-selected text in Mac OS X.
I want to build it with Xcode and not Automator, because I am more accustomed to Objective-C than Applescript.
I found several examples on the internet, e.g. this and also Apple's documentation. I got the Xcode project appropriately configured and building without problems. However, when I install my service and try to use it, nothing happens.
The service method itself is executed: I placed code to show an NSAlert inside its method body and it shows. However, the selected text does not get replaced.
Any idea what might be missing? This is the method that implements the service:
- (void) fixPath:(NSPasteboard*) pboard
userData:(NSString*) userData
error:(NSString**) error
{
// Make sure the pasteboard contains a string.
if (![pboard canReadObjectForClasses:#[[NSString class]] options:#{}])
{
*error = NSLocalizedString(#"Error: the pasteboard doesn't contain a string.", nil);
return;
}
NSString* pasteboardString = [pboard stringForType:NSPasteboardTypeString];
//NSAlert* alert = [[NSAlert alloc] init];
//[alert setMessageText:#"WORKING!"];
//[alert runModal];
// ^ This alert is displayed when selecting the service in the context menu
pasteboardString = #"NEW TEXT";
NSArray* types = [NSArray arrayWithObject:NSStringPboardType];
[pboard clearContents];
[pboard declareTypes:types owner:nil];
// Set new text:
[pboard writeObjects:[NSArray arrayWithObject:pasteboardString]];
// Alternatively:
[pboard setString:pasteboardString forType:NSStringPboardType];
// (neither works)
return;
}
After careful reading of Apple's documentation, I found the answer: My service app's plist file was missing a key under the Services section:
<key>NSReturnTypes</key>
<array>
<string>NSStringPboardType</string>
</array>
I only had the opposite NSSendTypes key, which lets you send data from the client app to the service. This one is needed to send the modified text back (in the other direction).
It is weird because, Apple's documentation seems to imply that specifying these two is no longer necessary since 10.6 (Snow Leopard).
For (hopefully) useful console spew, in terminal type:
defaults write -g ViewBridgeLogging -bool YES
Note: useful for services and extensions also.

Open image file only when dragging to application icon

I'm working on a small app that works with image (it is not a document based app).
I want to be able to open image files by dragging them to the app icon.
I have looked at this very clear explanation
Cocoa/Obj-C - Open file when dragging it to application icon
and have added the ability to open .png files by dragging to the app icon.
Do I have to add ALL the file types I require separately , or is there a way to specify ALL image types (for instance all image types that an NSImageView will accept)?
In your application’s Info.plist’s, you can specify the Uniform Type Identifier public.image in LSItemContentTypes for a document type even if your application is not document-based:
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSItemContentTypes</key>
<array>
<string>public.image</string>
</array>
</dict>
</array>
or
If you’re opening other types of files as well, you may want to check whether the dropped file is an image:
- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename {
NSError *error;
NSString *fileUTI = [[NSWorkspace sharedWorkspace] typeOfFile:filename error:&error];
if (!fileUTI)
NSLog(#"Error when trying to detect the type of file %#: %#", filename, error);
else if (UTTypeConformsTo((__bridge CFStringRef)fileUTI, kUTTypeImage))
NSLog(#"%# is an image", filename);
else
NSLog(#"%# is not an image", filename);
return YES;
}

Cocoa protocol handler using NSAppleEventManager and kInternetEventClass/kAEGetURL

This is the Cocoa version of this question:
AEInstallEventHandler handler not being called on startup
Here's my Info.plist protocol registration:
...
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>My Protocol</string>
<key>CFBundleURLIconFile</key>
<string>myicon</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
</dict>
</array>
Here's where I set the method to listen for the kInternetEventClass/kAEGetURL event when a browser link is clicked with the link "myapp://unused/?a=123&b=456":
- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
[[NSAppleEventManager sharedAppleEventManager] setEventHandler:self andSelector:#selector(getURL:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL];
...
}
Here's the handler method:
- (void)getURL:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)reply
{
[[[event paramDescriptorForKeyword:keyDirectObject] stringValue] writeToFile:#"/testbed/complete_url.txt" atomically:YES];
}
Here's the test web link:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
</head>
<body>
Open My App
</body>
</html>
This all works great if the application is already running.
The handler method is called and the complete url is captured.
However, if the app is not yet running the same link will launch the app, but the handler will not be invoked — which makes sense since the handler had not yet been bound to the event.
Those arguments in the URL are important for our application to coordinate with the webapp. Although the vast majority of the time our application will already be running when this click occurs, it is reasonable to expect that in some cases it will not.
I've tried inspecting the environment and process invocation arguments and I do not see the URL in either of them.
Anyone know how we can capture this URL reliably, even when our application isn't already running when the browser click happens?
Apple’s SimpleScriptingPlugin sample registers the handler in applicationWillFinishLaunching:, which is probably cleaner than using init. (And like mikker said, the handler gets called before applicationDidFinishLaunching:.)
I just faced this exact problem. I don't know if it's the proper solution but you can register the event handler in the app delegate's init-method instead.
// in AppDelegate.m
- (id)init
{
self = [super init];
if (self) {
[[NSAppleEventManager sharedAppleEventManager] setEventHandler:self andSelector:#selector(handleURLEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL];
}
return self;
}
One thing to note though is that handleURLEvent:withReplyEvent get's called before applicationDidFinishLaunching: if the app is started by an URL Scheme.

NSService not calling its NSMessage

I'm trying to add a Finder service and all looks fine until I want the service to do its job.
This is the method in my AppDelegate.m:
-(void)uploadFromPasteboard:(NSPasteboard *)pboard userData:(NSString *)udata error:(NSString **)err
{
NSString *filename = [pboard stringForType:NSURLPboardType];
dbg(#"file: %#", filename);
}
The plist configuration:
<key>NSServices</key>
<array>
<dict>
<key>NSRequiredContext</key>
<dict/>
<key>NSMenuItem</key>
<dict>
<key>default</key>
<string>Upload File</string>
</dict>
<key>NSMessage</key>
<string>uploadFromPasteboard</string>
<key>NSPortName</key>
<string>Finder</string>
<key>NSSendTypes</key>
<array>
<string>NSURLPboardType</string>
</array>
<key>NSReturnTypes</key>
<array/>
</dict>
</array>
All seems fine, the service is displayed in the service menu, but when I click it, nothing happens, no logs or anything else, like its not called at all.
Could someone point me whats wrong cos I'm starting to pull my hair hardly :(
Are you setting your service provider instance? Like this (from: Providing a Service):
EncryptoClass* encryptor = [[EncryptoClass alloc] init];
[NSApp setServicesProvider:encryptor];
Merely having this method in your app delegate class is not enough. In the standard application set-up, having this in your app delegate might be sufficient:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[NSApp setServicesProvider: self];
}
Also you're specifying "Finder" for NSPortName. This is probably not correct. From Services Properties (emphasis mine):
NSPortName is the name of the port on which the application should
listen for service requests. Its value depends on how the service
provider application is registered. In most cases, this is the
application name. This property is ignored for Automator workflows
being used as services.
My reading of the documentation is that the application whose name is in NSPortName is the application that will be used to handle the service request. If the name of your app isn't "Finder" (and it shouldn't be, for obvious reasons) then your app will never be called by the service.

Setting Bundle not outputting correct value

Here's the issue, I launch my app from Xcode and it gets up and running then I switch to settings.app and change a toggle from NO to YES and when I switch back to my app, the key outputs NO not YES.
i think i'm running up against the quote below, but not sure how to get around it, if the user launches the app, and goes to settings and changes the toggle, its now out of sync because Settings.app outputs on first launch to NO. Doesn't make sense that a user can't change the setting the first time they switch to settings.app
"For newly installed applications, default preference values from the application’s Settings bundle are not set until the Settings application runs. This means that if the user runs your application before running Settings, the default values specified in your Settings bundle are unavailable."
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *appDefaults = [NSDictionary dictionaryWithObject:#"NO" forKey:#"hideActionBar"];
[defaults registerDefaults:appDefaults];
[defaults synchronize];
and then the code i use to check it
- (void)applicationDidBecomeActive:(UIApplication *)application {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
BOOL hidden = [defaults boolForKey:#"hideActionBar"];
NSLog(#"%d",hidden);
if (hidden) {
viewController.actionButton.enabled = NO;
} else {
viewController.actionButton.enabled = YES;
}
}
and my settings Root.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Type</key>
<string>PSToggleSwitchSpecifier</string>
<key>Title</key>
<string>Hide Action Bar</string>
<key>Key</key>
<string>hideActionBar</string>
<key>DefaultValue</key>
<false/>
<key>TrueValue</key>
<true/>
<key>FalseValue</key>
<false/>
</dict>
</plist>
For BOOL variables you need not to set any value if want it to be NO. Because when you will try to access it first time it will return you the same. So remove registerDefaults code(all four lines) from your applicationDidFinishLaunchingWithOptions method.
Explanation:
When you change setting from NO to YES and launch your application, in applicationDidFinishLaunchingWithOptions this value is once changed to NO programmatically.
Note: Whenever you use settings bundle in your application, prior to registering a value(object value, because primitive types will return 0) check whether that value is nil(it means still unregistered), if the value for key is nil then only register with initial default values else you will end up in changing the values each time programmatically.
Thanks,