Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are cookies in UIWebView accepted?

I have to question for you.

1 : I'm using UIWebViews in my iPhone App. I wan't the users be able to add comments in the news. But, to comment they have to log-in.

If not, how can I accept cookies in UIWebViews ?

2 : Are the cookies created in on UIWebView available in others UIWebView in an other View ?

Ex : I have my LoginViewController, with an embedded UIWebView, where my user can login/logout. If they log-in in this view, the cookie will be still available in the CommentViewController ?

If not, how can I make this possible ?

Thanks in advance !

like image 540
ThibaultV Avatar asked Sep 23 '14 22:09

ThibaultV


People also ask

Is UIWebView deprecated?

Apple is phasing out UIWebView, which is used by developers for integrating web content into an app in a quick and secure manner. Apple is replacing UIWebView (and WebView) with WKWebView, an updated version, as UIWebView has been deprecated.

Does WKWebView store cookies?

The code above has no effect with the new web view since each WKWebView instance has its own cookie storage represented by WKHTTPCookieStore class.

Do Webviews share cookies?

Cookie from WebView to another WebViewAs long as they are in the same domain, hence the cookies are shared across them.

What is the difference between UIWebView and WKWebView?

Unlike UIWebView, which does not support server authentication challenges, WKWebView does. In practical terms, this means that when using WKWebView, you can enter site credentials for password-protected websites.


4 Answers

The UIWebView will automatically store the cookies in the [NSHTTPCookieStorage sharedHTTPCookieStorage] collection, and should be available in all other UIWebViews within your app, during the same app launch. However the UIWebView class does not automatically store cookies for the pages that are loaded between app launches. You need to manually store cookies when the app is moved into the background and reload the values when the app is brought back into the foreground.

Place the following code in your AppDelegate class:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    //Other existing code

    [self loadHTTPCookies];
    return YES;
}

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    //Other existing code

    [self saveHTTPCookies];
}

- (void)applicationWillEnterForeground:(UIApplication *)application
{
    [self loadHTTPCookies];
}

- (void)applicationWillTerminate:(UIApplication *)application
{
    //Other existing code
    [self saveHTTPCookies];
}

-(void)loadHTTPCookies
{
    NSMutableArray* cookieDictionary = [[NSUserDefaults standardUserDefaults] valueForKey:@"cookieArray"];

    for (int i=0; i < cookieDictionary.count; i++)
    {
        NSMutableDictionary* cookieDictionary1 = [[NSUserDefaults standardUserDefaults] valueForKey:[cookieDictionary objectAtIndex:i]];
        NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:cookieDictionary1];
        [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
    }
}

-(void)saveHTTPCookies
{
    NSMutableArray *cookieArray = [[NSMutableArray alloc] init];
    for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
        [cookieArray addObject:cookie.name];
        NSMutableDictionary *cookieProperties = [NSMutableDictionary dictionary];
        [cookieProperties setObject:cookie.name forKey:NSHTTPCookieName];
        [cookieProperties setObject:cookie.value forKey:NSHTTPCookieValue];
        [cookieProperties setObject:cookie.domain forKey:NSHTTPCookieDomain];
        [cookieProperties setObject:cookie.path forKey:NSHTTPCookiePath];
        [cookieProperties setObject:[NSNumber numberWithUnsignedInteger:cookie.version] forKey:NSHTTPCookieVersion];
        [cookieProperties setObject:[[NSDate date] dateByAddingTimeInterval:2629743] forKey:NSHTTPCookieExpires];

        [[NSUserDefaults standardUserDefaults] setValue:cookieProperties forKey:cookie.name];
        [[NSUserDefaults standardUserDefaults] synchronize];

    }

    [[NSUserDefaults standardUserDefaults] setValue:cookieArray forKey:@"cookieArray"];
    [[NSUserDefaults standardUserDefaults] synchronize];
}
like image 151
Brian Shamblen Avatar answered Nov 13 '22 01:11

Brian Shamblen


Expanding the other answers:

Since NSHTTPCookieStorage cookies can be archived using NSKeyedArchiver, you don't need to extract every single cookie property yourself. Furthermore, you will want to remove the NSUserDefaults cookie property, when there are no cookies to store.

So you can simplify your cookie storing/loading to this extension:

static NSString *const kCookiesKey = @"cookies";

@implementation NSHTTPCookieStorage (Persistence)

- (void)saveToUserDefaults
{
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    if (self.cookies != nil && self.cookies.count > 0) {
        NSData *cookieData = [NSKeyedArchiver archivedDataWithRootObject:self.cookies];
        [userDefaults setObject:cookieData forKey:kCookiesKey];
    } else {
        [userDefaults removeObjectForKey:kCookiesKey];
    }
    [userDefaults synchronize];
}

- (void)loadFromUserDefaults
{
    NSData *cookieData = [[NSUserDefaults standardUserDefaults] objectForKey:kCookiesKey];
    if (cookieData != nil) {
        NSArray *cookies = [NSKeyedUnarchiver unarchiveObjectWithData:cookieData];
        for (NSHTTPCookie *cookie in cookies) {
            [self setCookie:cookie];
        }
    }
}

@end

Then just use [[NSHTTPCookieStorage sharedHTTPCookieStorage] loadFromUserDefaults]; and [[NSHTTPCookieStorage sharedHTTPCookieStorage] saveToUserDefaults]; in your AppDelegate as mentioned above.

like image 25
Robert Avatar answered Nov 13 '22 01:11

Robert


swift 3 clear version

func saveCookies() {
    guard let cookies = HTTPCookieStorage.shared.cookies else {
        return
    }
    let array = cookies.flatMap { (cookie) -> [HTTPCookiePropertyKey: Any]? in
        cookie.properties
    }
    UserDefaults.standard.set(array, forKey: "cookies")
    UserDefaults.standard.synchronize()
}

func loadCookies() {
    guard let cookies = UserDefaults.standard.value(forKey: "cookies") as? [[HTTPCookiePropertyKey: Any]] else {
        return
    }
    cookies.forEach { (cookie) in
        guard let cookie = HTTPCookie.init(properties: cookie) else {
            return
        }
        HTTPCookieStorage.shared.setCookie(cookie)
    }
}
like image 41
Wane Avatar answered Nov 13 '22 03:11

Wane


From all the great other answers to this question I compiled a handy extension on UserDefaults that shortens the necessary code.

UserDefaults Extension for Swift 3

extension UserDefaults {

    /// A dictionary of properties representing a cookie.
    typealias CookieProperties = [HTTPCookiePropertyKey: Any]

    /// The `UserDefaults` key for accessing cookies.
    private static let cookieKey = "cookies"

    /// Saves all cookies currently in the shared `HTTPCookieStorage` to the shared `UserDefaults`.
    func saveCookies() {
        guard let cookies = HTTPCookieStorage.shared.cookies else {
            return
        }
        let cookiePropertiesArray = cookies.flatMap { $0.properties }
        set(cookiePropertiesArray, forKey: UserDefaults.cookieKey)
        synchronize()
    }

    /// Loads all cookies stored in the shared `UserDefaults` and adds them to the current shared `HTTPCookieStorage`.
    func loadCoookies() {
        let cookiePropertiesArray = value(forKey: UserDefaults.cookieKey) as? [CookieProperties]
        cookiePropertiesArray?.forEach {
            if let cookie = HTTPCookie(properties: $0) {
                HTTPCookieStorage.shared.setCookie(cookie)
            }
        }
    }

}

You can add this code to a separate file UserDefaults+Cookies.swift (for example) and then call the methods from your AppDelegate as described by Brian Shamblen in his original answer:

Calls from AppDelegate

func applicationDidBecomeActive(_ application: UIApplication) {
    UserDefaults.standard.loadCoookies()
}

func applicationWillEnterForeground(_ application: UIApplication) {
    UserDefaults.standard.loadCoookies()
}

func applicationDidEnterBackground(_ application: UIApplication) {
    UserDefaults.standard.saveCookies()
}

func applicationWillTerminate(_ application: UIApplication) {
    UserDefaults.standard.saveCookies()
}
like image 43
Mischa Avatar answered Nov 13 '22 02:11

Mischa