Unity Cross-Platform Migration
Led the migration of separate iOS (Swift) and Android (Kotlin) companion apps into a single Unity codebase. The client wanted to consolidate their technology stack so their Unity developers could maintain everything. The project involved matching all existing features while setting up a foundation that would be easier to build on going forward.
Note: Due to NDA restrictions, specific client details, app screenshots, and proprietary features cannot be disclosed. This page focuses on technical approaches, architectural decisions, and transferable learnings from the project.
Project Scope & Challenge
The client had two native mobile apps—one in Swift for iOS and another in Kotlin for Android. Maintaining both separately created several issues:
- ▸ Duplicated work: Every feature had to be built twice
- ▸ Inconsistent experience: The apps started to differ over time
- ▸ Slower updates: Changes needed coordination between separate teams
- ▸ Higher costs: Two codebases to maintain and the client didn't have Swift/Kotlin developers in-house
The goal was to combine both apps into one Unity project that could build for iOS and Android, matching what the native apps could do.
Subcontract Arrangement & Reverse-Engineering Challenge
We were brought in as subcontractors with minimal documentation. What we had to work with:
- • Access to native codebases: Full iOS (Swift) and Android (Kotlin) source code
- • Design specifications: UI/UX design files to match in the Unity implementation
- • API credentials: Authentication details and endpoint URLs for the backend GraphQL API
- • App Store releases: Published versions of the apps to analyze behavior and features
What we didn't have: Implementation docs, API schemas, architectural diagrams, or feature specs. We had to reverse-engineer everything by reading through the Swift and Kotlin code, watching how the apps behaved, and figuring out what the backend API expected. This meant a lot of investigation work happened alongside actual development.
My Role: Technical Lead
I handled the migration strategy, architecture, implementation, and delivery. Main responsibilities:
▸ Architecture & Planning
- • Reverse-engineered the Swift and Kotlin codebases to understand how everything worked
- • Analyzed the production apps to catalog all features and workflows
- • Designed the Unity architecture to handle both platforms
- • Set up code organization and project structure standards
- • Created a migration roadmap with milestones
▸ Backend Integration
- • Reverse-engineered the GraphQL API schema by analyzing Swift and Kotlin network calls
- • Built a custom GraphQL client for Unity from scratch using UniTask for async operations
- • Implemented strongly-typed response models and comprehensive error handling
- • Added automatic retry logic and request cancellation support
- • Validated API integration against native app behavior through extensive testing
▸ Custom GraphQL Client for Unity
- • Built a production-ready GraphQL client for Unity based on a minimal OSS implementation
- • Leveraged UnityWebRequest + UniTask for async/await patterns and cancellation support
- • Standardized error handling, automatic retries, and strongly-typed response models
- • Enabled rapid iteration on content management features across the team
▸ Implementation & Testing
- • Built the core app functionality and UI to match the native versions
- • Tested side-by-side with native apps to ensure everything worked the same
- • Validated performance on various iOS and Android devices
Technical Challenges & Solutions
Challenge: No Documentation
There were no specs, API docs, or architecture diagrams. Figuring out what the apps did and how they talked to the backend meant reading through Swift and Kotlin code, watching how things behaved at runtime, and reverse-engineering the API calls.
Solution:
Built a systematic approach: analyzed the native code to map features, used network tools to inspect API calls, ran the production apps to document behavior, and wrote our own specs as we went. Then it was an iterative process of building in Unity, comparing against the native apps, and adjusting until things matched. Kept detailed notes throughout for handoff later.
Challenge: Excessive Memory Usage
The app was running into memory issues, especially on older devices. Loading all screens at once and keeping all image assets in memory throughout the session caused crashes and performance problems. The native apps handled this better by being more selective about what stayed loaded.
Solution:
Split the main screens into separate Unity scenes that would load and unload dynamically as users navigated. For images from the backend, implemented a viewport-based system that only loaded assets when they were about to be visible and unloaded them as they scrolled out of view. This kept memory usage much lower and prevented crashes on devices with limited RAM.
Results
- ✓Matched the native apps: Replicated all features in Unity
- ✓Single codebase: No more need for separate iOS and Android teams
- ✓Faster updates: Features can be built once and released to both platforms
- ✓Consistent experience: Both platforms now behave identically
Technical Stack
Unity Development
- • Unity 2021
- • C# / .NET
- • IL2CPP build configuration
- • UniTask for async operations
Backend Integration
- • Custom GraphQL client for Unity
- • UniTask async/await patterns
- • Strongly-typed response models
- • Automatic retry & error handling
- • JSON serialization
- • Token authentication
Platform Targets
- • iOS (iPhone/iPad)
- • Android (phone/tablet)
- • Native plugin bridges
Testing & Deployment
- • NUnit/Play Mode tests
- • TestFlight / Internal Testing
- • CI/CD automation
- • Performance profiling