Bladeren bron

[Messaging] Standalone Watch App Sample (#10552)

* [Messaging] Standalone Watch App Sample

* Add comment on disabling method swizzling

* Add dividers

* Rename to notificationCenter

* Add README with instructions

* Fix whitespace

* #no-changelog, build regression test

* fixes to yml and scripts for sample

* Add podfile

* podfile fixes

* podfile fixes

* Make project format xcode 13 compatible

* fix scheme in build.sh

* fix reference to service info file

* fix assets compile failure

* fix podfile linkage

Co-authored-by: Peter Friese <peter@peterfriese.de>
Aashish 3 jaren geleden
bovenliggende
commit
67ae9555da

+ 24 - 0
.github/workflows/messaging.yml

@@ -229,3 +229,27 @@ jobs:
       run: scripts/install_prereqs.sh SwiftUISample iOS
     - name: Build
       run: ([ -z $plist_secret ] || scripts/build.sh SwiftUISample iOS)
+
+  messaging-watchos-standalone-sample-build-test:
+    # Don't run on private repo unless it is a PR.
+    if: (github.repository == 'Firebase/firebase-ios-sdk' && github.event_name == 'schedule') || github.event_name == 'pull_request'
+    env:
+      plist_secret: ${{ secrets.GHASecretsGPGPassphrase1 }}
+    runs-on: macos-12
+    steps:
+    - uses: actions/checkout@v3
+    - uses: mikehardy/buildcache-action@c87cea0ccd718971d6cc39e672c4f26815b6c126
+      with:
+        cache_key: ${{ matrix.os }}
+    - uses: ruby/setup-ruby@v1
+    - name: Setup Bundler
+      run: scripts/setup_bundler.sh
+    - name: Install Secret GoogleService-Info.plist
+      run: |
+        scripts/decrypt_gha_secret.sh scripts/gha-encrypted/messaging-sample-plist.gpg \
+          FirebaseMessaging/Apps/Shared/GoogleService-Info.plist "$plist_secret"
+    - name: Prereqs
+      run: scripts/install_prereqs.sh MessagingSampleStandaloneWatchApp watchOS
+    - name: Build
+      run: ([ -z $plist_secret ] || scripts/build.sh MessagingSampleStandaloneWatchApp watchOS)
+

+ 1 - 0
.gitignore

@@ -26,6 +26,7 @@ FirebaseMessaging/Tests/IntegrationTests/Resources/GoogleService-Info.plist
 FirebaseMessaging/Apps/Shared/GoogleService-Info.plist
 FirebaseMessaging/Apps/AdvancedSample/SampleWatchWatchKitExtension/GoogleService-Info.plist
 FirebaseMessaging/Apps/AdvancedSample/AppClips/GoogleService-Info.plist
+FirebaseMessaging/Apps/SampleStandaloneWatchApp/SampleStandaloneWatchAppWatchApp/GoogleService-Info.plist
 
 # Credentials for Firebase Storage Integration Tests
 FirebaseStorage/Tests/ObjCIntegration/Credentials.h

+ 15 - 0
FirebaseMessaging/Apps/SampleStandaloneWatchApp/Podfile

@@ -0,0 +1,15 @@
+use_frameworks!
+
+source 'https://github.com/firebase/SpecsDev.git'
+source 'https://github.com/firebase/SpecsStaging.git'
+source 'https://cdn.cocoapods.org/'
+
+target 'SampleStandaloneWatchApp Watch App' do
+  platform :watchos, '9.0'
+
+  pod 'FirebaseCore', :path => '../../../'
+  pod 'FirebaseMessaging', :path => '../../../'
+  pod 'FirebaseCoreInternal', :path => '../../../'
+  pod 'FirebaseInstallations', :path => '../../../'
+
+end

+ 26 - 0
FirebaseMessaging/Apps/SampleStandaloneWatchApp/README.md

@@ -0,0 +1,26 @@
+# Sample Standalone watchOS App
+
+This sample demonstrates how to use Firebase Cloud Messaging in a standalone watchOS app.
+
+## Getting started
+
+1. Turn on _Developer mode_ on your watch:
+    - Settings > Privacy & Security > Developer Mode
+    - You will have to restart your watch
+1. Change the bundle identifier to a unique ID (e.g. `dev.<yourcompany>.WatchKitApp.dev.WatchKitApp`)
+1. Enable automatic code signing for the Xcode project
+1. [Add Firebase to your watchOS Project](https://firebase.google.com/docs/ios/setup)
+    > **Warning**
+    > Make sure to add the `GoogleServices-Info.plist` file to the `SampleStandaloneWatchApp Watch App` target
+1. [Upload your APNs authentication key to Firebase](https://firebase.google.com/docs/cloud-messaging/ios/client#upload_your_apns_authentication_key)
+1. Run the app
+1. When the app first launches, you will need to accept the notification permission
+1. In the Firebase console, go to [Messaging](https://console.firebase.google.com/project/_/messaging/onboarding), and click on _Create your first campaign_
+1. Select _Firebase Notification messages_ and click on _Create_
+1. Enter a message in the _Notification text_ field
+1. Click on the blue  _Send test message_ button on the right
+    > **Note**
+    > It is easy to miss this button. You have to click on the blue **Send test message** button on the **RIGHT HAND** side of the screen.
+1. In the Xcode debug console, find the _FCM registration token_, and add it in the _Test on device_ dialog
+1. Tick the checkbox for the token, then click on the _Test_ button
+1. The message should now appear on your development watch

+ 12 - 0
FirebaseMessaging/Apps/SampleStandaloneWatchApp/SampleStandaloneWatchApp-Watch-App-Info.plist

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>FirebaseAppDelegateProxyEnabled</key>
+	<false/>
+	<key>UIBackgroundModes</key>
+	<array>
+		<string>remote-notification</string>
+	</array>
+</dict>
+</plist>

+ 504 - 0
FirebaseMessaging/Apps/SampleStandaloneWatchApp/SampleStandaloneWatchApp.xcodeproj/project.pbxproj

@@ -0,0 +1,504 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 55;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		D6B31A02293FE3EA0008A527 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = D6B31A01293FE3EA0008A527 /* GoogleService-Info.plist */; };
+		D6B31A03293FE99F0008A527 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D6E7BFEB293AAD83007A9699 /* Preview Assets.xcassets */; };
+		D6B31A05293FE9FF0008A527 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D6E7BFE8293AAD83007A9699 /* Assets.xcassets */; };
+		D6E7BFE0293AAD81007A9699 /* SampleStandaloneWatchApp Watch App.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = D6E7BFDF293AAD81007A9699 /* SampleStandaloneWatchApp Watch App.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
+		D6E7BFE5293AAD81007A9699 /* SampleStandaloneWatchAppApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E7BFE4293AAD81007A9699 /* SampleStandaloneWatchAppApp.swift */; };
+		D6E7BFE7293AAD81007A9699 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E7BFE6293AAD81007A9699 /* ContentView.swift */; };
+		D6E7BFF8293AB0EF007A9699 /* FirebaseMessaging in Frameworks */ = {isa = PBXBuildFile; productRef = D6E7BFF7293AB0EF007A9699 /* FirebaseMessaging */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+		D6E7BFE1293AAD81007A9699 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = D6E7BFD3293AAD81007A9699 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = D6E7BFDE293AAD81007A9699;
+			remoteInfo = "SampleStandaloneWatchApp Watch App";
+		};
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+		D6E7BFF2293AAD83007A9699 /* Embed Watch Content */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = "$(CONTENTS_FOLDER_PATH)/Watch";
+			dstSubfolderSpec = 16;
+			files = (
+				D6E7BFE0293AAD81007A9699 /* SampleStandaloneWatchApp Watch App.app in Embed Watch Content */,
+			);
+			name = "Embed Watch Content";
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+		D6B31A01293FE3EA0008A527 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "../Shared/GoogleService-Info.plist"; sourceTree = "<group>"; };
+		D6E7BFD9293AAD81007A9699 /* SampleStandaloneWatchApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SampleStandaloneWatchApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
+		D6E7BFDF293AAD81007A9699 /* SampleStandaloneWatchApp Watch App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "SampleStandaloneWatchApp Watch App.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+		D6E7BFE4293AAD81007A9699 /* SampleStandaloneWatchAppApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleStandaloneWatchAppApp.swift; sourceTree = "<group>"; };
+		D6E7BFE6293AAD81007A9699 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
+		D6E7BFE8293AAD83007A9699 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+		D6E7BFEB293AAD83007A9699 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
+		D6E7BFF9293ABC51007A9699 /* SampleStandaloneWatchAppWatchApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SampleStandaloneWatchAppWatchApp.entitlements; sourceTree = "<group>"; };
+		D6E7BFFA293ABC58007A9699 /* SampleStandaloneWatchApp-Watch-App-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "SampleStandaloneWatchApp-Watch-App-Info.plist"; sourceTree = SOURCE_ROOT; };
+		D6E7BFFB293ABC7E007A9699 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		D6E7BFDC293AAD81007A9699 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				D6E7BFF8293AB0EF007A9699 /* FirebaseMessaging in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		D6E7BFD2293AAD81007A9699 = {
+			isa = PBXGroup;
+			children = (
+				D6B31A01293FE3EA0008A527 /* GoogleService-Info.plist */,
+				D6E7BFE3293AAD81007A9699 /* SampleStandaloneWatchAppWatchApp */,
+				D6E7BFDA293AAD81007A9699 /* Products */,
+			);
+			sourceTree = "<group>";
+		};
+		D6E7BFDA293AAD81007A9699 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				D6E7BFD9293AAD81007A9699 /* SampleStandaloneWatchApp.app */,
+				D6E7BFDF293AAD81007A9699 /* SampleStandaloneWatchApp Watch App.app */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		D6E7BFE3293AAD81007A9699 /* SampleStandaloneWatchAppWatchApp */ = {
+			isa = PBXGroup;
+			children = (
+				D6E7BFFB293ABC7E007A9699 /* GoogleService-Info.plist */,
+				D6E7BFFA293ABC58007A9699 /* SampleStandaloneWatchApp-Watch-App-Info.plist */,
+				D6E7BFF9293ABC51007A9699 /* SampleStandaloneWatchAppWatchApp.entitlements */,
+				D6E7BFE4293AAD81007A9699 /* SampleStandaloneWatchAppApp.swift */,
+				D6E7BFE6293AAD81007A9699 /* ContentView.swift */,
+				D6E7BFE8293AAD83007A9699 /* Assets.xcassets */,
+				D6E7BFEA293AAD83007A9699 /* PreviewContent */,
+			);
+			path = SampleStandaloneWatchAppWatchApp;
+			sourceTree = "<group>";
+		};
+		D6E7BFEA293AAD83007A9699 /* PreviewContent */ = {
+			isa = PBXGroup;
+			children = (
+				D6E7BFEB293AAD83007A9699 /* Preview Assets.xcassets */,
+			);
+			path = PreviewContent;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		D6E7BFD8293AAD81007A9699 /* SampleStandaloneWatchApp */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = D6E7BFF3293AAD83007A9699 /* Build configuration list for PBXNativeTarget "SampleStandaloneWatchApp" */;
+			buildPhases = (
+				D6E7BFD7293AAD81007A9699 /* Resources */,
+				D6E7BFF2293AAD83007A9699 /* Embed Watch Content */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				D6E7BFE2293AAD81007A9699 /* PBXTargetDependency */,
+			);
+			name = SampleStandaloneWatchApp;
+			productName = SampleStandaloneWatchApp;
+			productReference = D6E7BFD9293AAD81007A9699 /* SampleStandaloneWatchApp.app */;
+			productType = "com.apple.product-type.application.watchapp2-container";
+		};
+		D6E7BFDE293AAD81007A9699 /* SampleStandaloneWatchApp Watch App */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = D6E7BFEF293AAD83007A9699 /* Build configuration list for PBXNativeTarget "SampleStandaloneWatchApp Watch App" */;
+			buildPhases = (
+				D6E7BFDB293AAD81007A9699 /* Sources */,
+				D6E7BFDC293AAD81007A9699 /* Frameworks */,
+				D6E7BFDD293AAD81007A9699 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = "SampleStandaloneWatchApp Watch App";
+			packageProductDependencies = (
+				D6E7BFF7293AB0EF007A9699 /* FirebaseMessaging */,
+			);
+			productName = "SampleStandaloneWatchApp Watch App";
+			productReference = D6E7BFDF293AAD81007A9699 /* SampleStandaloneWatchApp Watch App.app */;
+			productType = "com.apple.product-type.application";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		D6E7BFD3293AAD81007A9699 /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				BuildIndependentTargetsInParallel = 1;
+				LastSwiftUpdateCheck = 1410;
+				LastUpgradeCheck = 1410;
+				TargetAttributes = {
+					D6E7BFD8293AAD81007A9699 = {
+						CreatedOnToolsVersion = 14.1;
+					};
+					D6E7BFDE293AAD81007A9699 = {
+						CreatedOnToolsVersion = 14.1;
+					};
+				};
+			};
+			buildConfigurationList = D6E7BFD6293AAD81007A9699 /* Build configuration list for PBXProject "SampleStandaloneWatchApp" */;
+			compatibilityVersion = "Xcode 13.0";
+			developmentRegion = en;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+				Base,
+			);
+			mainGroup = D6E7BFD2293AAD81007A9699;
+			packageReferences = (
+				D6E7BFF6293AB0EF007A9699 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */,
+			);
+			productRefGroup = D6E7BFDA293AAD81007A9699 /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				D6E7BFD8293AAD81007A9699 /* SampleStandaloneWatchApp */,
+				D6E7BFDE293AAD81007A9699 /* SampleStandaloneWatchApp Watch App */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		D6E7BFD7293AAD81007A9699 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		D6E7BFDD293AAD81007A9699 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				D6B31A05293FE9FF0008A527 /* Assets.xcassets in Resources */,
+				D6B31A03293FE99F0008A527 /* Preview Assets.xcassets in Resources */,
+				D6B31A02293FE3EA0008A527 /* GoogleService-Info.plist in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		D6E7BFDB293AAD81007A9699 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				D6E7BFE7293AAD81007A9699 /* ContentView.swift in Sources */,
+				D6E7BFE5293AAD81007A9699 /* SampleStandaloneWatchAppApp.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+		D6E7BFE2293AAD81007A9699 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = D6E7BFDE293AAD81007A9699 /* SampleStandaloneWatchApp Watch App */;
+			targetProxy = D6E7BFE1293AAD81007A9699 /* PBXContainerItemProxy */;
+		};
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+		D6E7BFED293AAD83007A9699 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+				MTL_FAST_MATH = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+			};
+			name = Debug;
+		};
+		D6E7BFEE293AAD83007A9699 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				MTL_FAST_MATH = YES;
+				SWIFT_COMPILATION_MODE = wholemodule;
+				SWIFT_OPTIMIZATION_LEVEL = "-O";
+			};
+			name = Release;
+		};
+		D6E7BFF0293AAD83007A9699 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+				CODE_SIGN_ENTITLEMENTS = SampleStandaloneWatchAppWatchApp/SampleStandaloneWatchAppWatchApp.entitlements;
+				"CODE_SIGN_IDENTITY[sdk=watchos*]" = "iPhone Developer";
+				CODE_SIGN_STYLE = Manual;
+				CURRENT_PROJECT_VERSION = 1;
+				DEVELOPMENT_ASSET_PATHS = "\"SampleStandaloneWatchAppWatchApp/PreviewContent\"";
+				DEVELOPMENT_TEAM = "";
+				"DEVELOPMENT_TEAM[sdk=watchos*]" = EQHXZ8M8AV;
+				ENABLE_PREVIEWS = YES;
+				GENERATE_INFOPLIST_FILE = YES;
+				INFOPLIST_FILE = "SampleStandaloneWatchApp-Watch-App-Info.plist";
+				INFOPLIST_KEY_CFBundleDisplayName = SampleStandaloneWatchApp;
+				INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
+				INFOPLIST_KEY_WKWatchOnly = YES;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+				);
+				MARKETING_VERSION = 1.0;
+				PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.extensions.dev.WatchKitApp;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				PROVISIONING_PROFILE_SPECIFIER = "";
+				"PROVISIONING_PROFILE_SPECIFIER[sdk=watchos*]" = "Firebase iOS App Extensions WatchKit App Dev";
+				SDKROOT = watchos;
+				SKIP_INSTALL = YES;
+				SWIFT_EMIT_LOC_STRINGS = YES;
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = 4;
+				WATCHOS_DEPLOYMENT_TARGET = 9.0;
+			};
+			name = Debug;
+		};
+		D6E7BFF1293AAD83007A9699 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+				CODE_SIGN_ENTITLEMENTS = SampleStandaloneWatchAppWatchApp/SampleStandaloneWatchAppWatchApp.entitlements;
+				"CODE_SIGN_IDENTITY[sdk=watchos*]" = "iPhone Developer";
+				CODE_SIGN_STYLE = Manual;
+				CURRENT_PROJECT_VERSION = 1;
+				DEVELOPMENT_ASSET_PATHS = "\"SampleStandaloneWatchAppWatchApp/PreviewContent\"";
+				DEVELOPMENT_TEAM = "";
+				"DEVELOPMENT_TEAM[sdk=watchos*]" = EQHXZ8M8AV;
+				ENABLE_PREVIEWS = YES;
+				GENERATE_INFOPLIST_FILE = YES;
+				INFOPLIST_FILE = "SampleStandaloneWatchApp-Watch-App-Info.plist";
+				INFOPLIST_KEY_CFBundleDisplayName = SampleStandaloneWatchApp;
+				INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
+				INFOPLIST_KEY_WKWatchOnly = YES;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+				);
+				MARKETING_VERSION = 1.0;
+				PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.extensions.dev.WatchKitApp;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				PROVISIONING_PROFILE_SPECIFIER = "";
+				"PROVISIONING_PROFILE_SPECIFIER[sdk=watchos*]" = "Firebase iOS App Extensions WatchKit App Dev";
+				SDKROOT = watchos;
+				SKIP_INSTALL = YES;
+				SWIFT_EMIT_LOC_STRINGS = YES;
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = 4;
+				VALIDATE_PRODUCT = YES;
+				WATCHOS_DEPLOYMENT_TARGET = 9.0;
+			};
+			name = Release;
+		};
+		D6E7BFF4293AAD83007A9699 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				CODE_SIGN_STYLE = Manual;
+				CURRENT_PROJECT_VERSION = 1;
+				DEVELOPMENT_TEAM = "";
+				"DEVELOPMENT_TEAM[sdk=iphoneos*]" = EQHXZ8M8AV;
+				INFOPLIST_KEY_CFBundleDisplayName = SampleStandaloneWatchApp;
+				MARKETING_VERSION = 1.0;
+				PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.extensions.dev;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				PROVISIONING_PROFILE_SPECIFIER = "";
+				"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Firebase iOS App Extensions Dev";
+				SDKROOT = iphoneos;
+				SWIFT_VERSION = 5.0;
+			};
+			name = Debug;
+		};
+		D6E7BFF5293AAD83007A9699 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				CODE_SIGN_STYLE = Manual;
+				CURRENT_PROJECT_VERSION = 1;
+				DEVELOPMENT_TEAM = "";
+				"DEVELOPMENT_TEAM[sdk=iphoneos*]" = EQHXZ8M8AV;
+				INFOPLIST_KEY_CFBundleDisplayName = SampleStandaloneWatchApp;
+				MARKETING_VERSION = 1.0;
+				PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.extensions.dev;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				PROVISIONING_PROFILE_SPECIFIER = "";
+				"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Firebase iOS App Extensions Dev";
+				SDKROOT = iphoneos;
+				SWIFT_VERSION = 5.0;
+				VALIDATE_PRODUCT = YES;
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		D6E7BFD6293AAD81007A9699 /* Build configuration list for PBXProject "SampleStandaloneWatchApp" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				D6E7BFED293AAD83007A9699 /* Debug */,
+				D6E7BFEE293AAD83007A9699 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		D6E7BFEF293AAD83007A9699 /* Build configuration list for PBXNativeTarget "SampleStandaloneWatchApp Watch App" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				D6E7BFF0293AAD83007A9699 /* Debug */,
+				D6E7BFF1293AAD83007A9699 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		D6E7BFF3293AAD83007A9699 /* Build configuration list for PBXNativeTarget "SampleStandaloneWatchApp" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				D6E7BFF4293AAD83007A9699 /* Debug */,
+				D6E7BFF5293AAD83007A9699 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+
+/* Begin XCRemoteSwiftPackageReference section */
+		D6E7BFF6293AB0EF007A9699 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = {
+			isa = XCRemoteSwiftPackageReference;
+			repositoryURL = "https://github.com/firebase/firebase-ios-sdk";
+			requirement = {
+				branch = master;
+				kind = branch;
+			};
+		};
+/* End XCRemoteSwiftPackageReference section */
+
+/* Begin XCSwiftPackageProductDependency section */
+		D6E7BFF7293AB0EF007A9699 /* FirebaseMessaging */ = {
+			isa = XCSwiftPackageProductDependency;
+			package = D6E7BFF6293AB0EF007A9699 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;
+			productName = FirebaseMessaging;
+		};
+/* End XCSwiftPackageProductDependency section */
+	};
+	rootObject = D6E7BFD3293AAD81007A9699 /* Project object */;
+}

+ 11 - 0
FirebaseMessaging/Apps/SampleStandaloneWatchApp/SampleStandaloneWatchAppWatchApp/Assets.xcassets/AccentColor.colorset/Contents.json

@@ -0,0 +1,11 @@
+{
+  "colors" : [
+    {
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

+ 13 - 0
FirebaseMessaging/Apps/SampleStandaloneWatchApp/SampleStandaloneWatchAppWatchApp/Assets.xcassets/AppIcon.appiconset/Contents.json

@@ -0,0 +1,13 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "platform" : "watchos",
+      "size" : "1024x1024"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

+ 6 - 0
FirebaseMessaging/Apps/SampleStandaloneWatchApp/SampleStandaloneWatchAppWatchApp/Assets.xcassets/Contents.json

@@ -0,0 +1,6 @@
+{
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

+ 33 - 0
FirebaseMessaging/Apps/SampleStandaloneWatchApp/SampleStandaloneWatchAppWatchApp/ContentView.swift

@@ -0,0 +1,33 @@
+// Copyright 2022 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 SwiftUI
+
+struct ContentView: View {
+  var body: some View {
+    VStack {
+      Image(systemName: "globe")
+        .imageScale(.large)
+        .foregroundColor(.accentColor)
+      Text("Hello, world from FCM Sample!")
+    }
+    .padding()
+  }
+}
+
+struct ContentView_Previews: PreviewProvider {
+  static var previews: some View {
+    ContentView()
+  }
+}

+ 6 - 0
FirebaseMessaging/Apps/SampleStandaloneWatchApp/SampleStandaloneWatchAppWatchApp/PreviewContent/Preview Assets.xcassets/Contents.json

@@ -0,0 +1,6 @@
+{
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

+ 60 - 0
FirebaseMessaging/Apps/SampleStandaloneWatchApp/SampleStandaloneWatchAppWatchApp/SampleStandaloneWatchAppApp.swift

@@ -0,0 +1,60 @@
+// Copyright 2022 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 SwiftUI
+import Firebase
+
+@main
+struct SampleStandaloneWatchApp_Watch_AppApp: App {
+  @WKApplicationDelegateAdaptor(FCMWatchAppDelegate.self) var appDelegate
+  var body: some Scene {
+    WindowGroup {
+      ContentView()
+    }
+  }
+}
+
+// MARK: - WKApplicationDelegate
+
+class FCMWatchAppDelegate: NSObject, WKApplicationDelegate, MessagingDelegate {
+  func applicationDidFinishLaunching() {
+    FirebaseApp.configure()
+    let notificationCenter = UNUserNotificationCenter.current()
+    notificationCenter.requestAuthorization(options: [.alert, .sound]) { granted, error in
+      if granted {
+        WKApplication.shared().registerForRemoteNotifications()
+      }
+    }
+    Messaging.messaging().delegate = self
+  }
+
+  func didRegisterForRemoteNotifications(withDeviceToken deviceToken: Data) {
+    // Method swizzling should be disabled in Firebase Messaging on watchOS.
+    // Set the APNS token manually as is done here.
+    // More information on how to disable -
+    // https://firebase.google.com/docs/cloud-messaging/ios/client#method_swizzling_in
+
+    print("APNS didRegisterForRemoteNotifications. Got device token \(deviceToken)")
+    Messaging.messaging().apnsToken = deviceToken
+  }
+}
+
+// MARK: - FCM MessagingDelegate
+
+extension FCMWatchAppDelegate {
+  func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
+    // Use this FCM token to test sending a push using API or Firebase Console
+    print("FCM - didReceiveRegistrationToken \(String(describing: fcmToken))")
+  }
+}

+ 8 - 0
FirebaseMessaging/Apps/SampleStandaloneWatchApp/SampleStandaloneWatchAppWatchApp/SampleStandaloneWatchAppWatchApp.entitlements

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>aps-environment</key>
+	<string>development</string>
+</dict>
+</plist>

+ 10 - 0
scripts/build.sh

@@ -430,6 +430,16 @@ case "$product-$platform-$method" in
     fi
     ;;
 
+  MessagingSampleStandaloneWatchApp-*-*)
+    if check_secrets; then
+      RunXcodebuild \
+        -workspace 'FirebaseMessaging/Apps/SampleStandaloneWatchApp/SampleStandaloneWatchApp.xcworkspace' \
+        -scheme "SampleStandaloneWatchApp Watch App" \
+        "${xcb_flags[@]}" \
+        build
+    fi
+    ;;
+
   MLModelDownloaderSample-*-*)
   if check_secrets; then
     RunXcodebuild \

+ 5 - 0
scripts/install_prereqs.sh

@@ -144,6 +144,11 @@ case "$project-$platform-$method" in
     bundle exec pod install --project-directory=FirebaseMessaging/Apps/SwiftUISample --repo-update
     ;;
 
+  MessagingSampleStandaloneWatchApp-*)
+    install_xcpretty
+    bundle exec pod install --project-directory=FirebaseMessaging/Apps/SampleStandaloneWatchApp --repo-update
+    ;;
+
   MLModelDownloaderSample-*)
     install_xcpretty
     bundle exec pod install --project-directory=FirebaseMLModelDownloader/Apps/Sample --repo-update