Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable wk cookie store #171

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ If set to true, links with `target="_blank"` or `window.open` will be opened in

Set `sendCookies` to true to copy cookies from `sharedHTTPCookieStorage` when calling loadRequest. This emulates the behavior of react-native's `WebView` component. You can set cookies using `react-native-cookies` Default is false.

- **useWKCookieStore**

Set `useWKCookieStore` to true to use the webView's `WKHTTPCookieStorage`. All Cookies from `sharedHTTPCookieStorage` will be copied to it.

- **source={{file: '', allowingReadAccessToURL: '' }}**

This allows WKWebView loads a local HTML file. Please note the underlying API is only introduced in iOS 9+. So in iOS 8, it will simple ignores these two properties.
Expand Down
7 changes: 6 additions & 1 deletion WKWebView.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,10 @@ class WKWebView extends React.Component {
* Set this to true to emulate behavior of WebView component.
*/
sendCookies: PropTypes.bool,
/**
* Initializes the webView's WKHTTPCookieStorage and copies all cookies from sharedHTTPCookieStorage
*/
useWKCookieStore: PropTypes.bool,
/**
* If set to true, target="_blank" or window.open will be opened in WebView, instead
* of new window. Default is false to be backward compatible.
Expand Down Expand Up @@ -316,7 +320,8 @@ class WKWebView extends React.Component {
if (this.props.source && typeof this.props.source === 'object') {
source = Object.assign({}, this.props.source, {
sendCookies: this.props.sendCookies,
customUserAgent: this.props.customUserAgent || this.props.userAgent
customUserAgent: this.props.customUserAgent || this.props.userAgent,
useWKCookieStore: this.props.useWKCookieStore
});

if (this.props.html) {
Expand Down
69 changes: 69 additions & 0 deletions ios/RCTWKWebView/RCTWKWebView.m
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ @interface RCTWKWebView () <WKNavigationDelegate, RCTAutoInsetsProtocol, WKScrip
@property (nonatomic, copy) RCTDirectEventBlock onScroll;
@property (nonatomic, copy) RCTDirectEventBlock onNavigationResponse;
@property (assign) BOOL sendCookies;
@property (assign) BOOL useWKCookieStore;
@property (nonatomic, strong) WKUserScript *atStartScript;
@property (nonatomic, strong) WKUserScript *atEndScript;

Expand Down Expand Up @@ -306,11 +307,67 @@ - (void)stopLoading
[_webView stopLoading];
}

- (NSString *) cookieDescription:(NSHTTPCookie *)cookie {

NSMutableString *cDesc = [[NSMutableString alloc] init];
[cDesc appendFormat:@"%@=%@;",
[[cookie name] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding],
[[cookie value] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
if ([cookie.domain length] > 0)
[cDesc appendFormat:@"domain=%@;", [cookie domain]];
if ([cookie.path length] > 0)
[cDesc appendFormat:@"path=%@;", [cookie path]];
if (cookie.expiresDate != nil)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its missing HttpOnly, secure and sessionOnly property. Is this something you would be able to add?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for the HttpOnly: I can do that,
for the sessionOnly there ist no such thing, If there is no expire date than it should be a session cookie, otherwise it will be stored until the expire date

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://developer.apple.com/documentation/foundation/nshttpcookie/1392991-sessiononly?language=objc

I was referencing that boolean to indicate its a session only cookie

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And Thanks for fixing this issue!

[cDesc appendFormat:@"expiresDate=%@;", [cookie expiresDate]];


return cDesc;
}

- (void) copyCookies {

NSHTTPCookieStorage* storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSArray* array = [storage cookies];


if (@available(ios 11,*)) {

// The webView websiteDataStore only gets initialized, when needed. Setting cookies on the dataStore's
// httpCookieStore doesn't seem to initialize it. That's why fetchDataRecordsOfTypes is called.
// All the cookies of the sharedHttpCookieStorage, which is used in react-native-cookie,
// are copied to the webSiteDataStore's httpCookieStore.
// https://bugs.webkit.org/show_bug.cgi?id=185483
[_webView.configuration.websiteDataStore fetchDataRecordsOfTypes:[NSSet<NSString *> setWithObject:WKWebsiteDataTypeCookies] completionHandler:^(NSArray<WKWebsiteDataRecord *> *records) {
for (NSHTTPCookie* cookie in array) {
[_webView.configuration.websiteDataStore.httpCookieStore setCookie:cookie completionHandler:nil];
}
}];
} else {
// Create WKUserScript for each cookie
// Cookies are injected with Javascript AtDocumentStart
for (NSHTTPCookie* cookie in array){
NSString* cookieSource = [NSString stringWithFormat:@"document.cookie = '%@'", [self cookieDescription:cookie]];
WKUserScript* cookieScript = [[WKUserScript alloc]
initWithSource:cookieSource
injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];


[_webView.configuration.userContentController addUserScript:cookieScript];
}
}
}

- (void)setSource:(NSDictionary *)source
{
if (![_source isEqualToDictionary:source]) {
_source = [source copy];
_sendCookies = [source[@"sendCookies"] boolValue];
_useWKCookieStore = [source[@"useWKCookieStore"] boolValue];

if (_useWKCookieStore) {
[self copyCookies];
}

if ([source[@"customUserAgent"] length] != 0 && [_webView respondsToSelector:@selector(setCustomUserAgent:)]) {
[_webView setCustomUserAgent:source[@"customUserAgent"]];
}
Expand Down Expand Up @@ -455,6 +512,18 @@ - (void)scrollViewDidScroll:(UIScrollView *)scrollView

#pragma mark - WKNavigationDelegate methods

- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there are duplicate methods with this same name, can you please fix this? This is breaking my build with compilation issue

if (_sendCookies) {
NSHTTPURLResponse *response = (NSHTTPURLResponse *)navigationResponse.response;
NSArray *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:[response allHeaderFields] forURL:response.URL];
for (NSHTTPCookie *cookie in cookies) {
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
}
}

decisionHandler(WKNavigationResponsePolicyAllow);
}

#if DEBUG
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {
NSURLCredential * credential = [[NSURLCredential alloc] initWithTrust:[challenge protectionSpace].serverTrust];
Expand Down