Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/180 custom visitor ID #181

Merged
6 changes: 3 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

## Unreleased
* **feature** Added ability to customize user agent. [#168](https://github.com/piwik/piwik-sdk-ios/pull/168)
* **feature** Added ability to customize User ID.
[#180](https://github.com/piwik/piwik-sdk-ios/issues/180)

## 4.1.0
* **feature** Added Custom Dimension Tracking for the Visit Scope. [#111](https://github.com/piwik/piwik-sdk-ios/issues/111)
* **feature** Added Custom Dimension Tracking for the Visit Scope. [#111](https://github.com/piwik/piwik-sdk-ios/issues/111)
* **feature** Transmitting the Screen resolution to the Piwik Backend. [#149](https://github.com/piwik/piwik-sdk-ios/issues/149) (by @akshaykolte)
* **feature** Added a Logger that can log messages in different levels. [#147](https://github.com/piwik/piwik-sdk-ios/issues/147)
* **feature** Added macOS and tvOS compatibility. [#134](https://github.com/piwik/piwik-sdk-ios/issues/134)
Expand All @@ -29,5 +31,3 @@
* **feature** Tracking Events
* **feature** URLSessionDispatcher
* **feature** Non persistent Event Queue


20 changes: 0 additions & 20 deletions Example/ios/iOS Example/UserIDViewController.h

This file was deleted.

61 changes: 0 additions & 61 deletions Example/ios/iOS Example/UserIDViewController.m

This file was deleted.

37 changes: 37 additions & 0 deletions Example/ios/iOS Example/UserIDViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import Foundation
import PiwikTracker

class UserIDViewController: UIViewController {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great! Thanks for adding this to the Example Application!

@IBOutlet weak var userIDTextField: UITextField!
@IBOutlet weak var signinButton: UIButton!
@IBOutlet weak var signoutButton: UIButton!

@IBAction func signinAction(_ sender: UIButton) {
if (self.userIDTextField.text != nil) && (self.userIDTextField.text?.characters.count)! > 0 {
PiwikTracker.shared?.visitorId = self.userIDTextField.text
toggleState()
}
}

@IBAction func signOutAction(_ sender: UIButton) {
PiwikTracker.shared?.visitorId = nil
toggleState()
}

override func viewDidLoad() {
toggleState()
}

private func toggleState() {
self.userIDTextField.text = PiwikTracker.shared?.visitorId

self.signinButton.isEnabled = !isVisitorIdValid()
self.signoutButton.isEnabled = !self.signinButton.isEnabled
}

private func isVisitorIdValid() -> Bool {
let currentVisitorId = PiwikTracker.shared?.visitorId

return (currentVisitorId != nil) && (currentVisitorId?.characters.count)! > 0
}
}
16 changes: 8 additions & 8 deletions Example/ios/iOS Example/en.lproj/MainStoryboard.storyboard
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16F73" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES" initialViewController="3">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES" initialViewController="3">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
Expand Down Expand Up @@ -41,7 +41,7 @@
<rect key="frame" x="0.0" y="55.5" width="375" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="q3h-dc-9VC" id="6QM-lt-Nvm">
<rect key="frame" x="0.0" y="0.0" width="342" height="43"/>
<rect key="frame" x="0.0" y="0.0" width="342" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="Screens" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="EYc-RM-MMH">
Expand Down Expand Up @@ -249,7 +249,7 @@
</tableViewSection>
<tableViewSection headerTitle="Other" id="6kC-bt-yx9">
<cells>
<tableViewCell hidden="YES" contentMode="scaleToFill" selectionStyle="blue" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="YlU-MW-xot" style="IBUITableViewCellStyleDefault" id="2Bz-NF-AGQ" userLabel="Table View Cell - User ID">
<tableViewCell contentMode="scaleToFill" selectionStyle="blue" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="YlU-MW-xot" style="IBUITableViewCellStyleDefault" id="2Bz-NF-AGQ" userLabel="Table View Cell - User ID">
<rect key="frame" x="0.0" y="639.5" width="375" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="2Bz-NF-AGQ" id="XPR-nN-VPk">
Expand Down Expand Up @@ -776,7 +776,7 @@ Log keywords used and the number of results found.

 Searches are shown in a
<!--User ID-->
<scene sceneID="YPh-mF-L4v">
<objects>
<viewController id="xqt-PP-0XP" customClass="UserIDViewController" sceneMemberID="viewController">
<viewController id="xqt-PP-0XP" customClass="UserIDViewController" customModule="ios" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="aTn-Vi-fRv"/>
<viewControllerLayoutGuide type="bottom" id="guM-u1-IVi"/>
Expand All @@ -798,7 +798,7 @@ Log keywords used and the number of results found.

 Searches are shown in a
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="signOutAction:" destination="xqt-PP-0XP" eventType="touchUpInside" id="6lm-KG-r0i"/>
<action selector="signOutAction:" destination="xqt-PP-0XP" eventType="touchUpInside" id="Z1X-Ow-HrW"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6ib-Au-0C4">
Expand All @@ -808,7 +808,7 @@ Log keywords used and the number of results found.

 Searches are shown in a
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="signinAction:" destination="xqt-PP-0XP" eventType="touchUpInside" id="pTe-3M-ClI"/>
<action selector="signinAction:" destination="xqt-PP-0XP" eventType="touchUpInside" id="lRU-iU-0Gc"/>
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="8" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Kad-JQ-T2F">
Expand All @@ -826,8 +826,8 @@ Store the id locally in the app and set it directly after the tracker is created
</view>
<navigationItem key="navigationItem" title="User ID" id="PU0-p5-r10"/>
<connections>
<outlet property="signinButton" destination="6ib-Au-0C4" id="eWd-9D-zEn"/>
<outlet property="singoutButton" destination="euf-jm-xQs" id="u0b-8K-nhz"/>
<outlet property="signinButton" destination="6ib-Au-0C4" id="Soo-zc-gOR"/>
<outlet property="signoutButton" destination="euf-jm-xQs" id="Fw5-NG-bwP"/>
<outlet property="userIDTextField" destination="sZf-dU-TqW" id="Tfv-vQ-HRe"/>
</connections>
</viewController>
Expand Down
8 changes: 4 additions & 4 deletions Example/ios/ios.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
1FDC91781F1A648C0046F506 /* Device.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FDC91721F1A648C0046F506 /* Device.swift */; };
1FDC91791F1A648C0046F506 /* ObjectiveCCompatibilityChecker.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FDC91741F1A648C0046F506 /* ObjectiveCCompatibilityChecker.m */; };
1FDC917A1F1A648C0046F506 /* OptOutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FDC91751F1A648C0046F506 /* OptOutViewController.swift */; };
24F6AE8F1F61FDE200C6C22C /* UserIDViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24F6AE8E1F61FDE200C6C22C /* UserIDViewController.swift */; };
CD93EC8C17E76E290062BE20 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD1EA93417B0DA4400F63E14 /* UIKit.framework */; };
CD93EC8D17E76E290062BE20 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD10FABF17B0378D0012BE50 /* Foundation.framework */; };
CD93EC8E17E76E290062BE20 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD1EA93D17B0DB7500F63E14 /* CoreGraphics.framework */; };
Expand Down Expand Up @@ -81,6 +82,7 @@
1FDC91731F1A648C0046F506 /* ObjectiveCCompatibilityChecker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjectiveCCompatibilityChecker.h; sourceTree = "<group>"; };
1FDC91741F1A648C0046F506 /* ObjectiveCCompatibilityChecker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ObjectiveCCompatibilityChecker.m; sourceTree = "<group>"; };
1FDC91751F1A648C0046F506 /* OptOutViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OptOutViewController.swift; sourceTree = "<group>"; };
24F6AE8E1F61FDE200C6C22C /* UserIDViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserIDViewController.swift; sourceTree = "<group>"; };
3AEBBD63B45D1F968424585B /* libPods-iosafnetworking2.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-iosafnetworking2.a"; sourceTree = BUILT_PRODUCTS_DIR; };
9BB58213275F072D13140292 /* libPods-ios.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ios.a"; sourceTree = BUILT_PRODUCTS_DIR; };
A512D280229592361DDA7E8D /* Pods-example-ios.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example-ios.release.xcconfig"; path = "../../Pods/Target Support Files/Pods-example-ios/Pods-example-ios.release.xcconfig"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -119,8 +121,6 @@
CD9AC5451801C6BB00EE8F43 /* SearchViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SearchViewController.m; sourceTree = "<group>"; };
CD9AC5471801CA6500EE8F43 /* GoalsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GoalsViewController.h; sourceTree = "<group>"; };
CD9AC5481801CA6500EE8F43 /* GoalsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GoalsViewController.m; sourceTree = "<group>"; };
CDA9500519F05D56004A2277 /* UserIDViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UserIDViewController.h; sourceTree = "<group>"; };
CDA9500619F05D56004A2277 /* UserIDViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UserIDViewController.m; sourceTree = "<group>"; };
CDD56BFC19F587A100816D5A /* ContentTrackerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContentTrackerViewController.h; sourceTree = "<group>"; };
CDD56BFD19F587A100816D5A /* ContentTrackerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ContentTrackerViewController.m; sourceTree = "<group>"; };
CDEAEAB6180B36F800EB91C2 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = Library/Frameworks/Cocoa.framework; sourceTree = DEVELOPER_DIR; };
Expand Down Expand Up @@ -205,6 +205,7 @@
1F03BC9C1E86CFC30002F0AD /* SecondScreenViewController.swift */,
1FDC91751F1A648C0046F506 /* OptOutViewController.swift */,
1FDC91711F1A648C0046F506 /* CustomDimensionsViewController.swift */,
24F6AE8E1F61FDE200C6C22C /* UserIDViewController.swift */,
CD5C9813193E65CF00A8A801 /* CampaignViewController.h */,
CD5C9814193E65CF00A8A801 /* CampaignViewController.m */,
CDD56BFC19F587A100816D5A /* ContentTrackerViewController.h */,
Expand All @@ -228,8 +229,6 @@
CD9AC5451801C6BB00EE8F43 /* SearchViewController.m */,
CD93ECB517E77BEB0062BE20 /* SocialViewController.h */,
CD93ECB617E77BEB0062BE20 /* SocialViewController.m */,
CDA9500519F05D56004A2277 /* UserIDViewController.h */,
CDA9500619F05D56004A2277 /* UserIDViewController.m */,
1FDC91731F1A648C0046F506 /* ObjectiveCCompatibilityChecker.h */,
1FDC91741F1A648C0046F506 /* ObjectiveCCompatibilityChecker.m */,
1F72DA6D1E61ECAF00EFF764 /* ios-Bridging-Header.h */,
Expand Down Expand Up @@ -413,6 +412,7 @@
1FDC91781F1A648C0046F506 /* Device.swift in Sources */,
1F72DA6F1E61ECAF00EFF764 /* AppDelegate.swift in Sources */,
1F03BC991E86CC7E0002F0AD /* EventsViewController.swift in Sources */,
24F6AE8F1F61FDE200C6C22C /* UserIDViewController.swift in Sources */,
1F03BC9B1E86CF770002F0AD /* ScreenViewController.swift in Sources */,
1F72DA711E62E55200EFF764 /* MenuViewController.swift in Sources */,
1F72DA731E62E78E00EFF764 /* ConfigurationViewController.swift in Sources */,
Expand Down
12 changes: 12 additions & 0 deletions PiwikTracker/PiwikTracker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@ final public class PiwikTracker: NSObject {
}
}

/// Will be used to associate all future events with a given userID. This property
/// is persisted between app launches.
public var visitorId: String? {
get {
return PiwikUserDefaults.standard.visitorId
}
set {
PiwikUserDefaults.standard.visitorId = newValue
visitor = Visitor.current()
}
}

private let dispatcher: Dispatcher
private var queue: Queue
internal let siteId: String
Expand Down
15 changes: 13 additions & 2 deletions PiwikTracker/PiwikUserDefaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,20 @@ internal struct PiwikUserDefaults {

var clientId: String? {
get {
return userDefaults.string(forKey: PiwikUserDefaults.Key.visitorID)
return userDefaults.string(forKey: PiwikUserDefaults.Key.clientID)
}
set {
userDefaults.setValue(newValue, forKey: PiwikUserDefaults.Key.visitorID)
userDefaults.setValue(newValue, forKey: PiwikUserDefaults.Key.clientID)
userDefaults.synchronize()
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you change it this way, it will change the behavior of all old implementations. For all old Implementations the clientId was saved under the key PiwikUserDefaults.Key.visitorID. After this change, all existing installations will use the old clientId as their new visitorId. I thinks that's an issue.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I've adjusted this in a9e9bf3. I left my changes for using the correct "key name", but changing what value the key is using so it's using the old one. This should retain the existing functionality but be more clear in the future.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think that's the best solution in this case. It's a bit of a pity that it got introduced in the first place. Thanks for changing it this way.


var visitorId: String? {
get {
return userDefaults.string(forKey: PiwikUserDefaults.Key.visitorID);
}
set {
userDefaults.setValue(newValue, forKey: PiwikUserDefaults.Key.visitorID);
userDefaults.synchronize()
}
}
Expand All @@ -75,6 +85,7 @@ extension PiwikUserDefaults {
static let currentVisitTimestamp = "PiwikCurrentVisitTimestampKey"
static let previousVistsTimestamp = "PiwikPreviousVistsTimestampKey"
static let firstVistsTimestamp = "PiwikFirstVistsTimestampKey"
static let clientID = "PiwikClientIDKey"
static let visitorID = "PiwikVisitorIDKey"
static let optOut = "PiwikOptOutKey"
}
Expand Down
2 changes: 1 addition & 1 deletion PiwikTracker/Visitor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ extension Visitor {
PiwikUserDefaults.standard.clientId = newVisitorID()
return current()
}
let userId: String? = nil // we can add the userid later
let userId = PiwikUserDefaults.standard.visitorId
return Visitor(id: id, userId: userId)
}

Expand Down
15 changes: 11 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ The PiwikTracker can track hierarchical screen names, e.g. screen/settings/regis
PiwikTracker.shared?.track(view: ["path","to","your","page"])
```

You can also set the url of the page.
You can also set the url of the page.
```
let url = URL(string: "https://piwik.org/get-involved/")
PiwikTracker.shared?.track(view: ["community","get-involved"], url: url)
Expand Down Expand Up @@ -83,6 +83,16 @@ PiwikTracker.shared?.remove(dimensionAtIndex: 1)

Dimensions in the Visit Scope will be sent along every Page View or Event. Custom Dimensions are not persisted by the SDK and have to be re-configured upon application startup.

### Custom User ID

To add a [custom User ID](https://piwik.org/docs/user-id/), simply set the value you'd like to use on the `visitorId` field of the shared tracker:

```
PiwikTracker.shared?.visitorId = "coolUsername123"
```

All future events being tracked by the SDK will be associated with this userID, as opposed to the default UUID created for each Visitor.

### Advanced

#### Manual dispatching
Expand Down Expand Up @@ -161,13 +171,10 @@ Please read [CONTRIBUTING.md](https://github.com/piwik/piwik-sdk-ios/blob/swift3
- Content Impressions / Content Interactions
- Customizing the tracker
- ~~Custom User Agent~~
- userID
- add prefixing? (The objc-SDK had a prefixing functionality ![Example screenshot](http://piwik.github.io/piwik-sdk-ios/piwik_prefixing.png))
- set the dispatch interval
- use different dispatchers (Alamofire)

## License

PiwikTracker is available under the [MIT license](LICENSE.md).