我有一个 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/