14 Minutes
10 Common Non-Crashing Bugs and How to Fix Them
Fix Bugs Faster! Log Collection Made Easy
Non-crashing bugs don’t make your app crash, but they can ruin the user experience and hurt your app’s ratings. Fixing these issues is critical to keeping users happy. Here’s a quick rundown of the most common non-crashing bugs and how to address them:
- Screen Layout Problems: Misaligned or overlapping UI elements. Use tools like Android Studio’s Layout Inspector or Xcode’s View Hierarchy Debugger to spot and fix these issues.
- Data Loss During Background Tasks: Prevent data loss by managing background tasks properly, using custom storage solutions, and validating data integrity.
- Wrong Date and Number Formats: Ensure proper formatting for dates, numbers, and currencies using locale-specific tools like
DateFormatter(iOS) orSimpleDateFormat(Android). - Deep Link Failures: Fix navigation issues caused by improper setup or OS restrictions by configuring deep links correctly and testing thoroughly.
- List View Memory Issues: Optimize memory usage in scrolling lists to prevent laggy performance by recycling unused resources and avoiding memory leaks.
- Slow Push Notifications: Speed up notifications by managing tokens, improving network handling, and monitoring delivery performance.
Table of Contents
Why It Matters
Non-crashing bugs might not seem urgent, but they can lead to slow performance, user frustration, and poor app reviews. By addressing these issues, you can improve user satisfaction and maintain strong app ratings.
Quick Tip: Regularly test your app on multiple devices and OS versions to catch these bugs early.
Want to dive deeper? Keep reading for actionable fixes for each bug type.
Debugging Like a Pro: Expert Strategies for Diagnosing and Fixing Android Bugs
1. Screen Layout Problems
Screen layout issues – like inconsistent designs, overlapping elements, or misaligned views across devices and resolutions – can make an app harder to use.
Spotting Layout Bugs
Tools like Android Studio’s Layout Inspector and Xcode’s View Hierarchy Debugger are great for finding UI problems. These tools let you inspect views and uncover issues such as:
| Layout Issue | Impact | Detection Method |
|---|---|---|
| Overlapping Elements | Hard to read content | Component Tree inspection |
| Incorrect Constraints | Broken layouts | Attributes tool window |
| Density Issues | Inconsistent sizing | Visualization |
| Runtime Layout Changes | Unexpected positioning | Layout snapshots |
For example, Android Studio’s Layout Inspector allows live debugging, letting you examine individual views and their properties. This makes it easier to pinpoint and fix layout issues, ensuring your app looks good on all devices.
Fixing Layout Bugs
Modern UI frameworks like Jetpack Compose and SwiftUI make fixing layout bugs simpler. Here’s how:
Responsive Layouts
“Creating adaptive layouts in Jetpack Compose is easier than ever. As a declarative UI toolkit, Jetpack Compose is well suited for designing and implementing layouts that adjust themselves to render content differently across a variety of sizes.”
Handling Dynamic Content
Take inspiration from the JetLagged app (October 2024), which used FlowRow to limit items to three per row, optimizing dashboard space [1].
Framework-Specific Tools
- In SwiftUI, use
ViewThatFitsto display the view that best fits the available space [2]. - For Jetpack Compose, leverage
BoxWithConstraintsto adjust content based on screen dimensions [3].
Key Layout Improvements
- Set proper layout attributes for consistent alignment [4].
- Design layouts that work with density-independent pixels.
- Use FlowRow and FlowColumn for responsive arrangements [5].
- Add smooth transitions with the
animateBoundsmodifier [6].
These steps help ensure your app delivers a polished and functional experience, even as you tackle other non-critical bugs.
2. Data Loss During Background Tasks
Losing data during background tasks can harm user experience, even if the app doesn’t crash. This issue arises when apps switch between active and background states, potentially leading to corrupted or missing data.
Signs of Data Loss
Data loss during background activities often presents itself in specific ways:
| Warning Sign | Impact | Common Scenario |
|---|---|---|
| Missing User Preferences | Settings unexpectedly reset | App relaunch after device reboot |
| Incomplete Background Saves | Data updates fail silently | App suspended during save |
| Corrupted State | Inconsistent data between sessions | Task interrupted in the background |
| Failed Resume State | Loss of user progress | Memory pressure events |
These problems are particularly common when apps use UserDefaults for saving data. iOS encryption can make this storage temporarily inaccessible during background launches, compounding the issue.
Preventing Data Loss
Avoiding data loss in background tasks requires careful debugging and thoughtful implementation.
Verify System State
Before performing operations, ensure the system is ready. Use UIApplication.isProtectedDataAvailable to confirm data accessibility, especially after device reboots or during Live Activities.
Use Custom Data Storage
Build a more reliable storage solution by writing directly to the app’s shared container directory. For example:
let queue = DispatchQueue(label: "com.app.storage")
queue.async {
// Perform thread-safe operations
// Use Codable for serialization
}
Manage Background Tasks Properly
Keep a close eye on the lifecycle of background tasks. As Christian Selig explains: “The whole point of UserDefaults is that it’s supposed to reliably store simple, non-sensitive data so it can be accessed whenever. The fact that this has now changed drastically, and at the same time your app can be launched effectively whenever, makes for a complex and challenging scenario.”
Perform Data Integrity Checks
Validate new data before overwriting existing information, particularly during background launches when UserDefaults may be temporarily unavailable. Incorporate error handling and logging to identify and fix data issues. Use background task completion handlers to ensure critical processes finish before the app is suspended.
Preventing data loss in background tasks is just as important as fixing layout issues when it comes to building a dependable app.
3. Wrong Date and Number Formats
Incorrect date and number formats can confuse users and lead to misunderstandings.
Format Error Examples
Here are some common formatting issues and their impact:
| Error Type | Incorrect Format | Correct Format (US) | Impact |
|---|---|---|---|
| Date Display | 2022-01-31T16:27:23Z | Jan 31, 2022, 11:27 AM | Causes user confusion |
| Currency | 1234.56 | $1,234.56 | Leads to financial errors |
| Large Numbers | 1000000 | 1,000,000 | Hard to read |
| Time Zones | 17:27 UTC | 12:27 PM EST | Misinterpreted schedules |
Some real-world scenarios include:
- News feeds showing timestamps in UTC instead of the user’s local time.
- Currency values displayed without symbols or proper separators.
- Analytics dashboards missing thousand separators for large numbers.
- Date inputs accepting formats that don’t align with the user’s locale.
Format Error Solutions
To avoid these issues, ensure proper locale-specific formatting:
iOS (Swift/SwiftUI) Implementation
Use Locale.current to format currency:
Text(amount, format: .currency(code: Locale.current.currency?.identifier ?? "USD"))
Android (Kotlin/Jetpack Compose) Implementation
For date formatting, use the default locale:
val formatter = SimpleDateFormat.getDateTimeInstance(
DateFormat.MEDIUM,
DateFormat.SHORT,
Locale.getDefault()
)
Best Practices for Both Platforms
- Maintain Currency Precision: Use Swift’s
Decimalor Kotlin’sBigDecimalfor accurate financial calculations. These types help prevent rounding errors. - Use Relative Dates: Show dates like “Today” or “Yesterday” instead of fixed timestamps, ensuring localization is applied.
- Test Across Locales:
- Set devices to different regions to verify formats.
- Confirm currency symbols and placement.
- Check thousand separators and date/time patterns.
- Rely on System Formatters:
- iOS: Use
NumberFormatterandDateFormatter. - Android: Use
NumberFormat.getCurrencyInstance()andSimpleDateFormat.
- iOS: Use
Next, we’ll dive into common causes of deep link failures and how to resolve them effectively.
4. Deep Link Failures
Deep link failures can disrupt navigation and negatively impact the user experience. These issues often remain unnoticed until users encounter navigation problems or abandon sessions.
Common Deep Link Errors
Here are some typical deep link issues:
| Error Type | Description | Impact |
|---|---|---|
| Installation Status | Links break after app installation | Users lose context post-installation |
| Device Fragmentation | Inconsistent behavior across platforms | Varying experiences on iOS and Android |
| Security Restrictions | OS-level blocks on specific links | Causes navigation failures |
| Data Transmission | Context loss during routing | Breaks personalization and continuity |
With Android 12’s stricter domain verification, generic web intents now only resolve to app activities if the app is explicitly approved for the domain. This change has led to previously functioning deep links failing unexpectedly [7]. Below are solutions to address these problems effectively.
Fixing Deep Link Issues
Fixing these errors improves navigation and boosts user engagement.
Platform-Specific Fixes
For Android:
// Ensure the manifest handles deep links properly
<activity android:name=".MainActivity">
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
</intent-filter>
</activity>
For iOS:
// Handle universal links in SceneDelegate
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL else {
return
}
processDeepLink(url)
}
Testing and Validation
Use Android Debug Bridge (ADB) to test deep links:
adb shell am start -W -a android.intent.action.VIEW -d "your-deep-link-url"
Best Practices for Reliable Deep Linking
- Use deferred deep links to manage cases where the app isn’t installed.
- Set up fallback routes for when deep links fail.
- Test across multiple OS versions and device types.
- Monitor performance with logging and analytics tools.
“Deep links are simply the best way to build a quality, connected user experience that will bring in new customers and keep the ones you have engaged and happy.” – Megan Dobransky [6]
When implementing fixes, focus on:
- Route Configuration: Properly set up URI schemes and universal links.
- Data Continuity: Preserve user context during installations and updates.
- Error Handling: Create fallback mechanisms for failed links.
- Security Compliance: Follow platform-specific security guidelines.
- Comprehensive Testing: Test functionality across various scenarios and devices.
5. List View Memory Issues
List view memory problems can slow down your app’s performance without causing it to crash. These issues often appear in Android’s RecyclerView and iOS’s LazyVGrid, leading to laggy scrolling and draining the battery faster than expected.
Identifying Memory Problems
On Android, the heap may hold onto unused memory. For instance, a single photo from a Pixel phone can take up around 48MB of memory when using the ARGB_8888 bitmap configuration [7].
Here are some common causes of memory issues:
| Issue Type | Impact | Common Cause |
|---|---|---|
| Static References | Permanent Memory Leaks | UI components stored as static variables |
| Background Thread Leaks | Growing Memory Usage | UI references in background tasks |
| Circular References | Memory Not Released | Adapters indirectly referencing Activities |
| Image Caching | Excessive Memory Usage | Mismanaged bitmap handling and recycling |
Fixing Memory Problems
Here are some practical solutions to handle memory issues in scrolling lists:
Optimizing Android RecyclerView
// Clear adapter references to avoid leaks
override fun onDestroyView() {
super.onDestroyView()
recyclerView.adapter = null
}
Managing Image Memory
// Properly recycle bitmaps when they're no longer needed
bitmap?.let {
if (!it.isRecycled) {
it.recycle()
}
}
“Android UI components are not thread-safe. It’s possible to kill an Activity but this does not necessarily kill a background thread. Because of this volatile complexity, memory leaks are possible as any instances of a component can easily turn into dead weight.” [7] – Amanda Hinchman, Google Developer Experts
Best Practices for Memory Optimization
For Jetpack Compose:
- Use
remember{ }blocks to maintain state without triggering unnecessary recompositions. - Add
@Stableand@Immutableannotations to data classes to reduce redundant updates. - Manage keys properly in
LazyColumnto track items efficiently. - Use lightweight image formats and apply compression to save memory.
For SwiftUI:
- Opt for
Listinstead ofLazyVGridwhen possible, as it handles memory better. - Implement effective image caching strategies.
- Analyze memory usage during scrolling to identify problem areas.
Proper cleanup is critical to avoiding memory leaks. Mark inner classes as static, ensure UI updates happen on the main thread, and manage disposable resources like those in RxJava to prevent lingering memory issues.
Up next: how to speed up push notifications to keep your app running smoothly.
6. Slow Push Notifications
Slow push notifications can frustrate users, especially when they miss time-sensitive alerts. While these delays may not crash the app, they often go unnoticed until users start reporting problems.
Push Notification Challenges
Delays in push notifications can arise from several factors, including token management, network issues, device settings, and server configurations. Here’s a quick breakdown of common issues:
| Issue Type | Impact | Root Cause |
|---|---|---|
| Token Management | Failed Delivery | Expired or invalid push tokens |
| Network Handling | Delayed Messages | Poor connection management |
| Device Settings | Blocked Alerts | OS-level restrictions |
| Server Configuration | Queue Bottlenecks | Improper rate-limiting setup |
Fixing these issues is essential to maintaining a responsive app experience.
How to Speed Up Notifications
Addressing push notification delays requires targeted strategies. Below are some effective ways to ensure timely delivery and improve user experience.
Token Management and Validation
Regularly refreshing and validating tokens ensures accurate delivery. Here’s an example in Kotlin:
// Refresh tokens during app launch or user login
override fun onResume() {
super.onResume()
FirebaseMessaging.getInstance().token
.addOnSuccessListener { token ->
updateServerWithNewToken(token)
}
}
Improved Network Handling
Retry mechanisms can help manage delivery failures caused by network issues. Here’s a Swift example:
// Retry failed push notification deliveries
func scheduleRetry(for notification: PushNotification) {
let retryCount = 3
let retryInterval = 5.0 // seconds
for attempt in 1...retryCount {
DispatchQueue.main.asyncAfter(deadline: .now() + Double(attempt) * retryInterval) {
self.sendNotification(notification)
}
}
}
Additional Optimization Tips
- Prioritize Important Messages: Mark critical notifications as high-priority to reduce delays using Google Play Services.
- Track Delivery Status: Use silent push notifications to monitor delivery success and keep tokens updated.
- Send at the Right Time: Smart scheduling can increase engagement by up to 2.6×.
Monitoring and Debugging
It’s crucial to monitor metrics like delivery success rates, dispatch-to-delivery times, token refresh events, and user engagement. For iOS, use Console.app on macOS to filter push notification logs by searching for com.apple.pushLaunch. This helps identify and solve delivery issues quickly.
For example, KFC India’s “Bucket It” campaign saw impressive results: a 22% boost in daily orders, a 23% increase in revenue per store, and a 27% rise in repeat orders. These outcomes highlight the importance of timely and efficient push notifications.
Conclusion: Better Bug Detection and Fixes
Non-crashing bugs can hurt both user experience and app performance, making it critical to detect and resolve them systematically.
Key Focus Areas
Effective debugging involves a mix of monitoring and analysis. Here’s a quick breakdown:
| Area of Debugging | Tools to Use | How It Helps |
|---|---|---|
| Performance Testing | Xcode Instruments, Android Profiler | Spots bottlenecks and UI sluggishness |
| Code Analysis | IDE debugging tools, logging systems | Identifies logic errors early |
| Memory Management | Memory profilers, leak detectors | Avoids performance drops over time |
| Network Monitoring | Network inspection tools | Ensures smooth data flow |
Steps to Implement
Take these actions to enhance your app’s stability and overall performance:
1. Set Up Monitoring Systems
- Add logging points throughout your code to track behavior.
- Use analytics tools to monitor how your app performs in real-world scenarios.
- Keep an eye on memory usage across different devices.
2. Streamline Your Development Process
- Place breakpoints strategically to debug code in real-time.
- Incorporate automated tests to cover essential app functions.
3. Prioritize User Experience
- Test your app on various devices and operating systems.
- Evaluate its performance under different network conditions.
- Include error handling to ensure the app fails gracefully when issues arise.
FAQs
How can I test my app on different devices and OS versions to find non-crashing bugs early?
To catch non-crashing bugs early, it’s essential to test your app on a variety of devices and operating system versions. Start by using real devices whenever possible, as they provide more accurate results compared to emulators or simulators. Focus on testing across different screen sizes, resolutions, and hardware configurations to ensure your app performs consistently.
Prioritize testing on the most popular devices and OS versions used by your target audience. This approach helps you catch potential issues that could impact a significant portion of your users. If access to physical devices is limited, consider cloud-based testing services that provide remote access to a wide range of real devices. These strategies will help you identify and resolve subtle issues before they affect the user experience.
How can I manage background tasks effectively to avoid data loss in mobile apps?
To prevent data loss during background tasks, follow these best practices for mobile app development:
- Use reliable tools: On Android, implement
WorkManagerto ensure tasks continue even if the app is closed or the device restarts. For smoother background operations, leverage Kotlin Coroutines for efficient handling of tasks, especially those interacting with the UI thread. - Be lifecycle-aware: Tie background tasks to component lifecycles using lifecycle-aware components to avoid issues like memory leaks or tasks running unnecessarily.
- Handle constraints and errors: Ensure tasks only run under suitable conditions (e.g., device is charging or connected to Wi-Fi). Include retry mechanisms to recover from failures and maintain a seamless user experience.
For user-visible tasks, consider using foreground services with notifications to keep users informed while optimizing resource usage to avoid excessive battery drain or data consumption. These steps will help maintain app reliability and improve the overall user experience.
How can I ensure push notifications are delivered on time and remain effective for users?
To ensure push notifications are timely and effective, focus on sending them when users are most likely to engage. Track user activity and time zones to schedule notifications during active hours. Adjust the Time-to-Live (TTL) for messages to match their urgency, ensuring important notifications don’t expire before delivery.
If a notification fails to reach a user, implement retry mechanisms or fallback options, such as text messages, for critical alerts. Additionally, consider complementing push notifications with an in-app notification center to provide users with a reliable way to access important updates at their convenience.
Expect The Unexpected!
Debug Faster With Bugfender