flutter navigation :: '!_debugLocked': is not true - firebase

So in my app I'm using a navigation service here :
class NavigationService {
GlobalKey<NavigatorState> _navigationKey = GlobalKey<NavigatorState>();
GlobalKey<NavigatorState> get navigationKey => _navigationKey;
void pop() {
return _navigationKey.currentState.pop();
}
Future<dynamic> navigateTo(String routeName, {dynamic arguments}) {
return _navigationKey.currentState
.pushNamed(routeName, arguments: arguments);
}
}
and in order to toggle between login in and sign up I user this method :
void navigateToSignup(){
_navigationService.navigateTo(SignUpViewRoute);
}
and in the button :
onPressed: (){
model.navigateToSignup();
},
when I run and try to click the button I get this error
'package:flutter/src/widgets/navigator.dart':
Failed assertion: line 4028 pos 12: '!_debugLocked': is not true.
I've read different solutions but not sure where to exactly do It in my app

Related

How do I correct this issue with reading an Environment Object? (SwiftUI and working without a SceneDelegate)

I wanted to write a template for apps that logs into firebase so that I can use it in future projects. I followed a tutorial online found on YouTube :
https://www.youtube.com/watch?v=DotGrYBfCuQ&list=PLBn01m5Vbs4B79bOmI3FL_MFxjXVuDrma&index=2
So the issue I'm facing is that in the video the variable userInfo was instantiated in the SceneDelegate, allowing the coder on YouTube to reference userInfo in his code. I tried doing the same in the AppDelegate and the App Struct. with no avail.
Here is the code in the App Struct:
import SwiftUI
import Firebase
#main
struct WoobApp: App {
// Adapts AppDelegate to SwiftUI
#UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var userInfo = UserInfo()
var body: some Scene {
WindowGroup {
InitialView()
}
}
}
class AppDelegate : NSObject, UIApplicationDelegate {
// Configure Firebase When App Launches
func application(_ application : UIApplication, didFinishLaunchingWithOptions launchOptions : [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
FirebaseApp.configure()
return true
}
}
I think the issue is in here, however I will post the rest of my code in case Im wrong:
InitialView:
struct InitialView: View {
#EnvironmentObject var userInfo : UserInfo
var body: some View {
Group {
if userInfo.isUserAuthenticated == .undefined {
UndefinedView()
}
else if userInfo.isUserAuthenticated == .signedOut {
UndefinedView()
}
else if userInfo.isUserAuthenticated == .signedIn {
UndefinedView()
}
}
.onAppear{
self.userInfo.configureFirebaseStateDidChange()
}
}
And here is the User data :
class UserInfo : ObservableObject {
enum FBAuthState {
case undefined, signedIn, signedOut
}
#Published var isUserAuthenticated : FBAuthState = .undefined
func configureFirebaseStateDidChange() {
isUserAuthenticated = .signedIn
isUserAuthenticated = .signedOut
}
}
Thanks in advance for any help, Id really appreciate it so thank you!!!!
You have to actually pass that userInfo variable into your view hierarchy so that it's visible to InitialView and its children:
#ObservedObject var userInfo = UserInfo()
var body: some Scene {
WindowGroup {
InitialView()
.environmentObject(userInfo)
}
}
This principal of passing it via environmentObject is true whether you're using the SwiftUI lifecycle or a SceneDelegate. More reading on environmentObject: https://www.hackingwithswift.com/quick-start/swiftui/how-to-use-environmentobject-to-share-data-between-views
You have a choice of whether to declare userInfo as an ObservedObject or StateObject that may depend on your OS target version: What is the difference between ObservedObject and StateObject in SwiftUI

Why Firebase Task in RxJava Completable emitter doesn't execute?

I'm developing a Firebase Android application which connect to a Firestore. The nomenclature is that the collection is "Assets". The example code had simple actions like addAsset and deleteAsset, those work fine. This is the data repository layer which actually converse with Firebase, the view model layer is above this.
class FirestoreAssetRepository(secondaryDB: FirebaseFirestore) : IAssetRepository {
companion object {
private const val TAG = "FirestoreAssetRepo"
private const val ASSET_COLLECTION = "Assets"
}
private var remoteDB: FirebaseFirestore
private var changeObservable: Observable<List<DocumentSnapshot>>
init {
remoteDB = secondaryDB
}
override fun addAsset(asset: Asset): Completable {
return Completable.create { emitter ->
remoteDB.collection(ASSET_COLLECTION)
.add(mapToAssetData(asset))
.addOnSuccessListener {
if (!emitter.isDisposed) {
emitter.onComplete()
}
}
.addOnFailureListener {
if (!emitter.isDisposed) {
emitter.onError(it)
}
}
}
}
override fun deleteAsset(assetId: String): Completable {
return Completable.create { emitter ->
remoteDB.collection(ASSET_COLLECTION)
.document(assetId)
.delete()
.addOnSuccessListener {
if (!emitter.isDisposed) {
emitter.onComplete()
}
}
.addOnFailureListener {
if (!emitter.isDisposed) {
emitter.onError(it)
}
}
}
}
I'm adding an action to the repository which would modify a specific document.
override fun lockUnlockAsset(assetId: String): Completable {
Log.d(TAG, "lockUnlockAsset")
return Completable.create { emitter ->
remoteDB.collection(ASSET_COLLECTION)
.document(assetId)
.get()
.addOnSuccessListener {
Log.d(TAG, "Unlocking")
val remoteAsset = mapDocumentToRemoteAsset(it)
it.reference.update(getUnlockLocation())
if (!emitter.isDisposed) {
emitter.onComplete()
}
}
.addOnFailureListener {
Log.d(TAG, "Could not find asset to unlock")
if (!emitter.isDisposed) {
emitter.onError(it)
}
}
}
}
The execution reaches Log.d(TAG, "lockUnlockAsset") but never gets to Log.d(TAG, "Unlocking"). If I place a break point at that second logging command it is the usual red dot in the beginning, but when the call comes into the function the icon changes to a grey "don't enter" icon and when I hover over it Android Studio tells me that "No executable found at ...". So something is definitely wrong there.
I'm new to Kotlin and RxJava2. How can I get this to work?
Update: to answer Pavel's question: these functions are called from the ViewModel layer:
fun deleteAsset(assetId: String) {
repository.deleteAsset(assetId)
.subscribeOn(Schedulers.io())
.subscribe(
{},
{
it.printStackTrace()
})
.addTo(disposable)
}
fun addAsset(assetTitle: String) {
repository.addAsset(Asset("${System.currentTimeMillis()}", assetTitle))
.subscribeOn(Schedulers.io())
.subscribe(
{},
{
it.printStackTrace()
})
.addTo(disposable)
}
fun lockUnlockAsset(assetId: String) {
repository.lockUnlockAsset(assetId)
}
I was experimenting with combinations of .subscribeOn(Schedulers.io()).observe at the repository level. Maybe it's the .addTo(disposable) which got it working, I'm not sure what I was missing. Now it's working, I wait for Pavel for his answer.
I experimented with combinations of .subscribeOn(...) and observeOn(..) + .observe(...) at the data repository level, but I should have just followed the pattern in the view model (view model calls the functions of the data repository): it's a chained subscribeOn + subscribe + addTo(disposable):
fun lockUnlockAsset(assetId: String) {
repository.lockUnlockAsset(assetId)
.subscribeOn(Schedulers.io())
.subscribe(
{},
{
it.printStackTrace()
})
.addTo(disposable)
}
Thanks for Pavel for pointing this out.

how to publish the data of a network request using Combine framework and SwiftUI

I'm developing an iOS app using swiftUI and Combine framework and also MVVM.
I'm want to handle the login API request in a separate class called LoginService, which is used in the LoginViewModel.
Now I want to know how should I publish and observe the attributes between view and ViewModel.
I mean ViewModel is an ObservableObject and is being observed in the View, But since I'm handling the network request in a Service class how should LoginService notify LoginViewModel and LoginView that the data is received and the View should be updated?
import Foundation
import Combine
class LoginViewModel: ObservableObject {
#Published var user = UserModel()
#Published var LoginStatus: Bool = false
#Published var LoginMessage: String = ""
var service = LoginService()
func Login(With email: String, And password: String) -> Bool {
service.validateLogin(email: email, password: password)
return false
}
}
This is the Code for LoginViewModel.
How should LoginService change the values for LoginStatus, LoginMessage and user when data is received from the server to notify the View?
I'm saying this because as far as I know you can Observe the ObservableObjects only in the View(SwiftUI).
Okay so I take your example and I would do the following:
I assumed your service returns true or false
import Foundation
import Combine
class LoginViewModel: ObservableObject {
#Published var LoginStatus: Bool = false
#Published var LoginMessage: String = ""
var service = LoginService()
func Login(_ email: String, _ password: String) -> Bool {
self.LoginStatus = service.validateLogin(email: email, password: password)
return self.LoginStatus
}
}
In your view:
import SwiftUI
struct ContentView : View {
#ObservedObject var model = LoginViewModel()
var body: some View {
VStack {
Button(action: {
_ = self.model.Login("TestUser", "TestPassword")
}, label: {
Text("Login")
})
Text(self.model.LoginStatus ? "Logged In" : "Not Logged in")
}
}
}
It should be something around that.
I removed UserModel because you shouldn't nest models.
I hope this helps.
EDIT 1:
To validate something automatically you can use onApear() on your View
Or listen to changes with olnrecieve() to update the UI or the State
import SwiftUI
struct ContentView : View {
#ObservedObject var model = LoginViewModel()
var body: some View {
VStack {
Button(action: {
_ = self.model.Login("TestUser", "TestPassword")
}, label: {
Text("Login")
})
Text(self.model.LoginStatus ? "Logged In" : "Not Logged in")
}.onAppear {
// call a function that gets something from your server
// and modifies your state
self.model.validate()
}.onReceive(self.model.$LoginMessage, perform: { message in
// here you can update your state or your ui
// according the LoginMessage... this gets called
// whenever LoginMessage changes in your model
})
}
}
You've established a one-way binding, i.e; model -> view
Now it doesn't matter how your service updates model, whenever model changes, view updates.
There are several ways for your service to update model.
Usual URLSession, update LoginStatus and LoginMessage in callback as usual.
Via Combine publishers, e.g.; URLSessionDataTaskPublisher, and do update in its sink closure.

Rxdb Plugin: Using the RxCollectionBase#insert method in a plugin

I am trying to create a plugin for rxdb.
I want to catch the exception raised by insert and return an hash with
{[fieldName: string] => [error:string]}
When using my new method though, I am getting an exception, and it seems like the method is getting called directly on the prototype rather than on each RxColletion<T, T2, T3> instance.
The error i am getting is:
TypeError: Cannot read property 'fillObjectWithDefaults' of undefined
which happens here: https://github.com/pubkey/rxdb/blob/ac9fc95b0eda276110f371afca985f949275c3f1/src/rx-collection.ts#L443
because this.schema is undefined.. The collection I am running this method on does have a schema though..
Here is my plugin code:
export const validatedInsertPlugin: RxPlugin = {
rxdb: true,
prototypes: {
RxCollection(proto: IRxCollectionBaseWithValidatedInsert) {
proto.validatedInsert = async function validatedInsert<T, D>(
doc: T
): Promise<Insert<T>> {
try {
// this is the line that raises:
const product = await proto.insert(doc);
return [true, product];
} catch (e) {
// extract errors
return [false, {} as Errors<T>];
}
};
},
},
overwritable: {},
hooks: {},
};
To answer my own question,
proto.insert is targeting the prototype, which is not what I want.
function(this: RxCollection) is what I want. I have to use this which will target the actual instance.
proto.validatedInsert = async function validatedInsert<T1>(
this: RxCollection,
doc: T1
): Promise<ValidatedInsert<T1>> {
try {
const product = await this.insert(doc); // this, not proto
return [true, product];
} catch (e) {
...

Meteor 1.3 + React: detect subscription failure?

I have a simple Meteor subscription, and I display a loading message while the data is being loaded. But I don't know how to display error message if subscription failed.
export const MyAwesomeComponent = createContainer(() => {
let sub = Meteor.subscribe('some-data');
if (!sub.ready()) return { message: 'Loading...'};
if (sub.failed()) return { message: 'Failed.' }; // How to do this?
return {
data: Data.find().fetch()
}
}, MyInternalRenderComponent);
Problem is, the subscription object doesn't have a failed() method, only a ready() query. How to pass the failure of a subscription as props in a createContainer() method?
I know the Meteor.subscribe method has an onStop callback for this case, but I don't know how to glue it toghether that to pass a property.
After a lot of researching I managed to get this working and I think it answers your question.
Bear in mind I'm using Meteor 1.6, but it should give you the info to get it working on your side.
On the publication/publish:
try {
// get the data and add it to the publication
...
self.ready();
} catch (exception) {
logger.error(exception);
// send the exception to the client through the publication
this.error(new Meteor.Error('500', 'Error getting data from API', exception));
}
On the UI Component:
const errorFromApi = new ReactiveVar();
export default withTracker(({ match }) => {
const companyId = match.params._id;
let subscription;
if (!errorFromApi.get()) {
subscription = Meteor.subscribe('company.view', companyId, {
onStop: function (e) {
errorFromApi.set(e);
}
});
} else {
subscription = {
ready: () => {
return false;
}
};
}
return {
loading: !subscription.ready(),
company: Companies.findOne(companyId),
error: errorFromApi.get()
};
})(CompanyView);
From here all you need to do is get the error prop and render the component as desired.
This is the structure of the error prop (received on the onStop callback from subscribe):
{
error: String,
reason: String,
details: String
}
[Edit]
The reason there is a conditional around Meteor.subscribe() is to avoid an annoying infinite loop you'd get from the natural withTracker() updates, which would cause new subscriptions / new errors from the publication and so on.

Resources