ios - Google 登录与 Realm Swift 连接

标签 ios swift realm

我有一个 iOS 应用程序使用 google 身份验证进行 SignIn 并首次将用户保存在 Realm 数据库中,如果我注销然后用户对象保存正常再次使用同一用户登录应用程序崩溃。

Realm 模型

import Foundation
import RealmSwift
import CoreLocation

class UserDB: Object {

  @objc dynamic var id : String!
  @objc dynamic var name: String?
  @objc dynamic var email: String?
  @objc dynamic var phoneNumber: String?
  @objc dynamic var photo: String?
  @objc dynamic var latitute: String?
  @objc dynamic var longitute: String?
  @objc dynamic var supporter: String?


 override static func primaryKey()-> String? {
    return "email"
  }

}

此处登录功能

func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {

    if let error = error {
        if (error as NSError).code == GIDSignInErrorCode.hasNoAuthInKeychain.rawValue {
            print("The user has not signed in before or they have since signed out.")
        } else {
            print("\(error.localizedDescription)")
        }
        return
    }
    // Perform any operations on signed in user here.
     let userId = user.userID                 
     let idToken = user.authentication.idToken 
     let fullName = user.profile.name
     let givenName = user.profile.givenName
     let familyName = user.profile.familyName
     let email = user.profile.email
     let photo = user.profile.imageURL(withDimension: 200)

    //Print all fields
    if let email = email {
        print("Your email is \(email)")
    }
    if let userId = userId {
        print("Your Id is \(userId)")
    }
    if let fullName = fullName {
        print("Your Full name is \(fullName)")
    }
    if let idToken = idToken {
        print("Your token is \(idToken)")
    }
    if let familyName = familyName {
        print("Your family Name is \(familyName)")
    }

    if let givenName = givenName {
        print("Your givin name is \(givenName)")
    }
    if let photo = photo {
        print("Your image is : \(photo)")
    }

    //Realm Object
    account.id = userId
    account.name = givenName
    account.email = email
    if let photo = photo?.absoluteString {
        account.photo = photo
    }

    saveData(data: account)


        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let vc = storyboard.instantiateViewController(withIdentifier: "GoogleMaps") as! GoogleMaps
        vc.accountMap = account
        navigationController?.pushViewController(vc, animated: true)


    } else {
        let alert = UIAlertController(title: "UnSuccessful SignIn", message: "please try again", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "Close" , style: .cancel) { (action) in})
        self.present(alert,animated:true,completion:nil)

    }
}

然后调用saveData函数将对象保存到Realm数据库中

func saveData(data: UserDB) {
    do {
        try realm.write {
            let user = realm.object(ofType: UserDB.self, forPrimaryKey: data.email!)
            if let user = user {
                print("array count: ",user)
                print("We found ",user.email!)
                realm.add(user, update: .modified)
            } else {
                print("We didn't find ",data.email!)
                //realm.add(data)
                realm.create(UserDB.self, value: data, update: .all)
            }
        }
    } catch {
        print("Error saving account Data \(error)")
    }

    //here put the tableView reloadData()
    //tableView.realoadData()
}

因此,对于第一次登录,这是正常的,用户信息会被保存,但如果我注销然后再次登录,应用程序就会崩溃。

错误

2019-12-26 12:05:26.504060+0200 social2[1943:339412] *** Terminating app due to uncaught exception 'RLMException', reason: 'Attempting to modify object outside of a write transaction - call beginWriteTransaction on an RLMRealm instance first.'
*** **First throw call stack:**
(
    0   CoreFoundation                      0x00000001088d68db __exceptionPreprocess + 331
    1   libobjc.A.dylib                     0x000000010aa65ac5 objc_exception_throw + 48
    2   Realm                               0x000000010928e349 _ZL27RLMVerifyInWriteTransactionP13RLMObjectBase + 105
    3   Realm                               0x000000010929399d _ZN12_GLOBAL__N_18setValueEP13RLMObjectBasemP8NSString + 29
    4   Realm                               0x0000000109293971 _ZZZN12_GLOBAL__N_110makeSetterIU8__strongP8NSStringS3_EEP11objc_objectP11RLMPropertyEUb0_ENKUlvE_clEv + 97
    5   Realm                               0x00000001092938d6 ___ZN12_GLOBAL__N_110makeSetterIU8__strongP8NSStringS3_EEP11objc_objectP11RLMProperty_block_invoke_2 + 310
    6   social2                             0x0000000103b8264b $s7social214ViewControllerC4sign_12didSignInFor9withErrorySo07GIDSignG0CSg_So13GIDGoogleUserCSgs0J0_pSgtF + 9851
    7   social2                             0x0000000103b836f3 $s7social214ViewControllerC4sign_12didSignInFor9withErrorySo07GIDSignG0CSg_So13GIDGoogleUserCSgs0J0_pSgtFTo + 147
    8   social2                             0x0000000104123924 __37-[GIDSignIn addCallDelegateCallback:]_block_invoke + 116
    9   social2                             0x000000010411ea46 -[GIDCallbackQueue fire] + 161
    10  social2                             0x000000010411e489 +[GIDAuthentication handleTokenFetchEMMError:completion:] + 364
    11  social2                             0x0000000104122b95 __38-[GIDSignIn maybeFetchToken:fallback:]_block_invoke + 311
    12  AppAuth                             0x0000000104f97060 __86+[OIDAuthorizationService performTokenRequest:originalAuthorizationResponse:callback:]_block_invoke_13 + 48
    13  libdispatch.dylib                   0x0000000108f9dd7f _dispatch_call_block_and_release + 12
    14  libdispatch.dylib                   0x0000000108f9edb5 _dispatch_client_callout + 8
    15  libdispatch.dylib                   0x0000000108fac080 _dispatch_main_queue_callback_4CF + 1540
    16  CoreFoundation                      0x000000010883da79 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
    17  CoreFoundation                      0x0000000108838126 __CFRunLoopRun + 2310
    18  CoreFoundation                      0x00000001088374d2 CFRunLoopRunSpecific + 626
    19  GraphicsServices                    0x000000010e1042fe GSEventRunModal + 65
    20  UIKitCore                           0x0000000117cbdfc2 UIApplicationMain + 140
    21  social2                             0x0000000103b89b4b main + 75
    22  libdyld.dylib                       0x000000010c7cd541 start + 1
    23  ???                                 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb) 

最佳答案

我对这个问题有点不清楚,但我认为问题是

When signing in using Google Sign-In and the user object does not exist in Realm, how do I add it to Realm upon first sign in, and then be able to access it thereafter when the user signs in.

从问题中的代码看来,我们只是复制 Realm 中的所有 GIDGoogleUser 属性,因此我将省略其中的一些属性以使其简短。

此外,问题是利用用户的电子邮件地址作为 key ,随着电子邮件地址的更改,这可能会出现问题,并且可能会使所有数据失效。更安全的选择是利用 Google 分配给每个用户的唯一用户 ID。

因此,从主键为 user_id 的 Realm 用户对象开始

class RealmUserClass: Object {
    @objc dynamic var user_id = ""
    @objc dynamic var full_name = ""
    @objc dynamic var email = ""

    override static func primaryKey() -> String? {
        return "user_id"
    }
}

然后是登录委托(delegate)函数,我会这样写

func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
    if error == nil {
        self.handleSignIn(forGIDUser: user)
    } else {
        print(error.localizedDescription)
        return
    }

因此,当用户登录时,我们调用handleSignIn函数并向其传递google用户对象

func handleSignIn(forGIDUser: GIDGoogleUser) {
    let gidUserId = forGIDUser.userID

    let realm = try! Realm()
    if let user = realm.object(ofType: RealmUserClass.self, forPrimaryKey: gidUserId) {
        print(user) //the user exists in Realm
        //proceed to the rest of the app
    } else { //user did not exist in Realm, create it}
        let userToAdd = RealmUserClass()
        userToAdd.person_id = forGIDUser.userID
        userToAdd.full_name = forGIDUser.profile.name
        userToAdd.email = forGIDUser.profile.email
        try! realm.write {
            realm.add(userToAdd)
        }
        //proceed to the rest of the app
    }
}

请注意,为了简洁起见,我省略了很多错误检查,因此请务必将其添加进去。

关于ios - Google 登录与 Realm Swift 连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59486856/

相关文章:

ios - 手动设置 UITabBar selected Item 时遇到问题

ios - XCode 5 中的错误接收器类型 'void' 错误

ios - NSTimeZone和特殊时区等

ios - 如何定义多个Texts并随机输出?

swift - 将图像的 NSData 转换为字符串

ios - 带有子类 Objective-C 类的 Swift 构建错误(涉及单词 "class"作为参数)

java - Realm 迁移重复值

ios - 推送通知在前台不起作用

带有 Realm DB 的 Android 实现,主 UI 错误上的事务

Swift Realm 'RLMException' ,原因 : 'Index 0 is out of bounds (must be less than 0)