Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: dismiss message if message expires #733

Merged
merged 22 commits into from
Jun 27, 2024

Conversation

levibostian
Copy link
Contributor

@levibostian levibostian commented Jun 4, 2024

https://linear.app/customerio/issue/MBL-315/dismiss-message-if-message-expires

As a marketer I want to stop displaying an in-app message inline by setting a expiration on the in-app message. If a message is being displayed in the app > a fetch gets message from backend for the user > fetch no longer contains the message being displayed, it can be considered expired. If this scenario happens, we want the inline View to gracefully stop showing the in-app message. This is done by animating the height from N to 0 which makes the inline View no longer visible.

Here is a demo of this behavior:

dismiss.demo.mov

The most important part of the video is when the in-app message animates it's height back to 0 so the in-app message is no longer visible. This is the new behavior implemented in this PR.

Testing:

  • First, I QA tested this feature in our sample app.

I tested...

  1. the animation looks good
  2. if the inline View is currently animating the height from 0 to N, we cancel that animation and begin a new animation back down to 0.

Testing in-app message expiration is difficult because messages have a minimum of 1 hour of expiration.

Instead of waiting for 1 hour, I simulated this behavior by adding this line of code to the inline View's setupView():

// Run block of code in 10 seconds giving time to the SDK to perform fetch, find 1 in-app message I sent myself and render it. 
DispatchQueue.main.asyncAfter(deadline: .now() + 10) { 
   print("InAppMessageView: Simulating message expired.")
   Gist.shared.messageQueueManager.processFetchedMessages([]) // simulate message expired
   // This line of code above executes in the SDK after a fetch to the backend server is completed. Provide an empty array to indicate that the in-app message previously fetched has expired on the backend and so it's no longer being returned. 
}

The demo video was recorded with this block of code used.

  • After QA testing was done, I wrote automated tests. All of the test cases I was QA testing have been added to the automated test suite.

Based on PR #724

Full chain of PRs as of 2024-06-04

Part of: https://linear.app/customerio/issue/MBL-310/create-a-uikit-uiview-that-displays-an-inline-in-app-message-sent-from

Create a WebView to display inline in-app message and display in the Inline UIView. To do this, we can re-use a lot of the same logic that Modal messages use to create WebViews and display them.

Testing:
There are some automated tests added in this commit.

Reviewer notes:
This commit will not show an in-app message if you were to add it to a sample app. Autolayout constraints need to be added to have Views resize and that will come in a future commit.

commit-id:025c7bf5
…UIKit app

Part of: https://linear.app/customerio/issue/MBL-310/create-a-uikit-uiview-that-displays-an-inline-in-app-message-sent-from

This commit implements UIKit AutoLayout constraints to the Inline View and the WebView. Adding these constraints make inline messages visible inside of UIKit apps! The Inline View will start at height 0 and then after the webview content is loaded, it will instantly change the height to display the full in-app message.

Testing:
All testing is done manually in sample apps. AutoLayout constraints change the visuals of Views so viewing how the View looks in an app is required to test.

If you want to test this commit in a sample app build, follow these instructions:
* Open the UIKit app on device.
* Immediately after the app opens, send yourself a test in-app message with element ID "test".
* Wait 10 seconds. In this time, the in-app SDK will fetch the test in-app message and then will display it inline on the screen.

commit-id:5ab2c403
Part of: https://linear.app/customerio/issue/MBL-310/create-a-uikit-uiview-that-displays-an-inline-in-app-message-sent-from

Fetching of in-app message is an async operation running in the background. There is a chance that in-app messages are not available when the inline View is constructed but become available after. This commit gets notified when a fetch is complete so it can check if there are in-app messages to display after the View has already been constructed.

Testing:
* Automated tests are added.
* Verified that messages display in sample app.
If you want to test this commit in a sample app build, follow these instructions:
1. Kill the app on device.
2. Send yourself a test inline message from Fly.
3. Open app. After you wait a few seconds, the inline message you sent yourself will display.

commit-id:aea90dff
… View

Part of: https://linear.app/customerio/issue/MBL-311/provide-positive-ux-when-inline-messages-displayed-by-animating-height

Fetching of in-app message is an async operation running in the background. There is a chance that an inline in-app message is rendered and ready to display on the screen currently in the foreground. We want to display the inline message when it's ready to show, and we also want to display these messages without causing a negative UX.

This commit does the following:
1. Hides the inline View when the View is constructed by setting the height to 0.
2. When the in-app message web content is rendered, the inline View is given a height that matches the web content aspect ratio. The inline View animates it's height from 0 to the new height to make the inline View visible.

Testing:
This change is heavy on UI so testing is done mostly in UIKit sample app QA testing. If you want to test this commit in a sample app build, follow these instructions:
1. Open the app on device.
2. Send yourself a test inline message from Fly to one of the inline Views on Dashboard screen.
3. After you wait a few seconds, you will see the inline message animate into visibility.

Notes for reviewer:
* During development, I encountered difficulties with getting the animation to work when the inline View is embedded in a StackView. Because of this, I modified the Dashboard screen's UI to nest the inline Views inside of the StackView to make sure we QA test this scenario.
@levibostian levibostian self-assigned this Jun 4, 2024
@levibostian levibostian changed the title animate height to 0 chore: dismiss message if message expires Jun 4, 2024
just testing the animation here.

Testing
* I created a timer that after 5 seconds it dismisses.
Testing:
* in sample app, simulate a fetch by calling MessageQueueManager directly with an empty array.
@levibostian levibostian force-pushed the levi/inline-dismiss-when-expire branch from 236a9dc to 66e9b4d Compare June 4, 2024 18:52
Copy link

github-actions bot commented Jun 4, 2024

Sample app builds 📱

Below you will find the list of the latest versions of the sample apps. It's recommended to always download the latest builds of the sample apps to accurately test the pull request.


  • CocoaPods-FCM: levi/inline-dismiss-when-expire (1719426915)
  • APN-UIKit: levi/inline-dismiss-when-expire (1719426922)

Makefile Outdated
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After adding EngineWeb and EngineWebProvider to auto-generated files, auto-generated files need UIKit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I copied this change from this PR which also adds testing support to the in-app SDK.

Copy link

github-actions bot commented Jun 4, 2024

SDK binary size reports 📊

SDK binary size of this PR
    FILE SIZE        VM SIZE    
 --------------  -------------- 
  16.5%   143Ki  16.5%   143Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/autogenerated/AutoMockable.generated.swift
   4.0%  35.0Ki   4.0%  35.0Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingPush/autogenerated/AutoMockable.generated.swift
   4.0%  34.7Ki   4.0%  34.7Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Communication/EventBusHandler.swift
   3.4%  29.7Ki   3.4%  29.7Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/autogenerated/AutoMockable.generated.swift
   2.6%  22.8Ki   2.6%  22.8Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Migration/autogenerated/AutoMockable.generated.swift
   2.6%  22.7Ki   2.6%  22.7Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/DataPipeline/DataPipelineImplementation.swift
   2.4%  20.9Ki   2.4%  20.9Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Background Queue/QueueStorage.swift
   2.2%  19.0Ki   2.2%  19.0Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingPushFCM/autogenerated/AutoMockable.generated.swift
   2.2%  18.9Ki   2.2%  18.9Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingPushAPN/autogenerated/AutoMockable.generated.swift
   2.2%  18.7Ki   2.2%  18.7Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/EngineWeb/AnyEncodable.swift
   2.1%  18.6Ki   2.1%  18.6Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Communication/Event.swift
   1.8%  15.4Ki   1.8%  15.4Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Managers/MessageQueueManager.swift
   1.8%  15.2Ki   1.8%  15.2Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingPush/Type/PushNotification.swift
   1.7%  14.9Ki   1.7%  14.9Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Gist.swift
   1.7%  14.8Ki   1.7%  14.8Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Managers/MessageManager.swift
   1.7%  14.6Ki   1.7%  14.6Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Communication/EventMemoryStorage.swift
   1.6%  13.8Ki   1.6%  13.8Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/autogenerated/AutoDependencyInjection.generated.swift
   1.6%  13.5Ki   1.6%  13.5Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Communication/EventStorage.swift
   1.5%  12.9Ki   1.5%  12.9Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Service/Request/TrackDeliveryEventRequestBody.swift
   1.4%  11.8Ki   1.4%  11.8Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/DataPipeline/DataPipeline.swift
   1.2%  10.3Ki   1.2%  10.3Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingPush/PushHandling/PushEventHandlerProxy.swift
   1.1%  9.65Ki   1.1%  9.65Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Util/JsonAdapter.swift
   1.1%  9.50Ki   1.1%  9.50Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/CustomerIOInstance.swift
   1.0%  9.10Ki   1.0%  9.10Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Utilities/URLComponents.swift
   1.0%  8.61Ki   1.0%  8.61Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/DataPipeline/Plugins/AutoTrackingScreenViews.swift
   1.0%  8.53Ki   1.0%  8.53Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingPush/UserNotificationsFramework/Wrappers.swift
   0.9%  8.22Ki   0.9%  8.22Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Migration/DataPipelineMigrationAssistant.swift
   0.9%  8.17Ki   0.9%  8.17Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Views/InAppMessageView.swift
   0.9%  7.92Ki   0.9%  7.92Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingPush/RichPush/RichPushHttpClient.swift
   0.9%  7.77Ki   0.9%  7.77Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Background Queue/Queue.swift
   0.8%  7.23Ki   0.8%  7.23Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/EngineWeb/EngineWeb.swift
   0.8%  7.06Ki   0.8%  7.06Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Background Queue/Type/QueueTaskMetadata.swift
   0.8%  6.84Ki   0.8%  6.84Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingPush/Store/PushHistory.swift
   0.7%  6.45Ki   0.7%  6.45Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/MessagingInAppImplementation.swift
   0.7%  6.31Ki   0.7%  6.31Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Managers/ModalViewManager.swift
   0.7%  6.14Ki   0.7%  6.14Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Util/Log.swift
   0.7%  5.70Ki   0.7%  5.70Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Migration/TrackEventMigrationType.swift
   0.7%  5.64Ki   0.7%  5.64Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Background Queue/Type/QueueTask.swift
   0.6%  5.39Ki   0.6%  5.39Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingPush/autogenerated/AutoDependencyInjection.generated.swift
   0.6%  5.16Ki   0.6%  5.16Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Service/Response/ErrorMessageResponse.swift
   0.6%  5.15Ki   0.6%  5.15Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Util/KeyValueStorage.swift
   0.6%  4.86Ki   0.6%  4.86Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Network/GistQueueNetwork.swift
   0.5%  4.71Ki   0.5%  4.71Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingPush/Config/MessagingPushConfigBuilder.swift
   0.5%  4.52Ki   0.5%  4.52Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Communication/InAppEventBusEvents.swift
   0.5%  4.49Ki   0.5%  4.49Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/DataPipeline/Plugins/DataPipelinePublishedEvents.swift
   0.5%  4.44Ki   0.5%  4.44Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Service/Request/MetricRequest.swift
   0.5%  4.43Ki   0.5%  4.43Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Communication/EventBus.swift
   0.5%  4.40Ki   0.5%  4.40Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/DataPipeline/Plugins/DeviceContexualAttributes.swift
   0.5%  4.22Ki   0.5%  4.22Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Util/FileStorage.swift
   0.5%  4.22Ki   0.5%  4.22Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingPush/PushHandling/iOSPushEventListener.swift
   0.5%  4.10Ki   0.5%  4.10Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Managers/QueueManager.swift
   0.5%  3.95Ki   0.5%  3.95Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Managers/Models/Message.swift
   0.4%  3.79Ki   0.4%  3.79Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/EngineWeb/EngineWebConfiguration.swift
   0.4%  3.75Ki   0.4%  3.75Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Module/ModuleTopLevelObject.swift
   0.4%  3.70Ki   0.4%  3.70Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Service/HttpRequestRunner.swift
   0.4%  3.55Ki   0.4%  3.55Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingPush/MessagingPush.swift
   0.4%  3.41Ki   0.4%  3.41Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Views/GistModalViewController.swift
   0.4%  3.37Ki   0.4%  3.37Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Extensions/StringExtensions.swift
   0.4%  3.33Ki   0.4%  3.33Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Config/MessagingInAppConfigBuilder.swift
   0.4%  3.32Ki   0.4%  3.32Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Util/Atomic.swift
   0.4%  3.22Ki   0.4%  3.22Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingPush/RichPush/MessagingPush+RichPush.swift
   0.4%  3.10Ki   0.4%  3.10Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Util/StringAnyEncodable.swift
   0.4%  3.06Ki   0.4%  3.06Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/DIManager.swift
   0.3%  3.02Ki   0.3%  3.02Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/EngineWeb/EngineEventHandler.swift
   0.3%  2.98Ki   0.3%  2.98Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/DataPipeline/Plugins/Context.swift
   0.3%  2.98Ki   0.3%  2.98Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Network/BaseNetwork.swift
   0.3%  2.89Ki   0.3%  2.89Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Util/DeviceInfo.swift
   0.3%  2.88Ki   0.3%  2.88Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Background Queue/Task Data/DeletePushNotificationQueueTaskData.swift
   0.3%  2.84Ki   0.3%  2.84Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Background Queue/Task Data/IdentifyProfileQueueTaskData.swift
   0.3%  2.84Ki   0.3%  2.84Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Background Queue/Task Data/TrackEventQueueTaskData.swift
   0.3%  2.65Ki   0.3%  2.65Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Service/HttpRequestParams.swift
   0.3%  2.64Ki   0.3%  2.64Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingPush/PushHandling/ManualPushHandling+UserNotifications.swift
   0.3%  2.58Ki   0.3%  2.58Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Background Queue/Task Data/RegisterPushNotificationQueueTaskData.swift
   0.3%  2.55Ki   0.3%  2.55Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Service/HttpRequestError.swift
   0.3%  2.52Ki   0.3%  2.52Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/DataPipeline/Util/DeviceAttributesProvider.swift
   0.3%  2.34Ki   0.3%  2.34Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingPush/RichPush/RichPushRequestHandler.swift
   0.3%  2.30Ki   0.3%  2.30Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingPushAPN/MessagingPushAPN.swift
   0.3%  2.29Ki   0.3%  2.29Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/DataPipeline/Config/SDKConfigBuilder.swift
   0.3%  2.25Ki   0.3%  2.25Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingPush/MessagingPushImplementation.swift
   0.3%  2.23Ki   0.3%  2.23Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingPush/UserNotificationsFramework/UserNotificationsFrameworkAdapter.swift
   0.3%  2.20Ki   0.3%  2.20Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingPushFCM/MessagingPushFCM.swift
   0.2%  2.16Ki   0.2%  2.16Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Managers/ModalMessageManager.swift
   0.2%  2.09Ki   0.2%  2.09Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Background Queue/Type/QueueTaskRunResults.swift
   0.2%  1.95Ki   0.2%  1.95Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Util/LockManager.swift
   0.2%  1.80Ki   0.2%  1.80Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingPushAPN/MessagingPush+APN.swift
   0.2%  1.77Ki   0.2%  1.77Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingPush/RichPush/RichPushDeliveryTracker.swift
   0.2%  1.71Ki   0.2%  1.71Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/DataPipeline/autogenerated/AutoDependencyInjection.generated.swift
   0.2%  1.66Ki   0.2%  1.66Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Network/Models/UserQueueResponse.swift
   0.2%  1.65Ki   0.2%  1.65Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingPushFCM/MessagingPush+FCM.swift
   0.2%  1.62Ki   0.2%  1.62Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Extensions/DateExtensions.swift
   0.2%  1.57Ki   0.2%  1.57Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Views/GistView.swift
   0.2%  1.55Ki   0.2%  1.55Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/autogenerated/AutoDependencyInjection.generated.swift
   0.2%  1.49Ki   0.2%  1.49Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Service/Request/PushMetric.swift
   0.2%  1.46Ki   0.2%  1.46Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/MessagingInApp.swift
   0.2%  1.42Ki   0.2%  1.42Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Service/CIOApiEndpoint.swift
   0.2%  1.42Ki   0.2%  1.42Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/DataPipeline/autogenerated/AutoMockable.generated.swift
   0.2%  1.37Ki   0.2%  1.37Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingPush/PushHandling/PushClickHandler.swift
   0.2%  1.34Ki   0.2%  1.34Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Store/SdkConfig.swift
   0.2%  1.34Ki   0.2%  1.34Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingPush/RichPush/RichPushRequest.swift
   0.2%  1.32Ki   0.2%  1.32Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Service/Request/InAppMetric.swift
   0.2%  1.30Ki   0.2%  1.30Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Util/Mocks.swift
   0.1%  1.24Ki   0.1%  1.24Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Util/UIKitWrapper.swift
   0.1%  1.24Ki   0.1%  1.24Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Util/ThreadUtil.swift
   0.1%  1.22Ki   0.1%  1.22Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Extensions/DictionaryExtension.swift
   0.1%  1.21Ki   0.1%  1.21Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Store/GlobalDataStore.swift
   0.1%  1.18Ki   0.1%  1.18Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Util/KeyValueStorageKey.swift
   0.1%  1.14Ki   0.1%  1.14Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/DataPipeline/Config/DataPipelineConfigOptions.swift
   0.1%  1.12Ki   0.1%  1.12Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Extensions/DeviceExtension.swift
   0.1%  1.12Ki   0.1%  1.12Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Type/Region.swift
   0.1%  1.04Ki   0.1%  1.04Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingPushFCM/MessagingPushFCM+PushConfigs.swift
   0.1%  1.02Ki   0.1%  1.02Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/DIGraphShared.swift
   0.1%  1.02Ki   0.1%  1.02Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/EngineWeb/EngineRoute.swift
   0.1%    1024   0.1%    1024    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Util/AnyEncodable.swift
   0.1%    1004   0.1%    1004    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Provider/InAppProvider.swift
   0.1%     936   0.1%     936    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Service/UserAgentUtil.swift
   0.1%     936   0.1%     936    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingPushAPN/MessagingPushAPN+PushConfigs.swift
   0.1%     908   0.1%     908    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Network/Utilities/HTTPMethod.swift
   0.1%     880   0.1%     880    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Background Queue/Type/QueueTaskType.swift
   0.1%     872   0.1%     872    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Service/Request/EmptyRequestBody.swift
   0.1%     832   0.1%     832    /Users/runner/work/customerio-ios/customerio-ios/Sources/DataPipeline/CustomerIO+Events.swift
   0.1%     832   0.1%     832    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingPush/Util/DeepLinkUtil.swift
   0.1%     820   0.1%     820    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Extensions/ArrayExtensions.swift
   0.1%     792   0.1%     792    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Network/Endpoints/Utilities/GistNetworkRequest.swift
   0.1%     772   0.1%     772    /Users/runner/work/customerio-ios/customerio-ios/Sources/DataPipeline/Plugins/Logger.swift
   0.1%     764   0.1%     764    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Type/InAppMessage.swift
   0.1%     752   0.1%     752    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Utilities/Environment.swift
   0.1%     748   0.1%     748    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Managers/LogManager.swift
   0.1%     652   0.1%     652    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingPush/Type/CustomerIOParsedPushPayload.swift
   0.1%     624   0.1%     624    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Network/Utilities/ElapsedTimer.swift
   0.1%     612   0.1%     612    /Users/runner/work/customerio-ios/customerio-ios/Sources/DataPipeline/CustomerIO.swift
   0.1%     600   0.1%     600    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Service/DownloadFileType.swift
   0.1%     576   0.1%     576    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Extensions/UIKitExtensions.swift
   0.1%     572   0.1%     572    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingPush/Config/MessagingPushConfigOptions.swift
   0.1%     568   0.1%     568    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Network/Endpoints/QueueEndpoint.swift
   0.1%     508   0.1%     508    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Network/Endpoints/Utilities/GistNetworkRequestError.swift
   0.1%     504   0.1%     504    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Store/ProfileStore.swift
   0.1%     496   0.1%     496    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Background Queue/Type/QueueStatus.swift
   0.1%     484   0.1%     484    /Users/runner/work/customerio-ios/customerio-ios/Sources/DataPipeline/CustomerIO+Segment.swift
   0.1%     484   0.1%     484    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Managers/RouteManager.swift
   0.1%     480   0.1%     480    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Util/RingBuffer.swift
   0.1%     480   0.1%     480    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Utilities/Encodable.swift
   0.1%     476   0.1%     476    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Managers/InlineMessageManager.swift
   0.1%     468   0.1%     468    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Network/Endpoints/LogEndpoint.swift
   0.0%     380   0.0%     380    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Config/MessagingInAppConfigOptions.swift
   0.0%     332   0.0%     332    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Extensions/GistExtensions.swift
   0.0%     248   0.0%     248    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Utilities/Logger.swift
   0.0%     236   0.0%     236    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingPush/Util/NotificationCenterWrapper.swift
   0.0%     228   0.0%     228    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Network/NetworkSettings.swift
   0.0%     204   0.0%     204    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Util/Lock.swift
   0.0%     196   0.0%     196    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Util/DeviceMetricsGrabber.swift
   0.0%     192   0.0%     192    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingPush/PushHandling/AutomaticPushClickHandling.swift
   0.0%     164   0.0%     164    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Store/QueueInventoryMemoryStore.swift
   0.0%     128   0.0%     128    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Util/Timer.swift
   0.0%     120   0.0%     120    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Extensions/DataExtensions.swift
   0.0%     116   0.0%     116    /Users/runner/work/customerio-ios/customerio-ios/Sources/DataPipeline/Plugins/CustomerIODestination.swift
   0.0%      76   0.0%      76    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Managers/UserManager.swift
   0.0%      68   0.0%      68    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Util/SingleScheduleTimer.swift
   0.0%      64   0.0%      64    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Util/DateUtil.swift
   0.0%      52   0.0%      52    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Version.swift
   0.0%      48   0.0%      48    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/EngineWeb/EngineWebProvider.swift
   0.0%      36   0.0%      36    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Util/Time.swift
   0.0%      20   0.0%      20    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Deprecated/CustomerIO+Tracking.swift
   0.0%       8   0.0%       8    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Extensions/ErrorExtension.swift
 100.0%   867Ki 100.0%   867Ki    TOTAL
Filtering enabled (source_filter); omitted file = 2.52Mi, vm = 2.59Mi of entries

SDK binary size diff report between this PR and the main branch
    FILE SIZE        VM SIZE    
 --------------  -------------- 
  [NEW] +8.17Ki  [NEW] +8.17Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Views/InAppMessageView.swift
   +21% +5.16Ki   +21% +5.16Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/autogenerated/AutoMockable.generated.swift
  [NEW] +4.52Ki  [NEW] +4.52Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Communication/InAppEventBusEvents.swift
   +21% +2.65Ki   +21% +2.65Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Managers/MessageQueueManager.swift
  [NEW] +2.16Ki  [NEW] +2.16Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Managers/ModalMessageManager.swift
  [NEW] +1.55Ki  [NEW] +1.55Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/autogenerated/AutoDependencyInjection.generated.swift
  [NEW]    +476  [NEW]    +476    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Managers/InlineMessageManager.swift
  +5.4%    +476  +5.4%    +476    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Utilities/URLComponents.swift
  +8.9%    +344  +8.9%    +344    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Managers/QueueManager.swift
  +1.7%    +316  +1.7%    +316    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/EngineWeb/AnyEncodable.swift
  +1.7%    +260  +1.7%    +260    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Gist.swift
   +25%    +244   +25%    +244    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Util/KeyValueStorageKey.swift
  -0.3%     -80  -0.3%     -80    [6 Others]
  -0.8%    -104  -0.8%    -104    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Service/Request/TrackDeliveryEventRequestBody.swift
  -8.2%    -312  -8.2%    -312    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Views/GistModalViewController.swift
 -25.1%    -336 -25.1%    -336    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Provider/InAppProvider.swift
  -4.7%    -356  -4.7%    -356    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Background Queue/Type/QueueTaskMetadata.swift
 -39.9%    -496 -39.9%    -496    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Managers/LogManager.swift
 -14.4%    -680 -14.4%    -680    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Managers/Models/Message.swift
 -17.0% -1.05Ki -17.0% -1.05Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/Common/Util/KeyValueStorage.swift
 -15.3% -2.68Ki -15.3% -2.68Ki    /Users/runner/work/customerio-ios/customerio-ios/Sources/MessagingInApp/Gist/Managers/MessageManager.swift
  +2.4% +20.2Ki  +2.4% +20.2Ki    TOTAL
Filtering enabled (source_filter); omitted file = 2.52Mi, vm = 2.59Mi of entries

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I copied this change from this PR which also adds testing support to the in-app SDK.

By mocking the EngineWeb, we are able to do the same thing as if we were mocking HTTP requests in our SDK. We can mock the process of rendering in-app messages from the web server.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See MessageManager for use of the provider.

Copy link

codecov bot commented Jun 4, 2024

Codecov Report

Attention: Patch coverage is 95.74468% with 2 lines in your changes missing coverage. Please review.

Please upload report for BASE (feature-inline-inapp@2275ca5). Learn more about missing BASE report.

Files Patch % Lines
...s/MessagingInApp/Extensions/UIViewExtensions.swift 75.00% 2 Missing ⚠️
Additional details and impacted files
@@                   Coverage Diff                   @@
##             feature-inline-inapp     #733   +/-   ##
=======================================================
  Coverage                        ?   64.61%           
=======================================================
  Files                           ?      145           
  Lines                           ?     4123           
  Branches                        ?        0           
=======================================================
  Hits                            ?     2664           
  Misses                          ?     1459           
  Partials                        ?        0           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

queueMock.getInlineMessagesReturnValue = []

let inlineView = InAppMessageView(elementId: .random)

XCTAssertNil(getInAppMessageWebView(fromInlineView: inlineView))
XCTAssertFalse(isDisplayingInAppMessage(inlineView))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed some of the getInAppMessageWebView calls to the new isDisplayingInAppMessage to make the test function more accurate checking if the inline View is displaying a in-app message. By testing the height of the View is not 0, we are more certain that the inline View is visible/displayed to the user.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Besides adding new test functions, this file needed other changes.

  • I added a onDoneRenderingInAppMessage function to run the code that animates the height from 0 to N to display in-app messages. I designed onDoneRenderingInAppMessage to not return until the sizeChanged() function is called on the inline View.

  • Because onDoneRenderingInAppMessage is now async, I needed to make all test functions async to use it. When you do that, you can no longer create InAppMessageView instances in the test function unless you assign the test function to the main thread (@MainActor).

@levibostian levibostian requested a review from a team June 4, 2024 19:25
@levibostian levibostian marked this pull request as ready for review June 4, 2024 19:25
@Ahmed-Ali
Copy link
Contributor

How will the changes in that pr impact this one?
Also is there a chance to break down this PR farther? I wanted to review today but there were many things that I thought I would get back to it later when I have more capacity to go through it all properly. There are other changes that I could have easily approved. hence the ask for a break down.

}

// MARK: View constructed

@MainActor
func test_whenViewConstructedUsingStoryboards_expectCheckForMessagesToDisplay() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think we should check for view.inlineMessageManager to be nil and view.viewHeightConstraint and be 0?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added 2 new tests to check that after Views are constructed they start with a height of 0 (aka: dismissed state).


let webViewAfterFetch = getInAppMessageWebView(fromInlineView: inlineView)

// If the WebViews are different, it means the message was reloaded.
// If the WebViews are the same instance, it means the message was not reloaded.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good check? should we also probably check for inlinemanager.currentMessage.queueId, givenInlineMessage.queueId ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the single-source-of-truth is the UI, I like how this test does an assertion on the View layer. I don't know if the test would increase in accuracy by doing this check. Can you think of a reason to?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great collection of tests! few areas that I feel could use some tests

  • Multiple event bus events,

something like

queueMock.getInlineMessagesReturnValue = []
    DIGraphShared.shared.eventBusHandler.postEvent(InAppMessagesFetchedEvent())
    await waitForMainThreadToFinishPendingTasks()

    queueMock.getInlineMessagesReturnValue = [secondMessage]
    DIGraphShared.shared.eventBusHandler.postEvent(InAppMessagesFetchedEvent())
    await onDoneRenderingInAppMessage(secondMessage)
  • Multiple messages returned, should we be getting the one with higher priority to be displayed?
    queueMock.getInlineMessagesReturnValue = [firstMessage, secondMessage]
  • Delayed Setting of element ID?

  • Multiple instances of InAppMessageView operate independently without interference

  • Changes to dynamic constraints? we are already changing height constraints, if user changes height/width what end height width constraint are we expecting?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the test suggestions. You put a lot of thought into that, thanks!

Let me try and put these in my own words to see if we align.

  • Multiple event bus events

I can think of these tests:

  1. Given fetch event returning 0 events, expect View to not display any message. Then another fetch happens, event gets returned, we expect the View to display that message.
  2. Given fetch event returning N events, but none with element Id matching the View. We expect that the View does not display any messages. Then a fetch happens returning a message with element id, we expect the View to display that message.
  • Multiple messages returned, should we be getting the one with higher priority to be displayed?

Priority logic is in the business layer and tests exist for that so I don't see a need to add a test for that in the View.

  • Delayed Setting of element ID?

A test like this? Given no element id set of View yet, fetch occurs returns messages with a matching element id, then an element id is set on View, we expect that the inline View displays a message.

  • Multiple instances of InAppMessageView operate independently without interference

Good idea. Tests where 2 Views are created with matching IDs and non-matching IDs would be good tests.

  • Changes to dynamic constraints? we are already changing height constraints, if user changes height/width what end height width constraint are we expecting?

I will need some help with this one. Could you give an example of tests cases?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are spot on all the additions of tests you mentioned. We are on the same page.

Changes to dynamic constraints? we are already changing height constraints, if user changes height/width what end height width constraint are we expecting?

I was referring to, if user set the height and width of the view at beginning, and when we inflate the in-app view, the final height and width of the controller should be what we expect it not what user set its.

Similarly, if we inflate the view and set the height and width of the controller, if user updates the width/height, what size are we expecting at the end?

Copy link
Contributor Author

@levibostian levibostian Jun 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've looked into each of the suggestions. Either the test case is now covered, or I suggest a follow-up PR to introduce it. See my responses below:

Multiple event bus events

  • Test case: Given fetch event returning 0 events, expect View to not display any message. Then another fetch happens, event gets returned, we expect the View to display that message.

This is covered in test test_givenInAppMessageFetchedAfterViewConstructed_expectShowInAppMessageFetched

  • Test case: Given fetch event returning N events, but none with element Id matching the View. We expect that the View does not display any messages. Then a fetch happens returning a message with element id, we expect the View to display that message.

This is going to require some refactors to the test class to be more of an integration test then a unit test as it is now. Because this will require some refactors to the test suite, I suggest we don't block this PR for it and we introduce this change in a follow-up PR.

Delayed Setting of element ID

  • Test case: Given no element id set of View yet, fetch occurs returns messages with a matching element id, then an element id is set on View, we expect that the inline View displays a message.

This is covered in test test_whenViewConstructedUsingStoryboards_expectCheckForMessagesToDisplay, since a delay in setting element ID is only with constructing a View used in a Storyboard.

Multiple instances of InAppMessageView operate independently without interference

Same response as above when referring to integration tests. Same suggestion applies - unblock this PR and introduce follow-up PR.

Changes to dynamic constraints

Tests have been pushed for these scenarios.

Base automatically changed from levi/inline-animate-open to feature-inline-inapp June 12, 2024 13:00
@levibostian levibostian requested a review from Shahroz16 June 26, 2024 18:13
Comment on lines +267 to 268
// swiftlint:disable:next force_cast
(Gist.shared.messageQueueManager as! MessageQueueManagerImpl).processFetchedMessages(messages)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer to not have private functions such as processFetchedMessages as part of the MessageQueueManager protocol as the protocol is meant to be the public API that the SDK calls and only the tests uses processFetchedMessages.

Instead of directly calling processFetchedMessages, we could mock the GistQueueNetwork HTTP request which then calls processFetchedMessages. But I believe that change would go beyond the scope of this PR and may not be worth the effort when this call does the same thing.

Copy link
Contributor

@Shahroz16 Shahroz16 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking forward to the later PRs with the tests.

@levibostian levibostian merged commit 4587e57 into feature-inline-inapp Jun 27, 2024
11 checks passed
@levibostian levibostian deleted the levi/inline-dismiss-when-expire branch June 27, 2024 13:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants