Cross-platform development often demands a balance between shared code and native platform functionality. To use platform-specific features in cross-platform apps without sacrificing maintainability, you need smart techniques that keep the codebase clean yet flexible.
Why Access Platform-Specific Features?
Shared code maximizes productivity, but some features are tightly bound to the platform—such as Face ID on iOS, file pickers on Android, or Windows Toast Notifications. Relying solely on the lowest common denominator limits the app’s capabilities. Selective platform-specific implementation lets you provide the best experience on each device.
Key Strategies to Implement Platform-Specific Features
1. Use Conditional Compilation
Conditional compilation allows parts of the code to compile only for specific platforms.
#if defined(_WIN32)
// Windows-specific code
#elif defined(__APPLE__)
// macOS/iOS-specific code
#elif defined(__ANDROID__)
// Android-specific code
#endif
Benefits:
- Keeps platform-specific logic separated
- Reduces runtime overhead
- Easier to debug per platform
2. Abstract Native Features Behind Interfaces
Create abstract interfaces and provide platform-specific implementations. This shields your core logic from being tangled in platform details.
class INativeFeature {
public:
virtual void UseFeature() = 0;
};
class WindowsFeature : public INativeFeature {
public:
void UseFeature() override {
// Windows implementation
}
};
class MacFeature : public INativeFeature {
public:
void UseFeature() override {
// macOS implementation
}
};
Load the correct implementation at runtime based on the detected platform.
3. Dynamic Runtime Detection
Instead of compile-time detection, sometimes runtime detection makes sense, especially when targeting several versions of the same platform.
Use preprocessor macros for broad differences, but dynamic checks to adjust to smaller variations (e.g., Windows 10 vs Windows 11 APIs).
Example:
#ifdef _WIN32
if (IsWindows10OrGreater()) {
// Use newer APIs
}
#endif
4. Use Platform-Specific Units or Libraries
C++ Builder allows adding platform-specific units or linking native libraries. Keep separate units for platform-bound functionality. Then selectively compile them using build configurations.
Benefits include:
- Cleaner separation
- No accidental platform leaks into shared modules
- More efficient linking and build times
Practical Examples
iOS: Access Face ID
Create a separate unit that directly uses Objective-C headers and call it only when building for iOS.
Android: Launch a Native Intent
Use JNI (Java Native Interface) bindings inside a conditional block, preventing native Android code from touching the iOS build.
Windows: Use Windows Notifications
Directly call Windows APIs through #include <windows.h>
wrapped in a _WIN32
preprocessor check.
Best Practices
- Minimize Native Touchpoints: Keep platform-specific code small and contained.
- Use Logging Strategically: Log platform-specific behavior during testing to catch mismatches early.
- Test on Real Devices: Emulators often miss nuances in hardware-dependent features.
- Use Build Configurations: Properly configure platform profiles to include only the necessary platform-specific units.
- Document Everything: Make it obvious why a piece of code is platform-specific to prevent future confusion.
Final Thoughts
Cross-platform development thrives when the shared core remains strong, and platform-specific enhancements stay neatly isolated. Smart use of conditional compilation, interfaces, runtime detection, and native units allows you to build apps that feel native everywhere without duplicating effort or bloating your codebase. Mastering this balance creates experiences that respect both the platform’s strengths and your time as a developer.