Hi I have some code that gets the geolocationposition it works in andproid but not in iOS.
In iOS only using the Plugin.Geolocator I get
Location permission denied, can not get positions async. Unhandled
Exception: Plugin.Geolocator.Abstractions.GeolocationException: A
geolocation error occured: Unauthorized
below is what I have in the plist (removed not relevant stuff)
<?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>
etc.......
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>This app needs access to location when open and in the background.</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Can we use your location</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>We are using your location</string>
<key>RequestWhenInUseAuthorization</key>
<string>Need location for geolocator plugin</string>
</dict>
</plist>
It's working for android so I guess cannot be code but settings.
any suggestions?
EDIT
Tried the sample that comes with the plugin itself and even though seems to work
i see an error in the output window. Do I need to set some settings in the emulator ? how?
GeolocatorSampleiOS[16172:675663091] Location permission denied, can
not get positions async.
code I use for both android and iOS (works with android)
private async Task<Location> GetDeviceLocationAsync2()
{
try
{
var locator = CrossGeolocator.Current;
var status = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.Location);
if (status != PermissionStatus.Granted)
{
var results = await CrossPermissions.Current.RequestPermissionsAsync(Permission.Location);
if (results.ContainsKey(Permission.Location))
{
status = results[Permission.Location];
}
}
var currentLocation = await Xamarin.Essentials.Geolocation.GetLastKnownLocationAsync();
if (currentLocation != null)
{
return currentLocation;
}
if (!locator.IsGeolocationAvailable || !locator.IsGeolocationEnabled)
{
//todo
return null;
}
if (status == PermissionStatus.Granted)
{
var request = new GeolocationRequest(
GeolocationAccuracy.Best,
new TimeSpan(30000))
{
DesiredAccuracy = GeolocationAccuracy.Best
};
currentLocation =await Xamarin.Essentials.Geolocation.GetLocationAsync(request);
LogCurrentLocation(currentLocation);
}
else if (status != PermissionStatus.Unknown)
{
await DisplayDialogAsync("Location denied", "Do not have access to location");
}
return currentLocation;
}
catch (FeatureNotSupportedException ex)
{
// Handle not supported on device exception
//todo:add logging
}
catch (PermissionException ex)
{
// Handle permission exception
//todo:add logging
}
catch (Exception ex)
{
// Unable to get location
//todo:add logging
}
return null;
}
Solved.
I needed to set permissions in the emulator!!!
Related
I am using the CrossGeolocator.Current.StartListeningAsync method in my xamarin app to be able to listen to location updates in background for iOS.
I am using essentials to request permissions.
On first run we get location permission (in app only) and location using essentials and then we use StartListeningAsync for ios to be able to track location if the app is in background or foreground.
When the callback is hit we get a popup saying this app uses background location and gives you the option to use it or change back to use in app only option. On selection of any option the callback never completes and subsequent code isnt run.
Here is the popup I get after I have permission for when in use and then start listening:
Popup on ios
On subsequent runs once permissions are set manually the callback works.
Xamarin Forms Version: 5.0.0.1931
Xamarin Essential Version: 1.6.1
Geolocator Plugin Version: 4.6.2-beta
Code example:
private async Task StartListening()
{
if (CrossGeolocator.Current.IsListening)
return;
try
{
var settings = new ListenerSettings
{
ActivityType = ActivityType.Other,
DeferLocationUpdates = true,
DeferralDistanceMeters = 15,
DeferralTime = TimeSpan.FromSeconds(10),
ListenForSignificantChanges = false,
PauseLocationUpdatesAutomatically = false,
AllowBackgroundUpdates = true,
};
await CrossGeolocator.Current.StartListeningAsync(TimeSpan.FromSeconds(15), 5, true, settings);
CrossGeolocator.Current.PositionChanged += PositionChanged;
CrossGeolocator.Current.PositionError += PositionError;
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
You need to make sure the permission is ok before starting to listen to location updates. Have a try with following code:
public async Task GetLocationAsync()
{
var status = await CheckAndRequestPermissionAsync(new Permissions.LocationAlways());
if (status != PermissionStatus.Granted)
{
// Notify user permission was denied
return;
}else{
await StartListening();
}
}
public async Task<PermissionStatus> CheckAndRequestPermissionAsync<T>(T permission)
where T : BasePermission
{
var status = await permission.CheckStatusAsync();
if (status != PermissionStatus.Granted)
{
status = await permission.RequestAsync();
}
return status;
}
I have been trying to add Push notifications targeting iOS 10 in Xamarin iOS App but
Foreground i successfully get notification first time of deployment in UNUserNotificationCenterDelegate but on second time it doesn't capture that unless i uninstall the app and reinstall it.
Background i always get the notification but i'm unable to capture its tap.
InActive is working fine, i'm able to capture its tap in FinishedLaunching.
Code
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
// Remote Notifications
SetupRemoteNotifications(app, options);
}
private void SetupRemoteNotifications(UIApplication app, NSDictionary options)
{
// Handling multiple OnAppear calls
if (!UIApplication.SharedApplication.IsRegisteredForRemoteNotifications)
{
// register for remote notifications based on system version
if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
{
// iOS 10 or later
var authOptions = UNAuthorizationOptions.Alert | UNAuthorizationOptions.Badge | UNAuthorizationOptions.Sound;
UNUserNotificationCenter.Current.RequestAuthorization(authOptions, (granted, error) =>
{
if (granted)
{
InvokeOnMainThread(UIApplication.SharedApplication.RegisterForRemoteNotifications);
}
});
// Watch for notifications while the app is active
UNUserNotificationCenter.Current.Delegate = this;
}
else if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0))
{
var pushSettings = UIUserNotificationSettings.GetSettingsForTypes(
UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound,
new NSSet());
UIApplication.SharedApplication.RegisterUserNotificationSettings(pushSettings);
UIApplication.SharedApplication.RegisterForRemoteNotifications();
}
else
{
UIRemoteNotificationType notificationTypes = UIRemoteNotificationType.Alert | UIRemoteNotificationType.Badge | UIRemoteNotificationType.Sound;
UIApplication.SharedApplication.RegisterForRemoteNotificationTypes(notificationTypes);
}
}
// Check for a notification
if (options != null)
{
// check for a remote notification
if (options.ContainsKey(UIApplication.LaunchOptionsRemoteNotificationKey))
{
var remoteNotification = options[UIApplication.LaunchOptionsRemoteNotificationKey] as NSDictionary;
if (remoteNotification != null)
{
ProcessNotification(remoteNotification, true);
//new UIAlertView(remoteNotification.AlertAction, remoteNotification.AlertBody, null, "OK", null).Show();
}
}
}
}
[Export("userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:")]
public void DidReceiveNotificationResponse(UNUserNotificationCenter center, UNNotificationResponse response,
Action completionHandler)
{
var userInfo = response.Notification.Request.Content.UserInfo;
if (userInfo != null && userInfo.ContainsKey(new NSString("aps")))
{
NSDictionary aps = userInfo.ObjectForKey(new NSString("aps")) as NSDictionary;
NSDictionary message = aps.ObjectForKey(new NSString("alert")) as NSDictionary;
NSDictionary metadataNSDict = aps.ObjectForKey(new NSString("metadata")) as NSDictionary;
var metaDataDict = metadataNSDict.ConvertToDictionary();
var metaJson = metaDataDict.FromDictionaryToJson();
var metadata = JsonConvert.DeserializeObject<NotificationMetadata>(metaJson);
if (message != null && metadata != null)
{
NotificationGoToPage(metadata);
}
}
completionHandler();
}
[Export("userNotificationCenter:willPresentNotification:withCompletionHandler:")]
public void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification,
Action<UNNotificationPresentationOptions> completionHandler)
{
// Do something with the notification
Console.WriteLine("Active Notification: {0}", notification);
// Tell system to display the notification anyway or use
// `None` to say we have handled the display locally.
completionHandler(UNNotificationPresentationOptions.Alert |
UNNotificationPresentationOptions.Sound |
UNNotificationPresentationOptions.Badge);
}
public override void ReceivedRemoteNotification(UIApplication application, NSDictionary userInfo)
{
ProcessNotification(userInfo, false);
}
Even i have tried using DidReceiveRemoteNotification but this doesn't capture the Background
[Export("didReceiveRemoteNotification:")]
public override void DidReceiveRemoteNotification(UIApplication application, NSDictionary userInfo,
Action<UIBackgroundFetchResult> completionHandler)
{
if (application.ApplicationState == UIApplicationState.Active)
{
completionHandler(UIBackgroundFetchResult.NoData);
}
else if (application.ApplicationState == UIApplicationState.Background)
{
}
else
{
completionHandler(UIBackgroundFetchResult.NoData);
}
}
Entitlements.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>aps-environment</key>
<string>production</string>
</dict>
</plist>
Info.plist
<key>UIBackgroundModes</key>
<array>
<string>remote-notification</string>
</array>
Try this:
public override void ReceivedRemoteNotification(UIApplication application, NSDictionary userInfo)
{
NSString title = ((userInfo["aps"] as NSDictionary)["alert"] as NSDictionary)["title"] as NSString;
NSString message = ((userInfo["aps"] as NSDictionary)["alert"] as NSDictionary)["body"] as NSString;
if (!App.IsMinimized) // check if app is minimized
{
// your logic
}
}
In your app
public static bool IsMinimized { set; get; } = false;
protected override void OnStart()
{
// Handle when your app starts
IsMinimized = false;
}
protected override void OnSleep()
{
// Handle when your app sleeps
IsMinimized = true;
}
protected override void OnResume()
{
// Handle when your app resumes
IsMinimized = false;
}
I am using Xamarin.Essentials. When I try to get the last known location, the message about device location permission is shown.
If I deny the permission the PermissionException is caught.
How I can check for location and fired again the location permission message?
try
{
var location = await Geolocation.GetLastKnownLocationAsync();
if (location != null)
{
await this.Navigation.PushModalAsync(Nav_to_MAP);
}
}
catch (PermissionException pEx)
{
// if deny location
}
This issue was opened last year, this is the response from James Montemagno:
Right now it will request the permission for you based on how the system handles it. On iOS a permission can only be requested once and on Android, it can be multiple times. If the user declines you will get a permission denied exception.
You can use the Permission Plugin today to handle checking and requesting
https://github.com/jamesmontemagno/PermissionsPlugin
I will open a new proposal for permissions as they are a tad bit tricky.
So, You could use the Permissions Plugin for Xamarin to check for the permission before asking. like so:
var status = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.Location);
if (status != PermissionStatus.Granted)
{
if (await CrossPermissions.Current.ShouldShowRequestPermissionRationaleAsync(Permission.Location))
{
await DisplayAlert("Need location", "Gunna need that location", "OK");
}
var results = await CrossPermissions.Current.RequestPermissionsAsync(Permission.Location);
//Best practice to always check that the key exists
if (results.ContainsKey(Permission.Location))
status = results[Permission.Location];
}
if (status == PermissionStatus.Granted)
{
try
{
var location = await Geolocation.GetLastKnownLocationAsync();
if (location != null)
{
await Navigation.PushModalAsync(Nav_to_MAP);
}
}
catch (PermissionException pEx)
{
// if deny location
}
}
See the Docs on how to set it up
I'm building a Xamarin app and for the geolocation, I'm using the GeolocatorPlugin
The problem is that once the code wants to get the position, the code exists without warning.
My class fields:
private Position position;
private IGeolocator locator = CrossGeolocator.Current;
My page constructor:
public MainPage()
{
InitializeComponent();
locator.PositionChanged += Locator_PositionChanged;
locator.PositionError += Locator_PositionError;
}
OnAppearing event is calling the getLocationPermission:
private async Task GetLocationPermission()
{
var status = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.LocationWhenInUse);
if (status != PermissionStatus.Granted)
{
//Not granted, request permission
if (await CrossPermissions.Current.ShouldShowRequestPermissionRationaleAsync(Permission.LocationWhenInUse))
{
// This is not the actual permission request
await DisplayAlert("Need your permission", "We need to access your location", "Ok");
}
// This is the actual permission request
var results = await CrossPermissions.Current.RequestPermissionsAsync(Permission.LocationWhenInUse);
if (results.ContainsKey(Permission.LocationWhenInUse))
status = results[Permission.LocationWhenInUse];
}
//Already granted, go on
if (status == PermissionStatus.Granted)
{
//Granted, get the location
GetLocation();
await GetVenues();
await locator.StartListeningAsync(TimeSpan.FromMinutes(30), 500);
}
else
{
await DisplayAlert("Access to location denied", "We don't have access to your location.", "OK");
}
}
The permission is granted and gets to the GetLocation() method:
private async void GetLocation()
{
//var locator = CrossGeolocator.Current;
try
{
var myPosition = await locator.GetPositionAsync();
position = new Position(myPosition.Latitude, myPosition.Longitude);
}
catch (Exception ex)
{
throw;
}
if (position == null)
{
//Handle exception
}
}
Once the line is reached with locator.GetPositionAsync(), it stops. No exception is thrown, also the PositionError isn't raised.
I have no idea why, but in the beginning it worked once, never worked after that.
The location settings in de Android Emulator are as follow:
Based on my research, you did not acheved that Location Changes like this link
I wrote a demo about Location changes. This is running screenshot.
This is my demo
https://github.com/851265601/GeolocationDemo
I am trying to find some way which can be used to pick photos within forms without writing platform specific code in both iOS and Android.
You can use the package xam.Plugin.Media from NuGet.
//...
using Plugin.Media;
//...
async void Button_Clicked(object sender, System.EventArgs e)
{
await CrossMedia.Current.Initialize();
if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
{
await DisplayAlert("No Camera", ":( No camera available.", "OK");
return;
}
var file = await CrossMedia.Current.PickPhotoAsync();
if (file == null)
{
return;
}
else
{
await DisplayAlert("File Location", file.Path, "OK");
}
}
Don't forget add Privacy
in iOS (info.plist)
<key>NSCameraUsageDescription</key>
<string>This app needs access to the camera to take photos.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs access to photos.</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app needs access to microphone.</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>This app needs access to the photo gallery.</string>
in Android
if your users are running Marshmallow the Plugin will automatically prompt them for runtime permissions. You must add the Permission Plugin code into your Main or Base Activities:
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Android.Content.PM.Permission[] grantResults)
{
Plugin.Permissions.PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
For more detail you can access here