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/analytics #1721

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Mixin.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -5455,7 +5455,7 @@
attributes = {
BuildIndependentTargetsInParallel = YES;
LastSwiftUpdateCheck = 1420;
LastUpgradeCheck = 1600;
LastUpgradeCheck = 1620;
ORGANIZATIONNAME = Mixin;
TargetAttributes = {
277FF6FE1F909A1200DBB2EB = {
Expand Down
10 changes: 5 additions & 5 deletions Mixin/Service/CrashlyticalReporter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ class CrashlyticalReporter: Reporter {
}
}

override func report(event: Reporter.Event, userInfo: Reporter.UserInfo? = nil) {
super.report(event: event, userInfo: userInfo)
Analytics.logEvent(event.name, parameters: userInfo)
}

override func report(error: Error, userInfo: UserInfo? = nil) {
super.report(error: error, userInfo: userInfo)
Crashlytics.crashlytics().record(error: error)
}

override func report(event: Event, tags: [String: String]? = nil) {
super.report(event: event, tags: tags)
Analytics.logEvent(event.rawValue, parameters: tags)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -205,9 +205,6 @@ extension AudioInputViewController: OggOpusRecorderDelegate {
stopRedDotAnimation()
if reason != .userInitiated {
Logger.general.info(category: "AudioInput", message: "Recording is cancelled with \(reason.rawValue)")
var userInfo = userInfo ?? [:]
userInfo["reason"] = reason.rawValue
reporter.report(event: .cancelAudioRecording, userInfo: userInfo)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,6 @@ extension ConversationExtensionViewController: UICollectionViewDelegate {
} else {
let app = apps[indexPath.row - fixedExtensions.count].app
if let conversationId = composer?.conversationId, let parent = conversationViewController {
let userInfo = ["source": "ConversationExtension", "identityNumber": app.appNumber]
reporter.report(event: .openApp, userInfo: userInfo)
MixinWebViewController.presentInstance(with: .init(conversationId: conversationId, app: app), asChildOf: parent)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1331,8 +1331,6 @@ class ConversationViewController: UIViewController {
guard !conversationId.isEmpty else {
return
}
let userInfo = ["source": "Conversation", "identityNumber": app.appNumber]
reporter.report(event: .openApp, userInfo: userInfo)
MixinWebViewController.presentInstance(with: .init(conversationId: conversationId, app: app), asChildOf: self)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,6 @@ final class ConversationMessageComposer {
message.mediaUrl = sticker.assetUrl
message.stickerId = sticker.stickerId
queue.async {
reporter.report(event: .sendSticker, userInfo: ["stickerId": sticker.stickerId])
let transferData = TransferStickerData(stickerId: sticker.stickerId)
message.content = try! JSONEncoder().encode(transferData).base64EncodedString()
SendMessageService.shared.sendMessage(message: message,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ extension SwapTokenSelectorViewController: UICollectionViewDelegate {
case .recent:
let token = recentTokens[indexPath.item]
pickUp(token: token)
reporter.report(event: .swapCoinSwitch, method: "recent_click")
case .chainSelector:
if indexPath.item == 0 {
selectedChainID = nil
Expand All @@ -325,6 +326,15 @@ extension SwapTokenSelectorViewController: UICollectionViewDelegate {
self.reloadWithoutAnimation(section: .tokens)
self.reloadTokenSelection()
case .tokens:
if searchResults == nil {
if selectedChainID == nil {
reporter.report(event: .swapCoinSwitch, method: "all_item_click")
} else {
reporter.report(event: .swapCoinSwitch, method: "chain_item_click")
}
} else {
reporter.report(event: .swapCoinSwitch, method: "search_item_click")
}
let token = token(at: indexPath)
pickUp(token: token)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,6 @@ extension UserProfileViewController {
AppGroupUserDefaults.User.insertRecentlyUsedAppId(id: app.appId)
MixinWebViewController.presentInstance(with: .init(conversationId: conversationId, app: app), asChildOf: parent)
}
reporter.report(event: .openApp, userInfo: ["source": "UserWindow", "identityNumber": app.appNumber])
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ final class RestoreChatViewController: UIViewController, CheckSessionEnvironment
}

@IBAction func skipButton(_ sender: Any) {
reporter.report(event: .loginRestore, method: "skip")
Logger.general.info(category: "RestoreChat", message: "Restoration skipped")
AppGroupUserDefaults.Account.canRestoreFromPhone = false
AppGroupUserDefaults.Account.canRestoreMedia = false
Expand Down Expand Up @@ -59,13 +60,15 @@ extension RestoreChatViewController: UITableViewDelegate, UITableViewDataSource

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let vc: UIViewController
let next: UIViewController
if indexPath.section == 0 {
vc = RestoreFromPhoneViewController()
next = RestoreFromPhoneViewController()
reporter.report(event: .loginRestore, method: "another_phone")
} else {
vc = RestoreFromCloudViewController()
next = RestoreFromCloudViewController()
reporter.report(event: .loginRestore, method: "icloud")
}
navigationController?.pushViewController(vc, animated: true)
navigationController?.pushViewController(next, animated: true)
}

func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,6 @@ final class ExploreViewController: UIViewController {
AppGroupUserDefaults.User.insertRecentlyUsedAppId(id: app.appId)
MixinWebViewController.presentInstance(with: .init(conversationId: "", app: app), asChildOf: home)
}
reporter.report(event: .openApp, userInfo: ["source": "Explore", "identityNumber": app.appNumber])
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ final class CheckSessionEnvironmentViewController: UIViewController {
root = LegacyPINViewController()
} else if account.tipCounter == 0 {
if account.hasPIN {
reporter.report(event: .loginVerifyPIN, method: "upgrade_pin")
Logger.general.debug(category: "CheckSessionEnvironment", message: "Legacy PIN")
root = LegacyPINViewController()
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ final class LoginPINStatusCheckingViewController: LoginLoadingViewController {
let context = try await TIP.checkCounter(with: account)
await MainActor.run {
if let context {
reporter.report(event: .loginVerifyPIN, method: "change_pin")
let intro = TIPIntroViewController(context: context)
let navigation = TIPNavigationController(intro: intro)
AppDelegate.current.mainWindow.rootViewController = navigation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ final class LoginPINValidationViewController: FullscreenPINValidationViewControl
}
AppGroupUserDefaults.Wallet.lastPINVerifiedDate = Date()
AppGroupUserDefaults.User.loginPINValidated = true
reporter.report(event: .loginVerifyPIN, method: "verify_pin")
await MainActor.run {
AppDelegate.current.mainWindow.rootViewController = HomeContainerViewController()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,6 @@ extension LoginAccountHandler where Self: UIViewController {
AppGroupUserDefaults.User.loginPINValidated = false
AppGroupUserDefaults.Wallet.payWithBiometricAuthentication = false

if account.fullName.isEmpty {
reporter.report(event: .signUp)
} else if HomeViewController.showChangePhoneNumberTips {
reporter.report(event: .login, userInfo: ["source": "emergency"])
} else {
reporter.report(event: .login, userInfo: ["source": "normal"])
}

if let icloudDir = FileManager.default.url(forUbiquityContainerIdentifier: nil) {

func debugCloudFiles(baseDir: URL, parentDir: URL) -> [String] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ final class OnboardingViewController: UIViewController {
let mobileNumber = SignInWithMobileNumberViewController()
navigationController?.pushViewController(mobileNumber, animated: true)
Logger.general.info(category: "Login", message: "Sign in")
reporter.report(event: .loginStart)
}

}
10 changes: 7 additions & 3 deletions Mixin/UserInterface/Controllers/Login/SignUpViewController.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import UIKit
import MixinServices

final class SignUpViewController: UIViewController {

Expand Down Expand Up @@ -105,10 +106,13 @@ extension SignUpViewController: UITableViewDelegate {
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let controller = if indexPath.section == 0 {
SignUpWithMobileNumberViewController()
let controller: UIViewController
if indexPath.section == 0 {
controller = SignUpWithMobileNumberViewController()
reporter.report(event: .signUpStart, method: "mobile_number")
} else {
SignUpWithMnemonicIntroductionViewController()
controller = SignUpWithMnemonicIntroductionViewController()
reporter.report(event: .signUpStart, method: "mnemonic_phrase")
}
navigationController?.pushViewController(controller, animated: true)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ final class UsernameViewController: LoginInfoInputViewController, CheckSessionEn
showAutoHiddenHud(style: .error, text: error.localizedDescription)
}
}
reporter.report(event: .signUpFullname)
}

private func makeDefaultUsername() -> String? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,7 @@ extension MarketViewController: PillActionView.Delegate {
pickSingleToken { token in
let swap = MixinSwapViewController(sendAssetID: token.assetID, receiveAssetID: AssetID.erc20USDT)
self.navigationController?.pushViewController(swap, animated: true)
reporter.report(event: .swapStart, tags: ["source": "market"])
}
}
case .alert:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ final class MixinSwapViewController: SwapViewController {
sender.isBusy = false
}
}
reporter.report(event: .swapPreview)
}

override func prepareForReuse(sender: Any) {
Expand Down Expand Up @@ -302,6 +303,7 @@ extension MixinSwapViewController: SwapQuotePeriodicRequesterDelegate {
footerInfoProgressView.setProgress(1, animationDuration: nil)
reviewButton.isEnabled = quote.sendAmount > 0
&& quote.sendAmount <= quote.sendToken.decimalBalance
reporter.report(event: .swapQuote, tags: ["result": "success"])
case .failure(let error):
let description = switch error {
case let SwapQuotePeriodicRequester.ResponseError.invalidAmount(description):
Expand All @@ -317,6 +319,7 @@ extension MixinSwapViewController: SwapQuotePeriodicRequesterDelegate {
}
Logger.general.debug(category: "MixinSwap", message: description)
setFooter(.error(description))
reporter.report(event: .swapQuote, tags: ["result": "failure"])
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ class TransferPreviewViewController: AuthenticationPreviewViewController {
case .swap:
layoutTableHeaderView(title: R.string.localizable.sending_success(),
subtitle: R.string.localizable.swap_message_success())
reporter.report(event: .swapSend)
case .inscription(let context):
switch context.operation {
case .transfer:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,7 @@ extension TokenViewController: TokenActionView.Delegate {
case .swap:
let swap = MixinSwapViewController(sendAssetID: token.assetID, receiveAssetID: AssetID.erc20USDT)
navigationController?.pushViewController(swap, animated: true)
reporter.report(event: .swapStart, tags: ["source": "wallet"])
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ extension WalletViewController: TokenActionView.Delegate {
DispatchQueue.global().async {
PropertiesDAO.shared.set(true, forKey: .hasSwapReviewed)
}
reporter.report(event: .swapStart, tags: ["source": "wallet"])
}
}

Expand Down
3 changes: 1 addition & 2 deletions Mixin/UserInterface/Windows/UrlWindow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class UrlWindow {
if let navigationController = UIApplication.homeNavigationController {
let swap = MixinSwapViewController(sendAssetID: input, receiveAssetID: output ?? AssetID.erc20USDT)
navigationController.pushViewController(swap, animated: true)
reporter.report(event: .swapStart, tags: ["source": "schema"])
}
return true
case let .send(context):
Expand Down Expand Up @@ -202,8 +203,6 @@ class UrlWindow {
guard let parent = UIApplication.homeNavigationController?.visibleViewController else {
return
}
let userInfo = ["source": "UrlWindow", "identityNumber": app.appNumber]
reporter.report(event: .openApp, userInfo: userInfo)
let extraParams = params.filter { $0.key != "action" }
DispatchQueue.main.async {
MixinWebViewController.presentInstance(with: .init(conversationId: conversationId, app: app, extraParams: extraParams), asChildOf: parent)
Expand Down
20 changes: 13 additions & 7 deletions MixinServices/MixinServices/Database/Database.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,23 @@ open class Database {
let message: String
}
GRDB.Database.logError = { (code, message) in
if code.primaryResultCode == .SQLITE_ERROR {
switch code.primaryResultCode {
case .SQLITE_NOTICE:
// Ignore notices, mostly SQLITE_NOTICE_RECOVER_WAL
Logger.database.info(category: "DB", message: "code: \(code), message: \(message)\n")
case .SQLITE_ERROR:
if message.hasPrefix("no such table: grdb_migrations") {
return
Logger.database.info(category: "DB", message: "code: \(code), message: \(message)\n")
} else {
AppGroupUserDefaults.User.needsRebuildDatabase = true
fallthrough
}
default:
// Stupid error from CoreFoundation
if !message.hasSuffix("cfurl_cache_response.request_key") {
reporter.report(error: Error(code: code.rawValue, message: message))
Logger.database.error(category: "DB", message: "code: \(code), message: \(message)\n")
}
}
// Stupid error from CoreFoundation
if !message.hasSuffix("cfurl_cache_response.request_key") {
reporter.report(error: Error(code: code.rawValue, message: message))
Logger.database.error(category: "Error", message: "code: \(code), message: \(message)\n")
}
}
return {}
Expand Down
59 changes: 25 additions & 34 deletions MixinServices/MixinServices/Foundation/Reporter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,21 @@ import Sentry

open class Reporter {

public typealias UserInfo = [String: Any]

public enum Event {
case signUp
case login
case sendSticker
case openApp
case cancelAudioRecording

public var name: String {
switch self {
case .signUp:
return "sign_up"
case .login:
return "login"
case .sendSticker:
return "send_sticker"
case .openApp:
return "open_app"
case .cancelAudioRecording:
return "cancel_audio_record"
}
}
public enum Event: String {
case signUpStart = "sign_up_start"
case signUpFullname = "sign_up_fullname"
case signUpSetPIN = "sign_up_set_pin"
case loginStart = "login_start"
case loginRestore = "login_restore"
case loginVerifyPIN = "login_verify_pin"
case swapStart = "swap_start"
case swapCoinSwitch = "swap_coin_switch"
case swapQuote = "swap_quote"
case swapPreview = "swap_preview"
case swapSend = "swap_send"
}

public var basicUserInfo: UserInfo {
["last_update_or_install_date": AppGroupUserDefaults.User.lastUpdateOrInstallDate,
"client_time": DateFormatter.filename.string(from: Date())]
}
public typealias UserInfo = [String: Any]

public required init() {

Expand Down Expand Up @@ -64,16 +50,10 @@ open class Reporter {
SentrySDK.setUser(Sentry.User(userId: account.userID))
}

open func report(event: Event, userInfo: UserInfo? = nil) {
let event = Sentry.Event(level: .info)
event.extra = userInfo
SentrySDK.capture(event: event)
}

open func report(error: MixinAPIError) {
SentrySDK.capture(error: error)
}

open func report(error: Error, userInfo: UserInfo? = nil) {
if let info = userInfo {
let event = Sentry.Event(level: .error)
Expand All @@ -83,4 +63,15 @@ open class Reporter {
SentrySDK.capture(error: error)
}
}

open func report(event: Event, tags: [String: String]? = nil) {
let scope = Scope()
scope.setTags(tags)
SentrySDK.capture(message: event.rawValue, scope: scope)
}

public func report(event: Event, method: String) {
report(event: event, tags: ["method": method])
}

}
Loading