标题反映了我的想法,但我无法证明这一点超出了疑问。我已经向我们的sentry实例添加了一些日志调用,以尝试缩小正在发生的事情
背景:我们的应用程序有一个名为LoadingViewController的根VC,它有一些逻辑来确定我们是否有登录用户。如果我们这样做,它会显示主屏幕,如果我们不显示,则显示登录/注册屏幕。间歇性地,当主屏幕显示时显示登录屏幕。我们的应用程序使用JWT对我们的后端进行身份验证,但我相当自信令牌到期不是问题;根据日志
不会调用强制注销的过期令牌周围的日志记录研究:我也回顾了这些问题/问题,虽然类似,却没有解决我所看到的问题
我看到的内容:LoadingViewController启动。进入bootUser,没有从UserDataService返回用户,返回false,然后重置所有内容。这就是为什么我认为User对象在被查询时不存在的原因。
这通常发生在我暂时没有使用该应用程序之后,而不是所有时间。我无法通过强制退出应用程序或加载一堆游戏试图强制它从内存中恢复并回到它来强制解决问题。
我有点不知所措。有什么我没有想到或可以检查?
由于
代码
我们的LoadingViewController的子集:
class LoadingViewController: UIViewController {
override func viewDidLoad() {
breadcrumbs.append("LoadingViewController.viewDidLoad \(UserDataService.getCurrentUser()?.id) \(UserDataService.getCurrentUser()?.token)")
super.viewDidLoad()
// determine if we have a user
if self.bootUser() {
return
}
// If we got hereUser is not logged in. wipe it
NSOperationQueue().addOperationWithBlock() {
let realm = KidRealm.realm()
CacheUtils.purgeRealm(realm)
}
}
func bootUser() -> Bool {
breadcrumbs.append("LoadingViewController.bootUser 0 \(UserDataService.getCurrentUser()?.id) \(UserDataService.getCurrentUser()?.token)")
if let user = UserDataService.getCurrentUser(),
let checkToken = user.token
where checkToken != "" {
breadcrumbs.append("LoadingViewController - 1 have user \(UserDataService.getCurrentUser()?.id) \(UserDataService.getCurrentUser()?.token)")
KA.initUser(currentUser)
// user is a full user, bring them to the homepage
if let vc = self.storyboard?.instantiateViewControllerWithIdentifier("betacodeview") as? BetaCodeViewController {
breadcrumbs.append("LoadingViewController - 3 betacodeview \(UserDataService.getCurrentUser()?.id) \(UserDataService.getCurrentUser()?.token)")
self.performSegueWithIdentifier("gotohomeview", sender: self)
return true
}
breadcrumbs.append("LoadingViewController - 4 skip? \(UserDataService.getCurrentUser()?.id) \(UserDataService.getCurrentUser()?.token)")
logLogoutIssues("bootUser with token. !full !vc \(UserDataService.count())")
}
breadcrumbs.append("LoadingViewController - 5 no token / user \(UserDataService.getCurrentUser()?.id) \(UserDataService.getCurrentUser()?.token)")
logLogoutIssues("bootUser with no token. token: (\(token)) \(UserDataService.count())")
return false
}
用户模型的子集
class User: Object {
dynamic var id: Int = -1
dynamic var UUID = ""
dynamic var email: String?
dynamic var firstName: String?
dynamic var lastName: String?
dynamic var facebookId: String?
dynamic var instagramId: String?
dynamic var photoUrl: String?
dynamic var token: String?
dynamic var fullUser = false
dynamic var donationPercent: Int = 0
// Extra info
dynamic var birthday: NSDate?
dynamic var phone: String?
dynamic var address1: String?
dynamic var address2: String?
dynamic var zip: String?
dynamic var city: String?
dynamic var state: String?
// User settings
dynamic var allowNotification: Bool = true
// temporary in memory, not saved
var profilePhoto = NSData()
override static func primaryKey() -> String? {
return "UUID"
}
func setUuid(UUID: String) {
self.UUID = UUID
}
func setUuid() {
self.UUID = SwiftyUUID.UUID().CanonicalString()
}
}
LoadingDataController用于获取当前用户的UserDataService的子集
struct UserDataService: Saveable, Deletable {
static func getCurrentUser(realm: Realm) -> User? {
let getUser = realm.objects(User)
if let user = getUser.first {
return user
}
return nil
}
static func getCurrentUser() -> User? {
let realm = KidRealm.realm()
return UserDataService.getCurrentUser(realm)
}
}
CacheUtils的子集,用于清除用户登录或注册的域(在注销期间也称为
)struct CacheUtils {
static func purgeRealm(realm: Realm) {
try! realm.write {
realm.deleteAll()
}
}
}
最后,KidRealm设置加密。这在主应用程序中使用,在推送通知进入时使用后台进程
struct KidRealm {
static func realm() -> Realm {
let key = getKey()
let configuration = Realm.Configuration(encryptionKey: key)
let realm = try! Realm(configuration: configuration)
return realm
}
static func getKeyString(key: NSData) -> String {
return "\(key)".stringByTrimmingCharactersInSet(NSCharacterSet(charactersInString: "<>")).stringByReplacingOccurrencesOfString(" ", withString: "")
}
static func getKey() -> NSData {
// Identifier for our keychain entry - should be unique for your application
let keychainIdentifier = "com.kidfund.kidfund1.keychain"
let keychainIdentifierData = keychainIdentifier.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
// First check in the keychain for an existing key
var query: [NSString: AnyObject] = [
kSecClass: kSecClassKey,
kSecAttrApplicationTag: keychainIdentifierData,
kSecAttrKeySizeInBits: 512,
kSecReturnData: true,
kSecAttrAccessible: kSecAttrAccessibleAfterFirstUnlock
]
// To avoid Swift optimization bug, should use withUnsafeMutablePointer() function to retrieve the keychain item
// See also: https://stackoverflow.com/questions/24145838/querying-ios-keychain-using-swift/27721328#27721328
var dataTypeRef: AnyObject?
var status = withUnsafeMutablePointer(&dataTypeRef) { SecItemCopyMatching(query, UnsafeMutablePointer($0)) }
if status == errSecSuccess {
return dataTypeRef as! NSData
}
// No pre-existing key from this application, so generate a new one
let keyData = NSMutableData(length: 64)!
let result = SecRandomCopyBytes(kSecRandomDefault, 64, UnsafeMutablePointer<UInt8>(keyData.mutableBytes))
assert(result == 0, "Failed to get random bytes")
// Store the key in the keychain
query = [
kSecClass: kSecClassKey,
kSecAttrApplicationTag: keychainIdentifierData,
kSecAttrKeySizeInBits: 512,
kSecValueData: keyData,
kSecAttrAccessible: kSecAttrAccessibleAfterFirstUnlock
]
status = SecItemAdd(query, nil)
assert(status == errSecSuccess, "Failed to insert the new key in the keychain")
return keyData
}
}
更新1 :
我还有一个全局的currentUser帮助器,它包含对我的UserDataService的调用。我不认为这是一个问题,但增加了完整性。这种情况由于遗留原因而存在,并且在某些时候会在列表中重构。
var currentUser: User {
get {
if let user = UserDataService.getCurrentUser() {
return user
}
breadcrumbs.append("Getting empty currentUser \(UserDataService.count())")
logLogoutIssues("Getting empty currentUser \(UserDataService.count())")
return User()
}
}
更新2 :
基于Peba指出的thread,我现在正在推动一些修复,包括: