Creating Cross-Platform Apps with Corona SDK — Best PracticesCorona SDK (now maintained as Solar2D) is a lightweight, Lua-based framework designed for rapid development of 2D mobile applications and games. Its simplicity, fast iteration via live reloading, and cross-platform capabilities make it an attractive choice for indie developers and small teams. This article covers best practices for building robust, performant, and maintainable cross-platform apps using Corona SDK/Solar2D.
Why choose Corona SDK / Solar2D?
Corona SDK provides:
- Fast development cycle with live simulator and quick builds.
- Single Lua codebase that targets iOS, Android, Windows, macOS, and more.
- Built-in APIs for graphics, audio, touch, networking, and native UI components.
- Extensive plugin ecosystem and active community support.
These strengths let developers focus on app logic and user experience rather than platform-specific plumbing.
Project setup and organization
A clean, consistent project structure simplifies cross-platform development and reduces bugs.
Recommended structure:
- main.lua — app entry point
- conf.lua — configuration (content scaling, orientation)
- build.settings — platform-specific settings and plugins
- /scenes — scene modules (using Composer or another scene manager)
- /assets — images, fonts, audio (organized by type and resolution)
- /libs — third-party libraries and utilities
- /controllers — game/app logic
- /models — data models and persistence
- /views — UI components
- /platform — small platform-specific adapters (in-app purchases, ads)
Tips:
- Use Composer (built-in scene manager) or an alternative like Storyboard replacement patterns to manage screens and transitions.
- Keep main.lua minimal: initialize services, configure global settings, and go to the initial scene.
- Isolate platform-specific code in /platform adapters to avoid scattering conditional logic.
Cross-platform asset handling
Handling assets for multiple screen sizes and densities is crucial.
Content scaling:
- Configure content scaling in conf.lua. A common approach is to design for a “base” content width and height and use dynamic scaling for various devices.
- Example conf.lua settings:
- Set content width/height and use “letterbox” or “zoomEven” to preserve aspect ratio.
- Provide multiple image resolutions and use Solar2D’s dynamic image selection by naming (e.g., image.png, [email protected], [email protected]).
Organize assets:
- Store separate folders for high-density assets if necessary, but prefer Solar2D’s automatic image suffix handling.
- Use vector-like approaches where possible (e.g., Shapes, polygons) to reduce bitmap dependency.
Optimize images:
- Use appropriate formats: PNG for UI with transparency, JPEG for photos.
- Compress images and remove metadata.
- Trim unused alpha and reduce color depth when acceptable.
Responsive UI and layout
Design UI to adapt rather than hard-code positions.
Anchoring and alignment:
- Use display.contentCenterX/Y and calculated offsets instead of absolute coordinates.
- Create utility functions for left/center/right alignment that factor in content scaling and safe areas (notches).
Safe areas and notches:
- Query system.safeArea or platform-specific APIs to get top/bottom insets and adjust layout to avoid overlap with status bars and notches.
Relative sizing:
- Size UI elements relative to screen dimensions or parent containers.
- Use display.newGroup() to create reusable UI groups that can be scaled and positioned as units.
Font scaling:
- Use a base font size and multiply by a scale factor derived from screen height/width to maintain legibility.
Performance optimization
Small inefficiencies multiply on low-end devices. Optimize early and measure.
Graphics:
- Minimize display objects—reuse objects and use sprite sheets (image sheets) instead of many individual images.
- Use texture atlases to reduce texture binds and draw calls.
- Avoid excessive use of display.newText for high-frequency updates; use cached snapshots or replacing textures.
Physics:
- Reduce physics body complexity (use simple shapes).
- Turn off physics simulation when not needed (physics.pause()).
- Limit active physics objects and joints.
Memory:
- Release unused textures with display.remove() and set references to nil.
- Use collectgarbage(“collect”) sparingly and only where appropriate.
- Watch for Lua table leaks—ensure listeners and timers are removed on scene exit.
Audio:
- Use audio.loadSound for short effects and audio.loadStream for music.
- Dispose audio handles when not needed.
Profiling:
- Use the Solar2D profiler and print memory stats during development to catch leaks.
- Test on real devices, including low-end Android hardware.
Input and touch handling
Make touch interactions consistent across devices and platforms.
Unified touch listeners:
- Use Runtime:addEventListener(“touch”, …) or local object touch listeners depending on the context.
- Implement gesture recognizers (tap, swipe, pinch) in a central utility to maintain consistency.
Multi-touch:
- Enable multitouch where needed: system.activate(“multitouch”) or platform-appropriate APIs.
- Debounce inputs to prevent accidental multi-triggering on slower devices.
Accessibility:
- Provide larger touch targets for essential controls.
- Respect platform font scaling and accessibility settings where possible.
Platform-specific features and plugins
Abstract platform differences behind adapter modules.
Build.settings and plugins:
- Declare required plugins and platform permissions in build.settings.
- Use conditional requires and platform checks (system.getInfo(“platformName”)) inside adapter modules.
Examples of platform-specific features:
- In-app purchases: wrap store.* APIs in an IAP adapter with uniform callbacks.
- Push notifications: centralize registration, token handling, and handlers.
- Ads and analytics: create adapters to switch providers or disable in development.
Keep platform checks centralized; avoid sprinkling system.getInfo checks across the codebase.
Networking and backend integration
Design resilient network logic for mobile conditions.
HTTP and sockets:
- Use network.request for RESTful calls and LuaSocket/websocket plugins for persistent connections.
- Implement exponential backoff and retry logic for unstable networks.
Data handling:
- Serialize data with JSON (json.encode/decode) and validate responses.
- Cache important data locally (via Lua files or native storage) and implement sync strategies for offline use.
Security:
- Always use HTTPS. Verify TLS where possible.
- Avoid storing sensitive tokens in plain text; use native.keychain or encryption plugins when available.
Rate limits and batching:
- Batch small requests where feasible to conserve battery and reduce overhead.
- Respect backend rate limits and implement client-side throttling.
Data persistence
Choose the right persistence mechanism for your needs.
Options:
- System.pathForFile with io.* functions for small JSON or flat-file storage.
- sqlite3 plugin for structured relational data and queries.
- Native preference/keychain plugins for secure storage of tokens.
Best practices:
- Use an abstraction layer (e.g., dataStore.save/get) so storage backend can change without affecting app logic.
- Handle migrations: when schema changes, provide a migration path to update existing user data.
Testing and CI
Automate builds and tests to ensure cross-platform quality.
Automated builds:
- Use CI services to run builds for iOS and Android (export automation or build service integrations).
- Automate signing and provisioning profiles securely (use environment variables and secrets).
Testing:
- Unit-test Lua modules where possible.
- Use device farms or cloud testing services to run UI tests across multiple devices and OS versions.
Debugging:
- Use Composer scene enter/exit logs and centralized error handlers to capture stack traces.
- Implement remote logging/analytics (with user consent) to capture uncaught errors.
Monetization and store compliance
Prepare for app store requirements early.
Monetization strategies:
- Ads: integrate via adapter, ensure proper pause/resume behavior and resource cleanup.
- IAP: implement consumable/non-consumable flows and validate receipts server-side where possible.
- Paid apps: provide robust upgrade and restore functionality on both platforms.
Store policies:
- Follow App Store and Google Play guidelines for privacy, permissions, and ad behavior.
- Provide a privacy policy URL and comply with data collection notices, especially for EU/CCPA.
Internationalization and localization
Design for multiple languages and regions from the start.
String management:
- Store localized strings in external JSON or Lua files keyed by locale.
- Avoid concatenating localized fragments; use format strings with placeholders.
Layout adjustments:
- Allow UI to expand for longer translations.
- Handle right-to-left (RTL) layouts by mirroring UI where necessary.
Date, time, and numbers:
- Use locale-aware formatting for dates/times and numeric values where appropriate.
Packaging and release
Prepare separate builds and test flighting.
Configuration:
- Use build.settings to manage icons, splash screens, required permissions, and supported orientations per platform.
- Keep versioning consistent across manifests and metadata.
Testing releases:
- Use staged rollouts to detect platform-specific issues early.
- Gather crash reports and user feedback in early releases before global rollout.
Migration path: Corona SDK to Solar2D
If you’re moving from classic Corona SDK, Solar2D continues compatibility with most APIs but check:
- Plugin availability and any breaking changes.
- Build pipeline differences; set up Solar2D’s build servers or local builds.
- Update deprecated APIs and test thoroughly across targets.
Example: Minimal cross-platform scene structure
Below is a concise pattern (pseudocode) for a scene initializer and platform adapter usage:
-- main.lua local composer = require("composer") display.setStatusBar(display.HiddenStatusBar) composer.gotoScene("scenes.home")
-- platform/iap_adapter.lua local M = {} local platform = system.getInfo("platformName") function M.init() if platform == "iPhone OS" or platform == "iPad" then -- init Apple IAP elseif platform == "Android" then -- init Google Play IAP end end return M
Final checklist
- Use a consistent project structure and Composer for scenes.
- Handle multiple resolutions via conf.lua and image suffixes.
- Optimize textures, physics, and audio; profile on real devices.
- Abstract platform-specific code into adapters.
- Implement robust networking with offline support and HTTPS.
- Test on multiple devices and automate builds.
- Prepare for store compliance, localization, and secure data handling.
Creating cross-platform apps with Corona SDK/Solar2D is about balancing rapid iteration with disciplined architecture. Follow these best practices to keep your code maintainable, performant, and ready for multiple platforms.
Leave a Reply