Reputation: 14328
I'm sending http requests with session-cookie stored on the device side. The problem is that I want to persist it across app termination. But it seems like all the cookies of my app get deleted. I tried on both the simulator and device and they got the same behavior.
Is there any iOS way to prevent this cookie deletion? If not, how can I save it to disk and recover it back?
I'm planning to save this cookie in iOS keychain for security. And I think all NSHTTPCookie properties can be safely converted to NSString. So my current idea is to convert from NSHTTPCookie -> NSDictionary -> String -> Save in Keychain, and go backward to use get the original cookie. The problem is I don't want to go through the hassles of converting NSDictionary -> String and parsing String -> NSDictionary.
Upvotes: 3
Views: 3512
Reputation: 14328
===== SUMMARY =====
Well, I'm going to create another answer just to summarize possible ways to persist a NSDictionary (that doesn't contain objects of course!).
The easiest way to do this is to use NSDictionary's writeToFile:
method. This will generate a plist file. (Also available in NSArray) If you're worried about security, encrypt the array to an NSData object and write that.
Another way, not the fastest one though, is to use a tool to convert NSDictionary to a string format such as JSON or XML. You can save this string in iOS keychain and retrieve it later easily. (SFHFKeychainUtils
is a great for helping with keychain stuff). A good thing about using keychain is it is automatically encrypted.
Upvotes: 1
Reputation: 14328
Found a nice clean way of doing this. The trick is using JSONKit
(which came with RestKit) to convert between NSString and NSDictionary using JSON format. And here I use SFHFKeychainUtils
to help me with the keychain stuff. This way you don't have to worry about each property in the cookie and all the conversion work. :)
#import "RestKit/JSONKit.h"
#define WSC_serviceName @"WSC_serviceName"
#define WSC_username @"WSC_username"
#define WSC_cookieName @"_your_cookie_name" // name of the session cookie
- (void)saveSessionCookieToKeyChain {
for (NSHTTPCookie *c in [NSHTTPCookieStorage sharedHTTPCookieStorage].cookies) {
if ([c.name isEqualToString:WSC_cookieName]) {
NSString *cookieString = [c.properties JSONString];
NSLog(@"cookieString: %@",cookieString);
NSError *saveError = nil;
[SFHFKeychainUtils storeUsername:WSC_username
andPassword:cookieString
forServiceName:WSC_serviceName
updateExisting:YES error:&saveError];
}
}
}
- (BOOL)readsSessionCookieFromKeyChain {
NSError *readError = nil;
NSString *jsonString = [SFHFKeychainUtils getPasswordForUsername:WSC_username
andServiceName:WSC_serviceName
error:&readError];
if (!jsonString) return NO;
NSData* jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding]; // be sure that data is in UTF8 or it won't work
JSONDecoder* decoder = [[JSONDecoder alloc] initWithParseOptions:JKParseOptionNone];
NSDictionary* jsonDict = (NSDictionary*)[decoder objectWithData:jsonData];
NSLog(@"jsonDict: %@",jsonDict);
NSHTTPCookie *cookie = [[NSHTTPCookie alloc] initWithProperties:jsonDict];
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
return cookie!=nil;
}
- (void)clearSessionCookieAndRemoveFromKeyChain {
for (NSHTTPCookie *c in [NSHTTPCookieStorage sharedHTTPCookieStorage].cookies) {
[[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:c];
}
NSError *deleteError = nil;
[SFHFKeychainUtils deleteItemForUsername:WSC_username
andServiceName:WSC_serviceName
error:&deleteError];
}
Upvotes: 2
Reputation: 10069
Assuming you have an NSHTTPURLResponse, you can get an array of cookies like so:
NSDictionary * headers = [(NSHTTPURLResponse *)response allHeaderFields];
NSArray * cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:headers forURL:response.URL];
Where response
is the NSHTTPURLResponse.
You're going to get NSURLResponses in these 2 methods of the NSURLConnectionDelegate
- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse;
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
NSHTTPCookie has a properties
property which returns an NSDictionary
, so you can save them.
They can then be created with the same dictionary using -initWithProperties:
To send them, you'll need to create your own string for the Cookie header of a NSURLRequest
.
Something like:
NSMutableString * cookieString = [[NSMutableString alloc] init];
for (NSHTTPCookie * cookie in myLoadedCookies){
[cookieString appendFormat:@"%@=%@; ", cookie.name, cookie.value];
}
[request setValue:cookieString forHTTPHeaderField:@"Cookie"];
[cookieString release];
Where request
is an NSMutableURLRequest
.
You should also make sure to stop iOS managing cookies itself:
[request setHTTPShouldHandleCookies:NO];
Upvotes: 1