Realm opens 4 times on Login

huangapple go评论87阅读模式
英文:

Realm opens 4 times on Login

问题

只有以下部分代码会多次打印日志 "INFO: [REALM] Realm opened: /var/mobile/":

self.repo.login(email: $userId.wrappedValue, password: $password.wrappedValue) { user, error in
    if(user != nil){
        self.repo.getUserProfile(userId: user?.id as! String).watch(block:  {items in
            // ...
        })
    }else{
        print("login failed")
    }
}

这可能是因为您在 repo.login 的回调中调用了 self.repo.getUserProfile,而 watch 方法似乎会导致 Realm 多次打开。您可以尝试将这两个调用分开,以避免 Realm 多次打开。

此外,确保您在 repo.getUserProfile 方法内关闭 Realm,以防止不必要的 Realm 打开。确保您在完成 Realm 操作后调用 realm.close()

请注意,这只是基于您提供的代码片段的猜测,更详细的调试和分析可能需要检查完整的代码和 Realm 数据库配置。

英文:

I have a app that checks when loggin in the type of user, to know where to navigate.

Whenver I do the login, in the logs, the realm will open 4 times.

My login View:

struct LoginView: View {

    @State var userId : String = ""
    @State var password : String = ""
    @State var goToNextScreen : Int? = nil
    @State var myIsUser = false
    @State var myUser = UserInfo()
    
       
    var repo = RealmRepo()

    var body: some View {

        NavigationView{
            VStack(){
    
                TextField("Username", text: $userId)

                TextField("Password", text: $password)
                      

                Button("Login"){
  
                    repo.login(email: $userId.wrappedValue, password: $password.wrappedValue) { user, error in
                                                
                        if(user != nil){
                            
                            
                            self.repo.getUserProfile(userId: user?.id as! String).watch(block:  {items in
                                
                                self.myUser = items as! UserInfo
                                
                                                                
                                if(self.myUser.typeOfUser != true){
                                    print("this user type 1")
                                    UserDefaults.standard.set(false, forKey: "isUser")
                                    UserDefaults.standard.set(true, forKey: "isLogin")
                                    myIsUser=false
                                }else{
                                    print("this is user type2")
                                    UserDefaults.standard.set(true, forKey: "isUser")
                                    UserDefaults.standard.set(true, forKey: "isLogin")
                                    myIsUser=true
                                }
                                goToNextScreen=1
                            })
                            
                            
                        }else{
                            print("login failed")
                        }
                        
                       
                    }
                    
                }

                NavigationLink(destination: myIsUser ? AnyView(View1().navigationBarHidden(true)) : AnyView(View2().navigationBarHidden(true)) , tag: 1, selection: $goToNextScreen){
                    EmptyView()
                }
            }
        }
    }
}

Only this part of code will print 4 times the log: INFO: [REALM] Realm opened:
/var/mobile/

This part just checks the login and goes to a view.

How is this possible?

Is it because the the login block?
Should I make the login and getUserProfile calls in a different way?

The repo.login method:

suspend fun login(email: String, password: String): User {
        return appService.login(Credentials.emailPassword(email, password))
    }

The repo.getUserProfile() that will return the type of the user:

fun getUserProfile(userId: String): CommonFlow<UserInfo?> {
        val user = realm.query<UserInfo>("_id = $0", userId).asFlow().map {
            it.list.firstOrNull()
        }.asCommonFlow()

        return user
    }
enter code here

I ve uploaded a reproduction scenario: tmpfiles.org/1031395/mongo-conference-master3.zip

It is the same as the repository from mongodb github.com/mongodb-developer/mongo-conference, just with the getUserProfile changed with return CommonFlow

答案1

得分: 2

以下是您要翻译的内容:

  1. There is a very simple explanation why realm opens several times: Realm.open call is inside RealmRepo instance and you are creating multiple RealmRepo instances.

  2. For instance SwiftUI recreates RealmRepo with every state change. Also other views e.g. HomeView creates its own RealmRepo and so on.

  3. There are a lot of different ways to fix it. Here is some of them:

Make your RealmRepo object

Just change class RealmRepo to object RealmRepo. That will make your RealmRepo a singleton in your iOS code you can access it like this:

var repo = RealmRepo.shared

Use EnvironmentObject

There is a simple Dependency Injection mechanism in SwiftUI that can help you use a single instance of RealmRepo.

In your iOSApp.swift:

import SwiftUI

@main
struct iOSApp: App {
    @StateObject var repo = RealmRepo()

    var body: some Scene {
        WindowGroup {
            LoginScreenView()
                .environmentObject(repo)
        }
    }
}

and in all child views Login, HomeView, etc. use:

@EnvironmentObject var repo: RealmRepo

and do not create new instances.

More about @EnvironmentObject you can read here

Use some DI frameworks

You can use some DI frameworks, e.g. Koin and specify RealmRepo as a singleton when injected into the iOS app.

Refactor RealmRepo

You can change how realm field is initialized in RealmRepo. For example, you can extract logic that opens Realm in a singleton:


class RealmRepo {
// ...
    private val appService by lazy {
        RealmHolder.appService
    }

    private val realm by lazy {
        RealmHolder.realm
    }
// ...
}

object RealmHolder {
    private val schemaClass = set of(UserInfo::class, SessionInfo::class, ConferenceInfo::class)

    val appService by lazy {
        val appConfiguration =
            AppConfiguration.Builder(appId = "rconfernce-vkrny").log(LogLevel.ALL).build()
        App.create(appConfiguration)
    }

    val realm by lazy {
        val user = appService.currentUser!!

        val config =
            SyncConfiguration.Builder(user, schemaClass).name("conferenceInfo").schemaVersion(1)
                .initialSubscriptions { realm ->
                    add(realm.query<UserInfo>(), name = "user info", updateExisting = true)
                    add(realm.query<SessionInfo>(), name = "session info", updateExisting = true)
                    add(
                        realm.query<ConferenceInfo>(),
                        name = "conference info",
                        updateExisting = true
                    )
                }.waitForInitialRemoteData().build()
        Realm.open(config)
    }
}
英文:

There is a very simple explanation why realm opens several time: Realm.open call is inside RealmRepo instance and you are creating multiple RealmRepo instances.

For instance SwiftUI recreates RealmRepo with every state change. Also other views e.g. HomeView creates its own RealmRepo and so on.

There are a lot of different ways to fix it. Here is some of them:

Make your RealmRepo object

Just change class RealmRepo to object RealmRepo. That will make your RealmRepo a singleton in your iOS code you can access it like this:

var repo = RealmRepo.shared

Use EnvironmentObject

There is a simple Dependency Injection mechanism in SwiftUI that can help you use single instance of RealmRepo.

In your iOSApp.swift:

import SwiftUI

@main
struct iOSApp: App {
    @StateObject var repo = RealmRepo()

	var body: some Scene {
		WindowGroup {
            LoginScreenView()
              .environmentObject(repo)
		}
	}
}

and in all child views Login, HomeView, etc. use:

@EnvironmentObject var repo: RealmRepo

and do not create new instances.

More about @EnvironmentObject you can read here

Use some DI frameworks

You can use some DI frameworks, e.g. Koin and specify RealmRepo as singleton where when inject in iOS app.

Refactor RealmRepo

You can change how realm field initialized in RealmRepo. For example you can extract logic that opens Realm in a singleton:


class RealmRepo {
// ...
    private val appService by lazy {
        RealmHolder.appService
    }

    private val realm by lazy {
        RealmHolder.realm
    }
// ...
}

object RealmHolder {
    private val schemaClass = setOf(UserInfo::class, SessionInfo::class, ConferenceInfo::class)

    val appService by lazy {
        val appConfiguration =
            AppConfiguration.Builder(appId = &quot;rconfernce-vkrny&quot;).log(LogLevel.ALL).build()
        App.create(appConfiguration)
    }

    val realm by lazy {
        val user = appService.currentUser!!

        val config =
            SyncConfiguration.Builder(user, schemaClass).name(&quot;conferenceInfo&quot;).schemaVersion(1)
                .initialSubscriptions { realm -&gt;
                    add(realm.query&lt;UserInfo&gt;(), name = &quot;user info&quot;, updateExisting = true)
                    add(realm.query&lt;SessionInfo&gt;(), name = &quot;session info&quot;, updateExisting = true)
                    add(
                        realm.query&lt;ConferenceInfo&gt;(),
                        name = &quot;conference info&quot;,
                        updateExisting = true
                    )
                }.waitForInitialRemoteData().build()
        Realm.open(config)
    }
}

huangapple
  • 本文由 发表于 2023年3月9日 17:33:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/75682718.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定