瀏覽代碼

FirebaseAnalytics SwiftUI API for `screen_view` (#7562)

* FirebaseAnalytics SwiftUI API for `screen_view`

This is a new FirebaseAnalyticsSwift SDK that adds native Swift
extensions for FirebaseAnalytics, starting with a SwiftUI screen view
function.

* Formatting

* CHANGELOG updates.

* whitespace removal

* Add podspec.

* Add missing wrapper files.

* Update copyright dates.

* Update default class name.

* Update versions and fix dependency name in podspec

* Update version specified in the CHANGELOG

* Add FirebaseAnalyticsSwift to GHA

* Temporarily enable Analytics cron test.

* Moving to the public version of Analytics

* Move to source based pod testing.

* Static framework
Ryan Wilson 5 年之前
父節點
當前提交
b54c6531dc

+ 3 - 0
.github/workflows/analytics.yml

@@ -4,6 +4,7 @@ on:
   pull_request:
     paths:
     - 'FirebaseAnalytics.podspec.json'
+    - 'FirebaseAnalyticsSwift**'
     - 'GoogleAppMeasurement.podspec.json'
     - 'Gemfile'
   schedule:
@@ -22,6 +23,8 @@ jobs:
       run: scripts/setup_bundler.sh
     - name: GoogleAppMeasurement
       run: scripts/third_party/travis/retry.sh pod spec lint GoogleAppMeasurement.podspec.json
+    - name: FirebaseAnalyticsSwift
+      run: scripts/third_party/travis/retry.sh scripts/pod_lib_lint.rb FirebaseAnalyticsSwift.podspec --platforms=ios
 
 # TODO: Consider pushing GoogleAppMeasurement.podspec.json to SpecsDev to enable similar test
 # for FirebaseAnalytics.podspec.json

+ 2 - 1
.github/workflows/spm.yml

@@ -29,7 +29,8 @@ jobs:
 
   cron-only:
     # Don't run on private repo.
-    if: github.event_name == 'schedule' && github.repository == 'Firebase/firebase-ios-sdk'
+    # REVERT ME:
+    #    if: github.event_name == 'schedule' && github.repository == 'Firebase/firebase-ios-sdk'
 
     runs-on: macOS-latest
     strategy:

+ 42 - 0
.swiftpm/xcode/xcshareddata/xcschemes/Firebase-Package.xcscheme

@@ -734,6 +734,48 @@
                ReferencedContainer = "container:">
             </BuildableReference>
          </BuildActionEntry>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "FirebaseAnalyticsSwift"
+               BuildableName = "FirebaseAnalyticsSwift"
+               BlueprintName = "FirebaseAnalyticsSwift"
+               ReferencedContainer = "container:">
+            </BuildableReference>
+         </BuildActionEntry>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "FirebaseAnalyticsSwiftTarget"
+               BuildableName = "FirebaseAnalyticsSwiftTarget"
+               BlueprintName = "FirebaseAnalyticsSwiftTarget"
+               ReferencedContainer = "container:">
+            </BuildableReference>
+         </BuildActionEntry>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "FirebaseAnalyticsSwift-Beta"
+               BuildableName = "FirebaseAnalyticsSwift-Beta"
+               BlueprintName = "FirebaseAnalyticsSwift-Beta"
+               ReferencedContainer = "container:">
+            </BuildableReference>
+         </BuildActionEntry>
       </BuildActionEntries>
    </BuildAction>
    <TestAction

+ 31 - 0
FirebaseAnalyticsSwift.podspec

@@ -0,0 +1,31 @@
+Pod::Spec.new do |s|
+  s.name                    = 'FirebaseAnalyticsSwift'
+  s.version                 = '7.9.0-beta'
+  s.summary                 = 'Swift Extensions for Firebase Analytics'
+
+  s.description      = <<-DESC
+Firebase Analytics is a free, out-of-the-box analytics solution that inspires actionable insights based on app usage and user engagement.
+                       DESC
+
+  s.homepage                = 'https://firebase.google.com/features/analytics/'
+  s.license                 = { :type => 'Apache', :file => 'LICENSE' }
+  s.authors                 = 'Google, Inc.'
+
+  s.source                  = {
+    :git => 'https://github.com/Firebase/firebase-ios-sdk.git',
+    :tag => 'CocoaPods-' + s.version.to_s
+  }
+
+  s.static_framework        = true
+  s.swift_version           = '5.0'
+  s.ios.deployment_target   = '13.0'
+
+  s.cocoapods_version       = '>= 1.10.0'
+  s.prefix_header_file      = false
+
+  s.source_files = [
+    'FirebaseAnalyticsSwift/Sources/*.swift',
+  ]
+
+  s.dependency 'FirebaseAnalytics', '~> 7.7'
+end

+ 7 - 0
FirebaseAnalyticsSwift/CHANGELOG.md

@@ -0,0 +1,7 @@
+# Unreleased
+- Initial public beta release. Introduces new SwiftUI friendly APIs for
+  screen tracking. To use, add `pod 'FirebaseAnalyticsSwift', '~> 7.9-beta'` to the Podfile or
+  add the `FirebaseAnalyticsSwift-Beta` framework in Swift Package Manager, then
+  and `import FirebaseAnalyticsSwift` to the source. Please provide feedback about
+  these new APIs and suggestions about other potential Swift extensions to
+  https://github.com/firebase/firebase-ios-sdk/issues.

+ 71 - 0
FirebaseAnalyticsSwift/Sources/Analytics+SwiftUI.swift

@@ -0,0 +1,71 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#if canImport(SwiftUI)
+  import FirebaseAnalytics
+  import SwiftUI
+
+  /// Custom view modifier to allow for easily logging screen view events.
+  @available(iOS 13, *)
+  @available(tvOS, unavailable)
+  @available(macOS, unavailable)
+  @available(macCatalyst, unavailable)
+  @available(watchOS, unavailable)
+  internal struct LoggedAnalyticsModifier: ViewModifier {
+    /// The name of the view to log in the `AnalyticsParameterScreenName` parameter.
+    let screenName: String
+
+    /// The name of the view to log in the `AnalyticsParameterScreenClass` parameter.
+    let screenClass: String
+
+    /// Extra parameters to log with the screen view event.
+    let extraParameters: [String: Any]
+
+    func body(content: Content) -> some View {
+      // Take the content and add an onAppear action to know when the view has appeared on screen.
+      content.onAppear {
+        // Log the event appearing, adding the appropriate keys and values needed for screen
+        // view events.
+        var parameters = extraParameters
+        parameters[AnalyticsParameterScreenName] = screenName
+        parameters[AnalyticsParameterScreenClass] = screenClass
+        Analytics.logEvent(AnalyticsEventScreenView, parameters: parameters)
+      }
+    }
+  }
+
+  @available(iOS 13, *)
+  @available(tvOS, unavailable)
+  @available(macOS, unavailable)
+  @available(macCatalyst, unavailable)
+  @available(watchOS, unavailable)
+  public extension View {
+    /// Logs `screen_view` events in Google Analytics for Firebase when this view appears on screen.
+    /// - Parameters:
+    ///   - name: Current screen name logged with the `screen_view` event.
+    ///   - class: Current screen class or struct logged with the `screen_view` event.
+    ///   - extraParameters: Any additional parameters to be logged. These extra parameters must
+    ///       follow the same rules as described in the `Analytics.logEvent(_:parameters:)` docs.
+    /// - Returns: A view with a custom `ViewModifier` used to log `screen_view` events when this
+    ///    view appears on screen.
+    func analyticsScreen(name: String,
+                         class: String = "View",
+                         extraParameters: [String: Any] = [:]) -> some View {
+      // `self` is the view, we're just adding an `LoggedAnalyticsModifier` modifier on it.
+      modifier(LoggedAnalyticsModifier(screenName: name,
+                                       screenClass: `class`,
+                                       extraParameters: extraParameters))
+    }
+  }
+#endif

+ 16 - 0
Package.swift

@@ -27,6 +27,10 @@ let package = Package(
       name: "FirebaseAnalytics",
       targets: ["FirebaseAnalyticsTarget"]
     ),
+    .library(
+      name: "FirebaseAnalyticsSwift-Beta",
+      targets: ["FirebaseAnalyticsSwiftTarget"]
+    ),
     .library(
       name: "FirebaseAuth",
       targets: ["FirebaseAuth"]
@@ -250,6 +254,17 @@ let package = Package(
       url: "https://dl.google.com/firebase/ios/swiftpm/7.8.0/FirebaseAnalytics.zip",
       checksum: "1a833b113a8d877e978c4b4b55ad5ae7c7f10b148dc37f1321be7bb7e7053f3a"
     ),
+    .target(
+      name: "FirebaseAnalyticsSwiftTarget",
+      dependencies: [.target(name: "FirebaseAnalyticsSwift",
+                             condition: .when(platforms: [.iOS]))],
+      path: "SwiftPM-PlatformExclude/FirebaseAnalyticsSwiftWrap"
+    ),
+    .target(
+      name: "FirebaseAnalyticsSwift",
+      dependencies: ["FirebaseAnalyticsWrapper"],
+      path: "FirebaseAnalyticsSwift/Sources"
+    ),
 
     .target(
       name: "FirebaseAppDistributionTarget",
@@ -774,6 +789,7 @@ let package = Package(
     .testTarget(
       name: "analytics-import-test",
       dependencies: [
+        "FirebaseAnalyticsSwiftTarget",
         "FirebaseAnalyticsWrapper",
         "Firebase",
       ],

+ 18 - 0
SwiftPM-PlatformExclude/FirebaseAnalyticsSwiftWrap/dummy.m

@@ -0,0 +1,18 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import <TargetConditionals.h>
+#if !TARGET_OS_IOS
+#warning "Firebase Analytics only supports the iOS platform"
+#endif

+ 15 - 0
SwiftPM-PlatformExclude/FirebaseAnalyticsSwiftWrap/include/dummy.h

@@ -0,0 +1,15 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Prevent a missing umbrella header warning.

+ 16 - 0
SwiftPMTests/analytics-import-test/analytics-import.swift

@@ -14,10 +14,26 @@
 
 import XCTest
 import FirebaseAnalytics
+#if canImport(SwiftUI)
+  import SwiftUI
+  import FirebaseAnalyticsSwift
+#endif
 
 class importTest: XCTestCase {
   func testAnalyticsImported() {
     Analytics.logEvent(AnalyticsEventEcommercePurchase,
                        parameters: [AnalyticsParameterShipping: 10.0])
   }
+
+  @available(iOS 13, *)
+  @available(tvOS, unavailable)
+  @available(macOS, unavailable)
+  @available(macCatalyst, unavailable)
+  @available(watchOS, unavailable)
+  func testAnalyticsSwiftImported() {
+    _ = Text("Hello, Analytics")
+      .analyticsScreen(name: "analytics_text",
+                       class: "Greeting",
+                       extraParameters: ["greeted": true])
+  }
 }