Skip to content

Commit

Permalink
Merge pull request #56 from hotwired/user-agent-prefix
Browse files Browse the repository at this point in the history
Breaking API change: applications can set a User-Agent prefix
  • Loading branch information
svara authored Dec 16, 2024
2 parents 900e458 + 2fd3c78 commit fe0558d
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 15 deletions.
3 changes: 3 additions & 0 deletions Demo/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
.file(Bundle.main.url(forResource: "path-configuration", withExtension: "json")!)
])

// Set an optional custom user agent application prefix.
Hotwire.config.applicationUserAgentPrefix = "Hotwire Demo;"

// Register bridge components
Hotwire.registerBridgeComponents([
FormComponent.self,
Expand Down
13 changes: 10 additions & 3 deletions Source/Bridge/UserAgent.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import Foundation

public enum UserAgent {
public static func userAgentSubstring(for componentTypes: [BridgeComponent.Type]) -> String {
enum UserAgent {
static func build(applicationPrefix: String?, componentTypes: [BridgeComponent.Type]) -> String {
let components = componentTypes.map { $0.name }.joined(separator: " ")
return "bridge-components: [\(components)]"
let componentsSubstring = "bridge-components: [\(components)]"

return [
applicationPrefix,
"Hotwire Native iOS;",
"Turbo Native iOS;",
componentsSubstring
].compactMap { $0 }.joined(separator: " ")
}
}
1 change: 0 additions & 1 deletion Source/Hotwire.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ public enum Hotwire {
/// Use `Hotwire.config.makeCustomWebView` to customize the web view or web view
/// configuration further, making sure to call `Bridge.initialize()`.
public static func registerBridgeComponents(_ componentTypes: [BridgeComponent.Type]) {
Hotwire.config.userAgent += " \(UserAgent.userAgentSubstring(for: componentTypes))"
bridgeComponentTypes = componentTypes

Hotwire.config.makeCustomWebView = { configuration in
Expand Down
19 changes: 14 additions & 5 deletions Source/HotwireConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,16 @@ import WebKit
public struct HotwireConfig {
public typealias WebViewBlock = (_ configuration: WKWebViewConfiguration) -> WKWebView

/// Override to set a custom user agent.
/// - Important: Include "Hotwire Native" or "Turbo Native" to use `turbo_native_app?`
/// on your Rails server.
public var userAgent = "Hotwire Native iOS; Turbo Native iOS"
/// Set a custom user agent application prefix for every WKWebView instance.
///
/// The library will automatically append a substring to your prefix
/// which includes:
/// - "Hotwire Native iOS; Turbo Native iOS;"
/// - "bridge-components: [your bridge components];"
///
/// WKWebView's default user agent string will also appear at the
/// beginning of the user agent.
public var applicationUserAgentPrefix: String? = nil

/// When enabled, adds a `UIBarButtonItem` of type `.done` to the left
/// navigation bar button item on screens presented modally.
Expand Down Expand Up @@ -73,7 +79,10 @@ public struct HotwireConfig {
private func makeWebViewConfiguration() -> WKWebViewConfiguration {
let configuration = WKWebViewConfiguration()
configuration.defaultWebpagePreferences?.preferredContentMode = .mobile
configuration.applicationNameForUserAgent = userAgent
configuration.applicationNameForUserAgent = UserAgent.build(
applicationPrefix: applicationUserAgentPrefix,
componentTypes: Hotwire.bridgeComponentTypes
)
configuration.processPool = sharedProcessPool
return configuration
}
Expand Down
26 changes: 20 additions & 6 deletions Tests/Bridge/UserAgentTests.swift
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
import Foundation
import HotwireNative
@testable import HotwireNative
import XCTest

class UserAgentTests: XCTestCase {
func testUserAgentSubstringWithNoComponents() {
let userAgentSubstring = UserAgent.build(
applicationPrefix: nil,
componentTypes: []
)
XCTAssertEqual(userAgentSubstring, "Hotwire Native iOS; Turbo Native iOS; bridge-components: []")
}

func testUserAgentSubstringWithTwoComponents() {
let userAgentSubstring = UserAgent.userAgentSubstring(for: [OneBridgeComponent.self, TwoBridgeComponent.self])
XCTAssertEqual(userAgentSubstring, "bridge-components: [one two]")
let userAgentSubstring = UserAgent.build(
applicationPrefix: nil,
componentTypes: [OneBridgeComponent.self, TwoBridgeComponent.self]
)
XCTAssertEqual(userAgentSubstring, "Hotwire Native iOS; Turbo Native iOS; bridge-components: [one two]")
}

func testUserAgentSubstringWithNoComponents() {
let userAgentSubstring = UserAgent.userAgentSubstring(for: [])
XCTAssertEqual(userAgentSubstring, "bridge-components: []")
func testUserAgentSubstringCustomPrefix() {
let userAgentSubstring = UserAgent.build(
applicationPrefix: "Hotwire Demo;",
componentTypes: [OneBridgeComponent.self, TwoBridgeComponent.self]
)
XCTAssertEqual(userAgentSubstring, "Hotwire Demo; Hotwire Native iOS; Turbo Native iOS; bridge-components: [one two]")
}
}

0 comments on commit fe0558d

Please sign in to comment.