I've created a basic firebase login app using swiftui (using this tutorial: https://www.youtube.com/watch?v=FhLEwqyVSjE). Everything works as it should.
In my ContentView.swift I added a TextField for changing the user's display name. To do this, I added a changeDisplayName func in the SessionStore.swift with this logic:
let changeRequest = Auth.auth().currentUser?.createProfileChangeRequest()
changeRequest?.displayName = displayName
changeRequest?.commitChanges { (error) in
// ...
}
The func is called by a button in my ContentView. Changing the displayName works aswell, but my ContentView still holds the old displayName until I restart the app, because only then the ContentView calls session.listen() again and gets the new displayName from the SessionStore.
To fix this, I call session.unbind() and then session.listen() in my ContentView whenever the changeDisplayName func in my SessionStore is called by my ContentView and doesn't return an error. This works and my ContentView is reloaded with the new displayName everytime it is changed.
Is this good practice or am I missing a cleaner solution to this problem?
Maybe try using Auth.auth().currentUser?.reload(). This may not work because I've had trouble in the past with isEmailVerified but I guess it's worth a try!
Related
I'm trying to add the Firebase Phone Authentication code inside a View Model using Kotlin. The problem is that the PhoneAuthProvider requires an activity. Does anyone know how can this code be implemented inside a View Model without the need of an activity?
Thanks!
val mCallbacks: PhoneAuthProvider.OnVerificationStateChangedCallbacks ...
val options = PhoneAuthOptions.newBuilder(auth).apply {
setPhoneNumber(phoneNumber)
setTimeout(120L, TimeUnit.SECONDS)
setActivity(this) <-------------------------- // Activity (for callback binding)
setCallbacks(mCallbacks)
}.build()
PhoneAuthProvider.verifyPhoneNumber(options)
It turned out to be an intentional change in API 20 (check out this issue on Github), even though it violates the MVVM architecture. The reason an activity is needed is that the method falls back to reCAPTCHA. The right way to achieve it is "yet to be determined".
My approach is to include everything in the viewModel including the callbacks. I then call a function in the viewModel and pass in an activity parameter. see below:
fun verifyPhoneNumber(phoneNumber: String, activity: Activity) {
_validFullPhoneNumber.value = phoneNumber
val options = PhoneAuthOptions.newBuilder(mAuth)
.setPhoneNumber(phoneNumber) // Phone number to verify
.setTimeout(60L, TimeUnit.SECONDS) // Timeout and unit
.setActivity(activity)
.setCallbacks(callbacks) // OnVerificationStateChangedCallbacks
.build()
PhoneAuthProvider.verifyPhoneNumber(options)
}
and in the UI controller, in my case a fragment I call it as:
viewModel.verifyPhoneNumber(validatedPhoneNumber, requireActivity())
same with resend button function.
viewModel:
fun resendVerificationCode(activity: Activity) {
val options =
PhoneAuthOptions.newBuilder(mAuth)
.setPhoneNumber(_validFullPhoneNumber.value!!) // Phone number to verify
.setTimeout(60L, TimeUnit.SECONDS) // Timeout and unit
.setActivity(activity)
.setCallbacks(callbacks) // OnVerificationStateChangedCallbacks
.setForceResendingToken(_resendToken) // ForceResendingToken from callbacks
.build()
PhoneAuthProvider.verifyPhoneNumber(options)
_isVerificationCodeExpired.value = false
}
UI controller(fragment):
viewModel.resendVerificationCode(requireActivity())
I am trying to create a list view using Recycler View and display a list. Lets say what I am trying to display is like a typical chat screen - image, message, sender_name etc.
So all this data is stored in Firebase Realtime Database. I am also using ViewModel and would like to use DiffUtil for efficiency - since messages can be deleted, edited, starred, new ones added etc.. Due to DiffUtil, I am using a ListAdapter.
The issue I am facing is that the ListAdapter needs a List and I use a ChildEventListener.
How do I now observe changes from Firebase using LiveData and then update my list so that I can pass back into my DiffUtil? So if I add a new message, I'd like that to be added to my RecyclerView and I'd like to do that using DiffUtil since the list can also be updated.
What I found through research was that I might need to use Room for this purpose and observe the query for changes - so observe something like a getAllMessages() method which would return the complete list and then I can use that to pass into my DiffUtil. That sounds like an overkill to me - the implementation of Room.
Any pointers or suggestions on how I can achieve my need?
Here is the structure of my db:
if you dare to start programming an app, you won't be able to get along with Room soon. if you take this step, SQL knowledge is necessary as well...so don't be shy and just dare to do it
GGK
So I figured this out by doing the following in my ViewModel.
private val _message = MutableLiveData<List<TextListModel>>()
val message: LiveData<List<TextListModel>>
get() = _message
//...
private val eventListener =
object : ValueEventListener {
override fun onCancelled(databaseError: DatabaseError) {
Timber.e("Error!! ${databaseError.toException()}")
}
override fun onDataChange(dataSnapshot: DataSnapshot) {
val listOfMessages : MutableList<TextListModel> = mutableListOf()
for(msg in dataSnapshot.children) {
val item = msg.getValue(TextListModel::class.java)
listOfMessages.add(item!!)
}
_message.value = listOfMessages
}
}
In my fragment I observe message and perform adapter.submitList(newList) when there is an update.
This downloading of data using ValueEventListener is concerning though since it pulls the data from under the node. I am checking on how to use ChildEventListener for my purposes.
EDIT
Seems like Value events don't download the full tree over! Value events work with the data in the local state and only downloads the delta.
Some more information in this Youtube Video from 2016!
I have an independent watch app which tracks users calories throughout the day.
So when I build and run the app for the first time through Xcode the onAppear(_:) method of the view is called and the calories are properly retrieved from the HealthKit repository.
Whenever I put my hand down and raise my wrist after 5 min, the applicationDidBecomeActive of the ExtensionDelegate method is called but the onAppear(_:) method of the SwiftUI view is not getting called(my HealthKit code to fetch calories is called in this function) and the screen show's the same number of calories the previous time the app ran through Xcode.
Is this expected behaviour? if yes then how can I update my SwiftUI view through the Extension Delegate?
I think the failure to invoke .onAppear on wrist-raise is a bug, and I filed feedback about it. In the meantime, I got this to work as a "wrist-raise" event.
// ExtensionDelegate.swift
extension Notification.Name {
static var applicationIsActive: Notification.Name {
Notification.Name("applicationIsActive")
}
}
class ExtensionDelegate: NSObject, WKExtensionDelegate {
func applicationDidBecomeActive() {
NotificationCenter.default.post(name: .applicationIsActive, object: nil)
}
}
// ContentView.swift
private var isActive = NotificationCenter.default.publisher(for: .applicationIsActive)
.onReceive(isActive) {_ in self.viewModel.refresh()}
I have a Prism Xamarin.Forms App created with the Prism Template Pack (2.0.7).
All packages are up to date:
Xamarin.Forms v2.5.0.91635
Xamarin.Android.* v26.1.0.1
Prism.DryIoc.Forms v7.0.0.168-pre (Could not update to v7.0.0.269-pre due to a NullReferenceException in Android Renderer on startup, however my main application is using 269-pre without that problem)
I hosted the sample Application on https://github.com/dernippel/PrismNavApp
I have the following components:
Module "Attachments" (a Prism Module)
Module "Viewer" (a Prism Module)
Service "AttachmentService" registered with the Container with an Interface as Singleton
They should do this:
The AttachmentsPage (from the AttachmentsModule) lists some objects
Selecting one Attachment
AttachmentPageViewModel calls the "OpenAttachment"-Method of the Attachment Service
The Method determines the correct ViewerPage by type and uses the Prism-NavigationService to navigate directly to the Page (in the sample this is the "ImageViewerPage" of the ViewerModule)
This is only working when you do the following navigation:
MainPage -> AttachmentsPage -> ViewerPage -> (back Arrow) AttachmentsPage -> ViewerPage (and so on)
But if you navigate back to MainPage navigation to ViewerPage isn't working anymore:
MainPage -> AttachmentsPage -> ViewerPage -> (back Arrow) AttachmentsPage -> (back Arrow) MainPage -> AttachmentsPage -> (nothing happens anymore when tap on Button to navigate to ViewerPage)
The AttachmentsService gets the NavigationService via Constructor injection and navigates this way:
public AttachmentService(INavigationService navigationService)
{
this.navigationService = navigationService;
}
public async void OpenAttachmentWithViewer(object attachment)
{
// ToDo: handle parameter proberbly
var attachmentType = "image";
// select correct viewer
if (attachmentType == "image")
{
// navigate to image viewer
var navParams = new NavigationParameters();
navParams.Add("object",attachment);
var navTask = this.navigationService.NavigateAsync(
"ImageViewerPage",
navParams,
useModalNavigation: false);
await navTask;
var result = navTask.Status;
Debug.WriteLine($"Navigation State is {result}");
}
}
I tried to check the navigation Task result status, it is always "RanToCompletion".
Modifying the AttachmentsPageViewModel to navigate directly with the Prism NavigationService instead using the Service doesn't cause this behavior:
private void OnOpenAttachment()
{
// ToDo: get the selected attachment
object selectedAttachment = null;
// navigating inside a service -- not working when navigating back to MainPage
//this.attachmentService.OpenAttachmentWithViewer(selectedAttachment);
// navigation from view model -- working
var navParams = new NavigationParameters();
navParams.Add("object", selectedAttachment);
this.navigationService.NavigateAsync("ImageViewerPage", navParams, useModalNavigation: false);
}
Hint: I switched with my Main-Application from PCL-based to the new .NETStandard based solution and already had a similar functionality working successfully using Prism v6.3.0.1. This functionality is not even navigating once since the port.
Actually I don't know how to solve this.
Is it possible to have a look into the Prism NavigationService to determine why the navigation is not happen?
I didn't find any known bug in the Prism Repository yet.
You can't use the NavigationService in another service, especially if that service is a singleton. Navigation in Xamarin.Forms is specific to a Page, and only works in context of an associated page. Instead, your service should return a result and you should navigate from your VM based on that result. Don't try to navigate from within a service.
I have a simple login page that I am testing using Webdriver. I have the user and password fields declared as webelements, which I look by id. Same with the sign-up button. I populate the user, and password fields before hitting sign-up, but things happen so fast, that it looks like sign up gets clicked before the user and p/w fields get populated, thereby always resulting in an error stating that the two fields cannot be empty. This does not always happen though. To be sure, I inserted a Thread.sleep after user and password setters, but didn't see any change. I can see that the fields get filled, and there is a wait time (which I introduced through sleep), but when the sign-up button is clicked, the user and password fields become red again as if they were not filled before hitting continue. I used Webdriver's fluent API to make sure the buttons are all available to be clicked, but that also didn't make a difference. Has anyone seen this behavior before? How did you solve it? (I haven't attached a code snippet here, since the scenario is really very basic).
Edit : Attaching the code snippet here :
#FindBy(id = "signUp_id")
WebElement signUpButton;
#FindBy(id = "UserName")
WebElement userNameElement;
#FindBy(id = "Password")
WebElement passwordElement;
writeInTextField(call(By.id("UserName")), userName);
writeInTextField(call(By.id("Password")), password);
Thread.sleep(10000);
call(By.id("signUp_id")).click();
public WebElement call(final By locator) {
WebElement webElement = fluentWait.until(new Function<WebDriver, WebElement>() {
public WebElement apply(WebDriver driver) {
return driver.findElement(locator);
}
});
return webElement;
}
public void writeInTextField(WebElement we, String sText) {
we.clear();
we.sendKeys(sText);
}
I initialize fluentWait variable as :
fluentWait = new FluentWait<WebDriver>(driver).withTimeout(waitTimeOutInSeconds,
TimeUnit.SECONDS).pollingEvery(pollingInterval,
TimeUnit.MILLISECONDS).ignoring(NoSuchElementException.class,
StaleElementReferenceException.class);
This is more of common,
f developers have used any js based actions to verify fields are filled properly.
You should try entering "tab" character after doing "sendKeys". And then it should work.
I am using qaf, which has listeners capabilities and I am doing 'clear" of webelement via listener whenever click happens which makes actual code look clean.