diff --git a/WordPress/Classes/Utility/Analytics/WPAnalyticsEvent.swift b/WordPress/Classes/Utility/Analytics/WPAnalyticsEvent.swift index aae26e615bc7..a0760f4f9ace 100644 --- a/WordPress/Classes/Utility/Analytics/WPAnalyticsEvent.swift +++ b/WordPress/Classes/Utility/Analytics/WPAnalyticsEvent.swift @@ -509,6 +509,9 @@ import Foundation case freeToPaidPlansDashboardCardMenuTapped case freeToPaidPlansDashboardCardHidden + // Widgets + case widgetsLoadedOnApplicationOpened + /// A String that represents the event var value: String { switch self { @@ -1388,6 +1391,10 @@ import Foundation case .freeToPaidPlansDashboardCardMenuTapped: return "free_to_paid_plan_dashboard_card_menu_tapped" + // Widgets + case .widgetsLoadedOnApplicationOpened: + return "widgets_loaded_on_application_opened" + } // END OF SWITCH } diff --git a/WordPress/Classes/Utility/Analytics/WPAppAnalytics.m b/WordPress/Classes/Utility/Analytics/WPAppAnalytics.m index 47db3af7b578..b8ff12e99628 100644 --- a/WordPress/Classes/Utility/Analytics/WPAppAnalytics.m +++ b/WordPress/Classes/Utility/Analytics/WPAppAnalytics.m @@ -145,6 +145,7 @@ - (void)applicationDidBecomeActive:(NSNotification*)notification [self incrementSessionCount]; [self trackApplicationOpened]; [SearchAdsAttribution.instance requestDetails]; + [WidgetAnalytics trackLoadedWidgetsOnApplicationOpened]; } - (void)applicationDidEnterBackground:(NSNotification*)notification diff --git a/WordPress/JetpackStatsWidgets/LockScreenWidgets/LockScreenStatsWidget.swift b/WordPress/JetpackStatsWidgets/LockScreenWidgets/LockScreenStatsWidget.swift index 6eac12ea4154..952c6dee9b92 100644 --- a/WordPress/JetpackStatsWidgets/LockScreenWidgets/LockScreenStatsWidget.swift +++ b/WordPress/JetpackStatsWidgets/LockScreenWidgets/LockScreenStatsWidget.swift @@ -24,10 +24,6 @@ struct LockScreenStatsWidget: Widget { placeholderContent: config.placeholderContent ) ) { (entry: LockScreenStatsWidgetEntry) -> LockScreenStatsWidgetsView in - defer { - tracks.trackWidgetUpdatedIfNeeded(entry: entry, - widgetKind: config.kind) - } return LockScreenStatsWidgetsView( timelineEntry: entry, viewProvider: config.viewProvider diff --git a/WordPress/JetpackStatsWidgets/Tracks/Tracks+StatsWidgets.swift b/WordPress/JetpackStatsWidgets/Tracks/Tracks+StatsWidgets.swift deleted file mode 100644 index f1f6261f5e46..000000000000 --- a/WordPress/JetpackStatsWidgets/Tracks/Tracks+StatsWidgets.swift +++ /dev/null @@ -1,120 +0,0 @@ -import Foundation -import WidgetKit - -/// This extension implements helper tracking methods meant for Home & Lock screen widgets. -/// -extension Tracks { - - func trackWidgetUpdatedIfNeeded(entry: LockScreenStatsWidgetEntry, widgetKind: AppConfiguration.Widget.Stats.Kind) { - switch entry { - case .siteSelected(_, let context): - if !context.isPreview { - trackWidgetUpdated(widgetKind: widgetKind) - } - - case .loggedOut, .noSite, .noData: - trackWidgetUpdated(widgetKind: widgetKind) - } - } - - func trackWidgetUpdatedIfNeeded(entry: StatsWidgetEntry, widgetKind: AppConfiguration.Widget.Stats.Kind) { - switch entry { - case .siteSelected(_, let context): - if !context.isPreview { - trackWidgetUpdated(widgetKind: widgetKind) - } - - case .loggedOut, .noSite, .noData, .disabled: - trackWidgetUpdated(widgetKind: widgetKind) - } - } - - func trackWidgetUpdated(widgetKind: AppConfiguration.Widget.Stats.Kind) { - - DispatchQueue.global().async { - WidgetCenter.shared.getCurrentConfigurations { result in - - switch result { - - case .success(let widgetInfo): - let widgetKindInfo = widgetInfo.filter { $0.kind == widgetKind.rawValue } - self.trackUpdatedWidgetInfo(widgetInfo: widgetKindInfo, widgetKind: widgetKind) - - case .failure(let error): - DDLogError("Home Widget Today error: unable to read widget information. \(error.localizedDescription)") - } - } - } - } - - private func trackUpdatedWidgetInfo(widgetInfo: [WidgetInfo], widgetKind: AppConfiguration.Widget.Stats.Kind) { - let widgetPropertiesKey = widgetKind.countKey - - var properties: [String: Int] = [:] - - switch widgetKind { - case .homeToday, .homeThisWeek, .homeAllTime: - properties = ["total_widgets": widgetInfo.count, - "small_widgets": widgetInfo.filter { $0.family == .systemSmall }.count, - "medium_widgets": widgetInfo.filter { $0.family == .systemMedium }.count, - "large_widgets": widgetInfo.filter { $0.family == .systemLarge }.count] - default: - break - } - - let previousProperties = UserDefaults(suiteName: WPAppGroupName)?.object(forKey: widgetPropertiesKey) as? [String: Int] - - guard previousProperties != properties else { - return - } - - UserDefaults(suiteName: WPAppGroupName)?.set(properties, forKey: widgetPropertiesKey) - - trackExtensionEvent(ExtensionEvents.widgetUpdated(for: widgetKind), properties: properties as [String: AnyObject]?) - } - - // MARK: - Private Helpers - - fileprivate func trackExtensionEvent(_ event: ExtensionEvents, properties: [String: AnyObject]? = nil) { - track(event.rawValue, properties: properties) - } - - - // MARK: - Private Enums - - fileprivate enum ExtensionEvents: String { - // Events when user installs an instance of the widget - case homeTodayWidgetUpdated = "today_home_extension_widget_updated" - case homeAllTimeWidgetUpdated = "alltime_home_extension_widget_updated" - case homeThisWeekWidgetUpdated = "thisweek_home_extension_widget_updated" - case lockScreenTodayViewsWidgetUpdated = "today_views_lockscreen_widget_updated" - case lockScreenTodayLikesCommentsWidgetUpdated = "today_likes_comments_lockscreen_widget_updated" - case lockScreenTodayViewsVisitorsWidgetUpdated = "today_views_visitors_lockscreen_widget_updated" - case lockScreenAllTimeViewsWidgetUpdated = "all_time_views_lockscreen_widget_updated" - case lockScreenAllTimeViewsVisitorsWidgetUpdated = "all_time_views_visitors_lockscreen_widget_updated" - case lockScreenAllTimePostsBestViewsWidgetUpdated = "all_time_posts_best_views_lockscreen_widget_updated" - - static func widgetUpdated(for widgetKind: AppConfiguration.Widget.Stats.Kind) -> ExtensionEvents { - switch widgetKind { - case .homeToday: - return .homeTodayWidgetUpdated - case .homeAllTime: - return .homeAllTimeWidgetUpdated - case .homeThisWeek: - return .homeThisWeekWidgetUpdated - case .lockScreenTodayViews: - return .lockScreenTodayViewsWidgetUpdated - case .lockScreenTodayLikesComments: - return .lockScreenTodayLikesCommentsWidgetUpdated - case .lockScreenTodayViewsVisitors: - return .lockScreenTodayViewsVisitorsWidgetUpdated - case .lockScreenAllTimeViews: - return .lockScreenAllTimeViewsWidgetUpdated - case .lockScreenAllTimeViewsVisitors: - return .lockScreenAllTimeViewsVisitorsWidgetUpdated - case .lockScreenAllTimePostsBestViews: - return .lockScreenAllTimePostsBestViewsWidgetUpdated - } - } - } -} diff --git a/WordPress/JetpackStatsWidgets/Tracks/WidgetAnalytics.swift b/WordPress/JetpackStatsWidgets/Tracks/WidgetAnalytics.swift new file mode 100644 index 000000000000..11891f3e0c1b --- /dev/null +++ b/WordPress/JetpackStatsWidgets/Tracks/WidgetAnalytics.swift @@ -0,0 +1,68 @@ +import Foundation +import WidgetKit + +@objcMembers class WidgetAnalytics: NSObject { + static func trackLoadedWidgetsOnApplicationOpened() { + guard AppConfiguration.isJetpack else { return } + + WidgetCenter.shared.getCurrentConfigurations { result in + let properties = self.properties(from: result) + WPAnalytics.track(.widgetsLoadedOnApplicationOpened, properties: properties) + } + } + + private static func properties(from widgetInfo: Result<[WidgetInfo], Error>) -> [String: String] { + guard let installedWidgets = try? widgetInfo.get() else { + return [:] + } + + let widgetAnalyticNames: [String] = installedWidgets.map { widgetInfo in + guard let eventKind = AppConfiguration.Widget.Stats.Kind(rawValue: widgetInfo.kind) else { + DDLogWarn("⚠️ Make sure the widget: \(widgetInfo.kind), has the correct kind.") + return "\(widgetInfo.kind)_\(widgetInfo.family)" + } + return "\(Events.eventPrefix(for: eventKind).rawValue)_\(widgetInfo.family.description.lowercased())" + } + + let dict = Dictionary(uniqueKeysWithValues: widgetAnalyticNames.map { name in + return (name, "true") + }) + + return dict + } + + private enum Events: String { + case homeTodayWidget = "widget_today_home" + case homeAllTimeWidget = "widget_all_time_home" + case homeThisWeekWidget = "widget_this_week_home" + case lockScreenTodayViewsWidget = "widget_today_views_lockscreen" + case lockScreenTodayLikesCommentsWidget = "widget_today_likes_comments_lockscreen" + case lockScreenTodayViewsVisitorsWidget = "widget_today_views_visitors_lockscreen" + case lockScreenAllTimeViewsWidget = "widget_all_time_views_lockscreen" + case lockScreenAllTimeViewsVisitorsWidget = "widget_all_time_views_visitors_lockscreen" + case lockScreenAllTimePostsBestViewsWidget = "widget_all_time_posts_best_views_lockscreen" + + static func eventPrefix(for widgetKind: AppConfiguration.Widget.Stats.Kind) -> Events { + switch widgetKind { + case .homeToday: + return .homeTodayWidget + case .homeAllTime: + return .homeAllTimeWidget + case .homeThisWeek: + return .homeThisWeekWidget + case .lockScreenTodayViews: + return .lockScreenTodayViewsWidget + case .lockScreenTodayLikesComments: + return .lockScreenTodayLikesCommentsWidget + case .lockScreenTodayViewsVisitors: + return .lockScreenTodayViewsVisitorsWidget + case .lockScreenAllTimeViews: + return .lockScreenAllTimeViewsWidget + case .lockScreenAllTimeViewsVisitors: + return .lockScreenAllTimeViewsVisitorsWidget + case .lockScreenAllTimePostsBestViews: + return .lockScreenAllTimePostsBestViewsWidget + } + } + } +} diff --git a/WordPress/JetpackStatsWidgets/Widgets/HomeWidgetAllTime.swift b/WordPress/JetpackStatsWidgets/Widgets/HomeWidgetAllTime.swift index 5d6d5c071f0a..6c0fdd588372 100644 --- a/WordPress/JetpackStatsWidgets/Widgets/HomeWidgetAllTime.swift +++ b/WordPress/JetpackStatsWidgets/Widgets/HomeWidgetAllTime.swift @@ -22,12 +22,6 @@ struct HomeWidgetAllTime: Widget { placeholderContent: placeholderContent, widgetKind: .allTime) ) { (entry: StatsWidgetEntry) -> StatsWidgetsView in - - defer { - tracks.trackWidgetUpdatedIfNeeded(entry: entry, - widgetKind: AppConfiguration.Widget.Stats.Kind.homeAllTime) - } - return StatsWidgetsView(timelineEntry: entry) } .configurationDisplayName(LocalizableStrings.allTimeWidgetTitle) diff --git a/WordPress/JetpackStatsWidgets/Widgets/HomeWidgetThisWeek.swift b/WordPress/JetpackStatsWidgets/Widgets/HomeWidgetThisWeek.swift index 9d419523fefc..38c2cb596544 100644 --- a/WordPress/JetpackStatsWidgets/Widgets/HomeWidgetThisWeek.swift +++ b/WordPress/JetpackStatsWidgets/Widgets/HomeWidgetThisWeek.swift @@ -42,12 +42,6 @@ struct HomeWidgetThisWeek: Widget { placeholderContent: placeholderContent, widgetKind: .thisWeek) ) { (entry: StatsWidgetEntry) -> StatsWidgetsView in - - defer { - tracks.trackWidgetUpdatedIfNeeded(entry: entry, - widgetKind: AppConfiguration.Widget.Stats.Kind.homeThisWeek) - } - return StatsWidgetsView(timelineEntry: entry) } .configurationDisplayName(LocalizableStrings.thisWeekWidgetTitle) diff --git a/WordPress/JetpackStatsWidgets/Widgets/HomeWidgetToday.swift b/WordPress/JetpackStatsWidgets/Widgets/HomeWidgetToday.swift index e4637d5aee09..be7dfb66ad36 100644 --- a/WordPress/JetpackStatsWidgets/Widgets/HomeWidgetToday.swift +++ b/WordPress/JetpackStatsWidgets/Widgets/HomeWidgetToday.swift @@ -23,12 +23,6 @@ struct HomeWidgetToday: Widget { placeholderContent: placeholderContent, widgetKind: .today) ) { (entry: StatsWidgetEntry) -> StatsWidgetsView in - - defer { - tracks.trackWidgetUpdatedIfNeeded(entry: entry, - widgetKind: AppConfiguration.Widget.Stats.Kind.homeToday) - } - return StatsWidgetsView(timelineEntry: entry) } .configurationDisplayName(LocalizableStrings.todayWidgetTitle) diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index 940fd0ff0774..a403bd18bd18 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -87,7 +87,6 @@ 0107E0D028F97D5000DE87DB /* HomeWidgetThisWeek.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F8B138E25D09AA5004FAC0A /* HomeWidgetThisWeek.swift */; }; 0107E0D128F97D5000DE87DB /* SingleStatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F5689EF254209790048A9E4 /* SingleStatView.swift */; }; 0107E0D228F97D5000DE87DB /* UnconfiguredView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FAA18CB25797B85002B1911 /* UnconfiguredView.swift */; }; - 0107E0D328F97D5000DE87DB /* Tracks+StatsWidgets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98390AC2254C984700868F0A /* Tracks+StatsWidgets.swift */; }; 0107E0D428F97D5000DE87DB /* HomeWidgetData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F6DA04025646F96002AB88F /* HomeWidgetData.swift */; }; 0107E0D528F97D5000DE87DB /* HomeWidgetTodayData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FB34ACA25672A90001A74A6 /* HomeWidgetTodayData.swift */; }; 0107E0D628F97D5000DE87DB /* AllTimeWidgetStats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98BFF57D23984344008A1DCB /* AllTimeWidgetStats.swift */; }; @@ -195,6 +194,8 @@ 019D69A12A5EBF47003B676D /* WordPressAuthenticatorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 019D699F2A5EBF47003B676D /* WordPressAuthenticatorProtocol.swift */; }; 01A8508B2A8A126400BD8A97 /* support_chat_widget.css in Resources */ = {isa = PBXBuildFile; fileRef = 01A8508A2A8A126400BD8A97 /* support_chat_widget.css */; }; 01A8508C2A8A126400BD8A97 /* support_chat_widget.css in Resources */ = {isa = PBXBuildFile; fileRef = 01A8508A2A8A126400BD8A97 /* support_chat_widget.css */; }; + 01ABF1702AD578B3004331BD /* WidgetAnalytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01ABF16F2AD578B3004331BD /* WidgetAnalytics.swift */; }; + 01ABF1712AD578B3004331BD /* WidgetAnalytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01ABF16F2AD578B3004331BD /* WidgetAnalytics.swift */; }; 01CE5007290A889F00A9C2E0 /* TracksConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01CE5006290A889F00A9C2E0 /* TracksConfiguration.swift */; }; 01CE5008290A88BD00A9C2E0 /* TracksConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01CE5006290A889F00A9C2E0 /* TracksConfiguration.swift */; }; 01CE500E290A88C100A9C2E0 /* TracksConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01CE5006290A889F00A9C2E0 /* TracksConfiguration.swift */; }; @@ -5864,6 +5865,7 @@ 019D699D2A5EA963003B676D /* RootViewCoordinatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootViewCoordinatorTests.swift; sourceTree = ""; }; 019D699F2A5EBF47003B676D /* WordPressAuthenticatorProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WordPressAuthenticatorProtocol.swift; sourceTree = ""; }; 01A8508A2A8A126400BD8A97 /* support_chat_widget.css */ = {isa = PBXFileReference; lastKnownFileType = text.css; path = support_chat_widget.css; sourceTree = ""; }; + 01ABF16F2AD578B3004331BD /* WidgetAnalytics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetAnalytics.swift; sourceTree = ""; }; 01CE5006290A889F00A9C2E0 /* TracksConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TracksConfiguration.swift; sourceTree = ""; }; 01CE5010290A890300A9C2E0 /* TracksConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TracksConfiguration.swift; sourceTree = ""; }; 01D2FF5D2AA733690038E040 /* LockScreenFieldView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockScreenFieldView.swift; sourceTree = ""; }; @@ -7765,7 +7767,6 @@ 983002A722FA05D600F03DBB /* InsightsManagementViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsightsManagementViewController.swift; sourceTree = ""; }; 9833A29B257AE7CF006B8234 /* WordPress 105.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "WordPress 105.xcdatamodel"; sourceTree = ""; }; 9835F16D25E492EE002EFF23 /* CommentsList.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = CommentsList.storyboard; sourceTree = ""; }; - 98390AC2254C984700868F0A /* Tracks+StatsWidgets.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Tracks+StatsWidgets.swift"; sourceTree = ""; }; 983DBBA822125DD300753988 /* StatsTableFooter.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = StatsTableFooter.xib; sourceTree = ""; }; 983DBBA922125DD300753988 /* StatsTableFooter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatsTableFooter.swift; sourceTree = ""; }; 98458CB721A39D350025D232 /* StatsNoDataRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsNoDataRow.swift; sourceTree = ""; }; @@ -11668,7 +11669,7 @@ 3FA59DCC2582E53F0073772F /* Tracks */ = { isa = PBXGroup; children = ( - 98390AC2254C984700868F0A /* Tracks+StatsWidgets.swift */, + 01ABF16F2AD578B3004331BD /* WidgetAnalytics.swift */, ); path = Tracks; sourceTree = ""; @@ -20869,7 +20870,6 @@ 0188FE4C2AA62F800093EDA5 /* LockScreenTodayLikesCommentsStatWidgetConfig.swift in Sources */, 0107E0D228F97D5000DE87DB /* UnconfiguredView.swift in Sources */, 0107E1852900059300DE87DB /* LocalizationConfiguration.swift in Sources */, - 0107E0D328F97D5000DE87DB /* Tracks+StatsWidgets.swift in Sources */, 0107E0D428F97D5000DE87DB /* HomeWidgetData.swift in Sources */, 01D2FF652AA77F790038E040 /* LockScreenTodayViewsVisitorsStatWidgetConfig.swift in Sources */, 0107E0D528F97D5000DE87DB /* HomeWidgetTodayData.swift in Sources */, @@ -21813,6 +21813,7 @@ 570265152298921800F2214C /* PostListTableViewHandler.swift in Sources */, B5969E2220A49E86005E9DF1 /* UIAlertController+Helpers.swift in Sources */, 9A4E61F821A2C3BC0017A925 /* RevisionDiff+CoreData.swift in Sources */, + 01ABF1702AD578B3004331BD /* WidgetAnalytics.swift in Sources */, 7E7947AB210BAC5E005BB851 /* NotificationCommentRange.swift in Sources */, FE25C235271F23000084E1DB /* ReaderCommentsNotificationSheetViewController.swift in Sources */, C32A6A2C2832BF02002E9394 /* SiteDesignCategoryThumbnailSize.swift in Sources */, @@ -24408,6 +24409,7 @@ FABB22CB2602FC2C00C8785C /* FilterBarView.swift in Sources */, 98B88453261E4E09007ED7F8 /* LikeUserTableViewCell.swift in Sources */, FABB22CC2602FC2C00C8785C /* BlogToBlogMigration_61_62.swift in Sources */, + 01ABF1712AD578B3004331BD /* WidgetAnalytics.swift in Sources */, FABB22CD2602FC2C00C8785C /* WebKitViewController.swift in Sources */, FABB22CE2602FC2C00C8785C /* AnnouncementsDataSource.swift in Sources */, FABB22CF2602FC2C00C8785C /* AssembledSiteView.swift in Sources */,