Persistent Storage using Application.Current.Properties not working - xamarin

I'm trying to achieve a persistent storage in Xamarin.Forms. After researching in Xamarin.Forms, I decided to use Application.Current.Properties property.
It looks like it is working just only if the app still remains alive. If I close the app and start it again the Application.Current.Properties is empty.
Does anyone know if I'm doing something wrong? Can I achieve this feature in another way?
As usual, thanks guys.

I have had a ton of problems with Application.Current.Properties on Android. I highly suggest using Xamarin Settings plugin instead which I have never had any issues with. It is persistent even when the app is closed.
That being said Application.Current.Properties is supposed to work even when you close the app. Not sure why it wouldn't but it does not surprise me either.
*Edit: To use once it is installed, basically CrossSettings.Current is the plugin class that will do the work but the example just creates a separate property to access it. So create a new file, lets call it SettingsImplementation:
public static class SettingsImplementation {
#region Instance
private static Lazy<ISettings> _appSettings;
public static ISettings AppSettings {
get {
if(_appSettings == null) {
_appSettings = new Lazy<ISettings>(() => CrossSettings.Current, LazyThreadSafetyMode.PublicationOnly);
}
return _appSettings.Value;
}
set {
_appSettings = new Lazy<ISettings>(() => value, LazyThreadSafetyMode.PublicationOnly);
}
}
#endregion
private const string UserNameKey = "username_key"; //Key used to get your property
private static readonly string UserNameDefault = string.Empty; //Default value for your property if the key-value pair has not been created yet
public static string UserName {
get { return AppSettings.GetValueOrDefault<string>(UserNameKey, UserNameDefault); }
set { AppSettings.AddOrUpdateValue<string>(UserNameKey, value); }
}
}
Then to use that you would do this anywhere in your app:
SettingsImplementation.UserName = "something";
OR
string username = SettingsImplementation.UserName;

My own problem regarding this issue was due to me not explicitly saving the properties with the following line of code:
Application.Current.SavePropertiesAsync();

you can use Xamarin essentials "Preferences" instead:
Preferences.Set("Key", "Value");
Preferences.Get("Key", "Default");

I ran into the same issue.
The problem:
I was trying to throw complex objects into the Application Properties.
It turns out that the Properties can only take primitive data typs.
This Blog was very helpfull.
https://codemilltech.com/persist-whatever-you-want-with-xamarin-forms/

Related

using signalR .net core client

I have set up a signalR website .net core. My function in my hub is:
public async Task Notify(int id) {
await Clients.All.InvokeAsync("Notified", id);
}
I have also tested this with the following js:
let connection = new signalR.HubConnection(myURL);
connection.on('Notified', data => {
console.log(4, data);
});
connection.start();
The js code seems to work fine and I see the log when I try connection.Invoke('Notify').
Now I have a console app that can needs to make the invoke. I am trying this in two ways and don't mind either solution:
1. A mvc controller within the signalR website that can take the id and invoke 'Notified'.
2. Use the client library Microsoft.AspNetCore.SignalR.Client in the console app.
The way 1 I have only done in classic asp.net like this:
GlobalHost.ConnectionManager.GetHubContext(hubName)
But couldn't find a way to do this in .net core.
Way 2 I have used the library and tried this so far:
var con = new HubConnectionBuilder();
con.WithUrl(myURL);
var connection = con.Build();
connection.InvokeAsync("Notify",args[0]).Wait();
This is the closest I have come to create a connection in the same way as the js code. However this code throws a null pointer when calling connection.InvokeAsync. The connection object is not null. It seems to be an internal object that is null. According to the stack trace the exception is thrown when a MoveNext() function is internally called.
Well looks like both are not currently possible. As of now I just used a forced way which is hopefully temporary.
I have created and used the following base class for hubs:
public abstract class MyHub : Hub
{
private static Dictionary<string, IHubClients> _clients = new Dictionary<string, IHubClients>();
public override Task OnConnectedAsync()
{
var c = base.OnConnectedAsync();
_clients.Remove(Name);
_clients.Add(Name, Clients);
return c;
}
public static IHubClients GetClients(string Name) {
return _clients.GetValueOrDefault(Name);
}
}
GlobalHost is gone. You need to inject IHubContext<THub> like in this sample.
This can be a bug in SignalR alpha1. Can you file an issue on https://github.com/aspnet/signalr and include a simplified repro?

Requesting Android permissions in a class (Xamarin)

I'm trying to request a permission at runtime for my app. I use a service provider to talk between the portable class and Android.
I start by calling this code on button press in the PCL:
using (new Busy(this))
{
var locationHelper = scope.Resolve<ILocationHelper>();
locationHelper.GetLocation(this);
}
This calls my Android level service:
public class AndroidLocationHelper : ILocationHelper, ILocationListener
{
readonly string[] PermissionsLocation =
{
Manifest.Permission.AccessCoarseLocation
};
const int RequestLocationId = 0;
public void GetLocation(SearchViewModel viewModel)
{
try
{
const string permission = Manifest.Permission.AccessCoarseLocation;
if (((int)Build.VERSION.SdkInt < 23) || (CheckSelfPermission(permission) == Permission.Granted))
{
}
else
RequestPermissions(PermissionsLocation, RequestLocationId);
}
catch (Exception ex)
{
Debug.WriteLine("Error while getting Location service");
Debug.WriteLine(ex.Message);
Messaging.AlertUser("There was an error with determining your location");
}
}
However, I get two errors on CheckSelfPermission and RequestPermissions. These two methods are only available to activities. The code works fine in MainActivity; however, I want to ask for permissions when the user hits a button, not in OnCreate or OnResume, etc.
Thanks for any help.
In your Android project, You can use this and use the Dependency Service to call it in Xamarin.Forms PCL project later:
var thisActivity = Forms.Context as Activity;
ActivityCompat.RequestPermissions(thisActivity, new string[] {
Manifest.Permission.AccessFineLocation }, 1);
ActivityCompat.RequestPermissions(thisActivity,
new String[] { Manifest.Permission.AccessFineLocation },
1);
You can try with ContextCompat.CheckSelfPermission, passing the application context, like this:
ContextCompat.CheckSelfPermission(Android.App.Application.Context, permission)
Update
In case of ActivityCompat.RequestPermissions, which requires an activity reference, you can keep track of the current activity. There is a very handy lib for that, called "CurrentActivityPlugin". You can find at https://github.com/jamesmontemagno/CurrentActivityPlugin
Rafael came up with a solution but I found another option that is a lot less effort just using MessagingCenter. In the MainActivity's OnCreate add a receiver that runs all the location code, that way you have access to all of the activities methods (and there are a bunch of tutorials on doing location services in MainActivity). Then add the Send inside of your service (the class).
To expound Rafael Steil's answer, I tried the suggested CurrentActivityPlugin and it worked on me. In my case I am trying to execute a voice call which needs CALL_PHONE permission. Here is the code snippet in your case: I used the ContextCompat & ActivityCompat so that I don't need to check the VERSION.SdkInt
using Plugin.CurrentActivity;
public void GetLocation(SearchViewModel viewModel){
var context = CrossCurrentActivity.Current.AppContext;
var activity = CrossCurrentActivity.Current.Activity;
int YOUR_ASSIGNED_REQUEST_CODE = 9;
if (ContextCompat.CheckSelfPermission(context, Manifest.Permission.AccessCoarseLocation) == (int)Android.Content.PM.Permission.Granted)
{
//Permission is granted, execute stuff
}
else
{
ActivityCompat.RequestPermissions(activity, new string[] { Manifest.Permission.AccessCoarseLocation }, YOUR_ASSIGNED_REQUEST_CODE);
}
}
It's dead simple
public bool CheckPermission()
{
const string permission = Manifest.Permission.ReceiveSms;
return ContextCompat.CheckSelfPermission(Forms.Context, permission) == (int) Permission.Granted;
}

Automatically detect when storing an object with ServiceStack.Redis

I am looking for a way to subscribe to events like Storing a specific object type to ServiceStack.Redis.
For example I may
using (var redisClient = new RedisClient())
using (var redisMyObjects = redisClient.As<MyObject>())
{
redisMyObjects.Store(myObject);//<-- I want this to trigger an event somehow
}
Is there anything like a OnStore event which I can hook too, anything out of the box? if not, is there any recommendation about how this should be done?
I don't think there is anything you can hook into (could be wrong).
Two options that came to mind:
1 - Make an extension method
2 - Publish a message to store your object and have a handler that listens for a response and does something. This is probably overkill since it's heading into the publish/subscribe realm. But, I think, worth looking into. (Basic example here and see Pub/Sub here).
Extension Method
public static class RedisClientExtensions
{
public static void StoreWithTrigger<T>(this IRedisTypedClient<T> redisClient, T value, Action<T> trigger)
{
redisClient.Store(value);
trigger(value);
}
}
Using ExtensionMethod
public void MyMethod()
{
using (var redisClient = new RedisClient())
using (var redisMyObjects = redisClient.As<MyObject>())
{
redisMyObjects.StoreWithTrigger<MyObject>(new MyObject(), TriggerEvent);//<-- I want this to trigger an event somehow
}
}
private void TriggerEvent<T>(T value)
{
//dosomething
}
Hope this gives you some ideas.

Fetch windows setting value

How do I fetch the Measurement System setting value in javascript?
I'm guessing that it would be throw some WinJS call.
The logical place would be Windows.Globalization, but not seeing if offered there. One pretty simple workaround - faster to write than to research the setting :) is to create a Windows Runtime Component in C# that calls in to System.Globalization:
namespace WindowsRuntimeComponent
{
public sealed class RegionalSettings
{
public bool isMetric()
{
return System.Globalization.RegionInfo.CurrentRegion.IsMetric;
}
}
}
Then add as a reference to your JavaScript app and invoke there:
var r = new WindowsRuntimeComponent.RegionalSettings;
var isMetric = r.isMetric();

ViewModels and IsolatedStorageSettings

Im working on a MVVM Windows phone app that displays weather info.
When the app loads up it opens MainPage.xaml. It makes a call the the service to get weather info and binds that data to the UI. Both Fahrenheit and Celcius info are returned but only one is displayed.
On the setting page, the user can select to view the temp in either Fahrenheit or Celcius.
The user can change this setting at any time and its stored in IsolatedStorageSettings.
The issue Im having is this:
when the user navigates to the Settings page and changes their preference for either Fahrenheit or Celcius, this change is not reflected on the main page.
This issue started me thinking about this in a broader context. I can see this being an issue in ANY MVVM app where the display depends on some setting in IsolatedStorage. Any time any setting in the IsoStore is updated, how does the ViewModels know this? When I navigate back in the NavigationStack from the settings page back to MainPage how can I force a rebind of the page?
The data in my model hasnt changed, only the data that I want to display has changed.
Am I missing something simple here?
Thanks in advance.
Alex
Probably you have code like this:
public double DisplayTemperature
{
get { return (IsCelsium) ? Celsium : Fahrenheit; }
}
And IsCelsium is:
public double IsCelsium
{
get { return (bool)settings["IsCelsium"]; }
set { settings["IsCelsium"] = value; }
}
So you need to add NotifyPropertyChanged event to notify UI to get new values from DisplayTemperature property:
public double IsCelsium
{
get { return (bool)settings["IsCelsium"]; }
set
{
settings["IsCelsium"] = value;
NotifyPropertyChanged("DisplayTemperature");
}
}
Take a look at Caliburn Micro. You could implement something similar or use CM itself. When using CM I don't even think about this stuff, CM makes it so simple.
When your ViewModel inherits from Screen there are life-cycle events that fire that you can override. For example, OnInitialize fires the very first time the ViewModel is Activated and OnActivate fires every time the VM is activated. There's also OnViewAttached and OnViewLoaded.
These methods are the perfect place to put logic to populate or re-populate data.
CM also has some special built in features for allowing one to easily tombstone a single property or an entire object graph into Iso or phone state.
ok, so Ive come up with a solution. Before I get to it, let me provide some background. The app that Im working on uses both MVVM Light and WP7Contrib. That being the case, I am using Funq for DI and the MVVMLight Toolkit. After I posted my initial question, I gave the question a bit more thought. I remembered a video that I watched a while back from MIX2011 called Deep Dive MVVM with Laurent Bugnion
http://channel9.msdn.com/Events/MIX/MIX11/OPN03
In it, he talks about just this problem (view models not living at the same time) on Windows Phone. The part in question starts around the 19 minute mark.
Anyway, after I remembered that and realized that the ViewModel locator is exposed in App.xaml, this became a trivial problem to solve. When the user changes the Fahrenheit/Celcius option on the setting page, I simply get a reference to the MainViewModel via the ViewModelLocator and reset the collection that is bound to the UI thus causing the bindings to update.
public bool AddOrUpdateValue(string Key, Object value)
{
bool valueChanged = false;
// If the key exists
if (settings.Contains(Key))
{
// If the value has changed
if (settings[Key] != value)
{
// Store the new value
settings[Key] = value;
valueChanged = true;
}
}
// Otherwise create the key.
else
{
settings.Add(Key, value);
valueChanged = true;
}
return valueChanged;
}
public bool ImperialSetting
{
get
{
return GetValueOrDefault<bool>(ImperialSettingKeyName, ImperialSettingDefault);
}
set
{
if (AddOrUpdateValue(ImperialSettingKeyName, value))
{
Save();
RaisePropertyChanged("ImperialSettingText");
var vml = new ViewModelLocator();
vml.MainViewModel.Cities = (App.Current as App).Cities;
}
}
}
It was a mistake on my part not to realize that I could get access to the viewModel via the ViewModelLocator. Hopefully this post saves someone else the time I burned on this issue.

Resources