Share cookies between Xamarin WKWebView and UIWebView - ios

We know that we can share cookies between WKWebViews as discussed here and also in other discussions.
But is it possible to share cookies between a WKWebView and a UIWebView?
Both Http cookies and other cookies.
Did anyone managed to do that?

Is it possible to share cookies between a WKWebView and a UIWebView?
The problem with WKWebView cookies is that requests made by WKWebView do not automatically carry cookies stored in the NSHTTPCookieStorage container.
Solution:
WKWebView Cookie injection is achieved by copying the contents of NSHTTPCookieStorage into WKHTTPCookieStore before executing WKWebView.loadReques() . The sample code is as follows:
CopyCookieToWebviewWithHandler(() =>
{
NSUrl url = new NSUrl("xxx");
NSUrlRequest request = NSUrlRequest.FromUrl(url);
webView.LoadRequest(request);
});
void CopyCookieToWebviewWithHandler(Action completionHandler)
{
NSHttpCookie[] cookies = NSHttpCookieStorage.SharedStorage.Cookies;
WKHttpCookieStore cookieStore = webView.Configuration.WebsiteDataStore.HttpCookieStore;
if(cookies.Length==0)
{
completionHandler();
return;
}
foreach (NSHttpCookie cookie in cookies)
{
cookieStore.SetCookie(cookie,() =>
{
if(cookies[cookies.Length-1]==cookie)
{
completionHandler();
return;
}
});
}
}
Note:WKHttpCookieStore will only available after iOS 11.0

Related

Authentication from web view in a native iOS app?

I have a web backend server that handles web site (html) authentication from email, facebook, google....
Is it a possible option to load the authentication web form into a native web view, intercept the authentication response by native app, and store login state into native app, so the native app will be able then to send REST authorized queries to the backend server?
Is this scenario possible?
Yes it is possible using WKWebView. It has navigation delegate API
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
Using the above API, you can check for access token which is coming via cookie.
Objective-c
WKWebsiteDataStore *dataStore = webView.configuration.websiteDataStore;
WKHTTPCookieStore *cookieStore = dataStore.httpCookieStore;
[cookieStore getAllCookies:^(NSArray<NSHTTPCookie*> *cookieArray) {
NSString *cookieValue = nil;
for (NSHTTPCookie *cookie in cookieArray) {
if ([cookie.name isEqualToString:#"access_token"]) {
NSString *accessToken = cookie.value;
}
}
}];
Swift
let dataStore = webView.configuration.websiteDataStore
let cookieStore = dataStore.httpCookieStore
cookieStore.getAllCookies({ cookieArray in
let cookieValue: String? = nil
for cookie in cookieArray ?? [] {
if (cookie.name == "access_token") {
let accessToken = cookie.value
}
}
})
This is what I'm doing in our project. Hope this helps you.

Why does WKHTTPCookieStore's API -- getAllCookies called the completionHandler but have no session cookies contains

WKNavigationResponse is no longer providing access to cookies through its response.header in iOS 12,
So,I have to user the new API to get all cookies. when I use the getAllCookies:,sometimes it didn't call the completionHandler,sometimes it called the completionHandler without session cookies contain.
under certain circumstances,I need to get the session cookies.
Why does WKHTTPCookieStore's API -- getAllCookies can't always call the completionHandler ?And,why sometimes it called the completionHandler but have no session cookies contain?
Or,Does that have any way to get all cookies contains session cookies from WKWebView?
WKHTTPCookieStore *cookieStore = webView.configuration.websiteDataStore.httpCookieStore;
[cookieStore getAllCookies:^(NSArray* cookies) {
// case 1: didn't call this completionHandler
for (NSHTTPCookie *cookie in cookies) {
//case 2 called this completionHandler without session cookie contains
}
}];

URLSession to use a custom CookieStorage on iOS?

I need a urlsession that stores cookies in a separate cookieStorage
In the folllowing code the cookieStorage in urlSession is the same as the shares cookieStorage, is it possible to create a separate cookie store
let config = URLSessionConfiguration.default
session = URLSession(configuration: config)
config.httpCookieAcceptPolicy = .always
session.configuration.httpCookieStorage = HTTPCookieStorage.sharedCookieStorage(forGroupContainerIdentifier: "adfadf")
let task = session.dataTask(with: URL(string: "https://www.google.com")!) { (data, response, error) in
print((response as? HTTPURLResponse)?.allHeaderFields ?? "")
DispatchQueue.main.async {
print(self.session.configuration.httpCookieStorage?.cookies ?? "wtf")
print(HTTPCookieStorage.shared === self.session.configuration.httpCookieStorage)
}
}
task.resume()
Same result if I initialize the cookie store using HTTPCookieStorage()
EDIT
I tried creating a cookie store manually and add cookies to it after the request is completed
let cookies = HTTPCookie.cookies(withResponseHeaderFields: headers, for: url)
// cookies is not empty
self.cookieStore.setCookies(cookies, for: url, mainDocumentURL: nil)
print(self.cookieStore.cookies) //result is nil
and at the end I end up with nil as cookies
If you open up the header file for NSHTTPCookieStorage you'll see this documentation (for some reason these details don't appear in the regular documentation).
/*!
#method sharedCookieStorageForGroupContainerIdentifier:
#abstract Get the cookie storage for the container associated with the specified application group identifier
#param identifier The application group identifier
#result A cookie storage with a persistent store in the application group container
#discussion By default, applications and associated app extensions have different data containers, which means
that the sharedHTTPCookieStorage singleton will refer to different persistent cookie stores in an application and
any app extensions that it contains. This method allows clients to create a persistent cookie storage that can be
shared among all applications and extensions with access to the same application group. Subsequent calls to this
method with the same identifier will return the same cookie storage instance.
*/
#available(iOS 9.0, *)
open class func sharedCookieStorage(forGroupContainerIdentifier identifier: String) -> HTTPCookieStorage
In order to have a valid app group you need to add it following the instructions in Adding an App to an App Group.
I'm guessing that since you don't have the app groups added to your entitlements it's defaulting to NSHTTPCookieStorage.shared.
Apparently HTTPCookieStorage.sharedCookieStorage(forGroupContainerIdentifier: "groupName") seems to work only on iOS 10+, on iOS 9 it will return nil even if the documentation says #available(iOS 9.0, *)
open class func sharedCookieStorage(forGroupContainerIdentifier identifier: String) -> HTTPCookieStorage
You can use this workaround :
let cookies: HTTPCookieStorage
if #available(iOS 10.0, *) {
cookies = HTTPCookieStorage.sharedCookieStorage(forGroupContainerIdentifier: "groupName")
} else {
cookies = HTTPCookieStorage.shared
}

How to fetch complete cookies detail in WKWebView in IOS

In my app there is a login page on Webview and url is based on https. I want to get login username and password from cookies. But not able to fetch it
I tried [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies] it just send me other cookies details like domain,creation time,etc but not providing detail which user enter at the time of login.
If anyone has an idea about it please help in it.
So you are doing like user will login from UIWebView and it will login to the application.
If this is the case, then username and password related cookie should be saved from web page through web-developer. Also cookie name should be known.
So, when page will be reload, you need to check cookie that user is logged in or not. (this will be set from server side). For e.g. "isLogin" cookie is set to "1" then you can check with other cookie of "username" and "password" (which is also set from server side) and you can login to the app.
Thank you.
with ios 11 we can use WKWebsiteDataStore
extension WKWebView {
private var httpCookieStore: WKHTTPCookieStore {
return WKWebsiteDataStore.default().httpCookieStore
}
func getCookies(for domain: String? = nil, completion: #escaping ([String : Any])->()) {
var cookieDict = [String : AnyObject]()
httpCookieStore.getAllCookies { (cookies) in
for cookie in cookies {
if let domain = domain {
if cookie.domain.contains(domain) {
cookieDict[cookie.name] = cookie.properties as AnyObject?
}
} else {
cookieDict[cookie.name] = cookie.properties as AnyObject?
}
}
completion(cookieDict)
}
}
}

Alamofire - Modify Request object after creation

Short : Is there any way to modify the backed NSURLRequest (headers, body, or other) of the Request object after it has been created ?
Long : I have a custom Manager with startRequestsImmediately set to false. If my access token is currently being refreshed, all requests are waiting for refresh to finish, then they are resume.
Of course, I need to modify theirs HTTP headers to update access token before resuming them.
I can't just track directly NSURLRequests then recreating Requests object because I need to keep all completion closure previously set for those requests.
Is anyone find a way to do that ?
Thanks for your help.
You can use a RequestAdapter. Example:
class MyAdapter: RequestAdapter {
func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
var urlRequest = urlRequest
urlRequest.setValue("Bar", forHTTPHeaderField: "X-Foo")
return urlRequest
}
}
// Then...
let manager = SessionManager()
manager.adapter = MyAdapter()
manager.request("https://httpbin.org/get")

Resources