DEV Community

Mustafa ERBAY
Mustafa ERBAY

Posted on • Originally published at mustafaerbay.com.tr

Mobile App Size Optimization vs. Push Notification…

When I develop a mobile application, one of the first questions that comes to mind is usually, "How much space will this app take up?" We know users have limited storage space on their devices, and large apps often create the initial barrier to download. However, when you start playing with the app's size, you can inadvertently affect another critical metric: push notification reliability. In this post, I'll explain how I strike the delicate balance between these two important factors, the trade-offs I've faced, and the decisions I've ultimately made.

Why Does App Size Matter?

Mobile app size has a direct impact on user acquisition and retention rates. This is especially critical in emerging markets where users have limited mobile data plans and storage. A large app can deter users during download or create a negative experience by increasing data usage during updates. In the past, we observed a 15% increase in download rates by reducing an app's initial download size by 30% on one project. This was a concrete example of how even seemingly small optimizations can significantly influence user behavior.

ℹ️ Factors Affecting App Size

The main factors influencing app size include:

  • Codebase Size: The complexity of the application and the features added.
  • Asset Sizes: Images, sounds, and other multimedia files.
  • Libraries Used: The size and number of third-party libraries.
  • Native Modules: Modules developed with platform-specific languages like C/C++.
  • Asset Bundles: Additional packages like language files, themes, etc.

Each of these factors can unnecessarily increase the app's size if not managed properly.

When performing these optimizations, I typically resort to methods like code shrinking, resource compression, and removing unnecessary code or libraries. For example, on Android, I can shrink and optimize my code using ProGuard or R8. In Swift and Objective-C projects, I can achieve similar results by carefully adjusting LLVM's optimization levels. Using more efficient formats like WebP for images or providing optimized drawable/mipmap resources for different screen densities are also effective methods for size reduction.

Why is Push Notification Reliability Critical?

Push notifications are one of the most effective ways to communicate directly with users, bring them back to the app, and increase engagement. When a notification doesn't arrive on time and correctly, it can mean a lost user, a missed opportunity, or even user dissatisfaction. Especially for e-commerce, news, or messaging apps, the reliability of notifications is as crucial as the app's core functionality. On one occasion, delays or complete non-delivery of instant alert notifications sent to operators in a production ERP project caused significant disruptions in production. This situation once again showed me how critical the notification system is.

⚠️ Common Push Notification Problems

Common issues encountered with push notifications include:

  • Notifications Not Arriving: Device in power-saving mode, network connectivity issues, temporary glitches in platform services (FCM, APNS).
  • Delayed Notifications: Server-side processing delays or network path problems.
  • Incorrect or Missing Information: Notification content not being updated or incorrect data being sent.
  • Excessive Notifications: Sending notifications too frequently, which can annoy the user (not directly reliability, but critical for user experience).

Each of these issues can weaken the app's connection with its user.

To ensure the reliability of push notifications, it's necessary to integrate correctly with platform services (Firebase Cloud Messaging - FCM, Apple Push Notification Service - APNS), be able to process and send notifications quickly on the server side, and correctly receive and display notifications on the device side. Additionally, notification strategies should be determined considering the device's state (network connection, battery level, etc.). For instance, different channels or mechanisms might be considered for critical alerts.

Optimization Methods and Their Effects

Some methods I use to reduce app size can indirectly affect notification systems. For example, removing certain libraries or simplifying the codebase might lead to the removal or modification of modules that handle sending or receiving notifications. To give an example, after removing a large analytics library in one project, the background notification services also needed to be reconfigured because they were dependent on this library. This is a scenario more commonly encountered in projects with complex dependencies.

# Example R8 configuration used on Android for code shrinking and optimization.
# This removes unused code and makes the code more efficient.
# However, if misconfigured, it can also affect critical services.

-keepattributes *Annotation*,EnclosingMethod,Signature,Exceptions,InnerClasses
-keep class com.example.myapp.** { *; }
-keep class com.google.firebase.messaging.** { *; } # Preserve classes related to FCM

# Parts of some libraries accessed via reflection might need to be preserved
-keepclassmembers class * {
    @android.webkit.JavascriptInterface <methods>;
}
Enter fullscreen mode Exit fullscreen mode

When performing such optimizations, techniques like "tree shaking" (discarding unused code) reduce the app's total size, but if used incorrectly, they can also lead to the removal of necessary code snippets related to the notification service. Therefore, preserving classes and methods related to notifications with keep rules in R8 or ProGuard configurations is vital. Otherwise, your app will shrink, but your notifications will stop working.

Details of Push Notification Services

Push notification services (FCM, APNS) typically manage communication between a client (the app) and a server. Overlooking code that interacts with these services during app size optimization can lead to serious problems. For instance, when I replaced a local notification management library used in an app with a lighter alternative, I found that some advanced features provided by the old library (e.g., automatically grouping notifications in certain scenarios) were lost. This was a trade-off that, while reducing the app's size, could negatively impact user experience.

💡 Key Integration Points for FCM and APNS

The core components of push notification integration include:

  • Device Token: A unique identifier that allows FCM or APNS servers to recognize the device. The app needs to send this token to the server.
  • Notification Sending API: The interface the server uses to send notifications to FCM or APNS (e.g., FCM Admin SDK, APNS HTTP/2 API).
  • Receiving and Processing Notifications: The app receiving and displaying notifications to the user in the background or foreground (e.g., `FirebaseMessagingService` or `UNUserNotificationCenterDelegate`).

Preserving code related to these components during optimization ensures that notifications function.

These services generally operate through the device's background execution time and network connection. In app size optimization, we try to clean up code that restricts the operation of background services or consumes unnecessary resources. However, push notification services also need to continue operating at a certain level in the background. If the app's overall "aggressive" battery optimization prevents these services from running, notifications may be delayed or may not arrive at all. Striking this balance is truly an experience-driven task.

Real Scenarios and Trade-offs

While working on an e-commerce application, the goal was to keep the download size around 10 MB. For this purpose, we removed many features and libraries that were initially included. However, during these optimizations, it was crucial that the app's background service, which sends instant campaign notifications to users, was not affected. In the initial attempts, we noticed that some notifications were not reaching users or were arriving very late.

Upon investigating the source of the problem, I discovered that an optimization aimed at reducing size by restricting the app's background processing time and disabling some seemingly unnecessary services was actually affecting certain timers that were critical for the notification service. This was a direct trade-off: we were compromising push notification reliability while reducing app size.

🔥 The Cost of Incorrect Optimization

Mistakes made during size optimization can directly impact user experience:

  • Lost Notifications: Notifications don't arrive due to restricted background services.
  • Delayed Notifications: Increased notification processing time.
  • App Instability: Unexpected errors caused by the removal of necessary services.
  • Loss of User Trust: An unreliable notification system also damages the app's overall credibility.

To avoid such issues, optimization steps should be carefully planned and tested.

To resolve this, I specifically marked the code and dependencies related to the notification service, preventing R8 from removing them. Additionally, while restricting background processing times, I made some platform-specific adjustments to increase the operating priority of notification services. Although this slightly increased the app's size beyond our target, we managed to bring push notification reliability above 99.9%. Ultimately, enabling users to receive critical information on time was more important than a few MB difference in size.

Which Balance to Strike?

So, what's more important in the end? Mobile app size, or push notification reliability? The answer to this question varies depending on the type of app and its target audience. However, in my experience, push notification reliability is often a more critical factor than app size. An app's size might be a few MB larger, but preventing users from accessing important information is unacceptable.

ℹ️ As a General Rule

As a general rule, the reliability of your app's core functionality and user interaction mechanisms (notifications, critical alerts, instant messaging, etc.) should always take precedence over the app's sheer size. Users won't overlook missing or delayed notifications just for a lighter app.

Therefore, when optimizing, I always follow these steps:

  1. Prioritization: Identify the app's most critical features and user experience components. Push notifications should be high on this list.
  2. Comprehensive Analysis: Before performing size optimization, analyze which parts of the app take up the most space.
  3. Careful Optimization: When using methods like code shrinking and resource compression, identify potential areas where critical services (notifications, background synchronization, etc.) might be affected.
  4. Testing and Verification: After optimization, perform comprehensive tests to ensure that both the app's size and all critical functions (especially notifications) are working.
  5. Trade-off Management: If size reduction requires compromising reliability, make this trade-off consciously and try to minimize its impact on user experience.

It's important to remember that users aren't just running an app on their smart devices; they are having an experience with your app. Every step of this experience, even the smallest optimization step, is part of this whole.

Top comments (0)