Skip to content

Commit

Permalink
Add reset functionality (#434)
Browse files Browse the repository at this point in the history
* Add reset method to MatomoTracker

* Updated macOS and iOS versions in the test target

* Updated Quick and Nimble to their latest versions

* Fixed Cocoapods Platform for unit tests

* Updated Github Actions to macos 13

* Changed the UserDefault Keys to be an enum

* Added minor tweaks to the reset functionality

* Added Changelog Entry

# Conflicts:
#	CHANGELOG.md

* Added workaround for failing tests

# Conflicts:
#	Gemfile.lock

* Added running tests when merging to develop

* trying to fix testing

* Updating Bundler

---------

Co-authored-by: Tobias Ottenweller <[email protected]>
  • Loading branch information
brototyp and Tobias Ottenweller authored Sep 6, 2024
1 parent 8f90664 commit 1d2c640
Show file tree
Hide file tree
Showing 12 changed files with 204 additions and 76 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/danger.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: CI
name: Run Danger

on:
pull_request:
Expand Down
42 changes: 24 additions & 18 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
name: CI
name: Run Tests

on:
pull_request:
branches:
- develop

push:
branches:
- develop

jobs:
tests-ios:
runs-on: macos-11
runs-on: macos-13
strategy:
matrix:
include: # selection of https://github.com/actions/runner-images/blob/main/images/macos/macos-11-Readme.md#installed-sdks
- xcode: "13.2.1"
ios: "15.2"
device: "iPhone 8"
- xcode: "12.5.1"
ios: "14.5"
device: "iPhone 8"
include: # selection of https://github.com/actions/runner-images/blob/main/images/macos/macos-13-Readme.md#installed-sdks
- xcode: "14.3.1"
ios: "16.4"
device: "iPhone 14"
# - xcode: "12.5.1"
# ios: "14.5"
# device: "iPhone 8"
if: github.event_name == 'pull_request' # if only run pull request when multiple trigger workflow
name: "test ios ${{matrix.ios}}"
steps:
Expand All @@ -32,19 +35,22 @@ jobs:
- name: Run Unit Tests on ${{matrix.ios}} ${{matrix.device}}
run: xcodebuild -workspace MatomoTracker.xcworkspace -scheme 'MatomoTracker' -sdk 'iphonesimulator' -destination 'platform=iOS Simulator,name=${{matrix.device}},OS=${{matrix.ios}}' -configuration Debug clean test
tests-demo-builds:
runs-on: macos-11
runs-on: macos-13
strategy:
matrix:
include:
- scheme: "ios"
sdk: "iphonesimulator"
destination: "platform=iOS Simulator,name=iPhone 8,OS=15.2"
destination: "platform=iOS Simulator,name=iPhone 14,OS=16.4"
xcode: "14.3.1"
- scheme: "macos"
sdk: "macosx"
destination: "platform=macOS,arch=x86_64"
xcode: "14.3.1"
- scheme: "tvos"
sdk: "appletvsimulator"
destination: "platform=tvOS Simulator,name=Apple TV,OS=15.2"
destination: "platform=tvOS Simulator,name=Apple TV,OS=16.4"
xcode: "14.3.1"
if: github.event_name == 'pull_request' # if only run pull request when multiple trigger workflow
name: "test demo build ${{matrix.scheme}}"
steps:
Expand All @@ -55,16 +61,16 @@ jobs:
bundler-cache: true
- name: pod install
run: pod install || pod install --repo-update
- name: "Select Xcode 13.2.1"
run: sudo xcode-select -switch /Applications/Xcode_13.2.1.app && /usr/bin/xcodebuild -version
- name: "Select Xcode ${{matrix.xcode}}"
run: sudo xcode-select -switch /Applications/Xcode_${{matrix.xcode}}.app && /usr/bin/xcodebuild -version
- name: Build Demo for ${{matrix.scheme}}
run: xcodebuild -workspace MatomoTracker.xcworkspace -scheme '${{matrix.scheme}}' -sdk '${{matrix.sdk}}' -destination '${{matrix.destination}}' -configuration Release clean build
tests-package-managers:
runs-on: macos-11
runs-on: macos-13
steps:
- uses: actions/checkout@v3
- name: "Select Xcode 11.7"
run: sudo xcode-select -switch /Applications/Xcode_13.2.1.app && /usr/bin/xcodebuild -version
- name: "Select Xcode 14.3.1"
run: sudo xcode-select -switch /Applications/Xcode_14.3.1.app && /usr/bin/xcodebuild -version
# Carthage doesn't work well with Matomo anymore
# https://github.com/Carthage/Carthage/issues/3146
# - name: "Build Carthage"
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Changelog

## Unreleased
* **feature** Added functionality to reset a Matomo instance. [#434](https://github.com/matomo-org/matomo-sdk-ios/pull/434)

## 7.6.0
* **feature** Added support for watchOS. [#352](https://github.com/matomo-org/matomo-sdk-ios/issues/352)
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,4 @@ DEPENDENCIES
xcpretty

BUNDLED WITH
1.17.2
2.2.17
2 changes: 2 additions & 0 deletions MatomoTracker.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,7 @@
INFOPLIST_FILE = MatomoTrackerTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.15;
PRODUCT_BUNDLE_IDENTIFIER = org.matomo.MatomoTrackerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand All @@ -657,6 +658,7 @@
INFOPLIST_FILE = MatomoTrackerTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.15;
PRODUCT_BUNDLE_IDENTIFIER = org.matomo.MatomoTrackerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand Down
25 changes: 25 additions & 0 deletions MatomoTracker/MatomoTracker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -468,3 +468,28 @@ extension MatomoTracker {
/// The version of the Matomo SDKs
@objc public static let sdkVersion = "7.5"
}

extension MatomoTracker {
/// Resets all session, visitor and campaign information.
///
/// This function should only be called from the main thread
/// After calling this method this instance behaves like the app has been freshly installed.
public func reset() {
guard Thread.isMainThread else {
DispatchQueue.main.sync {
self.reset()
}
return
}
matomoUserDefaults.reset()

visitor = Visitor.current(in: matomoUserDefaults)
session = Session.current(in: matomoUserDefaults)
dimensions = []
customVariables = []
campaignName = nil
campaignKeyword = nil

startNewSession()
}
}
80 changes: 43 additions & 37 deletions MatomoTracker/MatomoUserDefaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,121 +13,127 @@ internal struct MatomoUserDefaults {

var totalNumberOfVisits: Int {
get {
return userDefaults.integer(forKey: MatomoUserDefaults.Key.totalNumberOfVisits)
return userDefaults.integer(forKey: MatomoUserDefaults.Key.totalNumberOfVisits.rawValue)
}
set {
userDefaults.set(newValue, forKey: MatomoUserDefaults.Key.totalNumberOfVisits)
userDefaults.set(newValue, forKey: MatomoUserDefaults.Key.totalNumberOfVisits.rawValue)
userDefaults.synchronize()
}
}

var firstVisit: Date? {
get {
return userDefaults.object(forKey: MatomoUserDefaults.Key.firstVistsTimestamp) as? Date
return userDefaults.object(forKey: MatomoUserDefaults.Key.firstVistsTimestamp.rawValue) as? Date
}
set {
userDefaults.set(newValue, forKey: MatomoUserDefaults.Key.firstVistsTimestamp)
userDefaults.set(newValue, forKey: MatomoUserDefaults.Key.firstVistsTimestamp.rawValue)
userDefaults.synchronize()
}
}

var previousVisit: Date? {
get {
return userDefaults.object(forKey: MatomoUserDefaults.Key.previousVistsTimestamp) as? Date
return userDefaults.object(forKey: MatomoUserDefaults.Key.previousVistsTimestamp.rawValue) as? Date
}
set {
userDefaults.set(newValue, forKey: MatomoUserDefaults.Key.previousVistsTimestamp)
userDefaults.set(newValue, forKey: MatomoUserDefaults.Key.previousVistsTimestamp.rawValue)
userDefaults.synchronize()
}
}

var currentVisit: Date? {
get {
return userDefaults.object(forKey: MatomoUserDefaults.Key.currentVisitTimestamp) as? Date
return userDefaults.object(forKey: MatomoUserDefaults.Key.currentVisitTimestamp.rawValue) as? Date
}
set {
userDefaults.set(newValue, forKey: MatomoUserDefaults.Key.currentVisitTimestamp)
userDefaults.set(newValue, forKey: MatomoUserDefaults.Key.currentVisitTimestamp.rawValue)
userDefaults.synchronize()
}
}

var optOut: Bool {
get {
return userDefaults.bool(forKey: MatomoUserDefaults.Key.optOut)
return userDefaults.bool(forKey: MatomoUserDefaults.Key.optOut.rawValue)
}
set {
userDefaults.set(newValue, forKey: MatomoUserDefaults.Key.optOut)
userDefaults.set(newValue, forKey: MatomoUserDefaults.Key.optOut.rawValue)
userDefaults.synchronize()
}
}

var clientId: String? {
get {
return userDefaults.string(forKey: MatomoUserDefaults.Key.clientID)
return userDefaults.string(forKey: MatomoUserDefaults.Key.clientID.rawValue)
}
set {
userDefaults.setValue(newValue, forKey: MatomoUserDefaults.Key.clientID)
userDefaults.setValue(newValue, forKey: MatomoUserDefaults.Key.clientID.rawValue)
userDefaults.synchronize()
}
}

var forcedVisitorId: String? {
get {
return userDefaults.string(forKey: MatomoUserDefaults.Key.forcedVisitorID)
return userDefaults.string(forKey: MatomoUserDefaults.Key.forcedVisitorID.rawValue)
}
set {
userDefaults.setValue(newValue, forKey: MatomoUserDefaults.Key.forcedVisitorID)
userDefaults.setValue(newValue, forKey: MatomoUserDefaults.Key.forcedVisitorID.rawValue)
userDefaults.synchronize()
}
}

var visitorUserId: String? {
get {
return userDefaults.string(forKey: MatomoUserDefaults.Key.visitorUserID);
return userDefaults.string(forKey: MatomoUserDefaults.Key.visitorUserID.rawValue);
}
set {
userDefaults.setValue(newValue, forKey: MatomoUserDefaults.Key.visitorUserID);
userDefaults.setValue(newValue, forKey: MatomoUserDefaults.Key.visitorUserID.rawValue);
userDefaults.synchronize()
}
}

var lastOrder: Date? {
get {
return userDefaults.object(forKey: MatomoUserDefaults.Key.lastOrder) as? Date
return userDefaults.object(forKey: MatomoUserDefaults.Key.lastOrder.rawValue) as? Date
}
set {
userDefaults.set(newValue, forKey: MatomoUserDefaults.Key.lastOrder)
userDefaults.set(newValue, forKey: MatomoUserDefaults.Key.lastOrder.rawValue)
}
}

func reset() {
for key in Key.allCases {
userDefaults.removeObject(forKey: key.rawValue)
}
}
}

extension MatomoUserDefaults {
public mutating func copy(from userDefaults: UserDefaults) {
totalNumberOfVisits = userDefaults.integer(forKey: MatomoUserDefaults.Key.totalNumberOfVisits)
firstVisit = userDefaults.object(forKey: MatomoUserDefaults.Key.firstVistsTimestamp) as? Date
previousVisit = userDefaults.object(forKey: MatomoUserDefaults.Key.previousVistsTimestamp) as? Date
currentVisit = userDefaults.object(forKey: MatomoUserDefaults.Key.currentVisitTimestamp) as? Date
optOut = userDefaults.bool(forKey: MatomoUserDefaults.Key.optOut)
clientId = userDefaults.string(forKey: MatomoUserDefaults.Key.clientID)
forcedVisitorId = userDefaults.string(forKey: MatomoUserDefaults.Key.forcedVisitorID)
visitorUserId = userDefaults.string(forKey: MatomoUserDefaults.Key.visitorUserID)
lastOrder = userDefaults.object(forKey: MatomoUserDefaults.Key.lastOrder) as? Date
totalNumberOfVisits = userDefaults.integer(forKey: MatomoUserDefaults.Key.totalNumberOfVisits.rawValue)
firstVisit = userDefaults.object(forKey: MatomoUserDefaults.Key.firstVistsTimestamp.rawValue) as? Date
previousVisit = userDefaults.object(forKey: MatomoUserDefaults.Key.previousVistsTimestamp.rawValue) as? Date
currentVisit = userDefaults.object(forKey: MatomoUserDefaults.Key.currentVisitTimestamp.rawValue) as? Date
optOut = userDefaults.bool(forKey: MatomoUserDefaults.Key.optOut.rawValue)
clientId = userDefaults.string(forKey: MatomoUserDefaults.Key.clientID.rawValue)
forcedVisitorId = userDefaults.string(forKey: MatomoUserDefaults.Key.forcedVisitorID.rawValue)
visitorUserId = userDefaults.string(forKey: MatomoUserDefaults.Key.visitorUserID.rawValue)
lastOrder = userDefaults.object(forKey: MatomoUserDefaults.Key.lastOrder.rawValue) as? Date
}
}

extension MatomoUserDefaults {
internal struct Key {
static let totalNumberOfVisits = "PiwikTotalNumberOfVistsKey"
static let currentVisitTimestamp = "PiwikCurrentVisitTimestampKey"
static let previousVistsTimestamp = "PiwikPreviousVistsTimestampKey"
static let firstVistsTimestamp = "PiwikFirstVistsTimestampKey"
internal enum Key: String, CaseIterable {
case totalNumberOfVisits = "PiwikTotalNumberOfVistsKey"
case currentVisitTimestamp = "PiwikCurrentVisitTimestampKey"
case previousVistsTimestamp = "PiwikPreviousVistsTimestampKey"
case firstVistsTimestamp = "PiwikFirstVistsTimestampKey"

// Note: To be compatible with previous versions, the clientID key retains its old value,
// even though it is now a misnomer since adding visitorUserID makes it a bit confusing.
static let clientID = "PiwikVisitorIDKey"
static let forcedVisitorID = "PiwikForcedVisitorIDKey"
static let visitorUserID = "PiwikVisitorUserIDKey"
static let optOut = "PiwikOptOutKey"
static let lastOrder = "PiwikLastOrderDateKey"
case clientID = "PiwikVisitorIDKey"
case forcedVisitorID = "PiwikForcedVisitorIDKey"
case visitorUserID = "PiwikVisitorUserIDKey"
case optOut = "PiwikOptOutKey"
case lastOrder = "PiwikLastOrderDateKey"
}
}
2 changes: 1 addition & 1 deletion MatomoTrackerTests/EventAPISerializerSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Quick
import Nimble

class EventAPISerializerSpec: QuickSpec {
override func spec() {
override class func spec() {
describe("queryItems") {
it("encodes special characters") {
let dimension = CustomDimension(index: 42, value: ###";'"|\,.<>?/+_=-)(*&^%$#@!"###)
Expand Down
8 changes: 4 additions & 4 deletions MatomoTrackerTests/MemoryQueueSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import Quick
import Nimble

class MemoryQueueSpec: QuickSpec {
override func setUp() {
Nimble.AsyncDefaults.timeout = .seconds(10)
Nimble.AsyncDefaults.pollInterval = .milliseconds(100)
override class func setUp() {
Nimble.PollingDefaults.timeout = .seconds(10)
Nimble.PollingDefaults.pollInterval = .milliseconds(100)
}

override func spec() {
override class func spec() {
describe("init") {
it("should return not null") {
let queue = MemoryQueue()
Expand Down
18 changes: 9 additions & 9 deletions MatomoTrackerTests/PiwikUserDefaultsSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Quick
import Nimble

class MatomoUserDefaultsSpec: QuickSpec {
override func spec() {
override class func spec() {
describe("suiteMigration") {
it("should migrate data for the default suite") {
self.setMigrateableData(totalNumberOfVisists: 10, firstVisit: Date(timeIntervalSince1970: 100), previousVisit: Date(timeIntervalSince1970: 101), currentVisit: Date(timeIntervalSince1970: 102), optOut: true, clientId: "_specVisitorID")
Expand All @@ -21,20 +21,20 @@ class MatomoUserDefaultsSpec: QuickSpec {
}

/// Tries to remove all data stored in a given suite
private func removeAllInSuite(suite: String) {
private class func removeAllInSuite(suite: String) {
guard let newuserDefaults = UserDefaults(suiteName: suite) else { return }
let allKeys: [String]? = Array(newuserDefaults.dictionaryRepresentation().keys)
for key in allKeys ?? [] {
newuserDefaults.removeObject(forKey: key)
}
}

private func setMigrateableData(totalNumberOfVisists: Int, firstVisit: Date?, previousVisit: Date?, currentVisit: Date?, optOut: Bool, clientId: String?) {
UserDefaults.standard.set(totalNumberOfVisists, forKey: MatomoUserDefaults.Key.totalNumberOfVisits)
UserDefaults.standard.set(firstVisit, forKey: MatomoUserDefaults.Key.firstVistsTimestamp)
UserDefaults.standard.set(previousVisit, forKey: MatomoUserDefaults.Key.previousVistsTimestamp)
UserDefaults.standard.set(currentVisit, forKey: MatomoUserDefaults.Key.currentVisitTimestamp)
UserDefaults.standard.set(optOut, forKey: MatomoUserDefaults.Key.optOut)
UserDefaults.standard.set(clientId, forKey: MatomoUserDefaults.Key.clientID)
private class func setMigrateableData(totalNumberOfVisists: Int, firstVisit: Date?, previousVisit: Date?, currentVisit: Date?, optOut: Bool, clientId: String?) {
UserDefaults.standard.set(totalNumberOfVisists, forKey: MatomoUserDefaults.Key.totalNumberOfVisits.rawValue)
UserDefaults.standard.set(firstVisit, forKey: MatomoUserDefaults.Key.firstVistsTimestamp.rawValue)
UserDefaults.standard.set(previousVisit, forKey: MatomoUserDefaults.Key.previousVistsTimestamp.rawValue)
UserDefaults.standard.set(currentVisit, forKey: MatomoUserDefaults.Key.currentVisitTimestamp.rawValue)
UserDefaults.standard.set(optOut, forKey: MatomoUserDefaults.Key.optOut.rawValue)
UserDefaults.standard.set(clientId, forKey: MatomoUserDefaults.Key.clientID.rawValue)
}
}
Loading

0 comments on commit 1d2c640

Please sign in to comment.