Skip to content

Commit ad031c7

Browse files
grdsdevCopilot
andauthored
feat: add standard client headers (#685)
* feat: add standard client headers * assert each sub-client inherits headers from supabase * adhere to macCatalyst * Update Sources/Helpers/Version.swift Co-authored-by: Copilot <[email protected]> --------- Co-authored-by: Copilot <[email protected]>
1 parent 6877053 commit ad031c7

File tree

5 files changed

+115
-18
lines changed

5 files changed

+115
-18
lines changed

Package.swift

+1
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ let package = Package(
181181
name: "SupabaseTests",
182182
dependencies: [
183183
.product(name: "CustomDump", package: "swift-custom-dump"),
184+
.product(name: "InlineSnapshotTesting", package: "swift-snapshot-testing"),
184185
"Supabase",
185186
]
186187
),

Sources/Helpers/Version.swift

+59
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import Foundation
12
import XCTestDynamicOverlay
23

34
private let _version = "2.27.0" // {x-release-please-version}
@@ -7,3 +8,61 @@ private let _version = "2.27.0" // {x-release-please-version}
78
#else
89
package let version = _version
910
#endif
11+
12+
private let _platform: String? = {
13+
#if os(macOS)
14+
return "macOS"
15+
#elseif os(visionOS)
16+
return "visionOS"
17+
#elseif os(iOS)
18+
#if targetEnvironment(macCatalyst)
19+
return "macCatalyst"
20+
#else
21+
if #available(iOS 14.0, *), ProcessInfo.processInfo.isiOSAppOnMac {
22+
return "iOSAppOnMac"
23+
}
24+
return "iOS"
25+
#endif
26+
#elseif os(watchOS)
27+
return "watchOS"
28+
#elseif os(tvOS)
29+
return "tvOS"
30+
#elseif os(Android)
31+
return "Android"
32+
#elseif os(Linux)
33+
return "Linux"
34+
#elseif os(Windows)
35+
return "Windows"
36+
#else
37+
return nil
38+
#endif
39+
}()
40+
41+
private let _platformVersion: String? = {
42+
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) || os(Windows)
43+
let majorVersion = ProcessInfo.processInfo.operatingSystemVersion.majorVersion
44+
let minorVersion = ProcessInfo.processInfo.operatingSystemVersion.minorVersion
45+
let patchVersion = ProcessInfo.processInfo.operatingSystemVersion.patchVersion
46+
return "\(majorVersion).\(minorVersion).\(patchVersion)"
47+
#elseif os(Linux) || os(Android)
48+
if let version = try? String(contentsOfFile: "/proc/version") {
49+
return version.trimmingCharacters(in: .whitespacesAndNewlines)
50+
} else {
51+
return nil
52+
}
53+
#else
54+
nil
55+
#endif
56+
}()
57+
58+
#if DEBUG
59+
package let platform = isTesting ? "macOS" : _platform
60+
#else
61+
package let platform = _platform
62+
#endif
63+
64+
#if DEBUG
65+
package let platformVersion = isTesting ? "0.0.0" : _platformVersion
66+
#else
67+
package let platformVersion = _platformVersion
68+
#endif

Sources/Supabase/Constants.swift

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//
2+
// Constants.swift
3+
// Supabase
4+
//
5+
// Created by Guilherme Souza on 06/03/25.
6+
//
7+
8+
import Foundation
9+
import Helpers
10+
11+
let defaultHeaders: [String: String] = {
12+
var headers = [
13+
"X-Client-Info": "supabase-swift/\(version)"
14+
]
15+
16+
if let platform {
17+
headers["X-Supabase-Client-Platform"] = platform
18+
}
19+
20+
if let platformVersion {
21+
headers["X-Supabase-Client-Platform-Version"] = platformVersion
22+
}
23+
24+
return headers
25+
}()

Sources/Supabase/SupabaseClient.swift

+10-8
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ public typealias SupabaseLogger = Helpers.SupabaseLogger
1717
public typealias SupabaseLogLevel = Helpers.SupabaseLogLevel
1818
public typealias SupabaseLogMessage = Helpers.SupabaseLogMessage
1919

20-
let version = Helpers.version
21-
2220
/// Supabase Client.
2321
public final class SupabaseClient: Sendable {
2422
let options: SupabaseClientOptions
@@ -163,12 +161,16 @@ public final class SupabaseClient: Sendable {
163161
databaseURL = supabaseURL.appendingPathComponent("/rest/v1")
164162
functionsURL = supabaseURL.appendingPathComponent("/functions/v1")
165163

166-
_headers = HTTPFields([
167-
"X-Client-Info": "supabase-swift/\(version)",
168-
"Authorization": "Bearer \(supabaseKey)",
169-
"Apikey": supabaseKey,
170-
])
171-
.merging(with: HTTPFields(options.global.headers))
164+
_headers = HTTPFields(defaultHeaders)
165+
.merging(
166+
with: HTTPFields(
167+
[
168+
"Authorization": "Bearer \(supabaseKey)",
169+
"Apikey": supabaseKey,
170+
]
171+
)
172+
)
173+
.merging(with: HTTPFields(options.global.headers))
172174

173175
// default storage key uses the supabase project ref as a namespace
174176
let defaultStorageKey = "sb-\(supabaseURL.host!.split(separator: ".")[0])-auth-token"

Tests/SupabaseTests/SupabaseClientTests.swift

+20-10
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1-
@testable import Auth
21
import CustomDump
3-
@testable import Functions
2+
import InlineSnapshotTesting
43
import IssueReporting
4+
import SnapshotTestingCustomDump
5+
import XCTest
6+
7+
@testable import Auth
8+
@testable import Functions
59
@testable import Realtime
610
@testable import Supabase
7-
import XCTest
811

912
final class AuthLocalStorageMock: AuthLocalStorage {
1013
func store(key _: String, value _: Data) throws {}
@@ -61,16 +64,22 @@ final class SupabaseClientTests: XCTestCase {
6164
"https://project-ref.supabase.co/functions/v1"
6265
)
6366

64-
XCTAssertEqual(
65-
client.headers,
67+
assertInlineSnapshot(of: client.headers, as: .customDump) {
68+
"""
6669
[
67-
"X-Client-Info": "supabase-swift/\(Supabase.version)",
6870
"Apikey": "ANON_KEY",
69-
"header_field": "header_value",
7071
"Authorization": "Bearer ANON_KEY",
72+
"X-Client-Info": "supabase-swift/0.0.0",
73+
"X-Supabase-Client-Platform": "macOS",
74+
"X-Supabase-Client-Platform-Version": "0.0.0",
75+
"header_field": "header_value"
7176
]
72-
)
73-
expectNoDifference(client._headers.dictionary, client.headers)
77+
"""
78+
}
79+
expectNoDifference(client.headers, client.auth.configuration.headers)
80+
expectNoDifference(client.headers, client.functions.headers.dictionary)
81+
expectNoDifference(client.headers, client.storage.configuration.headers)
82+
expectNoDifference(client.headers, client.rest.configuration.headers)
7483

7584
XCTAssertEqual(client.functions.region, "ap-northeast-1")
7685

@@ -79,7 +88,8 @@ final class SupabaseClientTests: XCTestCase {
7988

8089
let realtimeOptions = client.realtimeV2.options
8190
let expectedRealtimeHeader = client._headers.merging(with: [
82-
.init("custom_realtime_header_key")!: "custom_realtime_header_value"]
91+
.init("custom_realtime_header_key")!: "custom_realtime_header_value"
92+
]
8393
)
8494
expectNoDifference(realtimeOptions.headers, expectedRealtimeHeader)
8595
XCTAssertIdentical(realtimeOptions.logger as? Logger, logger)

0 commit comments

Comments
 (0)