From f2e71f7d031f616a1786bdd56f6e01b6e68a7513 Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Thu, 28 Nov 2024 10:34:14 +1100 Subject: [PATCH 01/11] Add `.editorconfig` --- .editorconfig | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..5a91fc9 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +# Apply to all files +[*] +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +# Ruby specific rules +[{*.rb,Fastfile,Gemfile}] +indent_style = space +indent_size = 2 + +[*.{swift,h,m}] +indent_style = space +indent_size = 4 From a2579133545fade62baf6dfdaec0673a40c7776d Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Thu, 28 Nov 2024 10:35:02 +1100 Subject: [PATCH 02/11] Add `Makefile` with tasks to open projects and run unit tests --- Makefile | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ea8b39c --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +# TODO: Use newer Sim. Sticking with it at the moment because we know it works in the existing GitHub action setup. +SIMULATOR_NAME ?= iPhone 14 +SIMULATOR_OS ?= latest +XCODE_PATH ?= /Applications/Xcode.app + +# Convenience to open the lib and demo project with Xcode, given it's not in the root. +open: + open -a $(XCODE_PATH) ./Demo/ParselyDemo.xcodeproj + +# TODO: Move off xcpretty to xcbeautify. Sticking with it at the moment because we know it works in the existing GitHub action setup. +test: + set -o pipefail \ + && xcodebuild test \ + -project Demo/ParselyDemo.xcodeproj \ + -scheme ParselyDemo \ + -sdk iphonesimulator \ + -destination 'platform=iOS Simulator,name=$(SIMULATOR_NAME),OS=$(SIMULATOR_OS)' \ + | xcpretty From 2346ec125d3e57974fe803931c49eecbef096957 Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Thu, 28 Nov 2024 10:35:33 +1100 Subject: [PATCH 03/11] Use `make test` in GH Action --- .github/workflows/ios.yml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml index e715ef5..1e176e3 100644 --- a/.github/workflows/ios.yml +++ b/.github/workflows/ios.yml @@ -34,11 +34,4 @@ jobs: with: xcode-version: ${{ matrix.xcode }} - name: Build and Test - run: | - xcodebuild test \ - -project Demo/ParselyDemo.xcodeproj \ - -scheme ParselyDemo \ - -sdk iphonesimulator \ - -destination 'platform=iOS Simulator,name=iPhone 14,OS=latest' \ - | xcpretty \ - && exit ${PIPESTATUS[0]} + run: make test From 6cc170ecf146ddfd0ae36939af53e9062831346f Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Thu, 28 Nov 2024 10:43:11 +1100 Subject: [PATCH 04/11] Set up SwiftLint and add `make format` --- .gitignore | 4 ++++ .swiftlint.yml | 7 +++++++ Makefile | 4 ++++ 3 files changed, 15 insertions(+) create mode 100644 .swiftlint.yml diff --git a/.gitignore b/.gitignore index 58f360b..a08e021 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,7 @@ xcuserdata/* # Ruby tooling vendor/bundle + + +# SwiftLint Remote Config Cache +.swiftlint/RemoteConfigCache \ No newline at end of file diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 0000000..81eecbd --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,7 @@ +swiftlint_version: 0.57.0 + +parent_config: https://raw.githubusercontent.com/Automattic/swiftlint-config/b497131f8d0fddbf3b23278cfc4ef8d86c9bcb20/.swiftlint.yml +remote_timeout: 10.0 + +excluded: + - .build diff --git a/Makefile b/Makefile index ea8b39c..e48ec50 100644 --- a/Makefile +++ b/Makefile @@ -16,3 +16,7 @@ test: -sdk iphonesimulator \ -destination 'platform=iOS Simulator,name=$(SIMULATOR_NAME),OS=$(SIMULATOR_OS)' \ | xcpretty + +# TODO: Add automation to set up SwiftLint +format: + swiftlint --fix --format From 86c56f1053d803dbca75eb3a8fcdd60c6e8b0d54 Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Thu, 28 Nov 2024 11:13:59 +1100 Subject: [PATCH 05/11] Make sure "sending event" log msg and event dump are in the same line This way, we avoid other `os_log` calls getting in between `os_log` and `dump` and breaking the association between the two which makes the logs harder to understand. --- Sources/Track.swift | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/Sources/Track.swift b/Sources/Track.swift index 9292c73..94ae2e8 100644 --- a/Sources/Track.swift +++ b/Sources/Track.swift @@ -22,8 +22,7 @@ class Track { parselyTracker.startFlushTimer(); pixel.beacon(event: event) - os_log("Sending an event from Track", log: OSLog.tracker, type:.debug) - dump(event.toDict()) + os_log_sending_event(event) } func pageview(url: String, urlref: String = "", metadata: ParselyMetadata?, extra_data: Dictionary?, idsite: String) { @@ -82,3 +81,18 @@ class Track { videoManager.sendHeartbeats() } } + +/// Utitlity to log sending event with a dump of the event. +private func os_log_sending_event(_ event: Event, log: OSLog = .tracker, type: OSLogType = .debug) { + var eventDump = DumpOutput() + dump(event.toDict(), to: &eventDump) + os_log("Sending an event from Track:\n%@", log: log, type: type, eventDump.content) +} + +private struct DumpOutput: TextOutputStream { + private(set) var content = "" + + mutating func write(_ string: String) { + content.append(string) + } +} From 06c8b36fbe4316f1a3bec08c5f020bc2b432dfad Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Fri, 3 Jan 2025 20:28:41 +0100 Subject: [PATCH 06/11] Remove unnecessary `DumpOutput` custom type Hat tip @crazytonyli https://github.com/Parsely/AnalyticsSDK-iOS/pull/93#discussion_r1862696995 --- Sources/Track.swift | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/Sources/Track.swift b/Sources/Track.swift index 94ae2e8..75d2770 100644 --- a/Sources/Track.swift +++ b/Sources/Track.swift @@ -84,15 +84,7 @@ class Track { /// Utitlity to log sending event with a dump of the event. private func os_log_sending_event(_ event: Event, log: OSLog = .tracker, type: OSLogType = .debug) { - var eventDump = DumpOutput() + var eventDump = "" dump(event.toDict(), to: &eventDump) - os_log("Sending an event from Track:\n%@", log: log, type: type, eventDump.content) -} - -private struct DumpOutput: TextOutputStream { - private(set) var content = "" - - mutating func write(_ string: String) { - content.append(string) - } + os_log("Sending an event from Track:\n%@", log: log, type: type, eventDump) } From d83f06a10775eb05fb1b18075e2f35269ef6bc36 Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Wed, 8 Jan 2025 17:50:22 +0100 Subject: [PATCH 07/11] Differentiate between SDK and demo app in Xcode console logs --- Demo/ParselyDemo/FirstViewController.swift | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Demo/ParselyDemo/FirstViewController.swift b/Demo/ParselyDemo/FirstViewController.swift index ef520bc..986d290 100644 --- a/Demo/ParselyDemo/FirstViewController.swift +++ b/Demo/ParselyDemo/FirstViewController.swift @@ -6,27 +6,31 @@ class FirstViewController: UIViewController { let delegate = UIApplication.shared.delegate as! AppDelegate @IBAction func didTouchButton(_ sender: Any) { - os_log("didTouchButton", log: OSLog.default, type: .debug) + log("didTouchButton") let demoMetas = ParselyMetadata(authors: ["Yogi Berr"]) delegate.parsely.trackPageView(url: "http://parsely.com/path/cool-blog-post/1?qsarg=nawp&anotherone=yup", metadata: demoMetas, extraData: ["product-id": "12345"], siteId: "subdomain.parsely-test.com") } @IBAction func didStartEngagement(_ sender: Any) { - os_log("didStartEngagement", log: OSLog.default, type: .debug) + log("didStartEngagement") delegate.parsely.startEngagement(url: "http://parsely.com/very-not-real", urlref:"http://parsely.com/not-real", extraData: ["product-id": "12345"], siteId: "engaged.parsely-test.com") } @IBAction func didStopEngagement(_ sender: Any) { - os_log("didStopEngagement", log: OSLog.default, type: .debug) + log("didStopEngagement") delegate.parsely.stopEngagement() } @IBAction func didStartVideo(_ sender: Any) { - os_log("didStartVideo", log: OSLog.default, type: .debug) + log("didStartVideo") let demoMetas = ParselyMetadata(authors: ["Yogi Berr"], duration: TimeInterval(10)) delegate.parsely.trackPlay(url: "http://parsely.com/path/cool-blog-post/1?qsarg=nawp&anotherone=yup", urlref: "not-a-real-urlref", videoID: "videoOne", duration: TimeInterval(6000), metadata: demoMetas, extraData: ["product-id": "12345", "ts": "should be overwritten"]) } @IBAction func didPauseVideo(_ sender: Any) { - os_log("didStopVideo", log: OSLog.default, type: .debug) + log("didStopVideo") delegate.parsely.trackPause() } + + private func log(_ message: String) { + os_log("[Parsely Demo App] %@", log: OSLog.default, type: .debug, message) + } } From c658af6c732a6d03f26013126f32cbd6becca326 Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Thu, 9 Jan 2025 03:52:08 +1100 Subject: [PATCH 08/11] Fix typo `s/Utitlity/Utility` Co-authored-by: Tanner Stokes --- Sources/Track.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Track.swift b/Sources/Track.swift index 75d2770..31f06e3 100644 --- a/Sources/Track.swift +++ b/Sources/Track.swift @@ -82,7 +82,7 @@ class Track { } } -/// Utitlity to log sending event with a dump of the event. +/// Utility to log sending event with a dump of the event. private func os_log_sending_event(_ event: Event, log: OSLog = .tracker, type: OSLogType = .debug) { var eventDump = "" dump(event.toDict(), to: &eventDump) From 103d3b46236b29ac86d91cfbd51e8924e869d3e5 Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Wed, 8 Jan 2025 18:39:09 +0100 Subject: [PATCH 09/11] Remove a couple of unnecessary `self.` and `;` --- Sources/Sampler.swift | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Sources/Sampler.swift b/Sources/Sampler.swift index 2eae593..bf8b25e 100644 --- a/Sources/Sampler.swift +++ b/Sources/Sampler.swift @@ -153,10 +153,11 @@ class Sampler { heartbeatFn(data: trackedData, enableHeartbeats: true) } trackedData.accumulatedTime = 0 - let totalTrackedTime: TimeInterval = Date().timeIntervalSince(trackedData.firstSampleTime!); - trackedData.heartbeatTimeout = self.getHeartbeatInterval( + let totalTrackedTime: TimeInterval = Date().timeIntervalSince(trackedData.firstSampleTime!) + trackedData.heartbeatTimeout = getHeartbeatInterval( existingTimeout: trackedData.heartbeatTimeout!, - totalTrackedTime: totalTrackedTime) + totalTrackedTime: totalTrackedTime + ) updateAccumulator(acc: trackedData) heartbeatInterval = trackedData.heartbeatTimeout! } From f603a1b2ef008036b499192465bb31f96b8bfbbb Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Wed, 8 Jan 2025 18:39:36 +0100 Subject: [PATCH 10/11] Add log with next heartbeat time --- Sources/Sampler.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sources/Sampler.swift b/Sources/Sampler.swift index bf8b25e..147a6dd 100644 --- a/Sources/Sampler.swift +++ b/Sources/Sampler.swift @@ -160,6 +160,8 @@ class Sampler { ) updateAccumulator(acc: trackedData) heartbeatInterval = trackedData.heartbeatTimeout! + + os_log("Send heartbeat completed. New heartbeat timeout %.2f seconds.", trackedData.heartbeatTimeout!) } @objc internal func sendHeartbeats() -> Void { From a257fd383c44781ab1a6fa66b4d8a295521e2542 Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Wed, 8 Jan 2025 18:40:27 +0100 Subject: [PATCH 11/11] Log time at which heartbeat is sent --- Sources/Sampler.swift | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Sources/Sampler.swift b/Sources/Sampler.swift index 147a6dd..4bffb44 100644 --- a/Sources/Sampler.swift +++ b/Sources/Sampler.swift @@ -147,13 +147,20 @@ class Sampler { os_log("No accumulator found for %s, skipping sendHeartbeat", log: OSLog.tracker, type:.debug, key) return } + let now = Date() let incSecs: TimeInterval = trackedData.accumulatedTime if incSecs > 0 { - os_log("Sending heartbeat for %s", log: OSLog.tracker, type:.debug, key) + os_log( + "Sending heartbeat for %s. Timestamp: %s.", + log: OSLog.tracker, + type:.debug, + key, + now.description + ) heartbeatFn(data: trackedData, enableHeartbeats: true) } trackedData.accumulatedTime = 0 - let totalTrackedTime: TimeInterval = Date().timeIntervalSince(trackedData.firstSampleTime!) + let totalTrackedTime: TimeInterval = now.timeIntervalSince(trackedData.firstSampleTime!) trackedData.heartbeatTimeout = getHeartbeatInterval( existingTimeout: trackedData.heartbeatTimeout!, totalTrackedTime: totalTrackedTime