I want to use the following class to store account data to make it accessible to every part of the app.
class AccountData {
static AccountData _instance = new AccountData._internal();
static GoogleSignInAccount _googleData;
factory AccountData() {
return _instance;
}
AccountData._internal();
static void setData(GoogleSignInAccount googleData)
{
_googleData = googleData;
}
GoogleSignInAccount get getData{
return _googleData;
}
String getUserID(){
return "5";
}
List<Workout> getWorkouts(){
return null;
}
Workout getWorkoutByDate(String workoutID, String date)
{
return null;
}
}
I setup the google account after the user logged in, using the following code.
_googleSignIn.onCurrentUserChanged.listen((GoogleSignInAccount account) {
AccountData.setData(account);
print("Logged in, id: " + account.id);
runApp(new HomeState());
});
At this point I can access all fields of account and everything is working fine. The only way to get past the login screen is through this part of the code. Therefore the account data always gets assigned.
#override
void initState() {
super.initState();
AccountData d = new AccountData();
print(d.getData);
}
If I try to access the account data in the HomeState, the GoogleSignInAccount that gets returned is null which it shouldn't be.
I hope that someone knows why this happens and knows how to solve the problem.
You are storing the data in a static variable of the class from a static method AccountData.setData(account);
You should also statically access the data to recover them:
AccountData.getData();
Previously, you need to define the method as static in the class body:
static GoogleSignInAccount get getData{
return _googleData;
}
Then, since everything is static for the _googleData you don't need to create an instance of AccountData.
Otherwise, you could change all of them to be instance variable and methods, and you should create the instance upfront.
Related
I use Lazy initialization for app singleton in Xamarin.Forms (app runs on iOS):
public sealed class DataSingleton
{
private static readonly Lazy<DataSingleton> lazy = new Lazy<DataSingleton>(() => new DataSingleton(), LazyThreadSafetyMode.PublicationOnly); // tried withou 2nd parameter as well
public static DataSingleton Instance
{
get { return lazy.Value; }
}
...
}
And i call it in webserver which runs to provide data for Front-end which is in Angular (used web view to show Angular code)
var server = new WebServer(o => o
.WithUrlPrefix($"http://localhost:{appSettings.ApiPort}")
.WithMode(HttpListenerMode.EmbedIO))
.WithCors()
.WithLocalSessionManager()
.WithWebApi("/api", m => m
.WithController<TestController>()
.WithController<SettingsController>()
.WithController<ReportController>()
.WithController<SystemController>())
.WithModule(new ActionModule("/", HttpVerbs.Any,
ctx => ctx.SendDataAsync(new { Message = "Error" })))
.RunAsync();
In controllers is called DataSingleton to get/set data, but after app returns from background, DataSingleton.Instance is null.
What should I do to don't lose data of singleton, while app is in background for short time (approximately 5 minutes)
Update - I've figured out that this problem is only in Controllers, cause when app gets back to front I can see all the data in AppDelegate WillEnterForeground event..
Given that it is the webserver that is having the problem, stop it when app goes into background. Start it again when app returns (or lazy-start as needed).
Code might be something like this:
App.xaml.cs:
public static Webserver MyWebServer
{
get
{
if (_server == null)
{
_server = new Webserver(...);
}
return _server;
}
}
public static void StopWebServer()
{
if (_server != null)
{
_server.Dispose();
// So will be created again, on next reference to MyWebServer.
_server = null;
}
}
private static Webserver _server;
...
protected override void OnSleep()
{
StopWebServer();
}
Usage elsewhere:
... App.MyWebServer ...
If you don't want to make static variable (though IMHO that is okay for App, because there is only one, and its lifetime is that of the app itself), then remove the "static"s above, usage elsewhere becomes:
... (Xamarin.Forms.Application.Current as App).MyWebServer ...
In this case, there can be race-condition.
If two (or more threads) simultaneously reads Instance for first time, there will be created multiple instances of DataSingleton. However every other read will get just one instance. It depends on your scenario, if it is ok.
public sealed class DataSingleton {
private static instance;
// will assign new DataSingleton only if "instance" is null.
public static Instance => instance ??= new DataSingleton();
}
Or you can use Interlocked class ensuring, the instance field will not be overriden if another thread already initialized the instance field.
public sealed class DataSingleton {
private static instance;
public static Instance {
get {
var result = instance;
// early exit if singleton is already initialized
if (result is not null) return result;
var created = new DataSingleton();
// thread-safe way how to assign "created" into "instance" only if "instance" refers to null. othervise no assignment will be made
var original = Interlocked.CompareExchange(ref instance, null, created);
// some other thread already initialized singleton
if (original is not null) return original;
// return newly created instance
return result;
}
}
}
Or you can use lock to ensure, just one instance is created.
public sealed class DataSingleton {
private static instance;
public static Instance {
get {
var result = instance;
// early exit if singleton is already initialized
if (result is not null) return result;
lock(typeof(DataSingleton)) {
result = instance;
// double check, if instance was not initialized by another thread
if (result is not null) return result;
return instance = new DataSingleton();
}
}
}
}
I've created a small interface:
import ...
abstract class IController {
void navigateTo(BuildContext context, String routeName);
Future<LocationData> get location;
// registration process
void registerGender(Gender gender);
void registerBirthday(DateTime birthday);
Future<bool> register(String email, String password);
}
And then I tried to implement this:
import ...
class Controller implements IController {
static final Controller _instance = Controller._internal();
final ServiceAuthenticate _serviceAuth = ServiceAuthenticate();
final ServiceDatabase _serviceDb = ServiceDatabase();
final ServiceGPS _serviceGPS = ServiceGPS();
User _user;
String _routeName;
UserData _userData;
Controller._internal() {
this._routeName = ROUTE_WELCOME;
}
factory Controller() => _instance;
void navigateTo(BuildContext context, String routeName) {
this._routeName = routeName;
Navigator.pushReplacementNamed(context, routeName);
}
Future<LocationData> get location async{
this._userData.location = await this._serviceGPS.location;
print(this._userData.location);
return this._userData.location;
}
void registerGender(Gender gender){
this._userData = UserData();
this._userData.gender = gender;
}
void registerBirthday(DateTime birthday) {
this._userData.birthday = birthday;
}
Future<bool> register(String email, String password) async {
User user = await this._serviceAuth.registerWithEmailAndPassword(email, password);
if(user == null){
return false;
}
this._user = user;
return true;
}
}
But that code produces the following error:
error: 'Controller.navigateTo' ('void Function(BuildContext, String)') isn't a valid override of 'IController.navigateTo' ('void Function(dynamic, String)'). (invalid_override at [prototype] lib\controller\controller.dart:30)
It looks like Dart thinks, that the BuildContext in the IController is dynamic, but this is obviously not the case.
How can I fix this? I'm new to Dart and don't know what to do.
Thanks for help :)
I'm stupid.
My import statement was wrong.
The line
import 'package:prototype/Controller/IController.dart';
produced this error, because the folder controller starts with a lowercase Letter.
The correct import statement is
import 'package:prototype/controller/IController.dart';
But regardless of my stupid mistake is the error message quite interesting.
A had a similar error and in my case the problem was that the return type of the buggy function was such that there were two different classes in the codebase with the same name. And the interface was using one and the implementation the other.
The one line answer is :
Your import statement is wrong.
But now , you need to take care in which file the import statement is going wrong.
There can be many scenarios, but I would like to give an example where I was stuck.
I had two different files in different package, but both files were importing some method where the method names were same.
So while importing the file which contain this method, I had imported the same name method from one file, and at other place, the same name method from second file.
So that's where everything went wrong!
So if import file is correct in the file which is giving some error, check the other dependent file, where same method import statement is written, that may be wrong.
I'm currently trying to create a GlobalExtension for my Geb-Spock framework. So far here is my extension:
class OnFailureListener extends AbstractRunListener {
private final String id
private final SauceREST sauceREST
public OnFailureListener(String id, String username, String accessKey) {
this.id = id
this.sauceREST = new SauceREST(username, accessKey)
}
def void error(ErrorInfo error) {
println error;
this.sauceREST.updateJobInfo(this.sessionIdProvider.getSessionId(), "failed")
}
}
class ResultExtension extends AbstractGlobalExtension {
protected final String username = System.getenv("SAUCE_USERNAME")
protected final String accesskey = System.getenv("SAUCE_ACCESS_KEY")
protected final String sessionId
#Override
void visitSpec(SpecInfo specInfo) {
specInfo.addListener(new OnFailureListener(sessionId, username, accesskey))
}
}
My issue is that the sesssionId value gets assigned in the GebSpec base class I'm using for other specs, and cannot be assigned directly in the extension class. Beyond using some gnarly reflection approaches, is there a way to access the sessionId value assigned in the base class in the extension? I'd also like to avoid using an AnnotationExtension since I'd like to apply this globally without modifying any spec code (similar to a JUnit TestWatcher pattern).
The easiest way for you would be to write the sessionId into a shared ThreadLocal that can be accessed by your listener and the spec, otherwise you'll have to implement an org.spockframework.runtime.extension.IMethodInterceptor so that you can gain access to the actual test instance to extract the field value.
I'm new to dart. Currently, working on a mobile app through flutter. I have a Helper class which has some common methods which I've planned throughout the app. I've included that Helper class in another class. But, can't able to fig. out how to access its methods.
My commom Helper class code:
import 'dart:async';
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
class Helper {
Map userDetails = {};
Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
// --- Method for getting user details from shared preference ---
Future<Map>getUserDetailsFromSharedPreference () async {
try {
final SharedPreferences prefs = await _prefs;
if(prefs.getString('user') != null) {
this.userDetails = json.decode(prefs.getString('user'));
} else {
print('Shared preference has no data');
}
} catch (e){
print('Exception caught at getUserDetails method');
print(e.toString());
}
return this.userDetails;
}
}
Here is my main program code where I've included the Helper class & trying to access it's getUserDetailsFromSharedPreference (). In this case, I'm getting an error like Only static memebers can be accessed in initializers. I also tried to extends Helper class in UserProfile class. But, there also I'm getting a different kind of errors. Can't able to identify how to do this thing.
import 'package:flutter/material.dart';
import 'helper.dart';
class UserProfile extends StatefulWidget {
#override
UserProfileState createState() => new UserProfileState();
}
class UserProfileState extends State<UserProfile> {
Helper helper = new Helper();
var userData = helper.getUserDetailsFromSharedPreference();
}
#Günter Zöchbauer I've made my Helper.dart file like this as you've suggested -
import 'dart:async';
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
class Helper {
Map userDetails = {};
Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
static Helper _instance;
factory Helper() => _instance ??= new Helper._();
Helper._();
// --- Method for getting user details from shared preference ---
Future<Map>getUserDetailsFromSharedPreference () async {
try {
final SharedPreferences prefs = await _prefs;
if(prefs.getString('user') != null) {
this.userDetails = json.decode(prefs.getString('user'));
} else {
print('Shared preference has no data');
}
} catch (e){
print('Exception caught at getUserDetails method');
print(e.toString());
}
return this.userDetails;
}
}
Now, in my tryint to access that getUserDetailsFromSharedPreference() method I'm getting the same error Only static memebers can be accessed in initializers .
You could ensure a singleton instance of the class using a public factory constructor with a private regular constructor:
class Helper {
static Helper _instance;
factory Helper() => _instance ??= new Helper._();
Helper._();
...
}
If you call new Helper(), you'll always get the same instance.
You need to import the file that contains class Helper {} everywhere where you want to use it.
??= means new Helper._() is only executed when _instance is null and if it is executed the result will be assigned to _instance before it is returned to the caller.
update
getUserDetailsFromSharedPreference is async and can therefore not be used in the way you use it, at least it will not lead to the expected result. getUserDetailsFromSharedPreference returns a Future that provides the result when the Future completes.
class UserProfileState extends State<UserProfile> {
Helper helper = new Helper();
Future<Map> _userData; // this with ??= of the next line is to prevent `getUserDetailsFromSharedPreference` to be called more than once
Future<Map> get userData => _userData ??= helper.getUserDetailsFromSharedPreference();
}
If you need to access userData you need to mark the method where you do with async and use await to get the result.
foo() async {
var ud = await userData;
print(ud);
}
To access other class method you can simply put static on the method.
class Helper {
static printing(String someText){
print(someText);
}
}
void main() {
Helper.printing('Hello World!');
}
I think this question is more related to accessing one class data in another class. So I explained on the basis of my understanding of the question but if I'm not correct about it.
but if you want to access data of class A, not directly but through class B.
so first you have to make an object of A in class B but remember one thing you would have to make the object static in order to get access to the data of class A within Class B
If you still are confused about all this, I made a solution video.
Check it out: https://youtu.be/shK7ZraruCI
I have a class that defines a method that returns a Future. The Future contains a list of class that also return a future.
class User{
Future<List<Album>> albums(){
};
}
class Album{
Future<List<Photos>> photos(){
}
};
What is the best way to mock the method in these classes when testing another class?
The class I am trying to test looks a bit like
class Presenter {
Presenter( User user){
user.albums().then( _processAlbums);
}
_processAlbums(List<Album> albums) {
albums.forEach( (album)=>album.photos.then( _processPhotos));
}
_processPhotos(List<Photo> photos) {
....stuff
}
}
I tried writing a unit test like this
class MockUser extends Mock implements User{}
class MockAlbum extends Mock implements Album{}
class MockPhoto extends Mock implements Photo{}
class MockFutureList<T> extends Mock implements Future<T>{
MockFutureList( List<T> items){
when( callsTo( "then")).thenReturn( items);
}
}
void main(){
test("constuctor should request the albums from the user ",(){
MockUser user = new MockUser();
MockAlbum album = new MockAlbum();
List<Album> listOfAlbums = [ album];
MockPhoto photo = new MockPhoto();
List<Album> listOfPhotos = [ album];
user.when( callsTo( "albums")).thenReturn( new MockFutureList(listOfAlbums));
album.when( callsTo( "photos")).thenReturn( new MockFutureList( listOfPhotos));
PicasaPhotoPresentor underTest = new PicasaPhotoPresentor( view, user);
user.getLogs( callsTo( "albums")).verify( happenedOnce);
album.getLogs( callsTo( "photos")).verify( happenedOnce);
});
}
This allowed me to test that the constructor called the user.photos() method, but not that the album.photos() method was called.
I am not sure that mocking a Future is a good idea - Would it not be better to create a 'real' Future that contains a list of Mocks?
Any ideas would be very helpful!
Since you're only interested in verifying that methods in User and Album are called, you won't need to mock the Future.
Verifying the mocks gets a bit tricky here, because you're chaining futures inside the constructor. With a little understanding of how the event loop works in Dart, I recommend using a future and calling expectAsync after you create your presenter.
The expectAsync function tells the unit test library to wait until it's called to verify your tests. Otherwise the test will complete successfully without running your expectations.
With this, here's what your test should would look like:
import 'package:unittest/unittest.dart';
class MockUser extends Mock implements User {}
class MockAlbum extends Mock implements Album {}
void main() {
test("constuctor should request the albums from the user ", () {
var user = new MockUser();
var album = new MockAlbum();
user.when(callsTo("albums")).thenReturn(new Future(() => [album]));
var presenter = new PicasaPhotoPresentor(view, user);
// Verify the mocks on the next event loop.
new Future(expectAsync(() {
album.getLogs(callsTo("photos")).verify(happendOnce);
}));
});
}
Here is how I managed to do it
1) Define FutureCallbackMock
class FutureCallbackMock extends Mock implements Function {
Future<void> call();
}
2) get function from a mock and set it up
FutureCallback onPressed = FutureCallbackMock().call;
completer = Completer<void>();
future = completer.future;
when(onPressed()).thenAnswer((_) => future);
3) Verify like so
verify(onPressed()).called(1);
4) Complete the future if needed:
completer.complete();
NOTE: in flutter tests I had to wrap my test in tester.runAsync like so
testWidgets(
'when tapped disables underlying button until future completes',
(WidgetTester tester) async {
await tester.runAsync(() async {
// test here
});
});
I was able to do this with Mocktail. This is the article that this is from, and explains how to integrate it into your app. This is a full widget test and depends on this gist code.
The crux is that you need to declare a Mock class that has a call method. Then, you can then mock the top-level function that returns a Future. You can use the when and verify methods with this.
//Gist code
import 'package:gist/main.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:flutter/material.dart';
class LaunchMock extends Mock {
Future<bool> call(
Uri url, {
LaunchMode? mode,
WebViewConfiguration? webViewConfiguration,
String? webOnlyWindowName,
});
}
void main() {
testWidgets('Test Url Launch', (tester) async {
//These allow default values
registerFallbackValue(LaunchMode.platformDefault);
registerFallbackValue(const WebViewConfiguration());
//Create the mock
final mock = LaunchMock();
when(() => mock(
flutterDevUri,
mode: any(named: 'mode'),
webViewConfiguration: any(named: 'webViewConfiguration'),
webOnlyWindowName: any(named: 'webOnlyWindowName'),
)).thenAnswer((_) async => true);
final builder = compose()
//Replace the launch function with a mock
..addSingletonService<LaunchUrl>(mock);
await tester.pumpWidget(
builder.toContainer()<MyApp>(),
);
//Tap the icon
await tester.tap(
find.byIcon(Icons.favorite),
);
await tester.pumpAndSettle();
verify(() => mock(flutterDevUri)).called(1);
});
}