Merge pull request #2764 from omnivore-app/feat/ios-update-analytics

feat/ios update analytics
This commit is contained in:
Jackson Harper 2023-09-14 14:55:25 +08:00 committed by GitHub
commit 2e96fc0fb7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 111 additions and 75 deletions

View file

@ -9,15 +9,6 @@
"version" : "0.20220203.1"
}
},
{
"identity" : "analytics-swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/segmentio/analytics-swift.git",
"state" : {
"revision" : "92cc824211160ab98c28c7d40c1e6d27645c2bf1",
"version" : "1.2.3"
}
},
{
"identity" : "appauth-ios",
"kind" : "remoteSourceControl",
@ -153,6 +144,15 @@
"version" : "2.6.0"
}
},
{
"identity" : "posthog-ios",
"kind" : "remoteSourceControl",
"location" : "https://github.com/PostHog/posthog-ios.git",
"state" : {
"revision" : "3a6afc24d6bde730a19470d4e6b713f44d076ad9",
"version" : "2.0.3"
}
},
{
"identity" : "promises",
"kind" : "remoteSourceControl",
@ -171,15 +171,6 @@
"version" : "12.0.1"
}
},
{
"identity" : "sovran-swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/segmentio/Sovran-Swift.git",
"state" : {
"revision" : "944c17d7c46bd95fc37f09136cabd172be5b413b",
"version" : "1.0.3"
}
},
{
"identity" : "swift-argument-parser",
"kind" : "remoteSourceControl",

View file

@ -47,7 +47,7 @@ let package = Package(
.target(
name: "Utils",
dependencies: [
.product(name: "Segment", package: "analytics-swift")
.product(name: "PostHog", package: "posthog-ios")
],
resources: [.process("Resources")]
),
@ -67,10 +67,10 @@ var dependencies: [Package.Dependency] {
.package(url: "https://github.com/Square/Valet", from: "4.1.2"),
.package(url: "https://github.com/maticzav/swift-graphql", from: "2.3.1"),
.package(url: "https://github.com/siteline/SwiftUI-Introspect.git", from: "0.1.4"),
.package(url: "https://github.com/segmentio/analytics-swift.git", .upToNextMajor(from: "1.0.0")),
.package(url: "https://github.com/google/GoogleSignIn-iOS", from: "6.2.2"),
.package(url: "https://github.com/gonzalezreal/swift-markdown-ui", from: "2.0.0"),
.package(url: "https://github.com/exyte/PopupView.git", from: "2.6.0")
.package(url: "https://github.com/exyte/PopupView.git", from: "2.6.0"),
.package(url: "https://github.com/PostHog/posthog-ios.git", from: "2.0.0")
]
// Comment out following line for macOS build
deps.append(.package(url: "https://github.com/PSPDFKit/PSPDFKit-SP", from: "12.0.1"))

View file

@ -41,6 +41,7 @@ import Views
do {
try await dataService.deleteAccount(userID: currentViewer.unwrappedUserID)
authenticator.logout(dataService: dataService, isAccountDeletion: true)
EventTracker.reset()
} catch {
deleteAccountErrorMessage = "We were unable to delete your account."
}

View file

@ -49,22 +49,10 @@ struct InnerRootView: View {
@ViewBuilder private var innerBody: some View {
if authenticator.isLoggedIn {
// GeometryReader { geo in
PrimaryContentView()
// #if os(iOS)
// .miniPlayer()
// .formSheet(isPresented: $viewModel.showNewFeaturePrimer,
// modalSize: CGSize(width: geo.size.width * 0.66, height: geo.size.width * 0.66)) {
// FeaturePrimer.recommendationsPrimer
// }
// .onAppear {
// DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300)) {
// viewModel.showNewFeaturePrimer = viewModel.shouldShowNewFeaturePrimer
// viewModel.shouldShowNewFeaturePrimer = false
// }
// }
// #endif
// }
#if os(iOS)
.miniPlayer()
#endif
} else {
WelcomeView()
.accessibilityElement()

View file

@ -17,8 +17,6 @@ public final class RootViewModel: ObservableObject {
@Published public var showNewFeaturePrimer = false
@AppStorage(UserDefaultKey.shouldShowNewFeaturePrimer.rawValue) var shouldShowNewFeaturePrimer = false
@Published var showMiniPlayer = false
public init() {
registerFonts()

View file

@ -45,12 +45,14 @@ extension SlideAnimatedTransitioning: UIViewControllerAnimatedTransitioning {
fromVC.view?.layer.shadowOpacity = 0.1
}, completion: { _ in
toVC.view?.layer.opacity = 1.0
toVC.view?.layer.shadowOpacity = 0
fromVC.view?.layer.opacity = 1.0
fromVC.view?.layer.shadowOpacity = 0
if !transitionContext.transitionWasCancelled {
toVC.view?.layer.opacity = 1.0
toVC.view?.layer.shadowOpacity = 0
fromVC.view?.layer.opacity = 1.0
fromVC.view?.layer.shadowOpacity = 0
fromVC.view.removeFromSuperview()
fromVC.view.removeFromSuperview()
}
// when cancelling or completing the animation, ios simulator seems to sometimes flash black backgrounds during the animation. on devices, this doesn't seem to happen though.
// containerView.backgroundColor = [UIColor whiteColor];
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)

View file

@ -43,6 +43,7 @@ public final class Authenticator: ObservableObject {
Authenticator.unregisterIntercomUser?()
isLoggedIn = false
showAppleRevokeTokenAlert = isAccountDeletion
EventTracker.reset()
}
public func clearCreds() {

View file

@ -12,7 +12,7 @@ public extension DataService {
let selection = Selection<MutationResult, Unions.LeaveGroupResult> {
try $0.on(
leaveGroupError: .init { .error(errorMessage: try $0.errorCodes().first.toString()) },
leaveGroupError: .init { .error(errorMessage: try $0.errorCodes().first?.rawValue ?? "Unknown Error") },
leaveGroupSuccess: .init {
.saved(success: try $0.success())
}

View file

@ -12,7 +12,7 @@ public extension DataService {
let selection = Selection<MutationResult, Unions.RecommendResult> {
try $0.on(
recommendError: .init { .error(errorMessage: try $0.errorCodes().first.toString()) },
recommendError: .init { .error(errorMessage: try $0.errorCodes().first?.rawValue ?? "Unknown Error") },
recommendSuccess: .init {
.saved(success: try $0.success())
}

View file

@ -35,7 +35,7 @@ public extension DataService {
let selection = Selection<MutationResult, Unions.UpdatePageResult> {
try $0.on(
updatePageError: .init { .error(errorMessage: try $0.errorCodes().first.toString()) },
updatePageError: .init { .error(errorMessage: try $0.errorCodes().first?.rawValue ?? "Unknown Error") },
updatePageSuccess: .init {
.saved(title: try $0.updatedPage(selection: Selection.Article { try $0.title() }))
}

View file

@ -39,7 +39,7 @@ extension DataService {
let selection = Selection<MutationResult, Unions.UpdatePageResult> {
try $0.on(
updatePageError: .init { .error(errorMessage: try $0.errorCodes().first.toString()) },
updatePageError: .init { .error(errorMessage: try $0.errorCodes().first?.rawValue ?? "Unknown Error") },
updatePageSuccess: .init {
.saved(title: try $0.updatedPage(selection: Selection.Article { try $0.title() }))
}

View file

@ -1,11 +1,24 @@
import Foundation
import Segment
import PostHog
public enum EventTracker {
public static func start() {
// invoke the closure that creates the segment instance
_ = segment?.version()
}
public static var posthog: PHGPostHog? = {
guard let writeKey = AppKeys.sharedInstance?.posthogClientKey else {
return nil
}
guard let posthogInstanceAddress = AppKeys.sharedInstance?.posthogInstanceAddress else {
return nil
}
let configuration = PHGPostHogConfiguration(apiKey: writeKey, host: posthogInstanceAddress)
configuration.recordScreenViews = false
configuration.captureApplicationLifecycleEvents = true
PHGPostHog.setup(with: configuration)
return PHGPostHog.shared()
}()
public static func trackForDebugging(_ message: String) {
#if DEBUG
@ -14,29 +27,14 @@ public enum EventTracker {
}
public static func track(_ event: TrackableEvent) {
segment?.track(name: event.name, properties: event.properties)
posthog?.capture(event.name, properties: event.properties)
}
public static func registerUser(userID: String) {
segment?.identify(userId: userID)
posthog?.identify(userID)
}
public static func recordUserTraits(userID: String, traits: [String: String]) {
segment?.identify(userId: userID, traits: traits)
public static func reset() {
posthog?.reset()
}
}
private let segment: Analytics? = {
guard let writeKey = AppKeys.sharedInstance?.segmentClientKey else {
return nil
}
let config = Configuration(writeKey: writeKey)
.flushAt(20) // default is 20
.trackApplicationLifecycleEvents(true) // default is true
.autoAddSegmentDestination(true) // default is true
.flushInterval(30) // default is 30 seconds
.trackDeeplinks(true) // default is true
return Analytics(configuration: config)
}()

View file

@ -6,7 +6,8 @@ public struct AppKeys: Decodable {
public let firebaseDemoKeys: FirebaseKeys?
public let firebaseProdKeys: FirebaseKeys?
public let iosClientGoogleId: String?
public let segmentClientKey: String?
public let posthogClientKey: String?
public let posthogInstanceAddress: String?
public static let sharedInstance = AppKeys.make()
private init() {
@ -15,7 +16,8 @@ public struct AppKeys: Decodable {
self.firebaseDemoKeys = nil
self.firebaseProdKeys = nil
self.iosClientGoogleId = nil
self.segmentClientKey = nil
self.posthogClientKey = nil
self.posthogInstanceAddress = nil
}
}

View file

@ -42,8 +42,6 @@ private let logger = Logger(subsystem: "app.omnivore", category: "app-delegate")
}
#endif
EventTracker.start()
if let intercomKeys = AppKeys.sharedInstance?.intercom {
Intercom.setApiKey(intercomKeys.apiKey, forAppId: intercomKeys.appID)

View file

@ -14,6 +14,7 @@ import {
createWebAuthToken,
suggestedUsername,
} from './jwt_helpers'
import { analytics } from '../../utils/analytics'
const appleBaseURL = 'https://appleid.apple.com'
const audienceName = 'app.omnivore.app'
@ -145,6 +146,17 @@ export async function handleAppleWebAuth(
? ssoRedirectURL(ssoToken)
: `${baseURL()}/home`
analytics.track({
userId: user.id,
event: 'login',
properties: {
method: 'apple',
email: user.email,
username: user.profile.username,
env: env.server.apiEnv,
},
})
return {
authToken,
redirectURL,

View file

@ -51,6 +51,7 @@ import {
} from './google_auth'
import { createWebAuthToken } from './jwt_helpers'
import { createMobileAccountCreationResponse } from './mobile/account_creation'
import { analytics } from '../../utils/analytics'
export interface SignupRequest {
email: string
@ -323,6 +324,17 @@ export function authRouter() {
)
}
analytics.track({
userId: user.id,
event: 'login',
properties: {
method: 'google',
email: user.email,
username: user.profile.username,
env: env.server.apiEnv,
},
})
res.setHeader('set-cookie', result.headers['set-cookie'])
await handleSuccessfulLogin(req, res, user, data.googleLogin.newUser)
@ -447,6 +459,17 @@ export function authRouter() {
)
}
analytics.track({
userId: user.id,
event: 'login',
properties: {
method: 'email',
email: user.email,
username: user.profile.username,
env: env.server.apiEnv,
},
})
await handleSuccessfulLogin(req, res, user, false)
} catch (e) {
logger.info('email-login exception:', e)
@ -548,6 +571,17 @@ export function authRouter() {
}
}
analytics.track({
userId: user.id,
event: 'login',
properties: {
method: 'email_verification',
email: user.email,
username: user.profile.username,
env: env.server.apiEnv,
},
})
res.set('Message', 'EMAIL_CONFIRMED')
await handleSuccessfulLogin(req, res, user, false)
} catch (e) {
@ -664,6 +698,17 @@ export function authRouter() {
)
}
analytics.track({
userId: user.id,
event: 'login',
properties: {
method: 'password_reset',
email: user.email,
username: user.profile.username,
env: env.server.apiEnv,
},
})
await handleSuccessfulLogin(req, res, user, false)
} catch (e) {
logger.info('reset-password exception:', e)