11import Foundation
22
33public class SessionCookieStore {
4-
4+
55 // MARK: - Private properties
66 private static let bundleIdentifier = Bundle . main. bundleIdentifier ?? " com.mendix.app "
77 private static let storageKey = bundleIdentifier + " sessionCookies "
88 private static let queue = DispatchQueue ( label: bundleIdentifier + " .session-cookie-store " , qos: . utility)
9-
9+
1010 // MARK: - Public API
1111 public static func restore( ) {
12-
12+
1313 guard let cookies = get ( key: storageKey) else {
1414 NSLog ( " SessionCookieStore: No cookies to restore " )
1515 return
1616 }
17-
17+
1818 let storage = HTTPCookieStorage . shared
1919 let existing = Set ( storage. cookies ?? [ ] )
2020 cookies. filter { !existing. contains ( $0) } . forEach { storage. setCookie ( $0) }
21-
22- clear ( ) // Clear stored cookies after restoration to avoid any side effects
21+
22+ clear ( )
2323 }
24-
24+
2525 public static func persist( ) {
2626 queue. async {
2727 let sessionCookies = HTTPCookieStorage . shared. cookies? . filter { isSessionCookie ( $0) } ?? [ ]
@@ -33,21 +33,21 @@ public class SessionCookieStore {
3333 set ( key: storageKey, cookies: sessionCookies)
3434 }
3535 }
36-
36+
3737 public static func clear( ) {
3838 clear ( key: storageKey)
3939 }
40-
40+
4141 // MARK: - Private API
4242 private static func isSessionCookie( _ cookie: HTTPCookie ) -> Bool {
4343 return cookie. expiresDate == nil
4444 }
45-
45+
4646 private static func set( key: String , cookies: [ HTTPCookie ] ) {
4747 do {
4848 let data = try NSKeyedArchiver . archivedData ( withRootObject: cookies, requiringSecureCoding: false )
49+ clear ( key: key)
4950 let storeQuery = [ kSecClass: kSecClassGenericPassword, kSecAttrAccount: key, kSecValueData: data] as CFDictionary
50- SecItemDelete ( storeQuery)
5151 let status = SecItemAdd ( storeQuery, nil )
5252 if status != noErr {
5353 NSLog ( " SessionCookieStore: Failed to persist session cookies with status: \( status) " )
@@ -56,29 +56,46 @@ public class SessionCookieStore {
5656 NSLog ( " SessionCookieStore: Failed to persist session cookies: \( error. localizedDescription) " )
5757 }
5858 }
59-
59+
6060 private static func get( key: String ) -> [ HTTPCookie ] ? {
61- do {
62- let query : [ CFString : Any ] = [ kSecClass: kSecClassGenericPassword, kSecAttrAccount: key, kSecReturnData: true ]
63- var item : CFTypeRef ?
64- let status = SecItemCopyMatching ( query as CFDictionary , & item)
65- if status == errSecSuccess, let data = item as? Data {
66- let cookies = try NSKeyedUnarchiver . unarchivedObject ( ofClasses: [ NSArray . self, HTTPCookie . self] , from: data) as? [ HTTPCookie ]
67- return cookies
68- } else {
69- NSLog ( " SessionCookieStore: No session cookies found with status: \( status) " )
61+ let query : [ CFString : Any ] = [
62+ kSecClass: kSecClassGenericPassword,
63+ kSecAttrAccount: key,
64+ kSecReturnData: true ,
65+ kSecUseAuthenticationUI: kSecUseAuthenticationUIFail
66+ ]
67+ var item : CFTypeRef ?
68+ let status = SecItemCopyMatching ( query as CFDictionary , & item)
69+
70+ if status == errSecInteractionNotAllowed {
71+ NSLog ( " SessionCookieStore: Item inaccessible (device locked), skipping restore " )
72+ return nil
73+ } else if status == errSecSuccess, let data = item as? Data {
74+ let result = ObjCExceptionCatcher . catchException {
75+ try ? NSKeyedUnarchiver . unarchivedObject ( ofClasses: [ NSArray . self, HTTPCookie . self] , from: data)
76+ }
77+
78+ guard let cookies = result as? [ HTTPCookie ] else {
79+ NSLog ( " SessionCookieStore: Failed to deserialize cookies, clearing " )
80+ clear ( key: key)
7081 return nil
7182 }
72- } catch {
73- NSLog ( " SessionCookieStore: Failed to retrieve session cookies: \( error. localizedDescription) " )
83+ return cookies
84+ } else if status != errSecItemNotFound {
85+ // Any other unreadable state — delete to prevent repeated failures
86+ NSLog ( " SessionCookieStore: Unreadable legacy item (status: \( status) ), clearing " )
87+ clear ( key: key)
88+ return nil
89+ } else {
90+ NSLog ( " SessionCookieStore: No session cookies found " )
7491 return nil
7592 }
7693 }
77-
94+
7895 private static func clear( key: String ) {
79- let query = [ kSecClass: kSecClassGenericPassword, kSecAttrAccount: key, kSecReturnData : true ] as CFDictionary
96+ let query = [ kSecClass: kSecClassGenericPassword, kSecAttrAccount: key] as CFDictionary
8097 let status = SecItemDelete ( query)
81- if status != errSecSuccess {
98+ if status != errSecSuccess && status != errSecItemNotFound {
8299 NSLog ( " SessionCookieStore: Failed to clear cookies with status: \( status) " )
83100 }
84101 }
0 commit comments