Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/v4-swift-tools-6.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'posthog-ios': major
---

**Breaking:** require Xcode 26 or later (Swift 6.2 toolchain, `swift-tools-version` 6.2) to build PostHog, the same Xcode the App Store already requires for uploads; the SDK still compiles in Swift 5 language mode and its API is unchanged.
6 changes: 3 additions & 3 deletions .github/workflows/build-examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,10 @@ jobs:
target: buildExamplePodsStaticLib
runner: macos-15
xcode-version: latest-stable
- name: Static Lib (Xcode 16.2)
- name: Static Lib (Xcode 26.0.1)
target: buildExamplePodsStaticLib
runner: macos-14-xlarge
xcode-version: '16.2'
runner: macos-15-xlarge
xcode-version: '26.0.1'
- name: Static Framework
target: buildExamplePodsStaticFramework
runner: macos-15
Expand Down
11 changes: 7 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,10 @@ jobs:
- name: Check public API snapshot
run: make apiCheck

build-sdk-swift53-compat:
build-sdk-min-toolchain:
needs: detect-markdown-only
runs-on: macos-14-xlarge
name: Build SDK (Swift 5 compatibility on Xcode 15.4 with CocoaPods)
runs-on: macos-15-xlarge
name: Build SDK (minimum toolchain, Xcode 26.0.1)
steps:
- name: Complete markdown-only PR check
if: needs.detect-markdown-only.outputs.markdown_only == 'true'
Expand All @@ -78,12 +78,15 @@ jobs:
- uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1.7.0
if: needs.detect-markdown-only.outputs.markdown_only != 'true'
with:
xcode-version: '15.4'
xcode-version: '26.0.1'
- name: Print toolchain versions
if: needs.detect-markdown-only.outputs.markdown_only != 'true'
run: |
xcodebuild -version
xcrun swift --version
- name: Build SDK via Swift Package Manager
if: needs.detect-markdown-only.outputs.markdown_only != 'true'
run: make buildSdkSpm
- name: Install CocoaPods
if: needs.detect-markdown-only.outputs.markdown_only != 'true'
run: gem install cocoapods
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:
jobs:
analyze:
name: Analyze (${{ matrix.language }})
runs-on: macos-14-xlarge
runs-on: macos-15-xlarge
permissions:
security-events: write # for uploading CodeQL results
packages: read
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ jobs:
publish-cocoapods:
name: Publish to CocoaPods
needs: [create-github-release, notify-approval-needed]
runs-on: macos-14
runs-on: macos-15
if: always() && needs.create-github-release.result == 'success'
permissions:
contents: read
Expand All @@ -312,7 +312,7 @@ jobs:

- uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1.7.0
with:
xcode-version: '16.2'
xcode-version: '26.0.1'

- name: Publish to CocoaPods
env:
Expand Down
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
- SDK entry point: [PostHog/PostHogSDK.swift](PostHog/PostHogSDK.swift); tests: `PostHogTests/`; replay/privacy: `PostHog/Replay/` and `PostHog/Resources/`.

## Repository invariants
- Maintain Swift 5 SDK language compatibility. Tests require Swift 6+ with Swift Testing (Xcode 16+); prefer Swift Testing for new tests. Use `PostHogTests/TestUtils/MockPostHogServer.swift` for HTTP stubbing.
- Preserve deployment minima: iOS 15, tvOS 13, macOS 10.15, watchOS 6, visionOS 1. Keep platform guards and graceful degradation, especially for session replay. Availability annotations/checks do not require adding platform support or raising targets. Intentional support changes require checking both `Package.swift` and `Package@swift-5.9.swift`, plus CocoaPods/Xcode packaging/build configuration; visionOS declarations belong in the Swift 5.9 manifest.
- Maintain Swift 5 SDK language mode. The package needs swift-tools-version 6.2 (Xcode 26+); prefer Swift Testing for new tests. Use `PostHogTests/TestUtils/MockPostHogServer.swift` for HTTP stubbing.
- Preserve deployment minima: iOS 15, tvOS 13, macOS 10.15, watchOS 6, visionOS 1. Keep platform guards and graceful degradation, especially for session replay. Availability annotations/checks do not require adding platform support or raising targets. Intentional support changes require checking `Package.swift` plus CocoaPods/Xcode packaging/build configuration.
- Public APIs must remain thread-safe and callable from any thread. Preserve offline operation and queue-based event batching; never assume connectivity. `PostHogSDK.shared` is the default singleton; independent instances use `PostHogSDK.with(_:)` with `PostHogConfig`.
- Preserve replay masking/privacy behavior and privacy-safe error logging; do not expose sensitive user data.
- Prefer no new dependencies. libwebp is embedded; PHPLCrashReporter is vendored, prefixed PLCrashReporter for native crash reporting on iOS/macOS/tvOS, excluded on watchOS/visionOS.
Expand Down
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ If you would like to contribute code to `posthog-ios` you can do so through GitH

Use the repository's `make` wrappers rather than invoking `swift` or `xcodebuild` directly; [Makefile](Makefile) owns the commands and options.

For initial setup, install Xcode and, if needed, use `make bootstrap` to install CocoaPods, xcpretty, SwiftLint, SwiftFormat and Periphery. Agents should report missing prerequisites rather than install tools without authorization. Tests import `Testing` and need a Swift 6+ toolchain with Swift Testing (Xcode 16+); use current stable Xcode as CI does. This is separate from the SDK's Swift 5 language compatibility.
For initial setup, install Xcode 26 or later and, if needed, use `make bootstrap` to install CocoaPods, xcpretty, SwiftLint, SwiftFormat and Periphery. Agents should report missing prerequisites rather than install tools without authorization. The package uses swift-tools-version 6.2 and compiles in Swift 5 language mode; use current stable Xcode as CI does.

Iterate with focused checks, then run the core CI-aligned checks before submitting **SDK changes**:

Expand Down Expand Up @@ -41,7 +41,7 @@ Markdown-only changes need documentation/link checks and `git diff --check`, not
| CocoaPods integration modes | `make buildExamplePodsStaticLib`, `make buildExamplePodsStaticFramework`, `make buildExamplePodsDynamicFramework` |
| XCFramework integration | `make buildExampleXCFramework` |

Use relevant example/package checks for integration or packaging changes, and smoke test changed behavior in an example where applicable. `make buildExamples` runs all example builds; `make build` combines that with `make buildSdk`. These broad targets include CocoaPods installation and XCFramework generation, so they are not the default iteration loop. For package/support changes, also consult the package lint and Swift 5 compatibility lanes in [build CI](.github/workflows/build.yml) and [example CI](.github/workflows/build-examples.yml).
Use relevant example/package checks for integration or packaging changes, and smoke test changed behavior in an example where applicable. `make buildExamples` runs all example builds; `make build` combines that with `make buildSdk`. These broad targets include CocoaPods installation and XCFramework generation, so they are not the default iteration loop. For package/support changes, also consult the package lint and minimum-toolchain lanes in [build CI](.github/workflows/build.yml) and [example CI](.github/workflows/build-examples.yml).

### Simulator, UI and masking prerequisites

Expand Down
7 changes: 5 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: testSurveyUI build buildSdk buildExamples format swiftLint swiftFormat swiftLintCheck swiftFormatCheck installSwiftLint installSwiftFormat test testUploadSymbols recordEventShapeSnapshots testDowngradeCompatibility testOniOSSimulator testOnMacSimulator maskSnapshots recordMaskSnapshots checkMaskSnapshotRuntime lint bootstrap releaseCocoaPods api apiCheck apiUpdate buildIOS
.PHONY: testSurveyUI build buildSdk buildExamples format swiftLint swiftFormat swiftLintCheck swiftFormatCheck installSwiftLint installSwiftFormat test testUploadSymbols recordEventShapeSnapshots testDowngradeCompatibility testOniOSSimulator testOnMacSimulator maskSnapshots recordMaskSnapshots checkMaskSnapshotRuntime lint bootstrap releaseCocoaPods api apiCheck apiUpdate buildIOS buildSdkSpm

build: buildSdk buildExamples

Expand All @@ -7,13 +7,16 @@ buildIOS:

buildSdk:
set -o pipefail && xcrun xcodebuild clean build -scheme PostHog -destination generic/platform=ios | xcpretty #ios
set -o pipefail && xcrun swift build --arch arm64 #macOS
$(MAKE) buildSdkSpm #macOS
set -o pipefail && xcrun xcodebuild clean build -scheme PostHog -destination generic/platform=macos | xcpretty #macOS
set -o pipefail && xcrun xcodebuild clean build -scheme PostHog -destination 'platform=macOS,variant=Mac Catalyst' | xcpretty #Mac Catalyst
set -o pipefail && xcrun xcodebuild clean build -scheme PostHog -destination generic/platform=tvos | xcpretty #tvOS
set -o pipefail && xcrun xcodebuild clean build -scheme PostHog -destination generic/platform=watchos | xcpretty #watchOS
set -o pipefail && xcrun xcodebuild clean build -scheme PostHog -destination generic/platform=xros | xcpretty #visionOS

buildSdkSpm:
set -o pipefail && xcrun swift build --arch arm64

buildExamples: \
buildExamplesPlatforms \
buildExampleXCFramework \
Expand Down
12 changes: 6 additions & 6 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
// swift-tools-version:5.3
// swift-tools-version:6.2
import PackageDescription

let package = Package(
name: "PostHog",
platforms: [
// visionOS is supported via Package@swift-5.9.swift for Swift 5.9+ users
.macOS(.v10_15), .iOS(.v15), .tvOS(.v13), .watchOS(.v6),
.macOS(.v10_15), .iOS(.v15), .tvOS(.v13), .watchOS(.v6), .visionOS(.v1),
],
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
Expand All @@ -28,7 +27,7 @@ let package = Package(
dependencies: [
"PostHogObjCExceptionSupport",
"phlibwebp",
.target(name: "PHPLCrashReporter", condition: .when(platforms: [.iOS, .macOS, .tvOS])),
.target(name: "PHPLCrashReporter", condition: .when(platforms: [.iOS, .macOS, .macCatalyst, .tvOS])),
],
path: "PostHog",
exclude: [
Expand Down Expand Up @@ -100,12 +99,13 @@ let package = Package(
"OHHTTPStubs",
.product(name: "OHHTTPStubsSwift", package: "OHHTTPStubs"),
// The crash-report processor tests import this directly to build a PHPLCrashReport.
.target(name: "PHPLCrashReporter", condition: .when(platforms: [.iOS, .macOS, .tvOS])),
.target(name: "PHPLCrashReporter", condition: .when(platforms: [.iOS, .macOS, .macCatalyst, .tvOS])),
],
path: "PostHogTests",
resources: [
.process("Resources"),
]
),
]
],
swiftLanguageModes: [.v5]
)
110 changes: 0 additions & 110 deletions Package@swift-5.9.swift

This file was deleted.

18 changes: 8 additions & 10 deletions PostHog/Autocapture/SwiftUITapAutocapture.swift
Original file line number Diff line number Diff line change
Expand Up @@ -262,16 +262,14 @@
}
}
}
#if compiler(>=6.0)
if #available(iOS 18.0, *),
let element = root.accessibilityHitTest(point, event: nil) as? NSObject,
element !== root
{
visit(element, depth: 0, path: "")
// Indexed traversal must still assign a path to an unlabeled hit-test leaf.
visited.removeAll(keepingCapacity: true)
}
#endif
if #available(iOS 18.0, *),
let element = root.accessibilityHitTest(point, event: nil) as? NSObject,
element !== root
{
visit(element, depth: 0, path: "")
// Indexed traversal must still assign a path to an unlabeled hit-test leaf.
visited.removeAll(keepingCapacity: true)
}
visit(root, depth: 0, path: "")
return AccessibilityTarget(identifier: best ?? fallback, excluded: excluded, traits: traits, developerIdentifier: best)
}
Expand Down
7 changes: 1 addition & 6 deletions PostHog/ErrorTracking/PostHogCrashReportProcessor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,7 @@
import Foundation

#if os(iOS) || os(macOS) || os(tvOS)
#if compiler(>=6.0)
internal import PHPLCrashReporter
#else
// swiftlint:disable:next duplicate_imports
@_implementationOnly import PHPLCrashReporter
#endif
internal import PHPLCrashReporter

enum PostHogCrashReportProcessor {
/// Process a PHPLCrashReport and convert it to PostHog $exception event properties
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,7 @@
import Foundation

#if os(iOS) || os(macOS) || os(tvOS)
#if compiler(>=6.0)
internal import PHPLCrashReporter
#else
// swiftlint:disable:next duplicate_imports
@_implementationOnly import PHPLCrashReporter
#endif
internal import PHPLCrashReporter

class PostHogErrorTrackingAutoCaptureIntegration: PostHogIntegration {
private static let integrationInstallState = PostHogIntegrationInstallState()
Expand Down
16 changes: 4 additions & 12 deletions PostHog/PostHogSDK.swift
Original file line number Diff line number Diff line change
Expand Up @@ -133,18 +133,10 @@ let maxRetryDelay = 30.0
private weak var surveysIntegration: PostHogSurveyIntegration?
#endif

// nonisolated(unsafe) is introduced in Swift 5.10
#if swift(>=5.10)
/// Shared singleton SDK instance used by most applications.
///
/// Call `setup(_:)` once with a `PostHogConfig` before using capture APIs.
@objc public nonisolated(unsafe) static let shared: PostHogSDK = .init(PostHogConfig(projectToken: ""))
#else
/// Shared singleton SDK instance used by most applications.
///
/// Call `setup(_:)` once with a `PostHogConfig` before using capture APIs.
@objc public static let shared: PostHogSDK = .init(PostHogConfig(projectToken: ""))
#endif
/// Shared singleton SDK instance used by most applications.
///
/// Call `setup(_:)` once with a `PostHogConfig` before using capture APIs.
@objc public nonisolated(unsafe) static let shared: PostHogSDK = .init(PostHogConfig(projectToken: ""))

deinit {
#if !os(watchOS)
Expand Down
7 changes: 1 addition & 6 deletions PostHog/Replay/Plugins/Network/URLSessionSwizzler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,7 @@
#if os(iOS)

import Foundation
#if compiler(>=6.0)
internal import PostHogObjCExceptionSupport
#else
// swiftlint:disable:next duplicate_imports
@_implementationOnly import PostHogObjCExceptionSupport
#endif
internal import PostHogObjCExceptionSupport

final class URLSessionInstrumentation {
typealias RequestModifier = (URLRequest) -> URLRequest
Expand Down
6 changes: 1 addition & 5 deletions PostHog/Utils/BoxedBeforeSend.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@
//

import Foundation
#if compiler(>=6.0)
internal import PostHogObjCExceptionSupport
#else
@_implementationOnly import PostHogObjCExceptionSupport
#endif
internal import PostHogObjCExceptionSupport

/// ObjC wrappers for the Swift function-typed `beforeSend` chains: Swift
/// function types aren't `@objc`-bridgeable, and `@objc` classes can't be
Expand Down
7 changes: 1 addition & 6 deletions PostHog/Utils/UIImage+WebP.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,7 @@
import Accelerate
import CoreGraphics
import Foundation
#if compiler(>=6.0)
internal import phlibwebp
#else
// swiftlint:disable:next duplicate_imports
@_implementationOnly import phlibwebp
#endif
internal import phlibwebp
import UIKit

extension UIImage {
Expand Down
Loading