Skip to content
This repository was archived by the owner on Sep 25, 2021. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all 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
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
4 changes: 4 additions & 0 deletions Turbolinks.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 */; };
Expand All @@ -32,6 +33,7 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
0391476B20AAC1C5009D356B /* WKWebViewConfiguration+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WKWebViewConfiguration+.swift"; sourceTree = "<group>"; };
1F8FC6AE1B55A37400A781C8 /* WebView.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = WebView.js; sourceTree = "<group>"; };
1F8FC6B01B55A37400A781C8 /* Session.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Session.swift; sourceTree = "<group>"; };
1F8FC6B11B55A37400A781C8 /* Visit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Visit.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -98,6 +100,7 @@
2286D72E1C81FCF500E34B1D /* VisitableView.swift */,
2286D7591C83F7C600E34B1D /* VisitableViewController.swift */,
22C197DD1B59835300749B4E /* WebView.swift */,
0391476B20AAC1C5009D356B /* WKWebViewConfiguration+.swift */,
1FC167F21B55A14900AA6F43 /* Supporting Files */,
);
path = Turbolinks;
Expand Down Expand Up @@ -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 */,
Expand Down
12 changes: 10 additions & 2 deletions Turbolinks/Session.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ public extension SessionDelegate {
}
}

public typealias HTTPHeaders = [String: String]

open class Session: NSObject {
open weak var delegate: SessionDelegate?

Expand All @@ -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
}

Expand Down Expand Up @@ -71,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()
Expand Down
11 changes: 10 additions & 1 deletion Turbolinks/Visit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
23 changes: 23 additions & 0 deletions Turbolinks/WKWebViewConfiguration+.swift
Original file line number Diff line number Diff line change
@@ -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)
}
}
2 changes: 1 addition & 1 deletion TurbolinksDemo/ApplicationController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
}()
Expand Down
4 changes: 4 additions & 0 deletions TurbolinksDemo/server/lib/turbolinks_demo/app.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down