Let’s go back to the philosophy outlined in the intro:
What does that mean with respect to your existing app, and what Doppl should be doing for you?
First, we should explore what Doppl can’t solve, where native implementations are inevitable.
The big one is the user interface. If users see it, make it native.
In Android terms, this not only includes your activities and fragments, but also your notifications, dialogs, and other UI elements.
Users expect these things to look normal for the device that they are using. Few users will see your app on both Android and iOS and be in position to compare and contrast them. All users will see your app alongside other ones on their device, and so comparisons will be made with the OS-standard apps and those that the user has installed. Your app needs to look like it belongs, and that inevitably requires some amount of platform-specific work.
Many things that are tied to the OS will also need to be custom-crafted for that OS.
For example, both Android and iOS allow you to work with other hardware over Bluetooth. However, the APIs for doing so are different, and so your code that directly works with those APIs will need to be different. In general, your code that directly works with those APIs should be minimal, abstracted away via interfaces, for testability. The code using those interfaces might be converted by Doppl, leaving you with only minimal platform-specific bridges to write, between your interfaces and the platform-specific Bluetooth APIs.
Another way to look at this: if you have to use an Android-specific API for it, most likely, Doppl will not convert it. This includes:
JobScheduler, and other periodic work
Again, in many cases, you can separate out much of your business logic to avoid directly working with those APIs. Doppl can convert the business logic; you would write the bridge code for connecting that business logic with the platform-specific APIs that you need for each platform.
Everything else — at least in principle — represents code that Doppl could convert for you into equivalent iOS code.
In modern mobile app architectures, a “repository” aggregates all of the logic
for storing and loading your application data. That could be local (SQLite, files,
SharedPreferences) or remote (REST Web services, GraphQL Web services, gRPC
endpoints). The repository itself exposes a clean API that hides all of those
details. Partly, that is so the details could change over time without affecting
the UI that is using the repository. Partly, that is so testing the UI can
be done in isolation (with a stub repository), and that testing the repository
can be done in isolation.
Even if your code is not designed specifically around a true repository, the sort of “on-device back-end” code that would be in a repository represents stuff that Doppl could convert.
Frequently, your repository will leverage third-party libraries. Occasionally, those libraries are “all-in-one” repository implementations, like Store. More often, those libraries implement pieces of a repository:
Doppl not only converts your app code but also your libraries’ code as well, to allow this logic to run on iOS. For example, the Droidcon NYC sample app uses RxJava, RxAndroid, Retrofit, Room, and much more.
If your app is structured with a formal GUI architecture — MVC, MVP, MVVM, etc. — many of the non-UI elements of that architecture could be migrated to iOS by Doppl. For example, presenters in MVP and view-models in MVVM are supposed to be independent of their respective views, and therefore should be something that Doppl could convert.
You may find that your presenters, view-models, and related code will need
some modification, as an iOS UI may need to consume that data somewhat differently
than would your Android UI. For example, a presenter that is set up to help
populate pages in an Android
ViewPager may not necessarily work with whatever
iOS UI you use to replace the
ViewPager. Hence, you may find that you need
to create custom versions of presenters or view-models, not because Doppl cannot
convert the existing code, but because the existing code may not make that
much sense on iOS.
Occasionally, we can still “make it once” even for things that the user can see.
For example, your app may need to create a PDF file, for printing, sending as an email attachment, or similar uses. While users can see a PDF, you may be able to use the same code on Android and iOS, as a PDF file itself is platform-agnostic.
Similarly, if you are generating HTML on the fly, perhaps to send in an email, since HTML is not specific to Android or iOS, your HTML generation code may be usable on both platforms, converted by Doppl.
Any app of significance has its associated set of utility code: grunge code for doing grunge work that is not handled by some existing framework for you. Much of that code is merely transforming data from one format to another, and therefore little of that code will be platform-specific. Doppl should be able to convert most, if not all, of this for you.
Back in 2012, “Uncle” Bob Martin posted his clean architecture vision. While the details differ, this architecture drives a lot of modern mobile app development.
The key concept is that you should divide your app logic into discrete layers, each testable in isolation via mocks and stubs. Only one layer — the outermost one in Martin’s diagram — should have any knowledge of platform-specific considerations like user interfaces or environment-specific considerations like Web service protocols.
A clean architecture works nicely with Doppl. Much of your logic in the inner layers will be convertible by Doppl from Android to iOS. Even some facets of the outer layer, such as Web service access, can be converted. The portions of the outer layer that cannot will need to be custom-written for each platform. That should be a small percentage of the overall code base for many apps, and well-tested inner layers should be able to work with replacement outer layers without issue.
In a nutshell, if it’s good in general for your Android app’s architecture, it is probably good for Doppl and a resulting iOS app based on the Android app.