mat*_*ttr 150
仅适用于iOS 11+
let cookie = HTTPCookie(properties: [
.domain: "example.com",
.path: "/",
.name: "MyCookieName",
.value: "MyCookieValue",
.secure: "TRUE",
.expires: NSDate(timeIntervalSinceNow: 31556926)
])!
webView.configuration.websiteDataStore.httpCookieStore.setCookie(cookie)
Run Code Online (Sandbox Code Playgroud)
由于您是从HTTPCookeStorage中提取它们,因此可以执行以下操作:
let cookies = HTTPCookieStorage.shared.cookies ?? []
for (cookie) in cookies {
webView.configuration.websiteDataStore.httpCookieStore.setCookie(cookie)
}
Run Code Online (Sandbox Code Playgroud)
适用于iOS 10及以下版本的旧答案
如果您需要在初始加载请求中设置cookie,可以在NSMutableURLRequest上设置它们.因为cookie只是一个特殊格式的请求标头,所以可以这样实现:
WKWebView * webView = /*set up your webView*/
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://example.com/index.html"]];
[request addValue:@"TeskCookieKey1=TeskCookieValue1;TeskCookieKey2=TeskCookieValue2;" forHTTPHeaderField:@"Cookie"];
// use stringWithFormat: in the above line to inject your values programmatically
[webView loadRequest:request];
Run Code Online (Sandbox Code Playgroud)
如果您要求页面上的后续AJAX请求设置其cookie,可以通过简单地使用WKUserScript在文档启动时通过javascript以编程方式设置值来实现,如下所示:
WKUserContentController* userContentController = WKUserContentController.new;
WKUserScript * cookieScript = [[WKUserScript alloc]
initWithSource: @"document.cookie = 'TeskCookieKey1=TeskCookieValue1';document.cookie = 'TeskCookieKey2=TeskCookieValue2';"
injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
// again, use stringWithFormat: in the above line to inject your values programmatically
[userContentController addUserScript:cookieScript];
WKWebViewConfiguration* webViewConfig = WKWebViewConfiguration.new;
webViewConfig.userContentController = userContentController;
WKWebView * webView = [[WKWebView alloc] initWithFrame:CGRectMake(/*set your values*/) configuration:webViewConfig];
Run Code Online (Sandbox Code Playgroud)
结合这两种技术应该为您提供足够的工具来将Cookie值从Native App Land传输到Web View Land.如果您需要更高级的cookie,可以在mozilla的页面上找到关于cookie javascript api的更多信息.
是的,它很糟糕,苹果不支持UIWebView的许多细节.不确定他们是否会支持他们,但希望他们能尽快得到这个.希望这可以帮助!
dea*_*rne 58
在玩了这个答案后(这非常有用:)我们不得不做一些改变:
NSHTTPCookieStorage
所以我们修改了我们的代码;
NSMutableURLRequest *request = [originalRequest mutableCopy];
NSString *validDomain = request.URL.host;
const BOOL requestIsSecure = [request.URL.scheme isEqualToString:@"https"];
NSMutableArray *array = [NSMutableArray array];
for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
// Don't even bother with values containing a `'`
if ([cookie.name rangeOfString:@"'"].location != NSNotFound) {
NSLog(@"Skipping %@ because it contains a '", cookie.properties);
continue;
}
// Is the cookie for current domain?
if (![cookie.domain hasSuffix:validDomain]) {
NSLog(@"Skipping %@ (because not %@)", cookie.properties, validDomain);
continue;
}
// Are we secure only?
if (cookie.secure && !requestIsSecure) {
NSLog(@"Skipping %@ (because %@ not secure)", cookie.properties, request.URL.absoluteString);
continue;
}
NSString *value = [NSString stringWithFormat:@"%@=%@", cookie.name, cookie.value];
[array addObject:value];
}
NSString *header = [array componentsJoinedByString:@";"];
[request setValue:header forHTTPHeaderField:@"Cookie"];
// Now perform the request...
Run Code Online (Sandbox Code Playgroud)
这可以确保第一个请求设置了正确的cookie,而不从共享存储中发送任何用于其他域的cookie,也不会将任何安全cookie发送到不安全的请求中.
我们还需要确保其他请求设置了cookie.这是使用在文档加载上运行的脚本来完成的,该脚本检查是否存在cookie集,如果没有,则将其设置为值NSHTTPCookieStorage
.
// Get the currently set cookie names in javascriptland
[script appendString:@"var cookieNames = document.cookie.split('; ').map(function(cookie) { return cookie.split('=')[0] } );\n"];
for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
// Skip cookies that will break our script
if ([cookie.value rangeOfString:@"'"].location != NSNotFound) {
continue;
}
// Create a line that appends this cookie to the web view's document's cookies
[script appendFormat:@"if (cookieNames.indexOf('%@') == -1) { document.cookie='%@'; };\n", cookie.name, cookie.wn_javascriptString];
}
WKUserContentController *userContentController = [[WKUserContentController alloc] init];
WKUserScript *cookieInScript = [[WKUserScript alloc] initWithSource:script
injectionTime:WKUserScriptInjectionTimeAtDocumentStart
forMainFrameOnly:NO];
[userContentController addUserScript:cookieInScript];
Run Code Online (Sandbox Code Playgroud)
...
// Create a config out of that userContentController and specify it when we create our web view.
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
config.userContentController = userContentController;
self.webView = [[WKWebView alloc] initWithFrame:webView.bounds configuration:config];
Run Code Online (Sandbox Code Playgroud)
我们还需要处理服务器更改cookie的值.这意味着添加另一个脚本来回调我们正在创建的Web视图以更新我们的NSHTTPCookieStorage
.
WKUserScript *cookieOutScript = [[WKUserScript alloc] initWithSource:@"window.webkit.messageHandlers.updateCookies.postMessage(document.cookie);"
injectionTime:WKUserScriptInjectionTimeAtDocumentStart
forMainFrameOnly:NO];
[userContentController addUserScript:cookieOutScript];
[userContentController addScriptMessageHandler:webView
name:@"updateCookies"];
Run Code Online (Sandbox Code Playgroud)
并实现委托方法来更新任何已更改的cookie,确保我们只更新当前域中的cookie!
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
NSArray<NSString *> *cookies = [message.body componentsSeparatedByString:@"; "];
for (NSString *cookie in cookies) {
// Get this cookie's name and value
NSArray<NSString *> *comps = [cookie componentsSeparatedByString:@"="];
if (comps.count < 2) {
continue;
}
// Get the cookie in shared storage with that name
NSHTTPCookie *localCookie = nil;
for (NSHTTPCookie *c in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:self.wk_webView.URL]) {
if ([c.name isEqualToString:comps[0]]) {
localCookie = c;
break;
}
}
// If there is a cookie with a stale value, update it now.
if (localCookie) {
NSMutableDictionary *props = [localCookie.properties mutableCopy];
props[NSHTTPCookieValue] = comps[1];
NSHTTPCookie *updatedCookie = [NSHTTPCookie cookieWithProperties:props];
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:updatedCookie];
}
}
}
Run Code Online (Sandbox Code Playgroud)
这似乎解决了我们的cookie问题,而我们不必处理我们使用WKWebView的每个地方.我们现在可以使用此代码作为帮助程序来创建我们的Web视图,并NSHTTPCookieStorage
为我们透明地更新.
编辑:结果我在NSHTTPCookie上使用了一个私人类别 - 这是代码:
- (NSString *)wn_javascriptString {
NSString *string = [NSString stringWithFormat:@"%@=%@;domain=%@;path=%@",
self.name,
self.value,
self.domain,
self.path ?: @"/"];
if (self.secure) {
string = [string stringByAppendingString:@";secure=true"];
}
return string;
}
Run Code Online (Sandbox Code Playgroud)
nte*_*iss 32
必须在WKWebView
创建之前在配置上设置cookie .否则,即使WKHTTPCookieStore
的setCookie
完成处理,饼干就不能可靠地同步到网络视图.这又回到了这条线从文档上WKWebViewConfiguration
Run Code Online (Sandbox Code Playgroud)@NSCopying var configuration: WKWebViewConfiguration { get }
这@NSCopying
有点像深刻的副本.实施超出了我的要求,但最终的结果是,除非您在初始化webview之前设置cookie,否则您不能指望那里的cookie.这会使app体系结构复杂化,因为初始化视图会变成异步过程.你最终会得到这样的东西
extension WKWebViewConfiguration {
/// Async Factory method to acquire WKWebViewConfigurations packaged with system cookies
static func cookiesIncluded(completion: @escaping (WKWebViewConfiguration?) -> Void) {
let config = WKWebViewConfiguration()
guard let cookies = HTTPCookieStorage.shared.cookies else {
completion(config)
return
}
// Use nonPersistent() or default() depending on if you want cookies persisted to disk
// and shared between WKWebViews of the same app (default), or not persisted and not shared
// across WKWebViews in the same app.
let dataStore = WKWebsiteDataStore.nonPersistent()
let waitGroup = DispatchGroup()
for cookie in cookies {
waitGroup.enter()
dataStore.httpCookieStore.setCookie(cookie) { waitGroup.leave() }
}
waitGroup.notify(queue: DispatchQueue.main) {
config.websiteDataStore = dataStore
completion(config)
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后使用类似的东西
override func loadView() {
view = UIView()
WKWebViewConfiguration.cookiesIncluded { [weak self] config in
let webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.load(request)
self.view = webView
}
}
Run Code Online (Sandbox Code Playgroud)
上面的示例将视图创建推迟到最后一刻,另一个解决方案是提前创建配置或webview并在创建视图控制器之前处理异步性质.
最后一点:一旦你创建了这个webview,你已经将它放到了野外,你不能在不使用本答案中描述的方法的情况下添加更多的cookie .但是,您可以使用WKHTTPCookieStoreObserver
api至少观察cookie发生的变化.因此,如果会话cookie在Webview中更新,您可以HTTPCookieStorage
根据需要使用此新cookie 手动更新系统.
有关详细信息,请跳至2017年WWDC会议自定义Web内容加载中的 18:00 .在本课程开始时,有一个欺骗性的代码示例,它省略了应该在完成处理程序中创建webview的事实.
cookieStore.setCookie(cookie!) {
webView.load(loggedInURLRequest)
}
Run Code Online (Sandbox Code Playgroud)
18:00的现场演示澄清了这一点.
编辑至少从Mojave Beta 7和iOS 12 Beta 7开始,我发现使用cookie的行为更加一致.该setCookie(_:)
方法甚至似乎允许WKWebView
在创建后设置cookie .我发现它虽然很重要,要不要触摸的processPool
变量都没有.如果没有创建其他池并且该属性保持单独,则cookie设置功能最有效.我认为可以说我们因WebKit中的一些错误而遇到问题.
小智 24
为我工作
func webView(webView: WKWebView, decidePolicyForNavigationAction navigationAction: WKNavigationAction, decisionHandler: (WKNavigationActionPolicy) -> Void) {
let headerFields = navigationAction.request.allHTTPHeaderFields
var headerIsPresent = contains(headerFields?.keys.array as! [String], "Cookie")
if headerIsPresent {
decisionHandler(WKNavigationActionPolicy.Allow)
} else {
let req = NSMutableURLRequest(URL: navigationAction.request.URL!)
let cookies = yourCookieData
let values = NSHTTPCookie.requestHeaderFieldsWithCookies(cookies)
req.allHTTPHeaderFields = values
webView.loadRequest(req)
decisionHandler(WKNavigationActionPolicy.Cancel)
}
}
Run Code Online (Sandbox Code Playgroud)
Mis*_*sha 18
这是我在Swift中的Mattrs解决方案版本,用于从HTTPCookieStorage注入所有cookie.这主要是为了注入身份验证cookie来创建用户会话.
public func setupWebView() {
let userContentController = WKUserContentController()
if let cookies = HTTPCookieStorage.shared.cookies {
let script = getJSCookiesString(for: cookies)
let cookieScript = WKUserScript(source: script, injectionTime: .atDocumentStart, forMainFrameOnly: false)
userContentController.addUserScript(cookieScript)
}
let webViewConfig = WKWebViewConfiguration()
webViewConfig.userContentController = userContentController
self.webView = WKWebView(frame: self.webViewContainer.bounds, configuration: webViewConfig)
}
///Generates script to create given cookies
public func getJSCookiesString(for cookies: [HTTPCookie]) -> String {
var result = ""
let dateFormatter = DateFormatter()
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
dateFormatter.dateFormat = "EEE, d MMM yyyy HH:mm:ss zzz"
for cookie in cookies {
result += "document.cookie='\(cookie.name)=\(cookie.value); domain=\(cookie.domain); path=\(cookie.path); "
if let date = cookie.expiresDate {
result += "expires=\(dateFormatter.stringFromDate(date)); "
}
if (cookie.secure) {
result += "secure; "
}
result += "'; "
}
return result
}
Run Code Online (Sandbox Code Playgroud)
设置cookie
self.webView.evaluateJavaScript("document.cookie='access_token=your token';domain='your domain';") { (data, error) -> Void in
self.webView.reload()
}
Run Code Online (Sandbox Code Playgroud)
删除cookie
self.webView.evaluateJavaScript("document.cookie='access_token=';domain='your domain';") { (data, error) -> Void in
self.webView.reload()
}
Run Code Online (Sandbox Code Playgroud)
小智 9
Swift 3更新:
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
if let urlResponse = navigationResponse.response as? HTTPURLResponse,
let url = urlResponse.url,
let allHeaderFields = urlResponse.allHeaderFields as? [String : String] {
let cookies = HTTPCookie.cookies(withResponseHeaderFields: allHeaderFields, for: url)
HTTPCookieStorage.shared.setCookies(cookies , for: urlResponse.url!, mainDocumentURL: nil)
decisionHandler(.allow)
}
}
Run Code Online (Sandbox Code Playgroud)
在iOS 11中,您现在可以管理cookie :),请参阅此会话:https://developer.apple.com/videos/play/wwdc2017/220/
发布此答案的原因是我尝试了很多解决方案,但没有一个正常工作,大多数答案在必须第一次设置 cookie 的情况下不起作用,并且结果 cookie 第一次不同步,请使用此解决方案,它适用于两者iOS >= 11.0 <= iOS 11 到 8.0,也第一次使用 cookie 同步。
对于 iOS >= 11.0 -- Swift 4.2
获取的HTTP cookies和集wkwebview cookie存储这样的方式,这是非常棘手的点加载在您的要求wkwebview,必须发送装载请求时会被完全设置cookies,这里是函数,我写的。
在完成时调用带有闭包的函数,您调用 load webview。仅供参考此功能仅处理 iOS >= 11.0
self.WwebView.syncCookies {
if let request = self.request {
self.WwebView.load(request)
}
}
Run Code Online (Sandbox Code Playgroud)
这是syncCookies函数的实现。
func syncCookies(completion:@escaping ()->Void) {
if #available(iOS 11.0, *) {
if let yourCookie = "HERE_YOUR_HTTP_COOKIE_OBJECT" {
self.configuration.websiteDataStore.httpCookieStore.setCookie(yourCookie, completionHandler: {
completion()
})
}
} else {
//Falback just sent
completion()
}
}
Run Code Online (Sandbox Code Playgroud)
适用于 iOS 8 至 iOS 11
您需要设置一些额外的东西,您需要通过使用WKUserScript设置两个时间 cookie,并且不要忘记在请求中添加 cookie,否则您的 cookie 不会第一次同步,您将看到您的页面第一次没有正确加载。这是我发现支持 iOS 8.0 cookie 的原因
在您创建 Wkwebview 对象之前。
func setUpWebView() {
let userController: WKUserContentController = WKUserContentController.init()
if IOSVersion.SYSTEM_VERSION_LESS_THAN(version: "11.0") {
if let cookies = HTTPCookieStorage.shared.cookies {
if let script = getJSCookiesString(for: cookies) {
cookieScript = WKUserScript(source: script, injectionTime: .atDocumentStart, forMainFrameOnly: false)
userController.addUserScript(cookieScript!)
}
}
}
let webConfiguration = WKWebViewConfiguration()
webConfiguration.processPool = BaseWebViewController.processPool
webConfiguration.userContentController = userController
let customFrame = CGRect.init(origin: CGPoint.zero, size: CGSize.init(width: 0.0, height: self.webContainerView.frame.size.height))
self.WwebView = WKWebView (frame: customFrame, configuration: webConfiguration)
self.WwebView.translatesAutoresizingMaskIntoConstraints = false
self.webContainerView.addSubview(self.WwebView)
self.WwebView.uiDelegate = self
self.WwebView.navigationDelegate = self
self.WwebView.allowsBackForwardNavigationGestures = true // A Boolean value indicating whether horizontal swipe gestures will trigger back-forward list navigations
self.WwebView.addObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress), options: .new, context: nil)
self.view.addConstraint(NSLayoutConstraint(item: WwebView, attribute: .trailing, relatedBy: .equal, toItem: self.webContainerView, attribute: .trailing, multiplier: 1, constant: 0))
self.view.addConstraint(NSLayoutConstraint(item: WwebView, attribute: .leading, relatedBy: .equal, toItem: self.webContainerView, attribute: .leading, multiplier: 1, constant: 0))
self.view.addConstraint(NSLayoutConstraint(item: WwebView, attribute: .top, relatedBy: .equal, toItem: self.webContainerView, attribute: .top, multiplier: 1, constant: 0))
self.view.addConstraint(NSLayoutConstraint(item: WwebView, attribute: .bottom, relatedBy: .equal, toItem: self.webContainerView, attribute: .bottom, multiplier: 1, constant: 0))
}
Run Code Online (Sandbox Code Playgroud)
重点关注这个函数getJSCookiesString
public func getJSCookiesString(for cookies: [HTTPCookie]) -> String? {
var result = ""
let dateFormatter = DateFormatter()
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
dateFormatter.dateFormat = "EEE, d MMM yyyy HH:mm:ss zzz"
for cookie in cookies {
if cookie.name == "yout_cookie_name_want_to_sync" {
result += "document.cookie='\(cookie.name)=\(cookie.value); domain=\(cookie.domain); path=\(cookie.path); "
if let date = cookie.expiresDate {
result += "expires=\(dateFormatter.string(from: date)); "
}
if (cookie.isSecure) {
result += "secure; "
}
result += "'; "
}
}
return result
}
Run Code Online (Sandbox Code Playgroud)
这是 wkuserscript 不立即同步 cookie 的其他步骤,使用 cookie 加载第一次页面有很多麻烦,如果终止进程,则再次重新加载 webview,但我不建议使用它,这不利于用户的观点,哎呀,每当您准备好以这种方式在请求标头中加载请求集 cookie 时,不要忘记添加 iOS 版本检查。在加载请求之前调用此函数。
request?.addCookies()
Run Code Online (Sandbox Code Playgroud)
我为URLRequest写了扩展
extension URLRequest {
internal mutating func addCookies() {
//"appCode=anAuY28ucmFrdXRlbi5yZXdhcmQuaW9zLXpOQlRTRmNiejNHSzR0S0xuMGFRb0NjbUg4Ql9JVWJH;rpga=kW69IPVSYZTo0JkZBicUnFxC1g5FtoHwdln59Z5RNXgJoMToSBW4xAMqtf0YDfto;rewardadid=D9F8CE68-CF18-4EE6-A076-CC951A4301F6;rewardheader=true"
var cookiesStr: String = ""
if IOSVersion.SYSTEM_VERSION_LESS_THAN(version: "11.0") {
let mutableRequest = ((self as NSURLRequest).mutableCopy() as? NSMutableURLRequest)!
if let yourCookie = "YOUR_HTTP_COOKIE_OBJECT" {
// if have more than one cookies dont forget to add ";" at end
cookiesStr += yourCookie.name + "=" + yourCookie.value + ";"
mutableRequest.setValue(cookiesStr, forHTTPHeaderField: "Cookie")
self = mutableRequest as URLRequest
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在你准备好去测试 iOS > 8
在查看了各种答案并且没有取得任何成功后,我梳理了WebKit文档并偶然发现了requestHeaderFields
静态方法HTTPCookie
,它将一组cookie转换为适合头字段的格式.将此与mattr的洞察力相结合,即URLRequest
在将其加载到cookie标题之前更新之前让我通过终点线.
var request = URLRequest(url: URL(string: "https://example.com/")!)
let headers = HTTPCookie.requestHeaderFields(with: cookies)
for (name, value) in headers {
request.addValue(value, forHTTPHeaderField: name)
}
let webView = WKWebView(frame: self.view.frame)
webView.load(request)
Run Code Online (Sandbox Code Playgroud)
为了使这更简单,使用扩展名:
extension WKWebView {
func load(_ request: URLRequest, with cookies: [HTTPCookie]) {
var request = request
let headers = HTTPCookie.requestHeaderFields(with: cookies)
for (name, value) in headers {
request.addValue(value, forHTTPHeaderField: name)
}
load(request)
}
}
Run Code Online (Sandbox Code Playgroud)
现在它变成了:
let request = URLRequest(url: URL(string: "https://example.com/")!)
let webView = WKWebView(frame: self.view.frame)
webView.load(request, with: cookies)
Run Code Online (Sandbox Code Playgroud)
如果您只想要一个插入式解决方案,也可以在LionheartExtensions中使用此扩展.干杯!
归档时间: |
|
查看次数: |
109529 次 |
最近记录: |