I saved a token in the mobile with SharedFunctions interface i created
In IOS:
public void SaveAccessToken(string token)
{
NSUserDefaults.StandardUserDefaults.SetString(token, "AccessToken");
}
In Android:
public void SaveAccessToken(string token)
{
var mSharedPrefs = PreferenceManager.GetDefaultSharedPreferences(Android.App.Application.Context);
var mPrefsEditor = mSharedPrefs.Edit();
mPrefsEditor.PutString("AccessToken", token);
mPrefsEditor.Commit();
mPrefsEditor.Dispose();
mSharedPrefs.Dispose();
}
I also want to save a object instead of a string.
Having a look at the documentation of NSUserDefaults, it seems as if writing an object is somewhat restricted, hence I'd suggest you to go another way: Serialize the object using Newtonsoft.JSON (or another JSON library) and write the serialized object.
For iOS:
public void SaveAccessToken<T>(string key, T value)
{
var serializedObject = JsonConvert.SerializeObject(value);
NSUserDefaults.StandardUserDefaults.SetString(serializedObject, key);
}
and similarly for Android
public void SaveAccessToken<T>(string key, T value)
{
var serializedObject = JsonConvert.SerializeObject(value);
var mSharedPrefs = PreferenceManager.GetDefaultSharedPreferences(Android.App.Application.Context);
var mPrefsEditor = mSharedPrefs.Edit();
mPrefsEditor.PutString(serializedObject, key);
mPrefsEditor.Commit();
mPrefsEditor.Dispose();
mSharedPrefs.Dispose();
}
Related
i am trying to implement fcm in my xamarin.forms app. so i am planning to use dependency service on each platform to get the token and save it in my database. i found that this FirebaseInstanceId.Instance.Token is deprecated. so i have to use this instead var instanceIdResult = await FirebaseInstanceId.Instance.GetInstanceId().AsAsync<IInstanceIdResult>(); var token = instanceIdResult.Token;
the thing is that i am not figuring out how to put in the code. i tried this:
GetToken.cs in my xamarin.forms app:
public interface GetToken
{
Task <string> get_token();
}
and this in my xamarin.android
[assembly: Xamarin.Forms.Dependency(typeof(gettoken))]
namespace App8.Droid
{
internal class gettoken : GetToken
{
public Task<string> get_token()
{
Task<string> t = Task.Run(() => tokenToget());
return t;
}
async private Task<string> tokenToget()
{
var instanceIdResult = await FirebaseInstanceId.Instance.GetInstanceId().AsAsync<IInstanceIdResult>();
var token = instanceIdResult.Token;
return token;
}
}
and implemented the service this way in my mainactivity.xaml.cs
Task<string> token = DependencyService.Get<GetToken>().get_token();
System.Diagnostics.Debug.WriteLine($"Token: {token.Result}");
but the app stops. how can i use the code to return the token. note that when i don't use a task and just use it this way (use void as a return type and only call the depedency from the xamarin.forms as DependencyService.Get<GetToken>().get_token();)
async public void get_token()
{
var instanceIdResult = await FirebaseInstanceId.Instance.GetInstanceId().AsAsync<IInstanceIdResult>();
var token = instanceIdResult.Token;
Log.Debug("token", "Refreshed token: " + token);
}
it works. so the problem seems to be in the way i am trying to use the task or something like that. what should i do? thanks in advance.
declare your interface
public interface IToken
{
Task <string> GetToken();
}
then implement it
public class Token : IToken
{
public async Task<string> GetToken()
{
var instanceIdResult = await FirebaseInstanceId.Instance.GetInstanceId().AsAsync<IInstanceIdResult>();
var token = instanceIdResult.Token;
return token;
}
}
i am new to flutter and firebase development, so i really don't know how much will it cost me to keep fetching user data from firebase in every screen that i need them in, so i decided to fetch them once and store them in class MyUser static variables as follows:
in MyApp class:
bool isAuthenticated = false;
Future checkAuthenticity() async {
AuthService.getCurrentUser().then((user) async {
if (user != null) {
String myUid = await AuthService.getCurrentUID();
await MyUserController().getCurrentUserFromFirebase(myUid);
if (mounted)
setState(() {
isAuthenticated = true;
});
} else {
if (mounted)
setState(() {
isAuthenticated = false;
});
}
});
}
#override
Widget build(BuildContext context) {
home: isAuthenticated ? Home(passedSelectedIndex: 0) : Register(),
}
from the above code, this line await MyUserController().getCurrentUserFromFirebase(myUid); is as follows:
getCurrentUserFromFirebase(String uid) async {
await FirestoreService().getCurrentUserData(uid);
}
from the above code, this line await FirestoreService().getCurrentUserData(uid); is as follows:
Future getCurrentUserData(String uid) async {
try {
var userData = await FirebaseFirestore.instance.collection('users').doc(uid).get();
MyUser.fromData(userData.data());
} catch (e) {
if (e is PlatformException) {
return e.message;
}
return e.toString();
}
}
from the above code, this line MyUser.fromData(userData.data()); is a constructor in
MyUser class as follows:
class MyUser {
static String uid;
static String name;
static String username;
static String email;
static String userAvatarUrl;
static String location;
static String phoneNumber;
MyUser.fromData(Map<String, dynamic> data) {
uid = data['id'];
name = data['name'];
username = data['username'];
email = data['email'];
userAvatarUrl = data['userAvatarUrl'];
location = data['location'];
phoneNumber = data['phoneNumber'];
}
}
and to make use of all of the following, in each page that i need to load the current user data in, i use for example:
var userId = MyUser.uid
or to show the current user name i use Text('${MyUser.name}');
when i close the app completely and relaunch it again, it should check for authenticity, and complete executing the rest of the code in main() function.
so my questions are:
1) does this have any performance issues when we release the app?
2) does this will really will prevent unnecessary reads that i can consume in every page i need the data in ?
3) is there any better approach to prevent unnecessary reads from firebase, for example to save the current user data as strings and a profile image locally?
pardon me for prolonging the question, but i wanted to share the code itself.
any help would be much appreciated.
As a short answer,
You can make a class of SharedPreferences to store data as strings in key: value manner.
So anywhere you want you can get an instance of that class and reach it from anywhere in the app.
If you also declare some functions which will decode string to json you will get a ready user class instance in return of your function which will make it easier.
So when you want to save user info to Local Storage(SharedPreferences) you may use a function which will encode your User object to string and save it to SharedPreferences as below..
user.dart' as theUser; for conflict issues
class SharedPrefs {
static SharedPreferences _sharedPrefs;
init() async {
if (_sharedPrefs == null) {
_sharedPrefs = await SharedPreferences.getInstance();
}
}
dynamic get user=> _sharedPrefs.getString('user')!=null?theUser.User.fromString(_sharedPrefs.getString('user')):null;
set user(theUser.User user)=> _sharedPrefs.setString('user', jsonEncode(user));
String get accessToken=> _sharedPrefs.getString('access_token');
set accessToken(String accessToken)=> _sharedPrefs.setString('access_token', accessToken);
void removeString(String entry){
_sharedPrefs.remove(entry);
}
}
final sharedPrefs = SharedPrefs();
And in the app anywhere you can use it directly by typing sharedPrefs.user
I'm trying to access images in Firebase Storage and cache them locally on the device.
My current attempt uses flutter_cache_manager. The documentation states:
Most common file service will be an [HttpFileService], however one can also make something more specialized. For example you could fetch files from other apps or from local storage.
class HttpFileService implements FileService {
http.Client _httpClient;
HttpFileService({http.Client httpClient}) {
_httpClient = httpClient ?? http.Client();
}
#override
Future<FileServiceResponse> get(String url,
{Map<String, String> headers = const {}}) async {
final req = http.Request('GET', Uri.parse(url));
req.headers.addAll(headers);
final httpResponse = await _httpClient.send(req);
return HttpGetResponse(httpResponse);
}
}
I've tried to extend this class to process the URL for Firebase
class FirebaseHttpFileService extends HttpFileService {
#override
Future<FileServiceResponse> get(String url, {Map<String, String> headers = const {}}) async {
var ref = FirebaseStorage.instance.ref().child(url);
var _url = await ref.getDownloadURL() as String;
return super.get(_url);
}
}
And extend the BaseCacheManager using a template from the GitHub repo, replacing the file service with my new one.
class FirebaseCacheManager extends BaseCacheManager {
static const key = "firebaseCache";
static FirebaseCacheManager _instance;
factory FirebaseCacheManager() {
if (_instance == null) {
_instance = new FirebaseCacheManager._();
}
return _instance;
}
FirebaseCacheManager._() : super(key,
maxAgeCacheObject: Duration(days: 7),
maxNrOfCacheObjects: 20,
fileService: FirebaseHttpFileService());
Future<String> getFilePath() async {
var directory = await getTemporaryDirectory();
return p.join(directory.path, key);
}
}
But I get the following error:
setState() called after dispose(): _ImageState#50d41(lifecycle state: defunct, not mounted, stream: ImageStream#ac6d5(MultiFrameImageStreamCompleter#0c956, [2448×3264] # 1.0x, 3 listeners), pixels: null, loadingProgress: null, frameNumber: null, wasSynchronouslyLoaded: false)
I can process the URL before attempting to retrieve the file but that needlessly wastes time. I've also tried to use other packages like Flutter Cache Image but it seems to crash the app after a short amount of time.
Thanks for any pointers in the right direction!
This problem is actually tied to the errorWidget as seen in the issue here.
The code is working if the error widget is commented out in CachedNetworkImage.
I try to do some chechking everytime when user try to start the application. This is my code example:
protected override async void OnStart()
{
// Handle when your app starts
var user = await FinDataStore.GetUserToken(DependencyService.Get<ISharedFunctions>().GetUser().UserName, DependencyService.Get<ISharedFunctions>().GetUserPassword());
if (user != null && user.AccessToken != null)
{
DependencyService.Get<ISharedFunctions>().SaveAccessToken(user.AccessToken);
DependencyService.Get<ISharedFunctions>().SaveUser(user);
DependencyService.Get<ISharedFunctions>().SaveRefreshToken(user.RefreshToken);
DependencyService.Get<ISharedFunctions>().SaveUserFirmi(user.Firmi);
}
else
{
((App)Application.Current).Logout();
}
}
but i get the error:
Error CS0120 An object reference is required for the non-static field, method, or property 'FinDataStore.GetUserToken(string, string)'
This is the call :
public async Task<User> GetUserToken(string username, string password)
How to solve this?
It's seems not to be a async problem.
It seems you should do something like:
var myclass = new FinDataStore();
then you can
var ret = await myClass.GetUserToken...
I'm currently developing my own AuthorizationManager, it looks something like that:
public class MyAuthorizationManager : ServiceAuthorizationManager
{
static bool initialize = false;
public override bool CheckAccess(OperationContext operationContext)
{
ServiceSecurityContext context = ServiceSecurityContext.Current;
string[] roles = Roles.GetRolesForUser(operationContext.ServiceSecurityContext.PrimaryIdentity.Name);
return roles.Count() > 0;
}
public override bool CheckAccess(OperationContext operationContext, ref System.ServiceModel.Channels.Message message)
{
MessageBuffer buffer = operationContext.RequestContext.RequestMessage.CreateBufferedCopy(int.MaxValue);
message = buffer.CreateMessage();
Console.WriteLine(message);
return base.CheckAccess(operationContext, ref message);
}
}
I would like to perform authorization check based on a service contract parameter, in example, if contract looks like:
[ServiceContract]
public interface IServerContract
{
[OperationContract]
[ServiceKnownType(typeof(ChildTypeOne))]
[ServiceKnownType(typeof(ChildTypeTwo))]
string SecuredMessage(ParentType incoming);
}
My goal is authorizing depending on type, in example, authorizing if incoming date is ChildTypeOne and deniying in case it was ChildTypeTwo.
I've checked "Message" and it looks like:
It must be decrypted
Seems to be highly dependent on binding
Is there any easy way to simply get parameter type?
Ok, i've figured out how to perform that. Anyway, if you know any better way to do so, let me know:
Here is the AuthorizationManager i'm using:
public class MyAuthorizationManager : ServiceAuthorizationManager
{
static bool initialize = false;
public override bool CheckAccess(OperationContext operationContext, ref System.ServiceModel.Channels.Message message)
{
bool returnedValue = base.CheckAccess(operationContext, ref message);
// messags in WCF are always read-once
// we create one copy to work with, and one copy to return back to the plumbing
MessageBuffer buffer = operationContext.RequestContext.RequestMessage.CreateBufferedCopy(int.MaxValue);
message = buffer.CreateMessage();
// get the username vale using XPath
XPathNavigator nav = buffer.CreateNavigator();
StandardNamespaceManager nsm = new StandardNamespaceManager(nav.NameTable);
nav = nav.SelectSingleNode("//#i:type",nsm);
returnedValue &= (nav.ToString() == "a:"+typeof(ChildTypeOne).Name);
return returnedValue;
}
public class StandardNamespaceManager : XmlNamespaceManager
{
public StandardNamespaceManager(XmlNameTable nameTable)
: base(nameTable)
{
this.AddNamespace("s", "http://schemas.xmlsoap.org/soap/envelope/");
this.AddNamespace("s11", "http://schemas.xmlsoap.org/soap/envelope/");
this.AddNamespace("s12", "http://www.w3.org/2003/05/soap-envelope");
this.AddNamespace("wsaAugust2004", "http://schemas.xmlsoap.org/ws/2004/08/addressing");
this.AddNamespace("wsa10", "http://www.w3.org/2005/08/addressing");
this.AddNamespace("i", "http://www.w3.org/2001/XMLSchema-instance");
}
}
}
Previous AuthorizationManager will work rejecting "ChildTypeTwo". You can use a RoleProvider in order to get role based on type.