I have one app with multiple targets (each target is for another client as separate application with different name, bundle identifier etc).
I have method:
fileprivate static func loadSessionFromKeychain() -> UserSession? {
if let sessionData = KeychainWrapper.standard.data(forKey: UserSession.sessionDefaultsKey) {
print("sessionData:")
print(sessionData.debugDescription)
if let session = NSKeyedUnarchiver.unarchiveObject(with: sessionData) as? UserSession {
_current = session
return session
} else {
print("ERROR: Could not parse UserSession from Keychain")
}
return nil
}
return nil
}
The line if let session = NSKeyedUnarchiver.unarchiveObject(with: sessionData) as? UserSession {
throws error:
* Terminating app due to uncaught exception 'NSInvalidUnarchiveOperationException', reason: '* -[NSKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class (_test__MyApp.UserSession) for key (root); the class may be defined in source code or a library that is not linked'
I've tried to catch do {} catch {}
but it didn't catch and throws still the same error + xCode says
catch' block is unreachable because no errors are thrown in 'do' block`
Any ideas how to fix this?
UserSessionSwift.swift
import UIKit
import SwiftKeychainWrapper
class UserSession: NSObject, NSCoding {
// Static vars
fileprivate static var _current: UserSession?
static var current: UserSession? {
get {
// If there is already a session return it
if _current != nil {
return _current
}
// If there is no session yet but one is persistently stored return it
if let session = self.persistentLoadCurrentSession() {
_current = session
self.persistentLoadCookies()
return session
}
// Otherwise return nil
return nil
}
set(value) {
// Store the actual value
_current = value
// Perform hooks after changing the current session
if value == nil {
self.afterLogout()
} else {
self.afterLogin()
}
}
}
// Constants
fileprivate static let cookiesDefaultsKey: String = "NSUserDefaultsKeyCookieStorage"
fileprivate static let sessionDefaultsKey: String = "NSUserDefaultsKeyUserSessionStorage"
// Instance properties
let client: Client
// -------------------------------------------------------------------------------
// MARK: - Lifecycle
// -------------------------------------------------------------------------------
required init(client: Client) {
// set local properties
self.client = client
// call super init
super.init()
// Store cookies after a session was initialized
UserSession.persistentStoreCookies()
}
required init?(coder aDecoder: NSCoder) {
self.client = aDecoder.decodeObject(forKey: "client") as! Client
super.init()
}
// -------------------------------------------------------------------------------
// MARK: - Public
// -------------------------------------------------------------------------------
func encode(with aCoder: NSCoder) {
aCoder.encode(self.client, forKey: "client")
}
/**
Performs all necessary operations after user logs in: stores current cookies and user session for the case user stops and reruns the application later
*/
static func afterLogin() {
// Persistently store session data
self.persistentStoreCookies()
self.persistentStoreCurrentSession()
// Register user & device for PUSH notifications
NotificationsManager.registerForNotifications()
}
/**
Performs all necessary operations after user logs out: deletes stored cookies and user session so that the next time the user runs this application he gets the login prompt
*/
static func afterLogout() {
// Erase user session data
self.persistentEraseCookies()
self.persistentEraseCurrentSession()
// Delete all offers from local database
CoreDataHelper.deleteEntitiesInContext(CoreDataHelper.mainContext, entityName: UsedOffer.entityName)
CoreDataHelper.saveContext()
}
static func requestPopup() {
// Get popup from server
print("INFO: Checking for popups on the server...")
ClientPopupRequest.send({ (popup) -> Void in
if let popup = popup {
// If there is one, show it
popup.showAlertAndPerform(in: RootVC.sharedInstance) {
// After the popup performs its action, ask for another one
self.requestPopup()
}
} else {
// If none, exit
print("INFO: No new popups found.")
}
}) { (error) -> Void in
}
}
// -------------------------------------------------------------------------------
// MARK: - Private
// -------------------------------------------------------------------------------
/**
Saves current user session to persistent store (currently NSUserDefaults)
*/
static func persistentStoreCurrentSession() {
if let session = _current {
// Archive session
let sessionData = NSMutableData()
let archiver = NSKeyedArchiver(forWritingWith: sessionData)
archiver.encode(session)
archiver.finishEncoding()
// Session encoded
KeychainWrapper.standard.set(session, forKey: UserSession.sessionDefaultsKey)
// UserDefaults.standard.set(sessionData, forKey: UserSession.sessionDefaultsKey)
// UserDefaults.standard.synchronize()
} else {
print("WARNING: No session to store")
}
}
/**
Tries to load an user session from persistent store (currently NSUserDefaults) and store it as current session in UserSession class. Returns the loaded instance of user session if it succeeds, otherwise returns nil
*/
fileprivate static func persistentLoadCurrentSession() -> UserSession? {
if let keychainData = loadSessionFromKeychain() {
persistentEraseUserDataSession()
return keychainData
} else if let userData = loadSessionFromStore() {
return userData
}
return nil
}
fileprivate static func loadSessionFromKeychain() -> UserSession? {
if let sessionData = KeychainWrapper.standard.data(forKey: UserSession.sessionDefaultsKey) {
print("sessionData:")
print(sessionData.debugDescription)
if let session = NSKeyedUnarchiver.unarchiveObject(with: sessionData) as? UserSession {
_current = session
return session
} else {
print("ERROR: Could not parse UserSession from Keychain")
}
return nil
}
return nil
}
fileprivate static func loadSessionFromStore() -> UserSession? {
if let sessionData = UserDefaults.standard.object(forKey: UserSession.sessionDefaultsKey) as? Data {
let unarchiver = NSKeyedUnarchiver(forReadingWith: sessionData)
if let session = unarchiver.decodeObject() as? UserSession {
unarchiver.finishDecoding()
// Session decoded
_current = session
return session
} else {
print("ERROR: Could not parse UserSession from Store")
}
return nil
}
return nil
}
fileprivate static func persistentEraseCurrentSession() {
// Remove the current session object
_current = nil
// Remove the persisted session object
UserDefaults.standard.removeObject(forKey: UserSession.sessionDefaultsKey)
KeychainWrapper.standard.removeObject(forKey: UserSession.sessionDefaultsKey)
}
fileprivate static func persistentEraseUserDataSession() {
// Remove the persisted session object
UserDefaults.standard.removeObject(forKey: UserSession.sessionDefaultsKey)
}
fileprivate static func persistentStoreCookies() {
if let cookies = HTTPCookieStorage.shared.cookies {
let cookieData = NSKeyedArchiver.archivedData(withRootObject: cookies)
UserDefaults.standard.set(cookieData, forKey: UserSession.sessionDefaultsKey)
KeychainWrapper.standard.set(cookieData, forKey: UserSession.cookiesDefaultsKey)
} else {
print("WARNING: No cookies to store")
}
}
fileprivate static func persistentLoadCookies() {
var cookieData: Data?
if let keychainData = KeychainWrapper.standard.data(forKey: UserSession.cookiesDefaultsKey) {
cookieData = keychainData
} else if let userData = UserDefaults.standard.object(forKey: UserSession.cookiesDefaultsKey) as? Data {
cookieData = userData
}
if (cookieData != nil) {
if let cookies = NSKeyedUnarchiver.unarchiveObject(with: cookieData!) as? [HTTPCookie] {
cookies.forEach { HTTPCookieStorage.shared.setCookie($0) }
} else {
print("ERROR: Could not parse [NSHTTPCookie] from unarchived data")
}
} else {
print("WARNING: No cookies to load")
}
}
fileprivate static func persistentEraseCookies() {
UserDefaults.standard.removeObject(forKey: UserSession.cookiesDefaultsKey)
KeychainWrapper.standard.removeObject(forKey: UserSession.cookiesDefaultsKey)
}
}
// EDIT: added UserSession.swift
class
UserSession
class at run time. Is that class included in the target you're building? – Delinquent