Transferring a mobile application from Android to iOS is a task that sooner or later faces any developer seeking to expand its audience. Despite the fact that both platforms operate in different programming languages โโ(Kotlin/Java vs Swift/Objective-C) and have fundamental differences in architecture, the conversion process can be optimized. The main thing is to understand the key differences: from design guidelines (Material Design vs Human Interface Guidelines) to the features of working with the hardware capabilities of devices.
Many people mistakenly believe that it is enough to simply rewrite the code in Swift โbut this is just the tip of the iceberg. Under the hood there are nuances with libraries, API, system permissions and even the logic of interaction with the server. For example, processing push notifications on iOS requires integration with Apple Push Notification Service (APNs), while on Android is used Firebase Cloud Messaging (FCM). And adapting the interface to iPhone taking into account Safe Area and the absence of a hardware "Back" button may require reworking the navigation from scratch.
In this article we will examine 4 main approaches conversion, compare them in terms of time, cost and complexity, and also warn against common mistakes. You'll learn when it makes sense to use cross-platform frameworks like Flutter or React Nativeand when it's better to write native code from scratch. We will pay special attention to automatic converters (for example, J2ObjC from Google) - why they rarely give ideal results and in what 10% of cases their use is justified.
1. Key Differences Between Android and iOS: What to Consider Before Converting
Before you begin porting code, it is important to understand the fundamental differences between the platforms. They affect not only the technical part, but also the user experience.
Programming languages โโand development environments:
- ๐ฑ Android:
Kotlin(recommended) orJavav Android Studio. Uses virtual machine ART. - ๐ iOS:
Swift(priority) orObjective-Cv Xcode. Compiled into native code for ARM64.
Architecture and components:
On Android applications are built on the basis of Activity i Fragment, while in iOS is used model UIViewController + UIView. For example, an analogue of RecyclerView on iOS is UITableView or UICollectionView, but their operating logic is radically different: on iOS often you have to manually manage the reuse of cells (dequeueReusableCell), whereas on Android it is automated.
Design and user interaction:
Guidelines Material Design i Human Interface Guidelines (HIG) dictate different approaches to animations, gestures and navigation. For example:
- โฌ ๏ธ On Android there is a hardware "Back" button, on iOS there is only a software one (and its behavior must be specified manually).
- ๐ On iOS swipes are popular to return to the previous screen, on Android this is less common.
- ๐จ System fonts:
RobotovsSan Francisco, different standards for indentation and line height.
2. Conversion methods: comparison of approaches
The choice of method depends on the budget, timing and performance requirements. Let's consider the pros and cons of each option.
| Method | Speed | Cost | Performance | Support complexity |
|---|---|---|---|---|
| Native development from scratch | โณ Slow (2-6 months) | $$$ High | โก Maximum | ๐ง Average |
| Kotlin Multiplatform (KMP) | โฑ๏ธ Fast (1-3 months) | $$ Average | โก High (native UI) | ๐ง Low |
| Flutter/React Native | โฑ๏ธ Fast (1-2 months) | $ Low | ๐ Average | ๐ง High (framework updates) |
| Automatic converters (J2ObjC) | โก Instantly | $ Low | ๐ข Low (requires improvements) | ๐ง Very high |
Native development is the most reliable, but time-consuming method. Suitable for applications with high performance requirements (games, AR/VR, complex animations). For example, Instagram and Spotify were originally written separately for each platform.
Kotlin Multiplatform (KMP) allows you to separate business logic between platforms, leaving the UI native. This approach was chosen in Netflix i VMware for their cross-platform projects. The main advantage is that the common code works on both platforms without bridges (unlike Flutter).
Flutter and React Native accelerate development due to a single code base, but may lose in performance. For example, Airbnb refused from React Native due to problems with the integration of native modules.
Automatic converters (for example, J2ObjC from Google) translate Javacode into Objective-C, but do not support Kotlin and modern features iOS. utilities without a complex UI.
If your application actively uses native APIs (for example, Camera2 on Android or ARKit on iOS), cross-platform solutions may add extra layers of abstraction. In this case, it is better to choose. native development or KMP.
3. Step-by-step guide: conversion via Kotlin Multiplatform (KMP)
This method is optimal for applications where business logic is separated from the UI. Let's look at the process using the example of a simple notes application.
Step 1. Setup. project
Create a multi-platform project in Android Studio with support iOS:
// In the build.gradle.kts file (project level)
plugins {
id("org.jetbrains.kotlin.multiplatform") version "1.9.20"
}
Step 2. Code separation
Take out the overall logic (working with the database, network requests) into the module commonMain, and platform-dependent code - into androidMain and iosMain. For example:
// commonMain/kotlin/Note.kt
expect class DatabaseDriver {
fun saveNote(note: Note)
fun loadNotes(): List
}
Step 3. Implementation for iOS
In the module iosMain connect native dependencies via CocoaPods:
// iosApp/Podfile
target 'iosApp' do
pod 'SQLite.swift', '~> 0.14.1'
end
And implement DatabaseDriver for iOS:
// iosMain/kotlin/DatabaseDriver.kt
actual class DatabaseDriver {
actual fun saveNote(note: Note) {
// Implementation via SQLite.swift
}
}
Step 4. Integration with Xcode
Generate a framework for iOS and connect it to Xcode:
./gradlew :shared:embedAndSignAppleFrameworkForXcode
Create a multi-platform project in Android Studio|Put common logic into commonMain|Implement expect/actual for platform-dependent code|Configure CocoaPods for iOS dependencies|Generate a framework for Xcode-->
4. Typical mistakes and how to avoid them
Even experienced developers encounter pitfalls when porting. Here are the most common of them:
1. Ignoring guidelines Apple
Apple strictly moderates applications in App Store. For example, if your application uses a custom navigation with a bottom bar, this may be rejected: according to Android uses custom navigation with a bottom bar, on iOS this may be rejected: according to HIG, the bottom bar (Tab Bar) must contain no more than 5 elements, and the active element must be highlighted.
2. Memory problems
There is no garbage collector - instead it is used iOS there is no garbage collector - it is used instead ARC (Automatic Reference Counting). If in Androidversion you actively used weak links (WeakReference), they need to be replaced with unowned or [weak self] in Swift, otherwise memory leaks will occur.
3. Incompatibility of libraries
Many popular Androidlibraries (for example, Retrofit or Room) do not have direct analogues on iOS. You'll have to look for alternatives:
- ๐ Instead of Retrofit โ Alamofire or native
URLSession. - ๐๏ธ Instead of Room โ CoreData or Realm.
- ๐ Instead of Glide/Picasso โ SDWebImage or
UIImageViewsURLSession.
4. Permissions and security
The iOS permission system is stricter. For example, to access Bluetooth or Camera you need not only to declare permissions in Info.plist, but also to request them at runtime. In this case Apple requires an explanation of the reason for the request (key NSPhotoLibraryUsageDescription).
What will happen if you do not specify a description of the permissions in Info.plist?
The application crashes the first time you request permission with an error This app has crashed because it attempted to access privacy-sensitive data without a usage description. This can only be corrected by rebuilding and re-uploading to the App Store.
5. Testing on real devices
Emulators Xcode do not always accurately reproduce the behavior on physical ones iPhone. s Touch ID/Face ID or ARKit will only appear on a real device. Apple requires testing on the latest versions iOS (at the time of publication - iOS 17+).
Always test the application on devices with different screen sizes (iPhone SE, iPhone 14 Pro Max, iPad) - autoscaling (Auto Layout) can work unexpectedly with custom views.
5. Automatic converters: is it worth using J2ObjC?
J2ObjC - a tool from Googlethat translates Java-code in Objective-C. It can save time, but is not suitable for everyone.
Pros:
- โก Fast conversion of basic logic.
- ๐ Supported by most
Java-libraries (for example, Guava). - ๐ ๏ธ Can be integrated with Xcode as a static library.
Disadvantages:
- ๐ซ Does not support
Kotlin(onlyJava 8). - ๐จ Does not convert XML markup and
Android-specific UI. - ๐ Generation
Objective-C, notSwift(which complicates support). - ๐ง Requires manual modification for iOS-specific features (for example,
Grand Central Dispatchinstead ofRxJava).
When does it make sense to use?
Only if:
- Your application is written in
Java(notKotlin). - UI is simple and can be rewritten from scratch for iOS.
- You are ready to manually adapt the work with the network, database and multithreading.
Example command for conversion:
j2objc --use-arc \
--xcode-project-project MyApp \
--xcode-project-company-identifier com.mycompany \
-d ../ios/GeneratedSources \
--no-package-directories \
src/main/java/
If you still decide to use J2ObjC, configure CI/CD to automatically convert when changing Java code. This will help to avoid discrepancies between versions.
6. Optimizing performance after conversion
Even if the application was successfully compiled and launched on iOS, this does not guarantee its effectiveness:
1. Memory and CPU
Use Instruments to Xcode to find memory leaks and bottlenecks:
- ๐
Allocationsโ monitors memory allocation. - โก
Time Profilerโ analyzes loading CPU. - ๐ผ๏ธ
Core Animationโchecks the performance of animations.
2. Network requests
On iOS restrictions on background work For example, Background Fetch works no more than once every 15 minutes. (on Android the interval can be set flexibly). For optimization:
- ๐ก Use
URLSessionsbackgroundConfiguration. - ๐๏ธ Cache responses using
URLCache. - ๐ For WebSocket, use
Network.frameworkinstead of third-party libraries.
3. Graphics and animations
On iOS for smooth animations it is recommended:
- ๐ญ Use
Core Animationinstead of custom onesUIView.animate. - ๐๏ธ For complex drawings โ
Core GraphicsorMetal. - ๐ฑ Disable unnecessary shadows and gradients (they eat up FPS on older devices).
4. Local data
CoreData may be slower Room for large amounts of data. Alternatives: - fast NoSQL database Use
- ๐๏ธ Realm โ fast NoSQL database.
- ๐ SQLite.swift - a wrapper over the native one
SQLite.
On iOS, avoid frequent calling viewDidLayoutSubviews - this can lead to unnecessary redraws. Use layoutIfNeeded only if necessary.
7. Publishing in the App Store: requirements and lifehacks
The moderation process is App Store stricter than in Google Play. Here are the key points that will help you avoid rejection:
1. Preparation of metadata
- ๐
Bundle IDmust be unique (format:com.companyname.appname). - ๐ผ๏ธ Screenshots must be for all supported resolutions (iPhone, iPad, iPhone Plus).
- ๐ฅ Preview video (optional, but increases conversion).
2. Security requirements
- ๐ All network requests must use
HTTPS(exceptions require justification). - ๐ฑ If the application collects user data, a privacy policy is needed (Privacy Policy).
- ๐ช For tracking (for example Facebook SDK) permission request is required
App Tracking Transparency (ATT).
3. Technical requirements
- ๐ฑ Support for the latest versions iOS (at the time of publication - iOS 17).
- ๐ฅ๏ธ Supports all screen resolutions (including iPad, if universality is declared).
- ๐ง No critical crashes (no more than 0.1% crashes per 1000 starts are allowed).
4. Lifehacks to speed up moderation
- โ Upload the build to TestFlight for internal testing before sending for review.
- ๐ In the
Review Notesfield, specify test accounts and instructions for moderators. - ๐ If an application is rejected, respond to the email with details - this often speeds up a second review.
Use fastlane to automate the loading of builds and metadata. This reduces the time for routine operations and reduces the chance of errors when filling out fields.
How much does publication cost?
One-time placement of an application in App Store costs $99 per year (subscription Apple Developer app). For comparison: a one-time payment of Google Play โ $25.
What to do if the application is rejected?
1. Carefully read the reason in the letter from Apple (often there are links to specific guidelines).
2. Fix the problem and download a new version with an increased build number.
3. In the "What's New" field, indicate: "Fixed issue reported by App Review".
4. If the rejection is unfounded, you can appeal through App Review Board (but it takes 1-2 weeks).
FAQ: Frequently asked questions about converting Android โ iOS
Is it possible to convert APK to IPA directly?
No, this is fundamental different package formats. APK (Android Package Kit) contains bytecode for ART/Dalvik, and IPA (iOS App Store Package) - compiled code for ARM64. The only way is to rewrite or convert the source code, as described in the article.
How long does it take to convert an average application?
Depends on the method:
- Native development from scratch: 3-6 months.
- Kotlin Multiplatform: 1-3 months.
- Flutter/React Native: 1-2 months.
- Automatic converters: 1-2 weeks (but some work will be required).
Do I need to buy a Mac for iOS development?
Yes, Xcode and other tools Apple work only on macOS. Minimum requirements:
- Mac with chip Apple Silicon (M1/M2) or Intel Core i5/i7.
- 16 GB RAM (for emulators iOS 17+).
- macOS Ventura or newer.
An alternative is to rent a cloud one Mac (for example, MacStadium or AWS EC2 Mac), but this is expensive for permanent work.
How to transfer a database from Android to iOS?
If used SQLite, you can transfer the database file directly (extension .dbTo do this:
- Export the database from
/data/data/your.package.name/databases/to Android. - Place the file in the application package (
Bundle) or upload to the server. - On iOS copy the file to
Documents Directory:
let dbPath = Bundle.main.path(forResource: "database", ofType: "db")let docDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let destPath = docDir.appendingPathComponent("database.db")
try? FileManager.default.copyItem(at: URL(fileURLWithPath: dbPath!), to: destPath)
For Realm or Room you will need to export/import data via JSON or custom format.
Is it possible to use one backend for both platforms?
Yes, and this is standard practice. The main thing is to take into account the differences in:
- Push notification format: FCM for Android, APNs for iOS.
- Authorizations: on iOS popular Sign in with Apple (required if there are other methods of social authorization).
- Error handling: error codes may differ (for example,
HTTP 403on iOS may require additional processing for App Tracking Transparency).