SwiftUI - Open a specific View when user opens a Push Notification - push-notification

A have an app made in SwiftUI, with Parse used for DB.
I'm some parts of the app i've integrated some cloud functions that send notifications (for example: when someone send's you a message, you will receive a push notification triggered by that cloud function).
In the past days i'm struggling and searching for how to open a specific view when you press the Notification to open the app.
I've found some solutions, but could not make them work.
This is the code that i have so far :
class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
//Parse Intialization
...
//notifications
registerForPushNotifications()
//Notification Badge
UIApplication.shared.applicationIconBadgeNumber = 0
// start notification while app is in Foreground
UNUserNotificationCenter.current().delegate = self
return true
}
// This function will be called right after user tap on the notification
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
print("app opened from PushNotification tap")
UIApplication.shared.applicationIconBadgeNumber = 0
completionHandler()
}
}
#main
struct MyApp: App {
#UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView(currentTab: Tab.home)
}
}
}
The app prints "app opened from PushNotification tap", but if I put a variable in AppDelegate and I listen for changes in ContentView with .onReceive or .onChange for that variable, nothing is hapenning
struct ContentView: View {
#ObservedObject var appState = AppState()
#State var currentTab : Tab
#State var noReloadAddItemView = false
var body: some View {
TabView(selection: $appState.currentTab) {
NavigationView {
HomeView(appState: appState, noReloadAddItemView: $noReloadAddItemView)
}
.tabItem {
if appState.currentTab == .home {
Image(systemName: "house.fill")
} else {
Image(systemName: "house")
}
Text(LocalizedStringKey("HomeTabMenu"))
}.tag(Tab.home)
NavigationView {
SearchView(appState: appState, noReloadAddItemView: $noReloadAddItemView)
}
.tabItem {
if appState.currentTab == .search {
Image(systemName: "magnifyingglass.circle.fill")
} else {
Image(systemName: "magnifyingglass")
}
Text(LocalizedStringKey("SearchTabMenu"))
}.tag(Tab.search)
NavigationView {
AddItemView(appState: appState, noReloadAddItemView: $noReloadAddItemView)
}
.tabItem {
if appState.currentTab == .add {
Image(systemName: "plus.circle.fill")
} else {
Image(systemName: "plus.circle")
}
Text(LocalizedStringKey("SellTabMenu"))
}.tag(Tab.add)
NavigationView {
ShoppingCartFavoritesView(appState: appState, noReloadAddItemView: $noReloadAddItemView)
}
.tabItem {
if appState.currentTab == .favorites {
Image(systemName: "cart.fill")
} else {
Image(systemName: "cart")
}
Text(LocalizedStringKey("CartTabMenu"))
}.tag(Tab.favorites)
NavigationView {
ProfileView(appState: appState, noReloadAddItemView: $noReloadAddItemView)
}
.tabItem {
if appState.currentTab == .profile {
Image(systemName: "person.fill")
} else {
Image(systemName: "person")
}
Text(LocalizedStringKey("ProfileTabMenu"))
}.tag(Tab.profile)
}
.accentColor(Color("ColorMainDark"))
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(currentTab: Tab.home)
}
}
class AppState: ObservableObject {
#Published var currentTab : Tab = .home
}
enum Tab {
case home, search, add, favorites, profile
}

You need some sort of shared state that you can modify that SwiftUI knows to react to. An ObservableObject is perfect for this:
class AppState: ObservableObject {
static let shared = AppState()
#Published var pageToNavigationTo : String?
}
Then, to listen to it and respond to it, you can do a couple different methods in your main view.
Option 1 -- NavigationLink binding based on the value of the ObservedObject:
struct ContentView : View {
#ObservedObject var appState = AppState.shared //<-- note this
#State var navigate = false
var pushNavigationBinding : Binding<Bool> {
.init { () -> Bool in
appState.pageToNavigationTo != nil
} set: { (newValue) in
if !newValue { appState.pageToNavigationTo = nil }
}
}
var body: some View {
NavigationView {
Text("My content")
.overlay(NavigationLink(destination: Dest(message: appState.pageToNavigationTo ?? ""),
isActive: pushNavigationBinding) {
EmptyView()
})
}
}
}
struct Dest : View {
var message : String
var body: some View {
Text("\(message)")
}
}
Or, you could use onReceive:
struct ContentView : View {
#ObservedObject var appState = AppState.shared
#State var navigate = false
var body: some View {
NavigationView {
VStack {
if navigate {
NavigationLink(destination: Text("Test"), isActive: $navigate ) {
EmptyView()
}
}
Text("My content")
.onReceive(appState.$pageToNavigationTo) { (nav) in
if nav != nil { navigate = true }
}
}
}
}
}
I'll leave the implementation details of your specific NavigationView, NavigationLink, TabView, etc to you, but this should get you started.
Finally, a fully-functional minimal example that mocks a notification and shows how the navigation view:
class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
print("Dispatch")
AppState.shared.pageToNavigationTo = "test"
}
return true
}
}
class AppState: ObservableObject {
static let shared = AppState()
#Published var pageToNavigationTo : String?
}
#main
struct MultiWindowApp: App {
#UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView : View {
#ObservedObject var appState = AppState.shared
#State var navigate = false
var pushNavigationBinding : Binding<Bool> {
.init { () -> Bool in
appState.pageToNavigationTo != nil
} set: { (newValue) in
if !newValue { appState.pageToNavigationTo = nil }
}
}
var body: some View {
NavigationView {
Text("My content")
.overlay(NavigationLink(destination: Dest(message: appState.pageToNavigationTo ?? ""),
isActive: pushNavigationBinding) {
EmptyView()
})
}
}
}
struct Dest : View {
var message : String
var body: some View {
Text("\(message)")
}
}

Related

Every time view is seen, the name keeps on adding to itself

I know that this sounds a bit dumb, but how do you call a function only once. I have a tab bar at the bottom of my app, and every time that it is called, the name that I got from my firebase database, keeps on being added. For example, the name in firebase is Bob. The app for the first time will display Bob. Then you would click on the settings, and go back to the home view. Then the app will say BobBob, and over and over again. How do I make this stop.
Code:
import SwiftUI
import Firebase
struct HomeView: View {
#State var name = ""
var body: some View {
NavigationView {
ZStack {
VStack{
Text("Welcome \(name)")
.font(.title)
Text("Upcoming Lessions/Reservations:")
.bold()
.padding()
Divider()
}
}
}
.navigationTitle("Home")
.onAppear(perform: {
downloadNameServerData()
})
}
private func downloadNameServerData() {
let db = Firestore.firestore()
db.collection("users").addSnapshotListener {(snap, err) in
if err != nil{
print("\(String(describing: err))")
return
}
for i in snap!.documentChanges {
_ = i.document.documentID
if let Name = i.document.get("Name") as? String {
DispatchQueue.main.async {
name.append(Name)
print("\(name)")
}
}
}
}
}
}
struct HomeView_Previews: PreviewProvider {
static var previews: some View {
HomeView()
}
}
import SwiftUI
import Firebase
struct HomeView: View {
#State var name = ""
var body: some View {
NavigationView {
ZStack {
VStack{
Text("Welcome \(name)")
.font(.title)
Text("Upcoming Lessions/Reservations:")
.bold()
.padding()
Divider()
}
}
}
.navigationTitle("Home")
.onAppear(perform: {
downloadNameServerData()
})
}
private func downloadNameServerData() {
if !name.isEmpty { return }
let db = Firestore.firestore()
db.collection("users").addSnapshotListener {(snap, err) in
if err != nil{
print("\(String(describing: err))")
return
}
for i in snap!.documentChanges {
_ = i.document.documentID
if let Name = i.document.get("Name") as? String {
DispatchQueue.main.async {
name = Name
print("\(name)")
}
}
}
}
}
}
struct HomeView_Previews: PreviewProvider {
static var previews: some View {
HomeView()
}
}
Did you consider only loading the name if you don't have one yet?
.onAppear(perform: {
if (name == null) downloadNameServerData()
})

Abrupt transition between views within a Navigation View

I'm having trouble figuring out how to smoothly navigate from my SignInView() to my FirstView(). I have my FirstView() inside of a Navigation Stack, but the transition between the views is very abrupt and devoid of the transition that you normally get with the use of a NavigationLink. How can I get the transition to work?
Much appreciated!
Here is the relevant code...
struct ContentView: View {
#EnvironmentObject var viewModel: AppViewModel
var body: some View {
VStack{
NavigationView {
if viewModel.signedIn {
FirstView()
.transition(.slide)
} else {
//.onAppear method is used for keyboard management (See Misc Functions...)
SignInView()
.onAppear(perform: UIApplication.shared.addTapGestureRecognizer)
.navigationBarHidden(true)
}
}
.onAppear {
viewModel.listen()
}
}
}
}
class AppViewModel: ObservableObject {
private var db = Firestore.firestore()
#Published var userInfo: User?
#Published var signedIn: Bool = false
var handle: AuthStateDidChangeListenerHandle?
let authRef = Auth.auth()
var authHandle : AuthStateDidChangeListenerHandle?
var rootInfoCollection : CollectionReference!
var userIdRef = ""
func fetchUserData(){
db.collection("Users").document("\(userIdRef)").getDocument { document, error in
// Check for error
if error == nil {
// Check that this document exists
if document != nil && document!.exists {
self.userInfo = document.map { (documentSnapshot) -> User in
let data = documentSnapshot.data()
let uid = data?["uid"] as? UUID ?? UUID()
let company = data?["company"] as? String ?? ""
let name = data?["name"] as? String ?? ""
let admin = data?["admin"] as? Bool ?? false
let photo = data?["photo"] as? String ?? ""
return User(uid: uid, company: company, name: name, admin: admin, photo: photo)
}
withAnimation {
self.signedIn = true
}
}
}
}
}
func listen(){
handle = authRef.addStateDidChangeListener({ auth, user in
print(user?.email ?? "No User Found")
if let user = auth.currentUser {
self.userIdRef = user.uid
self.rootInfoCollection = Firestore.firestore().collection("/Users/")
DispatchQueue.main.async {
self.fetchUserData()
}
} else {
self.signedIn = false
}
})
}
func signIn(email: String, password: String){
authRef.signIn(withEmail: email, password: password) { result, error in
guard result != nil, error == nil else {
return
}
}
}
}
struct SignInView: View {
#EnvironmentObject var viewModel: AppViewModel
#State private var username : String = ""
#State private var password : String = ""
#State private var shouldShowLoginAlert: Bool = false
#State var selectedImageArray : [Image] = []
var disableLoginButton : Bool {
return self.username.isEmpty || self.password.isEmpty
}
var body: some View {
VStack{
Image(uiImage: #imageLiteral(resourceName: "awText"))
.resizable()
.frame(width: 180, height: 100)
.padding(.bottom, 50)
TextField("Email", text: $username)
.padding(.leading)
.disableAutocorrection(true)
.autocapitalization(.none)
Rectangle().fill(Color.gray.opacity(0.25)).frame(height: 1, alignment: .center).padding(.bottom)
.padding(.bottom)
.onChange(of: self.username, perform: { value in
if value.count > 10 {
self.username = String(value.prefix(20)) //Max 10 Characters for Username.
}
})
SecureField("Password", text: $password)
.padding(.leading)
.disableAutocorrection(true)
.autocapitalization(.none)
Rectangle().fill(Color.gray.opacity(0.25)).frame(height: 1, alignment: .center)
.onChange(of: self.username, perform: { value in
if value.count > 10 {
self.username = String(value.prefix(10)) //Max 10 Characters for Password.
}
})
//SignIn Button
Button(action: {
viewModel.signIn(email: username, password: password)
}, label: {
Text("Sign In")
.disabled(disableLoginButton)
.frame(width: 300, height: 50)
.background(Color.green)
.clipShape(RoundedRectangle(cornerRadius: 20, style: .continuous))
.padding()
})
}
Replacing the default NavigationView behavior with your own animations isn't necessarily totally straightforward. I'll lay out one possibility, but another would be to use a real NavigationView transition, but just hide the back button once you're on FirstView.
To do the transition yourself, you'll need one root element to NavigationView, an if clause, a transition(.slide) and withAnimation. Here's a simplified version of your code showing just these elements:
class AppViewModel: ObservableObject {
#Published var signedIn = false
}
struct FirstView : View {
var body: some View {
Text("Signed in")
}
}
struct ContentView: View {
#StateObject var viewModel = AppViewModel()
var body: some View {
NavigationView {
VStack {
if viewModel.signedIn {
FirstView()
.transition(.slide)
} else {
Button("Sign me in") {
withAnimation {
viewModel.signedIn = true
}
}
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.navigationBarHidden(true)
}
}
}

swiftUI - Button Camera in TabBar

There is a button in the TabBar, this button should open the camera
"Button(action: {...}, label: { ..."
The camera code is written in "CameraView" and "CameraModel" :
struct CameraView: View {
#StateObject var camera = CameraModel()
var body: some View {
ZStack{
// Camera preview...
CameraPreview(camera: camera)
.ignoresSafeArea(.all, edges: .all)
VStack{
if camera.isTaken{
HStack {
Spacer()
Button(action: {}, label: {
Image(systemName: "arrow.triangle.2.circlepath.camera")
.foregroundColor(.black)
.padding()
.background(Color.white)
.clipShape(Circle())
})
.padding(.trailing,10)
}
}
Spacer()
HStack{
if camera.isTaken{
Button(action: {}, label: {
Text("Save")
.foregroundColor(.black)
.fontWeight(.semibold)
.padding(.vertical,10)
.padding(.horizontal,20)
.background(Color.white)
.clipShape(Capsule())
})
.padding(.leading)
Spacer()
}
else{
Button(action: {camera.isTaken.toggle()}, label: {
ZStack{
Circle()
.fill(Color.white)
.frame(width: 65, height: 65)
Circle()
.stroke(Color.white,lineWidth: 2)
.frame(width: 75, height: 75)
}
})
}
}
.frame(height: 75)
}
}
.onAppear(perform: {
camera.Check()
})
}
}
class CameraModel: ObservableObject{
#Published var isTaken = false
#Published var session = AVCaptureSession()
#Published var alert = false
// since were going to read pic data....
#Published var output = AVCapturePhotoOutput()
// preview....
#Published var preview : AVCaptureVideoPreviewLayer!
func Check(){
// first checking camerahas got permission...
switch AVCaptureDevice.authorizationStatus(for: .video) {
case .authorized:
setUp()
return
// Setting Up Session
case .notDetermined:
// retusting for permission....
AVCaptureDevice.requestAccess(for: .video) { (status) in
if status{
self.setUp()
}
}
case .denied:
self.alert.toggle()
return
default:
return
}
}
func setUp(){
// setting up camera...
do{
// setting configs...
self.session.beginConfiguration()
// change for your own...
let device = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back)
let input = try AVCaptureDeviceInput(device: device!)
// checking and adding to session...
if self.session.canAddInput(input){
self.session.addInput(input)
}
// same for output....
if self.session.canAddOutput(self.output){
self.session.addOutput(self.output)
}
self.session.commitConfiguration()
}
catch{
print(error.localizedDescription)
}
}
}
// setting view for preview...
struct CameraPreview: UIViewRepresentable {
#ObservedObject var camera : CameraModel
func makeUIView(context: Context) -> UIView {
let view = UIView(frame: UIScreen.main.bounds)
camera.preview = AVCaptureVideoPreviewLayer(session: camera.session)
camera.preview.frame = view.frame
// Your Own Properties...
camera.preview.videoGravity = .resizeAspectFill
view.layer.addSublayer(camera.preview)
// starting session
camera.session.startRunning()
return view
}
func updateUIView(_ uiView: UIView, context: Context) {
}
}
How to call the camera on click Button in TabBar "Button(action: {}, label: {
)"?
This is just an example of how I did it in the past (after all permissions etc... have been done).
Use your code to do the same.
struct ContentView: View {
#State var image: UIImage?
#State private var showCamera = false
var body: some View {
VStack {
Button(action: { self.showCamera.toggle() }) {
Image(systemName: "camera.circle").resizable().frame(width: 100, height: 100)
}
if image != nil {
Image(uiImage: image!).resizable().frame(width: 200, height: 200)
}
} // this is where it happens
.sheet(isPresented: $showCamera, onDismiss: {self.showCamera = false}) {
CameraViewController(photo: $image)
}
}
}
struct CameraViewController: UIViewControllerRepresentable {
#Environment(\.presentationMode) var presentationMode
#Binding var photo: UIImage?
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {
}
func makeUIViewController(context: UIViewControllerRepresentableContext<CameraViewController>) -> UIImagePickerController {
let vc = UIImagePickerController()
vc.sourceType = .camera
vc.delegate = context.coordinator
return vc
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate, AVCapturePhotoCaptureDelegate {
var parent: CameraViewController
var captureSession: AVCaptureSession!
var capturePhotoOutput: AVCapturePhotoOutput!
var theCamera: AVCaptureDevice!
var videoPreviewLayer: AVCaptureVideoPreviewLayer?
let photoQualityPrioritizationMode = AVCapturePhotoOutput.QualityPrioritization.speed
init(_ imagePickerController: CameraViewController) {
self.parent = imagePickerController
}
// called when a picture has been taken
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info:[UIImagePickerController.InfoKey : Any]) {
guard let image = info[.originalImage] as? UIImage else {
print("No image found")
return
}
parent.photo = image // <--- the photo image
parent.presentationMode.wrappedValue.dismiss()
}
}
}

How to chain ObservableObject?

I have a Game-object that may hold an image. Whenever an image URL is found for a game a new instance of GameImage-object should be created. It will then fetch the image and populate the UIImage property. When this happens the UI should be updated presenting the image.
class Game: ObservableObject {
#Published var image: GameImage?
}
class GameImage: ObservableObject {
let url: URL
#Published var image: UIImage?
private var cancellable: AnyCancellable?
init(url: URL) {
self.url = url
}
func fetch() {
self.cancellable = URLSession.shared.dataTaskPublisher(for: self.url)
.map { UIImage(data: $0.data) }
.replaceError(with: nil)
.receive(on: DispatchQueue.main)
.sink(receiveValue: { [weak self] (image) in
guard let self = self else { return }
self.image = image
print(self.url)
print(self.image)
})
}
func cancel() {
cancellable?.cancel()
}
deinit {
cancel()
}
}
struct ContentView: View {
#StateObject var game = Game()
var body: some View {
VStack {
if let image = game.image?.image {
Image(uiImage: image)
} else {
Text("No image.")
}
}
.onAppear(perform: {
guard let gameImageURL = URL(string: "https://cf.geekdo-images.com/itemrep/img/oVEpcbtyWkJjIjk1peTJo6hI1yk=/fit-in/246x300/pic4884996.jpg") else { return }
game.image = GameImage(url: gameImageURL)
game.image!.fetch()
})
}
}
The problem is. After fetch is done the debug console will show that image contains an UIImage. However the UI does not update to show the image. What am I missing here?
There is much more simpler solution than chaining ObservableObject, just separate dependent part into standalone subview... and all will work automatically.
Here is possible approach. Tested with Xcode 12 / iOS 14.
struct ContentView: View {
#StateObject var game = Game()
var body: some View {
VStack {
if nil != game.image {
GameImageView(vm: game.image!)
}
}
.onAppear(perform: {
guard let gameImageURL = URL(string: "https://cf.geekdo-images.com/itemrep/img/oVEpcbtyWkJjIjk1peTJo6hI1yk=/fit-in/246x300/pic4884996.jpg") else { return }
game.image = GameImage(url: gameImageURL)
game.image!.fetch()
})
}
}
struct GameImageView: View {
#ObservedObject var vm: GameImage
var body: some View {
if let image = vm.image {
Image(uiImage: image)
} else {
Text("No image.")
}
}
}

finding a nil value in session when accessing value in view

So i have a sessionStore:
class SessionStore: ObservableObject {
var handle: AuthStateDidChangeListenerHandle?
#Published var isLoggedIn = false
#Published var userInSession: User?
func listenAuthenticationState() {
handle = Auth.auth().addStateDidChangeListener({(auth, user) in
if let user = user {
let firestoreGetUser = Firestore.firestore().collection("users").document(user.uid)
firestoreGetUser.getDocument{(document, error) in
if let dict = document?.data() {
guard let decodedUser = try? User.init(fromDictionary: dict) else { return }
self.userInSession = decodedUser
print("decoded user = \(decodedUser)")
}
}
self.isLoggedIn = true
print("user logged in")
} else {
self.isLoggedIn = false
self.userInSession = nil
print("no one logged in")
}
})
}
func logout() {
do {
try Auth.auth().signOut()
} catch {
}
}
func unbind() {
if let handle = handle {
Auth.auth().removeStateDidChangeListener(handle)
}
}
deinit {
unbind()
}
}
Its working as expected, I am able to sign in etc.
I have the following to pull the current user data:
import Foundation
import Firebase
import FirebaseAuth
import FirebaseFirestore
class ProfileViewModel: ObservableObject {
var uid: String = ""
var email: String = ""
var username: String = ""
var profileURL: String = ""
var bio: String = ""
var occupation: String = ""
var city: String = ""
func LoadAUser(userId: String) {
Firestore.firestore().collection("users").document(userId).getDocument{(snapshot, error) in
guard let snap = snapshot else {
print("error fetching data")
return
}
let dict = snap.data()
guard let decodedUser = try? User.init(fromDictionary: dict!) else { return }
print("decoded user - load user - \(decodedUser)")
}
}
}
In my view im trying to call it like:
import SwiftUI
struct ProfileView: View {
#EnvironmentObject var session: SessionStore
#ObservedObject var profileViewModel = ProfileViewModel()
func loadUserData() {
profileViewModel.LoadAUser(userId: session.userInSession!.uid)
}
var body: some View {
VStack {
Text("Edit Profile")
.fontWeight(.semibold)
.font(.system(.title, design: .rounded))
.foregroundColor(Color("startColor"))
Spacer()
VStack(alignment: .leading) {
Text("view")
}.padding()
.onAppear(perform: loadUserData)
}
}
struct ProfileView_Previews: PreviewProvider {
static var previews: some View {
ProfileView()
}
}
Im using .onAppear(perform: loadUserData) which is causing an issue - Thread1: Fatal error: Unexpectedly found nil while unwrapping
I also tried:
init() {
profileViewModel.LoadAUser(userId: session.userInSession!.uid)
}
But this also causes the same error.
The thing is I should only be able to get to this view if I'm logged in as this already works:
struct InitialView: View {
#EnvironmentObject var session: SessionStore
func listen() {
session.listenAuthenticationState()
}
var body: some View {
Group {
if session.isLoggedIn {
MainView()
} else {
NavigationView {
SignUpView()
}
}
}.onAppear(perform: listen)
}
}
I have an initialView()
struct InitialView: View {
#EnvironmentObject var session: SessionStore
func listen() {
session.listenAuthenticationState()
}
var body: some View {
Group {
if session.isLoggedIn {
MainView()
} else {
NavigationView {
SignUpView()
}
}
}.onAppear(perform: listen)
}
}
which takes you to the MainView() which has tabs to control which screen you can navigate to, then from here i can go to ProfileView()
Anyway by the logic of provided code it is more correct to activate isLoggedIn in
let firestoreGetUser = Firestore.firestore().collection("users").document(user.uid)
firestoreGetUser.getDocument{(document, error) in
if let dict = document?.data() {
guard let decodedUser = try? User.init(fromDictionary: dict) else { return }
self.userInSession = decodedUser
print("decoded user = \(decodedUser)")
self.isLoggedIn = true // << here !!
print("user logged in")
}
}
So whats worked for me is passing in Auth instead of session data:
func loadUserData() {
profileViewModel.LoadAUser(userId: Auth.auth().currentUser!.uid)
}

Resources