EMASCurl/EMASCurlCookieStorage.m (121 lines of code) (raw):

// // EMASCurlCookieStorage.m // EMASCurl // // Created by xuyecan on 2025/2/3. // #import "EMASCurlCookieStorage.h" static NSString *const kEMASCurlCookieStorageKey = @"EMASCurlCookieStorage"; static NSTimeInterval const kPersistenceInterval = 15.0; @interface EMASCurlCookieStorage () @property (nonatomic, strong) dispatch_queue_t persistQueue; @property (nonatomic, strong) dispatch_source_t persistTimer; @end @implementation EMASCurlCookieStorage + (instancetype)sharedStorage { static EMASCurlCookieStorage *sharedStorage = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedStorage = [[self alloc] init]; }); return sharedStorage; } - (instancetype)init { self = [super init]; if (self) { // 创建用于做持久化操作的队列 _persistQueue = dispatch_queue_create("com.alicloud.emascurl.cookiestorage.persist", DISPATCH_QUEUE_SERIAL); // 从本地加载 cookies [self loadPersistedCookies]; // 设置定时器进行持久化 [self setupPersistenceTimer]; } return self; } - (void)dealloc { if (_persistTimer) { dispatch_source_cancel(_persistTimer); } } #pragma mark - Public Methods - (void)setCookieWithString:(NSString *)cookieString forURL:(NSURL *)url { if (!cookieString.length || !url) { return; } // 解析 Set-Cookie 字符串并设置到 NSHTTPCookieStorage NSArray<NSHTTPCookie *> *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:@{@"Set-Cookie": cookieString} forURL:url]; [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookies:cookies forURL:url mainDocumentURL:nil]; } - (NSString *)cookieStringForURL:(NSURL *)url { if (!url) { return nil; } NSArray<NSHTTPCookie *> *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:url]; NSDictionary *headers = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies]; return headers[@"Cookie"]; } #pragma mark - Private Methods - (void)setupPersistenceTimer { self.persistTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.persistQueue); dispatch_source_set_timer(self.persistTimer, dispatch_time(DISPATCH_TIME_NOW, kPersistenceInterval * NSEC_PER_SEC), kPersistenceInterval * NSEC_PER_SEC, 1 * NSEC_PER_SEC); __weak typeof(self) weakSelf = self; dispatch_source_set_event_handler(self.persistTimer, ^{ [weakSelf persistCookies]; }); dispatch_resume(self.persistTimer); } - (void)loadPersistedCookies { dispatch_async(self.persistQueue, ^{ NSArray *cookiesData = [[NSUserDefaults standardUserDefaults] objectForKey:kEMASCurlCookieStorageKey]; if (!cookiesData) { return; } NSArray<NSHTTPCookie *> *cookies = [self deserializeCookies:cookiesData]; for (NSHTTPCookie *cookie in cookies) { [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie]; } }); } - (void)persistCookies { NSArray<NSHTTPCookie *> *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]; NSArray *serializedCookies = [self serializeCookies:cookies]; [[NSUserDefaults standardUserDefaults] setObject:serializedCookies forKey:kEMASCurlCookieStorageKey]; [[NSUserDefaults standardUserDefaults] synchronize]; } - (NSArray *)serializeCookies:(NSArray<NSHTTPCookie *> *)cookies { NSMutableArray *serializedCookies = [NSMutableArray array]; for (NSHTTPCookie *cookie in cookies) { if ([self isCookieValid:cookie]) { NSDictionary *cookieProperties = [self serializableCookieProperties:cookie]; if (cookieProperties) { [serializedCookies addObject:cookieProperties]; } } } return [serializedCookies copy]; } - (NSArray<NSHTTPCookie *> *)deserializeCookies:(NSArray *)serializedCookies { NSMutableArray<NSHTTPCookie *> *cookies = [NSMutableArray array]; for (NSDictionary *properties in serializedCookies) { NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:properties]; if (cookie && [self isCookieValid:cookie]) { [cookies addObject:cookie]; } } return [cookies copy]; } - (BOOL)isCookieValid:(NSHTTPCookie *)cookie { NSDate *expiresDate = cookie.expiresDate; if (!expiresDate) { return YES; } return [expiresDate compare:[NSDate date]] == NSOrderedDescending; } - (NSDictionary *)serializableCookieProperties:(NSHTTPCookie *)cookie { NSMutableDictionary *properties = [NSMutableDictionary dictionary]; properties[NSHTTPCookieName] = cookie.name; properties[NSHTTPCookieValue] = cookie.value; properties[NSHTTPCookieDomain] = cookie.domain; properties[NSHTTPCookiePath] = cookie.path; if (cookie.secure) { properties[NSHTTPCookieSecure] = @YES; } if (cookie.expiresDate) { properties[NSHTTPCookieExpires] = cookie.expiresDate; } return [properties copy]; } @end