Skip to content

Commit

Permalink
Custom parameters can override existing parameters (#397)
Browse files Browse the repository at this point in the history
* Refactored all unit tests to use fixtures and mocks

* Refactored the event serializer so custom parameters can override existing parameters

* Added Changelog
  • Loading branch information
brototyp authored Oct 3, 2021
1 parent b84105c commit c060569
Show file tree
Hide file tree
Showing 18 changed files with 316 additions and 425 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Changelog

## Unreleased
* **improvement** Allow overriding any of the tracking parameters. [#360](https://github.com/matomo-org/matomo-sdk-ios/issues/360)
* **improvement** Fixed build warnings
* **improvement** Use the new XCode build system
* **improvement** Use the new XCode build system [#391](https://github.com/matomo-org/matomo-sdk-ios/pull/391)
* **bugfix** Fixed issue where only `UserDefaults.standard` is used despite specified another instance. [#384](https://github.com/matomo-org/matomo-sdk-ios/pull/384)

## 7.4.0
Expand Down
52 changes: 28 additions & 24 deletions MatomoTracker.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,27 @@
1F38EBF81EE568D10021FBF8 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F38EBF71EE568D10021FBF8 /* Logger.swift */; };
1F3CA58C1E09A30600121FDC /* Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F3CA58B1E09A30600121FDC /* Queue.swift */; };
1F5927552178716E001478DC /* CHANGELOG.md in Resources */ = {isa = PBXBuildFile; fileRef = 1F5927542178716E001478DC /* CHANGELOG.md */; };
1F5D08721F5D79AD0064314F /* AutoTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F5D08711F5D79AD0064314F /* AutoTracker.swift */; };
1F6B2F032529CE8A00D2A591 /* UserAgent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F6B2F012529CC3400D2A591 /* UserAgent.swift */; };
1F6F0CD71E61E35A008170FC /* MatomoTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F6F0CD61E61E35A008170FC /* MatomoTracker.swift */; };
1F6F0CDC1E61E377008170FC /* DispatcherStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F6F0CD81E61E377008170FC /* DispatcherStub.swift */; };
1F6F0CDD1E61E377008170FC /* QueueStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F6F0CD91E61E377008170FC /* QueueStub.swift */; };
1F6F0CDE1E61E377008170FC /* TrackerFixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F6F0CDA1E61E377008170FC /* TrackerFixtures.swift */; };
1F6F0CDC1E61E377008170FC /* DispatcherMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F6F0CD81E61E377008170FC /* DispatcherMock.swift */; };
1F6F0CDD1E61E377008170FC /* QueueMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F6F0CD91E61E377008170FC /* QueueMock.swift */; };
1F6F0CDF1E61E377008170FC /* TrackerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F6F0CDB1E61E377008170FC /* TrackerSpec.swift */; };
1F6F0CE11E61E4F3008170FC /* URLSessionDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F6F0CE01E61E4F3008170FC /* URLSessionDispatcher.swift */; };
1F7001CB216C9D8C007A9355 /* EventAPISerializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F7001CA216C9D8C007A9355 /* EventAPISerializer.swift */; };
1F7C667F1F8C096F0066CC64 /* MainThread.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F7C667E1F8C096F0066CC64 /* MainThread.swift */; };
1F80856F1E6B4B9800A61AAF /* Locale+HttpAcceptLanguage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F80856E1E6B4B9800A61AAF /* Locale+HttpAcceptLanguage.swift */; };
1F8DCCD425A1B9E100C98793 /* Event+Fixture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F8DCCD325A1B9E100C98793 /* Event+Fixture.swift */; };
1F8DCCD625A1BA2C00C98793 /* Visitor+Fixture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F8DCCD525A1BA2C00C98793 /* Visitor+Fixture.swift */; };
1F8DCCD825A1BA4900C98793 /* Session+Fixture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F8DCCD725A1BA4900C98793 /* Session+Fixture.swift */; };
1F8DCCDA25A1BAF700C98793 /* MatomoTracker+Fixture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F8DCCD925A1BAF700C98793 /* MatomoTracker+Fixture.swift */; };
1F8DCCDC25A1C7C700C98793 /* EventAPISerializerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F8DCCDB25A1C7C700C98793 /* EventAPISerializerSpec.swift */; };
1F963074201B37A3007B2AE7 /* PiwikUserDefaultsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F963073201B37A3007B2AE7 /* PiwikUserDefaultsSpec.swift */; };
1F963075201B37DC007B2AE7 /* MemoryQueueFixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F1949F71E17B2C800458199 /* MemoryQueueFixtures.swift */; };
1FC2B429201F8C010061F5AD /* CustomVariable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FC2B428201F8C010061F5AD /* CustomVariable.swift */; };
1FCA6D451DBE0B2F0033F01C /* MatomoTracker.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FCA6D3B1DBE0B2F0033F01C /* MatomoTracker.framework */; };
1FCA6D4C1DBE0B2F0033F01C /* MatomoTracker.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FCA6D3E1DBE0B2F0033F01C /* MatomoTracker.h */; settings = {ATTRIBUTES = (Public, ); }; };
1FCFF0241F82C7A50038BC17 /* CustomDimension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FDC917B1F1A64BB0046F506 /* CustomDimension.swift */; };
1FDC917F1F1A65150046F506 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FDC917D1F1A65150046F506 /* Application.swift */; };
1FDC91801F1A65150046F506 /* Device.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FDC917E1F1A65150046F506 /* Device.swift */; };
240CC0A223269FFD003393F5 /* EventSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 240CC0A123269FFD003393F5 /* EventSpec.swift */; };
5804E9A4755E642D58301330 /* Pods_MatomoTrackerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 63DC1BA4812B98CA8037194A /* Pods_MatomoTrackerTests.framework */; };
5ACB1FAD21426FFB007C766B /* OrderItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5ACB1FAC21426FFB007C766B /* OrderItem.swift */; };
FDCA325A2093BFFB007D6B42 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = FDCA32592093BFFB007D6B42 /* README.md */; };
Expand All @@ -60,21 +61,23 @@
1F0A15CF1E6335E900FEAE72 /* Session.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Session.swift; sourceTree = "<group>"; };
1F1949F11E17A91100458199 /* MemoryQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MemoryQueue.swift; sourceTree = "<group>"; };
1F1949F31E17B06600458199 /* MemoryQueueSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MemoryQueueSpec.swift; sourceTree = "<group>"; };
1F1949F71E17B2C800458199 /* MemoryQueueFixtures.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MemoryQueueFixtures.swift; path = Fixtures/MemoryQueueFixtures.swift; sourceTree = "<group>"; };
1F38EBF71EE568D10021FBF8 /* Logger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = "<group>"; };
1F3CA58B1E09A30600121FDC /* Queue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Queue.swift; sourceTree = "<group>"; };
1F5927542178716E001478DC /* CHANGELOG.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = "<group>"; };
1F5D08711F5D79AD0064314F /* AutoTracker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutoTracker.swift; sourceTree = "<group>"; };
1F6B2F012529CC3400D2A591 /* UserAgent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAgent.swift; sourceTree = "<group>"; };
1F6F0CD61E61E35A008170FC /* MatomoTracker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MatomoTracker.swift; sourceTree = "<group>"; };
1F6F0CD81E61E377008170FC /* DispatcherStub.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DispatcherStub.swift; sourceTree = "<group>"; };
1F6F0CD91E61E377008170FC /* QueueStub.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QueueStub.swift; sourceTree = "<group>"; };
1F6F0CDA1E61E377008170FC /* TrackerFixtures.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrackerFixtures.swift; sourceTree = "<group>"; };
1F6F0CD81E61E377008170FC /* DispatcherMock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DispatcherMock.swift; sourceTree = "<group>"; };
1F6F0CD91E61E377008170FC /* QueueMock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QueueMock.swift; sourceTree = "<group>"; };
1F6F0CDB1E61E377008170FC /* TrackerSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrackerSpec.swift; sourceTree = "<group>"; };
1F6F0CE01E61E4F3008170FC /* URLSessionDispatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLSessionDispatcher.swift; sourceTree = "<group>"; };
1F7001CA216C9D8C007A9355 /* EventAPISerializer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventAPISerializer.swift; sourceTree = "<group>"; };
1F7C667E1F8C096F0066CC64 /* MainThread.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainThread.swift; sourceTree = "<group>"; };
1F80856E1E6B4B9800A61AAF /* Locale+HttpAcceptLanguage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Locale+HttpAcceptLanguage.swift"; sourceTree = "<group>"; };
1F8DCCD325A1B9E100C98793 /* Event+Fixture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Event+Fixture.swift"; sourceTree = "<group>"; };
1F8DCCD525A1BA2C00C98793 /* Visitor+Fixture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Visitor+Fixture.swift"; sourceTree = "<group>"; };
1F8DCCD725A1BA4900C98793 /* Session+Fixture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Session+Fixture.swift"; sourceTree = "<group>"; };
1F8DCCD925A1BAF700C98793 /* MatomoTracker+Fixture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MatomoTracker+Fixture.swift"; sourceTree = "<group>"; };
1F8DCCDB25A1C7C700C98793 /* EventAPISerializerSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventAPISerializerSpec.swift; sourceTree = "<group>"; };
1F963073201B37A3007B2AE7 /* PiwikUserDefaultsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PiwikUserDefaultsSpec.swift; sourceTree = "<group>"; };
1FC2B428201F8C010061F5AD /* CustomVariable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomVariable.swift; sourceTree = "<group>"; };
1FCA6D3B1DBE0B2F0033F01C /* MatomoTracker.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MatomoTracker.framework; sourceTree = BUILT_PRODUCTS_DIR; };
Expand All @@ -85,7 +88,6 @@
1FDC917B1F1A64BB0046F506 /* CustomDimension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomDimension.swift; sourceTree = "<group>"; };
1FDC917D1F1A65150046F506 /* Application.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = "<group>"; };
1FDC917E1F1A65150046F506 /* Device.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Device.swift; sourceTree = "<group>"; };
240CC0A123269FFD003393F5 /* EventSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventSpec.swift; sourceTree = "<group>"; };
2FD38CC515C49A9A8922407D /* Pods-MatomoTrackerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MatomoTrackerTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-MatomoTrackerTests/Pods-MatomoTrackerTests.release.xcconfig"; sourceTree = "<group>"; };
5ACB1FAC21426FFB007C766B /* OrderItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderItem.swift; sourceTree = "<group>"; };
63DC1BA4812B98CA8037194A /* Pods_MatomoTrackerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MatomoTrackerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -132,18 +134,19 @@
1F1949F51E17B2A400458199 /* Fixtures */ = {
isa = PBXGroup;
children = (
1F6F0CDA1E61E377008170FC /* TrackerFixtures.swift */,
1F1949F71E17B2C800458199 /* MemoryQueueFixtures.swift */,
1F8DCCD325A1B9E100C98793 /* Event+Fixture.swift */,
1F8DCCD525A1BA2C00C98793 /* Visitor+Fixture.swift */,
1F8DCCD725A1BA4900C98793 /* Session+Fixture.swift */,
1F8DCCD925A1BAF700C98793 /* MatomoTracker+Fixture.swift */,
);
name = Fixtures;
sourceTree = "<group>";
};
1F63E1D5216B503D000C4C00 /* Stubs */ = {
isa = PBXGroup;
children = (
1F6F0CD81E61E377008170FC /* DispatcherStub.swift */,
1F6F0CD91E61E377008170FC /* QueueStub.swift */,
1F5D08711F5D79AD0064314F /* AutoTracker.swift */,
1F6F0CD81E61E377008170FC /* DispatcherMock.swift */,
1F6F0CD91E61E377008170FC /* QueueMock.swift */,
);
name = Stubs;
sourceTree = "<group>";
Expand Down Expand Up @@ -208,8 +211,8 @@
1F1949F51E17B2A400458199 /* Fixtures */,
1F1949F31E17B06600458199 /* MemoryQueueSpec.swift */,
1F6F0CDB1E61E377008170FC /* TrackerSpec.swift */,
240CC0A123269FFD003393F5 /* EventSpec.swift */,
1F963073201B37A3007B2AE7 /* PiwikUserDefaultsSpec.swift */,
1F8DCCDB25A1C7C700C98793 /* EventAPISerializerSpec.swift */,
1FCA6D4B1DBE0B2F0033F01C /* Info.plist */,
);
path = MatomoTrackerTests;
Expand Down Expand Up @@ -419,13 +422,14 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
1F6F0CDD1E61E377008170FC /* QueueStub.swift in Sources */,
240CC0A223269FFD003393F5 /* EventSpec.swift in Sources */,
1F6F0CDC1E61E377008170FC /* DispatcherStub.swift in Sources */,
1F8DCCDC25A1C7C700C98793 /* EventAPISerializerSpec.swift in Sources */,
1F6F0CDD1E61E377008170FC /* QueueMock.swift in Sources */,
1F8DCCDA25A1BAF700C98793 /* MatomoTracker+Fixture.swift in Sources */,
1F6F0CDC1E61E377008170FC /* DispatcherMock.swift in Sources */,
1F963074201B37A3007B2AE7 /* PiwikUserDefaultsSpec.swift in Sources */,
1F963075201B37DC007B2AE7 /* MemoryQueueFixtures.swift in Sources */,
1F5D08721F5D79AD0064314F /* AutoTracker.swift in Sources */,
1F6F0CDE1E61E377008170FC /* TrackerFixtures.swift in Sources */,
1F8DCCD825A1BA4900C98793 /* Session+Fixture.swift in Sources */,
1F8DCCD625A1BA2C00C98793 /* Visitor+Fixture.swift in Sources */,
1F8DCCD425A1B9E100C98793 /* Event+Fixture.swift in Sources */,
1F1949F41E17B06600458199 /* MemoryQueueSpec.swift in Sources */,
1F6F0CDF1E61E377008170FC /* TrackerSpec.swift in Sources */,
);
Expand Down
51 changes: 24 additions & 27 deletions MatomoTracker/EventAPISerializer.swift
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import Foundation

final class EventAPISerializer {
internal func queryItems(for event: Event) -> [String: String] {
event.queryItems.reduce(into: [String:String]()) {
$0[$1.name] = $1.value
}.compactMapValues {
$0.addingPercentEncoding(withAllowedCharacters: .urlQueryParameterAllowed)
}
}

internal func jsonData(for events: [Event]) throws -> Data {
let eventsAsQueryItems = events.map({ $0.queryItems })
let serializedEvents = eventsAsQueryItems.map({ items in
items.compactMap({ item in
guard let value = item.value,
let encodedValue = value.addingPercentEncoding(withAllowedCharacters: .urlQueryParameterAllowed) else { return nil }
return "\(item.name)=\(encodedValue)"
}).joined(separator: "&")
})
let eventsAsQueryItems: [[String: String]] = events.map { self.queryItems(for: $0) }
let serializedEvents = eventsAsQueryItems.map { items in
items.map {
"\($0.key)=\($0.value)"
}.joined(separator: "&")
}
let body = ["requests": serializedEvents.map({ "?\($0)" })]
return try JSONSerialization.data(withJSONObject: body, options: [])
}
Expand Down Expand Up @@ -94,14 +100,14 @@ fileprivate extension Event {
URLQueryItem(name: "ec_sh", value: orderShippingCost != nil ? "\(orderShippingCost!)" : nil),
URLQueryItem(name: "ec_dt", value: orderDiscount != nil ? "\(orderDiscount!)" : nil),
URLQueryItem(name: "_ects", value: lastOrderTimestamp),
].filter { $0.value != nil }
]

let dimensionItems = dimensions.map { URLQueryItem(name: "dimension\($0.index)", value: $0.value) }
let customItems = customTrackingParameters.map { return URLQueryItem(name: $0.key, value: $0.value) }
let customVariableItems = customVariables.count > 0 ? [URLQueryItem(name: "_cvar", value: customVariableParameterValue())] : []
let ecommerceOrderItemsAndFlag = orderItems.count > 0 ? [URLQueryItem(name: "ec_items", value: orderItemParameterValue()), URLQueryItem(name: "idgoal", value: "0")] : []

return items + dimensionItems + customItems + customVariableItems + ecommerceOrderItemsAndFlag
return items + dimensionItems + ecommerceOrderItemsAndFlag + customVariableItems + customItems
}
}
}
Expand All @@ -122,25 +128,16 @@ fileprivate extension DateFormatter {
dateFormatter.dateFormat = "ss"
return dateFormatter
}()
static let iso8601DateFormatter: DateFormatterProtocol = {
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .iso8601)
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(identifier: "UTC")
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
return formatter
static let iso8601DateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .iso8601)
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(identifier: "UTC")
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
return formatter
}()
}

fileprivate protocol DateFormatterProtocol {
func string(from date: Date) -> String
func date(from string: String) -> Date?
}

@available(iOS 10, OSX 10.12, watchOS 3.0, tvOS 10.0, *)
extension ISO8601DateFormatter: DateFormatterProtocol {}
extension DateFormatter: DateFormatterProtocol {}

fileprivate extension CharacterSet {

/// Returns the character set for characters allowed in a query parameter URL component.
Expand Down
31 changes: 0 additions & 31 deletions MatomoTrackerTests/AutoTracker.swift

This file was deleted.

Loading

0 comments on commit c060569

Please sign in to comment.