Skip to content

Commit df261f5

Browse files
Merge pull request #15 from maxhumber/main
Remove Alamofire and fix Swift 6 crash
2 parents 5247eed + 11ef015 commit df261f5

File tree

6 files changed

+44
-98
lines changed

6 files changed

+44
-98
lines changed

Package.resolved

Lines changed: 0 additions & 16 deletions
This file was deleted.

Package.swift

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,29 +9,10 @@ let package = Package(
99
.iOS(.v13), .tvOS(.v13), .watchOS(.v6), .macOS(.v10_15)
1010
],
1111
products: [
12-
// Products define the executables and libraries a package produces, making them visible to other packages.
13-
.library(
14-
name: "SimpleAnalytics",
15-
targets: ["SimpleAnalytics"]),
12+
.library(name: "SimpleAnalytics", targets: ["SimpleAnalytics"])
1613
],
17-
dependencies: [
18-
.package(url: "https://github.com/Alamofire/Alamofire.git", .upToNextMajor(from: "5.8.1")),
19-
],
20-
2114
targets: [
22-
// Targets are the basic building blocks of a package, defining a module or a test suite.
23-
// Targets can depend on other targets in this package and products from dependencies.
24-
.target(
25-
name: "SimpleAnalytics",
26-
dependencies: [
27-
.product(
28-
name: "Alamofire",
29-
package: "Alamofire"
30-
),
31-
],
32-
path: "Sources"),
33-
.testTarget(
34-
name: "SimpleAnalyticsTests",
35-
dependencies: ["SimpleAnalytics"]),
15+
.target(name: "SimpleAnalytics", path: "Sources"),
16+
.testTarget(name: "SimpleAnalyticsTests", dependencies: ["SimpleAnalytics"])
3617
]
3718
)

Sources/SimpleAnalytics/RequestDispatcher.swift

Lines changed: 9 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,50 +6,19 @@
66
//
77

88
import Foundation
9-
import Alamofire
109

1110
internal struct RequestDispatcher {
1211
/// Sends the event to Simple Analytics
1312
/// - Parameter event: the event to dispatch
1413
static internal func sendEventRequest(event: Event) async throws {
15-
return try await withCheckedThrowingContinuation { continuation in
16-
AF.request("https://queue.simpleanalyticscdn.com/events",
17-
method: .post,
18-
parameters: event,
19-
encoder: JSONParameterEncoder.default).responseData { response in
20-
21-
switch(response.result) {
22-
case .success(_):
23-
continuation.resume()
24-
case let .failure(error):
25-
continuation.resume(throwing: self.handleError(error: error))
26-
}
27-
}
28-
}
14+
guard let url = URL(string: "https://queue.simpleanalyticscdn.com/events") else { throw URLError(.badURL) }
15+
var request = URLRequest(url: url)
16+
request.httpMethod = "POST"
17+
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
18+
let jsonData = try JSONEncoder().encode(event)
19+
request.httpBody = jsonData
20+
let (_, response) = try await URLSession.shared.data(for: request)
21+
guard let httpResponse = response as? HTTPURLResponse else { throw URLError(.badServerResponse) }
22+
guard (200...299).contains(httpResponse.statusCode) else { throw URLError(URLError.Code(rawValue: httpResponse.statusCode)) }
2923
}
30-
31-
static private func handleError(error: AFError) -> Error {
32-
if let underlyingError = error.underlyingError {
33-
let nserror = underlyingError as NSError
34-
let code = nserror.code
35-
if code == NSURLErrorNotConnectedToInternet ||
36-
code == NSURLErrorTimedOut ||
37-
code == NSURLErrorInternationalRoamingOff ||
38-
code == NSURLErrorDataNotAllowed ||
39-
code == NSURLErrorCannotFindHost ||
40-
code == NSURLErrorCannotConnectToHost ||
41-
code == NSURLErrorNetworkConnectionLost
42-
{
43-
var userInfo = nserror.userInfo
44-
userInfo[NSLocalizedDescriptionKey] = "Unable to connect to the server"
45-
let currentError = NSError(
46-
domain: nserror.domain,
47-
code: code,
48-
userInfo: userInfo
49-
)
50-
return currentError
51-
}
52-
}
53-
return error
54-
}
5524
}

Sources/SimpleAnalytics/SimpleAnalytics.swift

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,11 @@ import Foundation
2525
final public class SimpleAnalytics: NSObject {
2626
/// The hostname of the website in Simple Analytics the tracking should be send to. Without `https://`
2727
let hostname: String
28-
private let userAgent: String
28+
private var userAgent: String?
2929
private let userLanguage: String
3030
private let userTimezone: String
3131
/// The last date a unique visit was tracked.
3232
private var visitDate: Date?
33-
private let userAgentProvider = UserAgentProvider()
3433
private var sharedDefaultsSuiteName: String?
3534

3635
/// Defines if the user is opted out. When set to `true`, all tracking will be skipped. This is persisted between sessions.
@@ -47,7 +46,6 @@ final public class SimpleAnalytics: NSObject {
4746
/// - Parameter hostname: The hostname as found in SimpleAnalytics, without `https://`
4847
public init(hostname: String) {
4948
self.hostname = hostname
50-
self.userAgent = userAgentProvider.userAgent
5149
self.userLanguage = Locale.current.identifier
5250
self.userTimezone = TimeZone.current.identifier
5351
self.visitDate = UserDefaults.standard.object(forKey: Keys.visitDateKey) as? Date
@@ -58,7 +56,6 @@ final public class SimpleAnalytics: NSObject {
5856
/// - Parameter: sharedDefaultsSuiteName: When extensions (such as a main app and widget) have a set of sharedDefaults (using an App Group) that unique user can be counted once using this (instead of two or more times when using app and widget, etc.)
5957
public init(hostname: String, sharedDefaultsSuiteName: String) {
6058
self.hostname = hostname
61-
self.userAgent = userAgentProvider.userAgent
6259
self.userLanguage = Locale.current.identifier
6360
self.userTimezone = TimeZone.current.identifier
6461
self.sharedDefaultsSuiteName = sharedDefaultsSuiteName
@@ -96,6 +93,7 @@ final public class SimpleAnalytics: NSObject {
9693
guard !isOptedOut else {
9794
return
9895
}
96+
let userAgent = try await getUserAgent()
9997
let event = Event(
10098
type: .pageview,
10199
hostname: hostname,
@@ -115,6 +113,7 @@ final public class SimpleAnalytics: NSObject {
115113
guard !isOptedOut else {
116114
return
117115
}
116+
let userAgent = try await getUserAgent()
118117
let event = Event(
119118
type: .event,
120119
hostname: hostname,
@@ -188,6 +187,14 @@ final public class SimpleAnalytics: NSObject {
188187
}
189188
}
190189

190+
/// Get the cached userAgent or fetch a new one
191+
internal func getUserAgent() async throws -> String {
192+
if let userAgent { return userAgent }
193+
let newUserAgent = try await UserAgentFetcher.fetch()
194+
userAgent = newUserAgent
195+
return newUserAgent
196+
}
197+
191198
/// Keys used to store things in UserDefaults
192199
internal struct Keys {
193200
static let visitDateKey = "simpleanalytics.visitdate"
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//
2+
// UserAgent.swift
3+
//
4+
//
5+
// Created by Max Humber on 2025-01-02.
6+
7+
import WebKit
8+
9+
enum UserAgentFetcher {
10+
@MainActor
11+
static func fetch() async throws -> String {
12+
let webView = WKWebView(frame: .zero)
13+
let result = try await webView.evaluateJavaScript("navigator.userAgent")
14+
guard let userAgent = result as? String else { throw UserAgentError.unableToFetchUserAgent }
15+
return userAgent
16+
}
17+
}
18+
19+
enum UserAgentError: Error {
20+
case unableToFetchUserAgent
21+
}

Sources/SimpleAnalytics/UserAgentProvider.swift

Lines changed: 0 additions & 16 deletions
This file was deleted.

0 commit comments

Comments
 (0)