streetcomplete / StreetComplete

Easy to use OpenStreetMap editor for Android
https://streetcomplete.app
GNU General Public License v3.0
3.82k stars 348 forks source link

iOS version #1892

Closed westnordost closed 8 months ago

westnordost commented 4 years ago

Edit: Planning for an iOS version continues in #5421

See also the last comment on this ticket


Quite a few times, it has been requested that an iOS version of this app is made available. This ticket shall serve as an explanation why this isn't possible but also as an entry point for why it is perhaps possible after all, but with enormous effort. So if you are an iOS developer and interested in creating an iOS version, also read this.

It's not possible

StreetComplete is developed as a native Android app and iOS is a completely different platform. There is no compatibility at all. Usually, if you see an app that is both available for Android and iOS, it is either not native (for example, just a website being displayed within an app or using a cross-platform framework such as ReactNative) or you are looking at two completely different applications, likely developed by two teams. This is my experience as a professional app developer.

Maybe it's possible

So, this is interesting if you are an iOS developer yourself. It is not planned from my side to do this.

Kotlin Multiplatform Mobile

This app is written in Kotlin, a modern programming language that is quite similar to Swift. Originally, Kotlin was built to run on the JVM, but by now, it can be transpiled into JavaScript, and with Kotlin Native, also to machine code, just like Swift. This makes it possible to write iOS apps with Kotlin. Thus, Kotlin code of this project that is not dependent on any Java library (Http library etc) and also not dependent on the Android platform (UI, sensors, persistance management etc) could be used 1:1 in an iOS port of this app. Such "pure" Kotlin code would be put into a common module that is shared between the iOS and the Android app. However, not a big percentage of the total code in this project is such pure Kotlin code that has no dependencies to any other Java or Android dependencies. Most of the iOS app would still need to be written from scratch, in Swift or better in Kotlin, if possible. On the other hand, some of the code that is currently dependent on some Java library or Android dependency could be changed to not be dependent on it directly anymore but on a wrapper interface. Then, only that wrapper would need to also be implemented for iOS.

Multi-OS Engine

The MOE is a little different approach. So, the JVM of Android is called ART and is open source. On Android, we write apps that run within this virtual machine provided by the Android system. For iOS, there is no such virtual machine, apps run natively. But, what we could do, is to write an app that comes bundled with the ART virtual machine and does nothing else than to load the provided JVM bytecode. This is the approach of MOE. In other words, ART is automatically bundled in the app so that one can write an iOS app in the Java environment, making use of the Java ecosystem and libraries. iOS platform dependencies are accessed over an interface provided by MOE. The difference to the Kotlin Native approach is, that Kotlin code that depends on Java libraries can also be moved to the common module that is shared between the iOS and the Android app. The only things that cannot be shared is code that is dependent on the Android platform, such as UI or currently persistance with SQLite (because it uses Android dependencies). (see https://github.com/streetcomplete/StreetComplete/issues/1892#issuecomment-1042296087)

peternewman commented 4 years ago

Is it worth pinning this issue?

RubenKelevra commented 4 years ago

Also there's new approaches to this, like Beeware, which could be an interesting alternative somewhere in the future, since it also offers native desktop apps as well as browser apps (for example for chrome books).

It would also require a full rewrite into python, but all API calls are native Python and they are translated while compiling or while running on the python virtual machine to native API calls. So there's zero code which needs to be platform-native.

The advantage of using Python is clearly the amount of libraries available, for communication with OSM, OSM data etc.

Just my two cents :)

westnordost commented 4 years ago

Well, rewriting the entire app is obviously out of question and out of scope. There are many other cross platform frameworks available.

RubenKelevra commented 4 years ago

@westnordost yeah I know. Beeware is also in a very very alpha stage, but quite an interesting concept. :)

I'm a python developer and would have fun to invest some time into such a project. But at the current stage of Beeware this doesn't make any sense. :)

wtimme commented 4 years ago

Thanks for creating a dedicated issue for this!

I've created a branch, set up a "Shared" module and integrated it into an empty Xcode project. The build succeeds and I can use the code inside the Swift files. (For testing, I've moved StringMapChange into the "Shared" module.)

It looks as if using the Kotlin code on both platforms could work. When looking at the bigger picture, however, I can't help but agree with you when you point out that it will be "enormous effort". The main issue at the moment that I see is that a lot of the classes' dependencies are Android-specific, or written in Java.

As an example, take the dependency on osmapi. A lot of the StreetComplete code is importing that library, because it contains basic classes such as BoundingBox and LatLon. In order for iOS to be able to use it, one would probably need to port that library so that it is usable with Kotlin Native, too.

westnordost commented 4 years ago

Yes, correct. This is why I think the Multi-OS Engine approach may be more feasible.

As for that kind of dependencies, it would be possible of course to wrap the classes from osmapi. Unfortunately, Kotlin, unlike Swift doesn't have a feature that would allow to make third-party classes implement interfaces via extension functions, however, there is an experimental feature to have inline classes. That, or typealiases could maybe be used to have such a very thin wrapper layer.

jscheffl1 commented 4 years ago

Hi thanks for the clarification! I was already seeking for an alternative option to be able to contribute to OSM on my iOS - I was quite happy with my legacy Android phone to gain points and correct small glitches. Unfortunately I'm not an iOS developer but very probably the efforts are enormous. But anyway I can also say there is demand :-)

Anyway THANKS for the efforts in the Android edition, I really like the app!

westnordost commented 4 years ago

@wtimme and I analyzed in a little more detail what needs to be done.

1. What needs to be done completely from scratch

Anything view- and Android-related needs to be done from scratch. In a little more detail with very rough (week-wise) estimations:

So in total about 5 months or more. Some things (~2 months) are not so important for the core functionality and can be added later or bit by bit.

2. Things that can be shared but must be adapted to work

These are things that are pure Kotlin but currently have Android and no Java dependencies. Most classes that are in the /data/ directory. All DAOs to access the database, almost all controllers (classes that manage DAOs), download and upload logic, metadata, data, diff data, filters parser (similar to Overpass Wizard syntax) and all the quest types.

So, about 6 months or less (~1-3 months less) if one just ports/copypastas the used java libraries into swift code or finds very similar ready-made libraries that do the same.

westnordost commented 4 years ago

As far as a minimum viable product is concerned:

So, the estimation for a MVP could be as little as 2 months for UI and 2 months for the core, so in total 4 months. If Kotlin Native is chosen over Multi-OS-Engine, adaptions to the core would take 4.5 months, so in total 6.5 months. This is not so unachievable anymore, especially if multiple people would be working on it. I also remember working on the app about half a year before I could release the first public version. So, if any iOS developer who is interested in it reads this but wouldn't want to stem this alone, give a shout!

westnordost commented 3 years ago

Short notice: iOS is going to remove OpenGL for new iOS versions, which means that the cross platform library tangram-es (for rendering the map) will not work anymore. It is very unlikely that tangram-es will be updated any time soon to support Metal instead on iOS, so for iOS, another library would need to be found.

oliviernguyenquoc commented 3 years ago

Can’t you considered this open source iOS app (GoMap) as the iOS equivalent of StreetComplete ?

Don’t know that much both projects but it seems to be very close.

matkoniecz commented 3 years ago

Go Map!! is an equivalent of Vespucci, full scale OSM editor. See https://github.com/bryceco/GoMap/issues/240

See also https://wiki.openstreetmap.org/wiki/Editors and https://wiki.openstreetmap.org/wiki/Go_Map!! and https://wiki.openstreetmap.org/wiki/Vespucci

westnordost commented 3 years ago

data types from the osmapi library need to be wrapped and instead own data classes must be used (1.5W)

The app currently uses the Java library Kryo for serialization. The library is stable and works without issues, but I am thinking about migrating to kotlinx-serialization-json, because

  1. kotlinx.serialization is pure Kotlin, so it will run just fine on native etc. as well
  2. I found out that serializingto binary is a little unstable, in the sense that one may break something easily: Deserializing a data class will break if any field has been added or deleted or maybe even (not sure) if the order of the fields changed. In case of Kryo, it fails silently, so one gets garbage data without noticing. So, when editing any class that one knows is serialized somewhere within the app, one needs to be very cautious. It would be better not to have to be that cautious.
  3. Serialization to/from JSON is more foreward-compatible because it's no problem to add fields as long as they are optional or have default values

When migrating to kotlinx-serialization-json, it lends itself to define own data classes for OSM data because that library does not work with reflection but auto-generates the serializer/deserializer classes using a gradle plugin and this automation does only work for classes that are specially annotated, hence, must be defined in the source code proper, not in some external library.

Further, for some technical reasons I don't want to go into detail right now, to implement #2441 kinda requires or it is very much recommended to do that migration before that.

Summary:

Help wanted: Most of the migration is kind of an easy mostly search&replace but very effortful refactor task. I added the data classes that shall be used already in the no-osmapi-data branch. If there are any people who would like to help out, you are very welcome!

westnordost commented 3 years ago

Here is some interesting insight from someone who is using Kotlin Multiplatform in his day job:

https://discuss.kotlinlang.org/t/google-flutter-what-could-be-their-reason-for-choosing-dart-over-kotlin/21179/16

matkoniecz commented 3 years ago

to make it easier to find: iPhone (sorry for a new comment, but I have no existing comment here to edit and notification seemed lesser evil than editing someones else post)

Edit: also Apple

westnordost commented 3 years ago

The no-osmapi-data branch is already merged and I updated that list https://github.com/streetcomplete/StreetComplete/issues/1892#issuecomment-686495406 after that. This is just one one many many things that would need to be changed for an iOS port to be possible.

matkoniecz commented 3 years ago

I was thinking about plans a bit and I am wondering whether things from

2.1 However, the following changes need to be made:

are worth doing right now. How likely is that work done now (without iPhone etc) is going to be useful and get us closer to potential iPhone version?

Is any of this things useful on its own, or is it just making things not worse on Android and being a small piece of work toward iPhone version?

Is it worth doing them just in case and to make iPhone version a tiny bit closer?

For example in

Logging in certain classes uses an Android framework. Should switch to an own implementation or wrap it in an interface (1.5W) SharedPreferences must be wrapped behind an interface so that UserDefaults can be used instead for iOS (1W)

it seems that creating intermediate interface should not be something horrible complicated. But does it make sense to do it right now, given that it will be a bit more confusing for people used to Android development?

westnordost commented 3 years ago

Some things make sense, because they don't make the code more complicated. Some others would only make sense if one really started to work on an iOS port because it would make the code more complicated. I'd say, these things could make sense on their own:

  • Logging in certain classes uses an Android framework. Should switch to an own implementation or wrap it in an interface (1.5W)

would make it possible to (also) log the logs to some place within the app, so that the log could be attached to crash reports and/or accessed by the user when asked about it.

  • SharedPreferences must be wrapped behind an interface so that UserDefaults can be used instead for iOS (1W)

That would be cleaner anyway, in my opinion. Maybe it is two things:

  1. logic classes should not have SharedPreferences as dependency directly (just as they should not depend on the database directly)
  2. logic classes should not have access to the whole shared preferences but only through something like for example LastEditTimeStore
westnordost commented 3 years ago

MapLibre (ex Mapbox SDK) now supports Metal, so an iOS port would probably (need to) use MapLibre instead of tangram-es. This requres to

As tangram is not really developed further, it might be a good choice to change to using the maplibre SDK for Android too. Or - bluesky - use/create a map view that directly renders the OSM data.

Echolon commented 3 years ago

You have my true respect not simply throwing away the implementation request and keep on evaluating!

matkoniecz commented 3 years ago

Just to check:

Quest types use Android resources (for icon, title), these must be outsourced from the actual quest type definitions (1W) QuestAutoSyncer depends on Android location framework and must be wrapped (at least), other location-related stuff (1W) More/all constants defined in ApplicationConstants should be injected instead (1W) Controllers (etc.) use CopyOnWriteArrayList to manage listeners, must replace with something else that is not Java (0.5W) and experimenting with replacing Tangram by Maplibre

would it be also a good idea to do as separate tasks, done as improving Android version? And doing them as first steps, without plans for immediate continuation?

westnordost commented 3 years ago

No, because it does not improve the code.

peternewman commented 2 years ago

For people who want StreetComplete on iOS in the interim, take a look a this.

Additionally, @bryceco now parses the kotlin code to include some of the StreetComplete quests in GoMap!! but this parser relies on that the tag filter expression is at the usual place and has not no further logic added.

Originally posted by @westnordost in https://github.com/streetcomplete/StreetComplete/issues/3456#issuecomment-951822330

Which exists in: https://github.com/bryceco/GoMap/tree/master/src/Shared/Quests

mr-elbe5 commented 2 years ago

I have already written some apps for IOS in Swift and as well tried some with cross-platform intents. As a result I would not recommend a complete cross-platform approach, but write at least the UI native. The core data and logic can then be integrated as a framework. IOS native with Swift and UIKit is not that complicated and can even be quite readable if you don't use Storyboards or Xibs, but write everything in code. To make things really readable (especially auto layout), some extensions to UIKit classes and some other base classes should be used. I could provide these. For map views it is not even necessary to use Apple's MapKit. I have written and can provide a MapView for OSM, which does not have Apple's unreliable caching mechanisms. You can check this in SwiftyMaps here on Github (though not very much documented) I don't know how far this topic has already developed, but if you are interested in this approach, please let me know.

RubenKelevra commented 2 years ago

@mr-elbe5 well, there's no "development" in this topic which I'm aware of. So feel free to write a "clone" application for iOS, if you like.

But I think your have to ask @westnordost if he is fine with using the same name. The default would probably to alter the name a bit to make it clear that it's not the same project :)

smichel17 commented 2 years ago

My limited experience working with cross-platform UI (React Native + Expo) has also been that you still have to write platform-specific code; the abstractions are leaky (e.g. for recording audio you will probably end up with different file types on each device. You thought you could avoid server-side transcoding? think again).

My gut says that if you're willing to accept a degraded experience on iOS, using kotlin multiplatform with a single code base would be less effort to maintain than writing native UI. Inversely, I suspect shared libraries + native UI will be less effort to get a polished experience on both platforms.

At the end of the day, it doesn't really matter in terms of what incremental steps to take towards making it a possibility: Separating the code into platform dependent/independent parts will be helpful for either approach.

westnordost commented 2 years ago

Just to clear any misconceptions uttered above: Both in the MOE and in the KMM approach, the iOS UI is written natively with UIKit. The key difference between MOE (Multi-OS-Engine) and KMM (Kotlin Multiplatform Mobile) is that


Disregard MOE

I met with @mr-elbe5 and he showed me how he does the layouting purely with UIKit in code (no storyboards, no SwiftUI) with extension functions on UIViews in his projects. It looks all pretty swift. In my opinion, with SwiftUI not really production ready and maybe never will, plus with Storyboards being quite unflexible/horrific (in my experience), this is the way to go.

I took away from the meeting that the MOE approach should not be pursued further, because:

  1. MOE runs basically the whole app in ART which could maybe be considered a VM. The iOS App Store disallows VMs. It is not clear if using MOE is even (officially) allowed and we know no example of any app on the iOS App Store that is based on the MOE framework. That's a too shaky foundation for a major committment.
  2. The last MOE release was from 5 years ago, it is not really clear if Migeran still supports this, even uses it for own projects
  3. With MOE, you write ViewControllers etc. code in Kotlin / Java. So, someone who would like to get involved here (writing UI code) would need to know both Java/Kotlin and iOS/Swift (and the possible quirks of MOE). The barrier for contribution seems a little high there.

KMM on the other hand is already used by quite a few major companies, has excellent up-to-date documentation and the iOS-only code is written in Swift/Objective-C, so the same concerns don't hold for KMM. One open question from the meeting was how Kotlin native does memory management. The answer from the official FAQ is

Kotlin/Native provides an automated memory management scheme, similar to what Java or Swift provides. The current implementation includes an automated reference counter with a cycle collector to collect cyclical garbage.

What does this mean?

With the framework decision settled to KMM (or at least not MOE), it is clear that

How can people contribute

...to an eventual iOS port now?

  1. iOS developers could write custom views that mirror the functionality (and best the appearance) of the forms currently available in the Android UI such as UIs for recording opening hours, for selecting a picture with title from a list of items (surface quest etc.), for measuring widths, etc etc.
    Side-effect: Well-designed custom Swift UIs for entering certain OpenStreetMap data could be used in and thus benefit Go Map!!, too!
  2. (Re-)build the main UI for this app on iOS (the map view, the settings screen, the about screen, stats screen etc.). With no data or just stub data, pure iOS.
  3. Kotlin/Java/Android developers can help eliminate or wrap Java dependencies from Kotlin classes that could be shared. (I may post some "help wanted" tickets soon)
  4. Developers interested in reading into Kotlin Multiplatform Mobile can contribute by researching and experimenting how to convert Java libraries (such as the ones named above) to Kotlin multiplatform libraries. Other option if that does not work well: Port the Java libraries to Swift libraries.
  5. Developers interested in reading into Kotlin Multiplatform Mobile can further contribute by setting up the project as a KMM project and (start to) move pure Kotlin code into the shared library, see if there are any problems using those in the (stub "Hello world") iOS project
  6. Developers interested in automation could research into creating (gradle Kotlin) tasks to automatically sync/download resources (pictures, graphics, vector graphics, strings, metadata, etc) into the iOS project (in iOS specific format) after it has been set up (point 5)

This is not a kickoff insofar as the time I can invest into this myself will be severely limited, but each of the above points is a meaningful and sustainable contribution to an eventual iOS port. Especially point 1 and 2 are something that could be done with zero setup and independent of any other work towards this goal. I'd even propose a separate repository at first, so one doesn't need to take care how it can fit into the current setup of this repository. Eventually, it would be incorporated in this repo.

mr-elbe5 commented 2 years ago

I have now tested the Android app (thanks to westnordost) and as far as I could analyzed some of the code. My conclusion is that an IOS app should be created independently from Android either under westnordosts's lead or as a completely independent app.

My reasons:

  1. Business logic/data and UI are very dependent on each other. The data features a list of specific questions/anwers and these are reflected in a very specific UI (not generic by concept). So each time a question is added or modified, the UI has to change. In any case the data has to be the "source of truth" and not follow the UI.
  2. Swift (together with libs) offers a lot of native solutions for UI (layout, images, forms,...), network requests (OSM server), xml handling (OSM API) and data storage.
  3. In my view a generic wrapper around a data core would mean more work than writing everything from scratch.

So from my side I will not be able to do all of this, but if somebody else takes the lead I will be happy to help with a sort of template-like test app to bring data to the UI, depending on the grade of generification. Meanwhile I will try to bring data from the OSM API to UI in a branch of my SwiftyMaps and hopefully this can be reused here.

matkoniecz commented 2 years ago

One thing worth noting (and basically question to @westnordost ) - it is worth considering whether you would be fine with name "StreetComplete" used by app that is created independently and released by a different person.

Main risk here is that something similar to Ublock/Ublock Origin fiasco can happen ( https://en.wikipedia.org/wiki/UBlock_Origin#uBlock )

westnordost commented 2 years ago

A clone of this app on iOS that shares no common code with this app should use a different name as there is no way such an app would ever reach the same feature set or even behave similarily as StreetComplete. Note though that such attempts failed twice before. Also note that neither the UI, the network requests, XML handling nor data storage1 would be part of a the shared code, because they are platform specific, so there is maybe a misconception what code would be shared. @mr-elbe5 FYI maybe this is of interest for you.

An iOS port can be developed iteratively in the sense as that only a few quest types are supported from the start and over time, the UI for more quests are added. When a quest that is already supported on the iOS port is extended (e.g. by adding another answer option: "Is this a self-service laundry?" -> [no] [yes] ... becomes ... [no] [optionally] [only] ), it is true that UI changes from then on need to be done on the iOS and Android app simultaneously. But this is by design: It ensures that all quests that are available on both platforms work the same and have the same logic.

It looks like mr-elbe5 has a different view on that matter than me, but let's - when required - open a topic in the discussions forum about it as this ticket should remain succinct and just outline the tasks that need to be done, see my previous comment.


1 only the data storage logic, not the data storage itself.

RubenKelevra commented 2 years ago

@westnordost wrote:

This is not a kickoff insofar as the time I can invest into this myself will be severely limited, but each of the above points is a meaningful and sustainable contribution to an eventual iOS port.

How about pledging on this?

Would we be able to just buy more time on this matter? It comes up pretty often as a request, so there's some demand. If I got just a buck for each time I got asked about that :'D

rugk commented 2 years ago

Well you could use https://bountysource.com/ or a similar service to collect bountries for issues like this one… if you want…

Echolon commented 2 years ago

Stupid question: Wouldn't is be "simplest" (atm) to deploy a web-version? So iOS and tablet devices can calm the website and one can login?

RubenKelevra commented 2 years ago

Well you could use https://bountysource.com/ or a similar service to collect bountries for issues like this one… if you want…

Yeah, I was just wondering if that would be an option. There are a couple of services available. Not sure what kind of service would be appropriate for pledging goal on this for a German developer – so I didn't want to recommend a service.

Stupid question: Wouldn't is be "simplest" (atm) to deploy a web-version? So iOS and tablet devices can calm the website and one can login?

I don't think so. Web services would require a server able to handle the load. This increases the cost to run this project for no real benefit – as there's also a lot of developing time involved to convert an Android app to a web service.

However, you can run Android apps for example on ChromeOS in the browser – if this is a thing you're looking for.

westnordost commented 2 years ago

I really don't want this ticket to dwelve into a discussion. That's what the discussions forum is for. This ticket shall just list what needs to be done and how it is/can be organized. I hide every post as off topic that is about something else.

There are various possible funding sources one could try and people have contacted me tentatively privately about the possibility to fund this. Maybe it would help if those organizations that are thinking about maybe funding or helping to fund this to state this publicly - but, I think the key necessary ingredient for an iOS port of StreetComplete is a knowledgable Swift/iOS developer that is willing to take a leading role in this effort. Then we can see if we can find funding for this, not the other way round. After all, we strive for committment to the project in that they are also willing to maintain it over time. Hence, why I wrote before that anyone who is (vaguely) interested in this as a programmer or even as a possible funding organization to give a shout here because if enough people are found, this could lead to a team effort and/or a common application for a funding. Certainly less of a hurdle than committing to doing it alone (though, you won't do it alone, iOS developers will work closely with developers of the core and the Android part anyway)

westnordost commented 2 years ago

Might want to monitor https://github.com/icerockdev/moko-mvvm - would, when using MVVM (the app currently doesn't though), make it possible to put even the ViewModel code into the common code. (Was mentioned in the Kotlin Multiplatform Mobile Beta Roadmap Update 2022-05-31)

westnordost commented 2 years ago

Also, https://www.jetbrains.com/lp/compose-mpp/ looks very promising. Basically, the idea is that you write your UI in Jetpack Compose (similar to but more workable as SwiftUI, AFAIK) and that UI then works on both iOS and Android. This won't save any work as all the UI would need to be rewritten one way or another, but would somewhat cut the maintenance cost (as the Android UI code would be thrown away thereafter).

Edit: Uhh, though, I notice that iOS is not mentioned as a target for compose-mpp. Not sure why - is it really not supported and no plans to do so?

FloEdelmann commented 2 years ago

There seem to be people using it on iOS though: https://github.com/JetBrains/compose-jb/issues?q=is%3Aissue+sort%3Aupdated-desc+ios Interesting comment by the main developer: JetBrains/compose-jb#2048 (comment)

Compose for iOs is currently in an experimental stage and a lot of things just isn't implemented yet. We aware about issues with TextField, LazyList, etc, and we will fix them in the future.

westnordost commented 2 years ago

@FloEdelmann Hmm, interesting. That could really be a game changer because that has the potential to making it much more painless to get to an iOS version: Instead of building the iOS UI from scratch, one could ("just") convert the current UI one by one to using compose-mpp and already use it in Android, knowing it can be used the same in iOS. Of course, Fragments, Activities etc. are still platform dependent, so what this mostly really does is replacing the Android layout XMLs with a Kotlin-based DSL that would be usable on iOS too.

YoshiRulz commented 1 year ago

JetBrains/compose-jb#2292 Though it seems it's already working? Albeit behind an experimental flag: https://github.com/JetBrains/compose-jb/blob/a018ac802adea8572fe5e5b49d892c5126801fda/experimental/examples/falling-balls-mpp/build.gradle.kts#L165 Ctrl+F for uikit in that file, it doesn't look like you need much iOS-specific setup. All that said, I'm guessing it's in a similar situation to Compose for Desktop, maybe trailing by a few months—if you want to use the same Material Design components as on Android (rather than draw your own), you're going to have to wait. (If my experience with Compose for Desktop is anything to go by, the components aren't actually multiplatform anyway, they just have the same name and similar behaviour. nope)

westnordost commented 1 year ago

@YoshiRulz does that mean that one has to implement the UI for each platform separately anyway? Just for example, the UI to input the capacity of bicycle parkings?

YoshiRulz commented 1 year ago

On a closer look, that sample project appears to have the main composable in common code, so I was mistaken about that. But I'm sure you could make actual/expect abstractions if not.

HolgerJeromin commented 1 year ago

https://blog.jetbrains.com/kotlin/2022/10/kmm-beta/ https://android-developers.googleblog.com/2022/10/announcing-experimental-preview-of-jetpack-multiplatform-libraries.html?m=1

And a well known german news site announcing this beta with some explanation: https://heise.de/-7304527

YoshiRulz commented 1 year ago

@westnordost What's next after java.time? Did you have a list of JVM/Android APIs in mind, or maybe a third-party library that can be ported to Kotlin Multiplatform? You mentioned your countryboundaries and osmfeatures libs but IMO it's better to do first-party code last.

In my personal projects, I'd at this point move the test code to common and add expect declarations until it compiled. From https://github.com/streetcomplete/StreetComplete/issues/1892#issuecomment-1042296087 I gather you wanted to switch to Multiplatform and add the common module; if you or someone else familiar with the build system could do that now, I'll evaluate how much expect/actual noise will be required for moving tests to common.

westnordost commented 1 year ago

There is no definitive list, but one thing on my tally is that all *Controller classes that are observable use java's CopyOnWriteArrayList to keep the list of listeners in a thread-safe way. This should be replaced with for example a synchronized (or "copy on write") HashSet. An to not litter synchronized everywhere, could make that a (thin) wrapper class around HashSet called Listeners or something.

Regarding your proposed next steps: I agree that porting the libraries to Kotlin is something that would only be worthwhile if another non-Java project would like to use them at this point. Regarding reconfiguring the build system for multiplatform - that could be done, but on the other hand, there are some known needed refactorings still to do, e.g. at least https://github.com/streetcomplete/StreetComplete/issues/3597#issuecomment-1107931316 (for which I do not know the status of, it could be that @matkoniecz started on it already, I don't know). Also, I was asking myself if the common test code can/should depend on kotlin.test rather than JUnit so it could be moved to common, but on the other hand, a lot of it uses Mockito as a mocking framework and I am not aware of any pure Kotlin replacement for Mockito.

susrisha commented 1 year ago

I run a team of mobile developers who are into both iOS and Android. Would love to give this one a try.

westnordost commented 1 year ago

That's great to hear!

Let me summarize for you the current state of affairs:

Kotlin Multiplatform Mobile (KMM)

A possible iOS port should be developed using Kotlin Multiplatform Mobile so that as much code can be shared as possible. (Yes, the amount of sharable code is considerate. Already 2-3 attempts at creating an iOS port from scratch were abandoned because they all underestimated the sheer size of the project.) In a nutshell: Application logic shall be implemented in pure Kotlin without direct dependencies to Java or Android. This code can then also be used for the iOS part. Currently, most of that code is already free of such dependencies.

The next self-contained steps (I can think of) in that regard are, for which PRs are welcome:

1. Get rid of Java and Android dependencies in application logic (=non-UI) code

2. big (once most application logic is free from Java/Android deps) create a Kotlin Multiplatform Mobile project setup and move the pure kotlin code to the shared module.

(If I see there is some commitment, I can also write these into tasks with more precise descriptions, rough time estimations, the github kanban-like board etc. later)

Compose Multiplatform

The Android UI code is currently done with layout XMLs. In KMM, by default, one would write the iOS specific part (mostly: the UI) in Swift like in other iOS projects and access the application logic as a library. This means that the entire UI for iOS has to be re-written from scratch and also - importantly - having to be maintained separately from the Android UI. An alternative to this is Compose Multiplatform, built on top of KMM, currently in alpha. It is a fork / extension of Android's Jetpack Compose, a UI framework comparable to SwiftUI. In Compose Multiplatform, you write the UI once in shared code and it renders on both Android and iOS. This still means that the UI needs to be written from scratch, however

  1. in the end, there will only be one UI implementation, not two. This makes a huge difference. To expect a long-term commitment of any code contributors to also maintain their contributed code would be illusory. And as the app is not backed by any company or organization, the time the main developer (that's me) can put into app maintenance is severely limited. Maintaining two UI implementations is going to be double as time-consuming as maintaining one, i.e. not really viable if I remain to be alone.

  2. since Compose can apparently be integrated seamlessly into Android XML layout UI code and vice-versa (same with UIKit and SwiftUI, but this is not relevant here), a migration from Android XML-based UI code can be done step by step. This, too, is a huge plus as it enables different contributors to contribute their piece and come and go without one contributor in particular committing (alone) to a huge undertaking that may work or may fail entirely. I've learned that such an iterative model is key to success in an open source project where you can't oblige people to see anything to the end like it would be the case for employer-employee relationships.

The main downside of Compose Multiplatform is that we'd have to replace working code and that it is still in alpha. Working with cutting edge technology usually isn't pretty because the environment is changing under your feet and certain things don't work or only with messy workarounds. Additionally, Compose Multiplatform is not the silver bullet for the UI, as many Android/Java-only dependencies are used in the UI which would need to be replaced with an appropriate replacement on iOS. (E.g. the map renderer, the QR code generator, the physics simulation in the profile screen, ...).

However, Jetpack Compose on the other hand is stable and has an almost 1:1 API as its Multiplatform brother, so "just" migrating step-by-step to Compose doesn't seem too risky. Also, as the list in the previous heading shows, there are enough other things to do (before) anyway, so when that is done, Compose Multiplatform and the environment around it matured more.

In any case, I am currently familiarizing myself with the technology, so I might be able to say more about that in the future.

In conclusion

, there are many small tasks that can be done to further advance towards an eventual iOS port. These do not require a heavy or long-term commitment and can be included in the project as contributions just when they are done each. I know it can be somewhat demotivating to want to contribute to a project a huge project and then see that it will never be merged (because it becomes bigger and bigger along the process, a neverending construction site). For the above tasks, this is not a concern, as they are all each modular, self-contained and once merged live in the same repo and branch as the original Android version.

Working with and getting to know new (Kotlin Multiplatform Mobile is in beta) or even cutting edge technology (Compose Multiplatform is in alpha) can be a motivation on its own. Especially, I find the prospect of (learning how to use) the two technologies combined together somewhat appealing also from a business perspective, as it may potentially cut the cost of developing an Android + iOS app to almost half.

mnalis commented 1 year ago
HolgerJeromin commented 10 months ago

Kotlin Multiplatform (KMP) is making progress: https://blog.jetbrains.com/kotlin/2023/11/kotlin-1-9-20-released/ https://blog.jetbrains.com/kotlin/2023/11/kotlin-multiplatform-stable/

sum up from a german IT new site: https://www.heise.de/news/Kotlin-macht-Ernst-mit-der-plattformuebergreifenden-Programmierung-9352485.html

mcliquid commented 9 months ago

A roadmap for 2024 was introduced yesterday: https://blog.jetbrains.com/kotlin/2023/11/kotlin-multiplatform-development-roadmap-for-2024/