Push Notifications on iOS with Cloud Build?

Do push notifications work with Cloud Build, or is there something you have to setup specifically so that it will work with iOS?

With Xcode you need to do this:

  • Scroll down to Linked Frameworks and Libraries and click the + button to add a framework.

  • In the window that appears, scroll to UserNotifications.framework and click on that entry, then click on Add.

  • Select the Capabilities tab from the Editor area.

  • Switch Push Notifications to On.

How do I do this in Cloud Build and is it required or does Cloud Build auto handle this?
I’m using Firebase Messaging.

Hello @SniperED007 ,

You can use a PostProcessBuild Script to manipulate the resulting XCode via code to add the necessary Capabilities for Push Notifications.

There are several examples online if you search for it. The PostProcessScript should do the following steps: یک برنامه Ł…Ų“ŲŖŲ±ŪŒ Firebase Cloud Messaging ŲØŲ§ Unity راه Ų§Ł†ŲÆŲ§Ų²ŪŒ Ś©Ł†ŪŒŲÆ

Hereby follows the one I implemented:

public static class PushNotificationsPostBuildScript {
    [PostProcessBuild(100)]
    public static void OnPostprocessBuild(BuildTarget buildTarget, string path) {    
       // Only perform these steps for iOS builds
      if (buildTarget == BuildTarget.iOS) {
        ProcessPostBuildIOS(buildTarget, path);
      }
    }

    private static void ProcessPostBuildIOS(BuildTarget buildTarget, string path) {

      Debug.Log("[PushNotificationsPostBuildScript] ProcessPostBuild - iOS - Adding Push Notification capabilities.");

      // get XCode project path
      string pbxPath = PBXProject.GetPBXProjectPath(path);

      // Add linked frameworks
      PBXProject pbxProject = new PBXProject();
      pbxProject.ReadFromString(File.ReadAllText(pbxPath));
      string targetName = PBXProject.GetUnityTargetName();
      string targetGUID = pbxProject.TargetGuidByName(targetName);
      pbxProject.AddFrameworkToProject(targetGUID, "UserNotifications.framework", false);
      File.WriteAllText(pbxPath, pbxProject.WriteToString());

      // Add required capabilities: Push Notifications, and Remote Notifications in Background Modes
      var isDevelopment = Debug.isDebugBuild;
      var capabilities = new ProjectCapabilityManager(pbxPath, "app.entitlements", "Unity-iPhone");
      capabilities.AddPushNotifications(isDevelopment);
      capabilities.AddBackgroundModes(BackgroundModesOptions.RemoteNotifications);
      capabilities.WriteToFile();
    }
  }
7 Likes

Can you help me with the latest solution. I’ve tried 20 different solutions using postProcess to enable push notification but still with no luck.

#if APPLE
using UnityEngine;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEditor.iOS.Xcode;

public class EntitlementsPostProcess : ScriptableObject
{
    public DefaultAsset m_entitlementsFile;

    [PostProcessBuild]
    public static void OnPostProcess(BuildTarget buildTarget, string buildPath)
    {
        if (buildTarget != BuildTarget.iOS)
            return;

        var dummy = CreateInstance<EntitlementsPostProcess>();
        var file = dummy.m_entitlementsFile;
        DestroyImmediate(dummy);
        if (file == null)
            return;
              
        var proj_path = PBXProject.GetPBXProjectPath(buildPath);
        var proj = new PBXProject();

        var unityFrameworkGUID = proj.GetUnityFrameworkTargetGuid();

        proj.ReadFromFile(proj_path);       
        var target_name = proj.GetUnityMainTargetGuid();

        // NEW
        //Set the entitlements file name to what you want but make sure it has this extension
        string entitlementsFileName = "my_app.entitlements";

        var entitlements = new ProjectCapabilityManager(proj_path, entitlementsFileName, "Unity-iPhone", target_name);            
entitlements.AddPushNotifications(true);
      
        //Apply
        entitlements.WriteToFile();
    }
}
#endif

Try this, I removed a few things that I don’t think you need but hopefully should work.

1 Like

Thank you for the solution, I suppose it should work because I see the entitlement finally set into production automatically and so does the background, push notification enabled in xcode automatically, but still when I try to send cloud messaging from firebase. My app just don’t receive it. I’m sure my firebase script and setting are correct since I can receive it on android.

What might be the problem. Is it because I run the app through test flight mode.

No, nothing to do with test flight, it will work even on a unity cloud build without test flight etc. Check your iOS settings on Firebase.

Thanks, wow didn’t know to set cloud messaging for iOS is this complicated. Gatta need some times to study into it.

So I have followed the steps to upload my APN to firebase but still can’t get things going. I’ve checked my APN, package ID, App store ID, and team ID. All four sees correct.

The only thing I haven’t done is to configure the push notification under app id configuration because I realize to use keychain generating certificate is an old method (it eventually cannot export to p8 file).

What possibly am I still missing here.

SniperED007 :

Thanks! It finally gets to work after 24 hours of uploading my APN (cloud messaging won’t start right away after uploaded). Thank you again, I’ve been working on this for two weeks.

1 Like

We have a big problem with notifications and UCB.
For the third day I have been trying any options, but the cloud build refuses to assemble.
Unity version: 2022.2.14f
xcode version: 14.1
Mobile Provision was recreated after the push certificate was added.
True, in the mobile itself there is nothing about the notification service. Is this correct?

error from cloud
[error] 7.3.5.2.7.4 - INFO: ā–ø /BUILD_PATH/Unity-iPhone.xcodeproj: error: Provisioning profile has app ID ā€œcom.redrift.StorySparkā€, which does not match the bundle ID ā€œcom.redrift.StorySpark.notificationserviceā€. (in target ā€˜notificationservice’ from project ā€˜Unity-iPhone’)

Here is the postbuild processing code we are using.

[PostProcessBuild(1)]
        public static void OnPostProcessBuild(BuildTarget target, string path)
        {
            try
            {
                if (target != BuildTarget.iOS)
                    return;

                Debug.Log($"{nameof(SignInWithApplePostprocessor)} Start Post Process Build");

                var projectPath = PBXProject.GetPBXProjectPath(path);
                var project = new PBXProject();
                project.ReadFromString(File.ReadAllText(projectPath));
                var projectCapabilityManager = new ProjectCapabilityManager(projectPath,
                    "Entitlements.entitlements",
                    null,
                    project.GetUnityMainTargetGuid());
                projectCapabilityManager.AddSignInWithAppleWithCompatibility(project.GetUnityFrameworkTargetGuid());
                projectCapabilityManager.AddPushNotifications(true);
                projectCapabilityManager.WriteToFile();
                Debug.Log(
                    $"{nameof(SignInWithApplePostprocessor)} End success Post Process Build. Entitlements: {string.Join(";", projectCapabilityManager.Entitlements.root.values.Select(x => x.Key))}");
            }
            catch (Exception e)
            {
                Debug.LogError(
                    $"{nameof(SignInWithApplePostprocessor)} End failed Post Process Build. Message: {e.Message}");
                Debug.LogException(e);
                throw;
            }
        }
private static readonly string SwiftStandardLibraries = "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES";
        private static readonly string BitCode = "ENABLE_BITCODE";
        private static readonly string AlwaysSearchUserPaths = "ALWAYS_SEARCH_USER_PATHS";
        private static readonly string UseHeadermap = "USE_HEADERMAP";
        private static readonly string Notificationservice = "notificationservice";
        private static readonly string No = nameof(No).ToUpper();
        private static readonly string Yes = nameof(Yes).ToUpper();

        [PostProcessBuild(999)]
        public static void OnPostProcessBuild(BuildTarget target, string path)
        {
            if (target != BuildTarget.iOS)
                return;

            try
            {
                Debug.Log($"{nameof(ModifyFrameworksPostProcess)} Start Post Process Build");
                var projPath = PBXProject.GetPBXProjectPath(path);
                Debug.Log($"{nameof(projPath)}: {projPath}");

                var project = new PBXProject();
                project.ReadFromFile(projPath);

                Debug.Log($"{nameof(ModifyFrameworksPostProcess)} Project successfully initialized");

                project.DisableSwiftStandardLibraries();
                project.EnableSwiftStandardLibraries();
                project.DisableBitcode();
                project.DisableHeaderMap();
                project.WriteToFile(projPath);

                Debug.Log($"{nameof(ModifyFrameworksPostProcess)} End success Post Process Build");
            }
            catch (Exception e)
            {
                Debug.LogError(
                    $"{nameof(ModifyFrameworksPostProcess)} End failed Post Process Build. Message: {e.Message}");
                Debug.LogException(e);
                throw;
            }
        }
private static PBXProject DisableHeaderMap(this PBXProject project)
        {
            var notificationserviceGuid = project.TargetGuidByName(Notificationservice);
            project.SafeSetBuildProperty(notificationserviceGuid, AlwaysSearchUserPaths, No);
            project.SafeSetBuildProperty(notificationserviceGuid, UseHeadermap, No);

            Debug.Log($"{nameof(ModifyFrameworksPostProcess)} {nameof(DisableHeaderMap)} end");
            return project;
        }

I omitted some methods, they do not apply to the work of other SDKs
I would be grateful if you give me more options. Or at least point to the cause of the problem

I’m running into this issue today, wondering if you found a resolution?

No. We tried different options.
Also tried to switch to the OneSignal.
Together with OneSiganl support, we came to the conclusion that Unity Cloud does not support building projects with push notifications.

Because for correct operation, it is required to use extensions that the cloud assembly cannot correctly sign

We workedaround the issue of ā€œ.notificationservicesā€ suffix being added to the appid by writting this post processing script:

internal static class IOSRichPushNotificationPostProcess2
    {
        [PostProcessBuild(2)]
        public static void ConfigureRichPushNotificationTarget(BuildTarget buildTarget, string buildOutputPath)
        {
            if (buildTarget != BuildTarget.iOS)
            {
                return;
            }

            var xcodeProjectPath = PBXProject.GetPBXProjectPath(buildOutputPath);
            var project = new PBXProject();
            project.ReadFromFile(xcodeProjectPath);

            var displayName = "notificationservice";
           
            var notificationserviceGuid = project.TargetGuidByName(displayName);

            var pathToNotificationServiceImplementation = Path.Combine(buildOutputPath, displayName);

            var pathToNotificationServicePlist = Path.Combine(pathToNotificationServiceImplementation, "Info.plist");
            var notificationServicePlist = new PlistDocument();
            notificationServicePlist.ReadFromFile(pathToNotificationServicePlist);
            notificationServicePlist.root.SetString("CFBundleIdentifier", "$(PRODUCT_BUNDLE_IDENTIFIER)");
            notificationServicePlist.WriteToFile(pathToNotificationServicePlist);

            project.SetBuildProperty(notificationserviceGuid, "PRODUCT_BUNDLE_IDENTIFIER", Application.identifier);

            project.WriteToFile(xcodeProjectPath);
        }
    }

Forget it, this prevents installing it to a device.

So we opted to remove push notifications from ios builds:

internal class RemovePushNotifications : IPreprocessBuildWithReport
    {
        public int callbackOrder => -10000;
        public void OnPreprocessBuild(BuildReport report)
        {
            if (report.summary.platform != BuildTarget.iOS) return;

            var request = UnityEditor.PackageManager.Client.Remove("com.unity.services.push-notifications");
            while (!request.IsCompleted)
            {
                AssetDatabase.Refresh();
            }
        }
    }