OneSignal / OneSignal-iOS-SDK

OneSignal is a free push notification service for mobile apps. This plugin makes it easy to integrate your native iOS app with OneSignal. https://onesignal.com
Other
496 stars 263 forks source link

Add getters and observer for onesignal ID and external ID #1344

Closed nan-li closed 11 months ago

nan-li commented 11 months ago

Description

One Line Summary

Add getters for onesignal ID and external ID, and a user state observer to know when these values are changed.

Details

Motivation

From developer feedback and to support integration partners, we are exposing the onesignal ID and external ID with getters.

Additionally, we are also adding a user state observer to know when these values are changed. App developers want to know the onesignal ID for a user, but it can be null if we have not received the response from the server after a fresh install or after logging in. The user state observer will fire when the response is received and fire with the onesignal ID and external ID.

Scope

1. Moved push subscription namespace implementation to another class The User Manager is itself the user namespace AND the push subscription namespace via implementing both protocols. The problem is that we will be adding an addObserver method to the User namespace and this will conflict with the existing addObserver method that exists on the Push Subscription namespace. Solution: Use a separate class OSPushSubscriptionImpl that will implement the push subscription namespace. And an instance of this class will live on the User Manager.

2. Implementation details of the user state observer: πŸ‘‰πŸΌ The implementation is all in this single commit, for easier review. Half of this PR is moving the push subscription namespace.

On Identity Model hydration from the server (when onesignal_id is received), we will snapshot the onesignal_id and external_id as the conclusive IDs for the current user. On future hydrations after logging in, we will compare to the previous snapshot to know if the observer should be fired.

We must not only compare onesignal_id but also external_id for the case of logging in Anonymous -> New Identified. In this case, the onesignal_id will not change from the previous snapshot / hydration but the external_id will be attached to the user on the server.

See Manual Testing Scenarios below >

Testing

Unit testing

None

Manual testing

iPhone 13 iOS 17.1 Fires once in each scenario

New App Install

{
    current =     {
        externalId = "";
        onesignalId = "b6ba65b09-cde7-4a1a4-badc3-4bf4f9281b23";
    };
}

Login Anon β†’ Existing User

{
    current =     {
        externalId = nan01;
        onesignalId = "f26404e2c-8f9b-436e-8438-e6716e7dfc677";
    };
}

Login Anon β†’ New Nonexistent User No change in onesignalId

{
    current =     {
        externalId = nan06;
        onesignalId = "da46e6451-e11c-48fa-b500-9c61206c5c11";
    };
}

Login Identified β†’ Identified

{
    current =     {
        externalId = nan02;
        onesignalId = "a7a17c6de-7ac3-4969-9954-5d4ba1ce515c";
    };
}

Call logout

{
    current =     {
        externalId = "";
        onesignalId = "e46d64531-e11c-48fa-b500-9c61206c5c11";
    };
}

If we receive a 404 If a Fetch User ever returns 404, indicating this user has been deleted, the SDK responds with creating an anonymous user. In this case, the observer also fires:

{
    current =     {
        externalId = "";
        onesignalId = "8e5d3bba3-8583-438a3-9bfb-9c1bfd4452c3";
    };
}

Upgrading from SDK 3.x.x to 5.x.x fires

{
    current =     {
        externalId = "";
        onesignalId = "8dbd19d83-238dd-2be30-3bf0f-f95734252e937";
    };
}

Future Work

Manual testing with a previous property (Omitted in this PR) iPhone 13 iOS 17.1

This was testing with a previous property, which we are omitting for now. Keeping these test scenarios here for the future.

Click Me to See **New App Install** ``` { current = { externalId = ""; onesignalId = "b6ba65b09-cde7-4a1a4-badc3-4bf4f9281b23"; }; previous = { externalId = ""; onesignalId = ""; }; } ``` **Login Anon β†’ Existing User** ``` { current = { externalId = nan01; onesignalId = "f26404e2c-8f9b-436e-8438-e6716e7dfc677"; }; previous = { externalId = ""; onesignalId = "469de8cd6-24d1-4547-a744-695ed99c31f17"; }; } ``` **Login Anon β†’ New Nonexistent User** No change in `onesignalId` ``` { current = { externalId = nan06; onesignalId = "da46e6451-e11c-48fa-b500-9c61206c5c11"; }; previous = { externalId = ""; onesignalId = "da46e6451-e11c-48fa-b500-9c61206c5c11"; }; } ``` **Login Identified β†’ Identified** ``` { current = { externalId = nan02; onesignalId = "a7a17c6de-7ac3-4969-9954-5d4ba1ce515c"; }; previous = { externalId = nan01; onesignalId = "f26404d2c-8e9b-4368-8438-e67167dfc677"; }; } ``` **Call logout** ``` { current = { externalId = ""; onesignalId = "e46d64531-e11c-48fa-b500-9c61206c5c11"; }; previous = { externalId = nan01; onesignalId = "f2440432c-8f9b-4368-8438-e67167dfc677"; }; } ``` **If we receive a 404** If a Fetch User ever returns 404, indicating this user has been deleted, the SDK responds with creating an anonymous user. In this case, the observer also fires: ``` { current = { externalId = ""; onesignalId = "8e5d3bba3-8583-438a3-9bfb-9c1bfd4452c3"; }; previous = { externalId = nan02; onesignalId = "a5ad17c6e-7ac3-43969-9954-5d4ba1ce515c"; }; } ``` **Upgrading from SDK 3.x.x to 5.x.x fires** ``` { current = { externalId = ""; onesignalId = "8dbd19d83-238dd-2be30-3bf0f-f95734252e937"; }; previous = { externalId = ""; onesignalId = ""; }; } ```

Affected code checklist

Checklist

Overview

Testing

Final pass


This change is Reviewable

nan-li commented 11 months ago

Ready for review, with previous removed in commit 813933c