From 76118a1d41a4b7c1b2fcba79835262c341172972 Mon Sep 17 00:00:00 2001 From: Nicolas VERINAUD Date: Tue, 15 May 2018 09:04:25 +0200 Subject: [PATCH 1/4] Log custom header for test purpose. --- TurbolinksDemo/server/lib/turbolinks_demo/app.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/TurbolinksDemo/server/lib/turbolinks_demo/app.rb b/TurbolinksDemo/server/lib/turbolinks_demo/app.rb index c12459c..0e74dae 100644 --- a/TurbolinksDemo/server/lib/turbolinks_demo/app.rb +++ b/TurbolinksDemo/server/lib/turbolinks_demo/app.rb @@ -7,6 +7,10 @@ module TurbolinksDemo class App < Sinatra::Base helpers Sinatra::Cookies + before do + puts "X-Custom = #{request.env["HTTP_X_CUSTOM"]}" + end + get '/' do @title = 'Demo' erb :index, layout: :layout From 6cfe444e90c8d851bda83f2cad921962113f31a7 Mon Sep 17 00:00:00 2001 From: Nicolas VERINAUD Date: Tue, 15 May 2018 09:23:19 +0200 Subject: [PATCH 2/4] Add customization of headers for ajax calls. --- Turbolinks.xcodeproj/project.pbxproj | 4 ++++ Turbolinks/Session.swift | 10 +++++++++- Turbolinks/WKWebViewConfiguration+.swift | 23 ++++++++++++++++++++++ TurbolinksDemo/ApplicationController.swift | 2 +- 4 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 Turbolinks/WKWebViewConfiguration+.swift diff --git a/Turbolinks.xcodeproj/project.pbxproj b/Turbolinks.xcodeproj/project.pbxproj index aca613f..14c936f 100644 --- a/Turbolinks.xcodeproj/project.pbxproj +++ b/Turbolinks.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 0391476C20AAC1C5009D356B /* WKWebViewConfiguration+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0391476B20AAC1C5009D356B /* WKWebViewConfiguration+.swift */; }; 1F8FC6B31B55A37400A781C8 /* WebView.js in Resources */ = {isa = PBXBuildFile; fileRef = 1F8FC6AE1B55A37400A781C8 /* WebView.js */; }; 1F8FC6B71B55A37400A781C8 /* Session.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F8FC6B01B55A37400A781C8 /* Session.swift */; }; 1F8FC6B91B55A37400A781C8 /* Visit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F8FC6B11B55A37400A781C8 /* Visit.swift */; }; @@ -32,6 +33,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 0391476B20AAC1C5009D356B /* WKWebViewConfiguration+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WKWebViewConfiguration+.swift"; sourceTree = ""; }; 1F8FC6AE1B55A37400A781C8 /* WebView.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = WebView.js; sourceTree = ""; }; 1F8FC6B01B55A37400A781C8 /* Session.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Session.swift; sourceTree = ""; }; 1F8FC6B11B55A37400A781C8 /* Visit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Visit.swift; sourceTree = ""; }; @@ -98,6 +100,7 @@ 2286D72E1C81FCF500E34B1D /* VisitableView.swift */, 2286D7591C83F7C600E34B1D /* VisitableViewController.swift */, 22C197DD1B59835300749B4E /* WebView.swift */, + 0391476B20AAC1C5009D356B /* WKWebViewConfiguration+.swift */, 1FC167F21B55A14900AA6F43 /* Supporting Files */, ); path = Turbolinks; @@ -235,6 +238,7 @@ files = ( 2286D72F1C81FCF500E34B1D /* VisitableView.swift in Sources */, 22960AAB1B9F51E100AA144A /* ScriptMessage.swift in Sources */, + 0391476C20AAC1C5009D356B /* WKWebViewConfiguration+.swift in Sources */, 2286D75A1C83F7C600E34B1D /* VisitableViewController.swift in Sources */, 1F8FC6B71B55A37400A781C8 /* Session.swift in Sources */, 1F8FC6BB1B55A37400A781C8 /* Visitable.swift in Sources */, diff --git a/Turbolinks/Session.swift b/Turbolinks/Session.swift index 2d62c26..315f032 100644 --- a/Turbolinks/Session.swift +++ b/Turbolinks/Session.swift @@ -26,6 +26,8 @@ public extension SessionDelegate { } } +public typealias HTTPHeaders = [String: String] + open class Session: NSObject { open weak var delegate: SessionDelegate? @@ -36,10 +38,16 @@ open class Session: NSObject { fileprivate var _webView: WebView fileprivate var initialized = false fileprivate var refreshing = false + + private let headers: HTTPHeaders? - public init(webViewConfiguration: WKWebViewConfiguration) { + public init(webViewConfiguration: WKWebViewConfiguration, headers: HTTPHeaders? = nil) { + webViewConfiguration.customizeTurbolinksAjaxRequests(withHeaders: headers) _webView = WebView(configuration: webViewConfiguration) + self.headers = headers + super.init() + _webView.delegate = self } diff --git a/Turbolinks/WKWebViewConfiguration+.swift b/Turbolinks/WKWebViewConfiguration+.swift new file mode 100644 index 0000000..efcb450 --- /dev/null +++ b/Turbolinks/WKWebViewConfiguration+.swift @@ -0,0 +1,23 @@ +// +// Copyright © 2018 Basecamp. All rights reserved. +// + +import WebKit + +extension WKWebViewConfiguration { + func customizeTurbolinksAjaxRequests(withHeaders headers: HTTPHeaders?) { + guard let headers = headers else { return } + + let headersContent = headers.map { + return "event.data.xhr.setRequestHeader('\($0)', '\($1)');" + }.joined() + + let scriptContent = """ + document.addEventListener("turbolinks:request-start", function(event) { \(headersContent) }); + """ + + let script = WKUserScript(source: scriptContent, injectionTime: .atDocumentStart, forMainFrameOnly: true) + + userContentController.addUserScript(script) + } +} diff --git a/TurbolinksDemo/ApplicationController.swift b/TurbolinksDemo/ApplicationController.swift index 668bab1..06da04f 100644 --- a/TurbolinksDemo/ApplicationController.swift +++ b/TurbolinksDemo/ApplicationController.swift @@ -19,7 +19,7 @@ class ApplicationController: UINavigationController { }() fileprivate lazy var session: Session = { - let session = Session(webViewConfiguration: self.webViewConfiguration) + let session = Session(webViewConfiguration: self.webViewConfiguration, headers: [ "X-Custom": "foo" ]) session.delegate = self return session }() From 92e8d0afd77257871c782983032eda715e8146f7 Mon Sep 17 00:00:00 2001 From: Nicolas VERINAUD Date: Tue, 15 May 2018 09:30:06 +0200 Subject: [PATCH 3/4] Add headers to ColdBootVisit. --- Turbolinks/Session.swift | 2 +- Turbolinks/Visit.swift | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Turbolinks/Session.swift b/Turbolinks/Session.swift index 315f032..e6f614c 100644 --- a/Turbolinks/Session.swift +++ b/Turbolinks/Session.swift @@ -79,7 +79,7 @@ open class Session: NSObject { visit = JavaScriptVisit(visitable: visitable, action: action, webView: _webView) visit.restorationIdentifier = restorationIdentifierForVisitable(visitable) } else { - visit = ColdBootVisit(visitable: visitable, action: action, webView: _webView) + visit = ColdBootVisit(visitable: visitable, action: action, webView: _webView, headers: headers) } currentVisit?.cancel() diff --git a/Turbolinks/Visit.swift b/Turbolinks/Visit.swift index 24f39fb..b95bec1 100644 --- a/Turbolinks/Visit.swift +++ b/Turbolinks/Visit.swift @@ -135,13 +135,22 @@ class Visit: NSObject { } class ColdBootVisit: Visit, WKNavigationDelegate, WebViewPageLoadDelegate { + init(visitable: Visitable, action: Action, webView: WebView, headers: HTTPHeaders? = nil) { + self.headers = headers + + super.init(visitable: visitable, action: action, webView: webView) + } + + private let headers: HTTPHeaders? + fileprivate var navigation: WKNavigation? override fileprivate func startVisit() { webView.navigationDelegate = self webView.pageLoadDelegate = self - let request = URLRequest(url: location) + var request = URLRequest(url: location) + request.allHTTPHeaderFields = headers navigation = webView.load(request) delegate?.visitDidStart(self) From b79aea4e38fcbf09d3ba2f2908e6e4fcb58e944c Mon Sep 17 00:00:00 2001 From: Nicolas VERINAUD Date: Tue, 15 May 2018 09:37:02 +0200 Subject: [PATCH 4/4] Update README with instruction to customize HTTP Headers. --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index f04c23c..45cd4fb 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,14 @@ Turbolinks calls `session:didFailRequestForVisitable:withError:` when a visit’ See [Handling Failed Requests](#handling-failed-requests) for more details. +## Customizing HTTP Headers + +You can customize the headers sent when loading the first page and when navigating by giving them to the session upon its creation. + +```swift +let session = Session(webViewConfiguration: self.webViewConfiguration, headers: [ "X-Custom": "foo" ]) +``` + ## Working with Visitables Visitable view controllers must conform to the Visitable protocol by implementing the following three properties: