9 final class URLSessionDispatcher: Dispatcher {
11 let serializer = EventSerializer()
12 let timeout: TimeInterval
13 let session: URLSession
16 private(set) var userAgent: String?
18 /// Generate a URLSessionDispatcher instance
21 /// - baseURL: The url of the Matomo server. This url has to end in `piwik.php`.
22 /// - userAgent: An optional parameter for custom user agent.
23 init(baseURL: URL, userAgent: String? = nil) {
24 self.baseURL = baseURL
26 self.session = URLSession.shared
27 DispatchQueue.main.async {
28 self.userAgent = userAgent ?? URLSessionDispatcher.defaultUserAgent()
32 private static func defaultUserAgent() -> String {
35 let webView = WebView(frame: .zero)
36 let currentUserAgent = webView.stringByEvaluatingJavaScript(from: "navigator.userAgent") ?? ""
38 let webView = UIWebView(frame: .zero)
39 var currentUserAgent = webView.stringByEvaluatingJavaScript(from: "navigator.userAgent") ?? ""
40 if let regex = try? NSRegularExpression(pattern: "\\((iPad|iPhone);", options: .caseInsensitive) {
41 let deviceModel = Device.makeCurrentDevice().platform
42 currentUserAgent = regex.stringByReplacingMatches(
44 options: .withTransparentBounds,
45 range: NSRange(location: 0, length: currentUserAgent.count),
46 withTemplate: "(\(deviceModel);"
50 let currentUserAgent = ""
52 return currentUserAgent.appending(" MatomoTracker SDK URLSessionDispatcher")
55 func send(events: [Event], success: @escaping ()->(), failure: @escaping (_ error: Error)->()) {
58 jsonBody = try serializer.jsonData(for: events)
63 let request = buildRequest(baseURL: baseURL, method: "POST", contentType: "application/json; charset=utf-8", body: jsonBody)
64 send(request: request, success: success, failure: failure)
67 private func buildRequest(baseURL: URL, method: String, contentType: String? = nil, body: Data? = nil) -> URLRequest {
68 var request = URLRequest(url: baseURL, cachePolicy: .reloadIgnoringCacheData, timeoutInterval: timeout)
69 request.httpMethod = method
70 body.map { request.httpBody = $0 }
71 contentType.map { request.setValue($0, forHTTPHeaderField: "Content-Type") }
72 userAgent.map { request.setValue($0, forHTTPHeaderField: "User-Agent") }
76 private func send(request: URLRequest, success: @escaping ()->(), failure: @escaping (_ error: Error)->()) {
77 let task = session.dataTask(with: request) { data, response, error in
78 // should we check the response?
79 // let dataString = String(data: data!, encoding: String.Encoding.utf8)
80 if let error = error {