-
Notifications
You must be signed in to change notification settings - Fork 19
Conversation
} | ||
|
||
fileprivate func initId() { | ||
if let uuid = UIDevice.current.identifierForVendor?.uuidString { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure if identifierForVendor
fits the purpose here.
According to this from apple:
The value of this property is the same for apps that come from the same vendor running on the same device.
I believe it will return the same value for the same developer. In another word, if I have 3 different apps installed on the same device, it will return the same value for all 3 apps. Not sure this is what you are looking for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I checked couple options:
- custom libraries on github
- advertisingidentifier
identifierForVendor
Was thinking that identifierForVendor will be the most appropriate option, but in fact I will go with aproach to generate this random string per installation.
I see that metrics and analytics for most of the cases are tied to user like here:
https://developers.google.com/analytics/devguides/collection/ios/v3/user-id
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, I think generating the device id and save it locally is enough for this use case here.
} | ||
|
||
func applicationDidBecomeActive(_: UIApplication) { | ||
metrics.foreground(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the method name is a bit confusing. In metrics it is called foreground
, but you are calling it from applicationDidBecomeActive
, while there is also a applicationWillEnterForeground
method here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is temporary and will be replaced with proper metrics implementation.
Our metrics can hook into application lifecycle and other specific events.
Going to rename method and remove background method as it's not used.
} | ||
|
||
fileprivate func setupUri() { | ||
if let uri = core.getConfiguration("metrics")?.config?.uri { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to https://github.com/aerogear/proposals/blob/master/sdks/Mobile-configuration.md we should use url
instead of uri
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We using old format for Android and IOS. New format has uri field on the top level.
Once cmd line tool will be changed and released after sprint we will adjust formats.
I'm going to create issue to get that aligned, but it will be separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually going to fix that here: https://issues.jboss.org/browse/AGIOS-648
|
||
fileprivate func initId() { | ||
let defaults = UserDefaults.standard | ||
if let versionId = defaults.string(forKey: "metrics-sdk-installation-id") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe use a constant for metrics-sdk-installation-id
?
AgsCore.logger.error("clientId! \(clientId)") | ||
} else { | ||
let uuid = UUID().uuidString | ||
clientId = uuid |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clientId
vs. installationId
, i think we should have consistent naming (will also use installationId on Android).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+100 - Going to refactor names here as they are not consistent.
let requestDictionary = try? payload.adaptToDictionary() | ||
core.getHttp().post(uri, body: requestDictionary, { (response, error) -> Void in | ||
if let error = error { | ||
AgsCore.logger.error("An error has occurred when sending app metrics! \(error)") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just my preference but i would remove the exclamation mark from the message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, but added a few comments.
modules/metrics/AgsMetrics.swift
Outdated
open func collectMetrics() { | ||
if config.metricsEnabled() { | ||
for metricsEngine: Collectable in metrics { | ||
// TODO: Use single URL payload vs metrics sending data to individual URLS. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@aliok @pb82 - What will be your take on this? Should we have single UDP/HTTP metrics endpoint or each metrics should be as separate endpoint?
From mobile point of view it may be better to just have single endpoint and collect metrics into single payload that is being sent when device is online. WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 on this. not just when offline though. even when the device is online, we should send stuff in batches into a single URL.
but I wouldn't worry about this right now. perhaps we can start a discussion with the wider group.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@wtrocki maybe it is better to talk about this once we have more metrics (custom or system). currently all we have is 2 metrics (sdk version and app version) that can be joined in a single payload. but we shouldn't forget this subject.
Retrieving Version in IOSAfter experimenting with multiple options like custom cocoapods plugins, plist appenders I decided to include version in core. Version will be changed by release script so there will be no additional overhead for developers to update that. |
} | ||
|
||
func collect() { | ||
// TODO: Store version in User Preferences and detect that was changed? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@aliok @pb82 WDYT? Technically this metrics will change only when user will update application so sending this metrics every time we launch app seems to be battery drainer, antipattern.
SDK version can be saved in preferences and we can detect when it was changed.
Additionally both IOS and Android have way to detect if application was installed.
However if we agree to have one single endpoint for all "OnStart" metrics then this optimization will not make sense as we could always send this information as metrics payload.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think sending SDK init events every time is very useful. Anybody could make use of this data.
Sending once would answer "how many clients use SDK 1.2.3?".
Sending every time would also answer "how many init events happened for SDK 1.2.3 in last N days?" which is basically the how actively an SDK version is used. This is IMO important because there can be 1 installation of SDK 1.0.0 with a very active user, but there can be 100 installations of SDK 0.0.1 with nobody actually using the app.
Also, probably sending one request when app starts won't drain too much battery IMO :)
If we do this, from backend point of view it doesn't change too much. We would just need to adjust the Grafana dashboard so that it doesn't show sdk init event time series data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@danielpassos Apart from couple unanswered questions I think we are in good shape to review this. |
@aliok Thanks for responding. Fully agree with you. To summarize: Going to push that to list as well. |
@@ -27,6 +29,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { | |||
} | |||
|
|||
func applicationDidBecomeActive(_: UIApplication) { | |||
metrics.collectMetrics() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MetricsContainer
don't have the collectMetrics
method. I think it should be AgsMetrics.collectMetrics
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Outdated. I have fixed that before.
import Foundation | ||
import XCTest | ||
|
||
class MetricsTests: XCTestCase { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this test testing? 😕
modules/core/AgsMetadata.swift
Outdated
// Metadata.swift | ||
// AGSCore | ||
// | ||
// Created by Wojciech Trocki on 2/7/18. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs copyright update
* Metrics publisher that logs metrics to console | ||
* Can be used when remote server is not available/supported | ||
*/ | ||
public class MetricsLoggerPublisher: MetricsPublisher { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like I said before I think we should not have more than one class/protocol/whatever in the same file, It means things easy to be found.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+100, Sorry I missed that one
import AGSCore | ||
import Foundation | ||
|
||
struct MetricsConfigData { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where are we using it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's wrapper to get url and future. I wanted to showcase common swift pattern here.
While this class has little code it's good showcase (metrics is first and it will be good to show some good patterns here like optional unwrapping. Initially I had much more code here but decided to simplify that.
modules/metrics/data/AppData.swift
Outdated
/** | ||
* Holds static application data that can be used for metrics | ||
*/ | ||
public class AppData { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just wondering if this kind of class should not be on core
to be used in other modules or in the app.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
/** | ||
* Protocol used for for classes that will colect metrics | ||
*/ | ||
public protocol Collectable { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this protocol are in processor
folder and the implementations are in the metrics
folder?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
metrics folder contains all metrics. "processor' is maybe bad name - I will rename that to engine
/** | ||
* Protocol used for for classes that will colect metrics | ||
*/ | ||
public protocol Collectable { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it should have metrics
in the name to be more explicit it's a Collectable for Metrics. Makes sense?
modules/metrics/AgsMetrics.swift
Outdated
private let core: AgsCore | ||
private let appData: AppData | ||
private let config: MetricsConfig | ||
private var publisher: MetricsPublisher! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about to have more than one MetricsPublisher
. I mean what if I wanna have Log and Network or whatever new I write?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Idea now is to use single one - strategy pattern. If there will be use case for two we could change that and iterate on publishers without hassle. Currently we support only app init event. Once more triggers will appear this functionality may change.
* Protocol used for mobile metrics management | ||
* Allows other SDK and implementations to manage (add) metrics | ||
*/ | ||
public protocol MetricsContainer { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need a protocol for this? Why not only a simple method in AgsMetrics
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was created in order to use it in different SDK in order to register metrics.
I'm not sure if this Idea will last, but I wanted to propose some solution for extending metrics without implementing them directly in the metrics SDK.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternative path will be to just get specific metrics injected on user app.
I think we could talk about couple options here with @pb82 as we will still continue work on this by adding security etc.
@wtrocki I know this is merged, but do you mind updating the PR description? Seems outdated and could be confusing in the future. |
Motivation
Initial version of Metrics Swift SDK
Progress