streetcomplete / StreetComplete

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

iOS - Planning #5421

Open westnordost opened 9 months ago

westnordost commented 9 months ago

This ticket is something like the master ticket to coordinate development on an iOS port of StreetComplete. It replaces #1892 which also included a lot of discussion and research / observation work.

TLDR: Have a look at the Project Board for a list of tasks. Contributions are welcome!

Also have a look at the Slides from SotM Europe 2024 talk (I added speaker notes to make this understandable when flipping through).

Approach

The code base of this app is written in 100% Kotlin, a modern programming language that is quite similar to Swift. Kotlin code can be transpiled to JavaScript and also to machine code, just like Swift. Furthermore, many dependencies of this app by now are pure Kotlin libraries.

An iOS version of this app hence can be done with Kotlin Multiplatform.

The UI code can be shared using Compose Multiplatform, which is currently in alpha beta for iOS. It is a fork/extension of Jetpack Compose with largely the same API. Jetpack Compose is a reactive UI framework in which developers define the UI completely in code (similar idea as SwiftUI, Flutter, ...). We'll have to re-create the entire UI (incrementally) in Compose, though.

This will keep the amount of code that needs to be platform dependent (Android / iOS) to a minimum.

This approach has the big advantage over others in that further maintenance of this project will not increase significantly, as there will continue to be just one code base. In comparison to using the framework Flutter for multiplatform (as used by Every Door), it allows us to largely not touch the Kotlin codebase. When using Flutter, we'd need to rewrite it all in Dart.

Steps in a nutshell

Current State

This section will be updated continuously.

The steps necessary are described in detail in a Kanban-style Project Board. (Some tasks have not been created yet because they 100% depend on other tasks completed first and some research). Unless stated otherwise, they are explicitly up for the taking by people who read this and (I hope 😊) want to contribute to the development towards an iOS port. If you would like to implement one of the tasks described, comment in that ticket.

We very roughly estimated some time ago that developing a full port to iOS would take one man-year of work. So, the scope of this remains very large. We will only realistically achieve it together with lots of contributions from the community. Luckily, this is exactly what has been happening in the last years. In the first half of 2024, I have been able to work on this full-time. The migration is now about 50% complete.

So, let's shift gears!

How you can help

westnordost commented 8 months ago

On what needs to be done on the UI side of things:

TL;DR:

(Disclaimer: I (too) need to find my feet with those Jetpack Frameworks I haven't worked with yet, namely mostly Composables and Navigation. The below is a result of some research, looking at example code etc., so if I did misunderstand something I describe below, please call it out!)

Current UI architecture

current_arch

Android Activitys are mostly just containers and Fragments make up the brunt of components that define the view behavior, state, manage lifecycle, handle data (updates) and communication and navigation with/to their parent and child components.

Views are generally defined with Android XML layouts, a few "*ViewController" classes exist that define the behavior of certain view constructs in a reusable manner. Some compound/custom views may also (still) exist.

Projected UI architecture

On Android, it would look something like this:

target_arch

View layout, their behavior and their (ui) state is done via Composables from Jetpack Compose / Compose Multiplatform.

The data used for display in the UI is accessed through ViewModels. It is probably better to use Kotlin's StateFlow for the fields in the ViewModels instead of LiveData from the very start, because the former are already multiplatform.

Finally, the navigation in-between the different composables is managed using NavHost of Jetpack Navigation somehow (didn't read much into that yet). This means that the Activity is just the entry-point of the app and there are actually no Fragments left, everything is Composables and ViewModels, which means everything can be shared. (Hooray)

UI architecture steps

Now, we must avoid at all costs to open up too many construction sites at the same time. It is not feasible to go from our current architecture to the other one in one big step. Instead, we must split up the migration in as many self-contained steps that make sure the codebase stays consistent in itself:

Step 1. ViewModel

Slim Fragments: The data access and view state management shall go into ViewModels, owned by the Fragments. We have a ticket for that already: #5530. Even independently of iOS, this makes sense, as it properly encapsulates view logic from accessing data / managing state.

Step 2. Jetpack Compose

Convert the UI layout definition and behavior code bottom-up (see migration guide, migration tutorial) to Jetpack Compose. This involves not only converting the Android XML layouts to Compose code, but also the code of "*ViewController"s, RecyclerView view holders and finally of course also Fragments, as that's where most of the view behavior is currently defined.


current_view_arch drawio (After step 1 and 2)

Step 3. Compose Multiplatform

Go the (hopefully small) step from Jetpack Compose to Compose Multiplatform. The biggest issue here will be how to access resources in a multiplatform way. There are some libraries already which facilitate this, like MOKO resources, but hopefully a popular and stable solution will have emerged by then. I really hope there will be something official from JetBrains. (They did announce this.)

Step 4. Multiplatform ViewModel / Navigation

Edit 2024-09: The ecosystem changed! It will be possible to use the Android ViewModels and Android Navigation for multiplatform, too, so this makes the below research obsolete, we can just keep using what we are using now.

Already by the end of step 2, Fragments will be very slim. We have the option to stop here view-architecture-wise and just duplicate the rest of the code that still lives in these Fragments on iOS in UIViewControllers defined in Swift. I think we should revisit this when we finished these steps, as these steps are really big on its own already anyway.

However, we still need to make at least ViewModels multiplatform. Interestingly, most libraries that provide some concept of ViewModels but as multiplatform also come with a solution to completely do away with Fragments (i.e. a replacement for Navigation and related things).

Those that provide a multiplatform replacement for only ViewModels are KMM-ViewModel, MOKO MVVM, Summer, kmp-viewmodel and others but most of these are either kind of alpha or older and quite inactive.

Here's a list of frameworks I had a little longer look at, all of these do something something ViewModel and also provide some glue for navigation:

PreCompose

PreCompose provides both ViewModels and Navigation whose API is pretty similar to the Android equivalent in Android. The project's readme itself mentions...

If you familiar with Jetpack Lifecycle, ViewModel and Navigation, there will be nothing to learn.

Additionally, it has some integration with Koin, which is the dependency injection framework we use.

The project exists since 2021 and is continuously active since mid-2022, 0.6k people starred it on GitHub.

Voyager

Voyager also provides both for view models and navigation.

In particular, it introduces the concept of Screens. A Screen conceptually is a replacement for a Activity or full-screen Fragment.

Instead of ViewModels, we have ScreenModels, which have the same purpose as ViewModels. Good thing: ScreenModel is just an interface. Also there is integration with Koin, the dependency injection framework we use.

Additionally, it has some setup for navigation with bottom sheets (which we use extensively) as well as tab navigation (used in profile screen).

The project exists since end of 2021 and continues to be very active, 1.8k people starred it on GitHub. Version 1.0.0 was released in December 2023 and shall bring API stability, the documentation is quite good. Of the three projects I've looked at, it is the youngest.

Decompose

Decompose can also serve as a replacement for the functionality described, although of the three libraries, it brings the greatest departure from the terminology, concepts and APIs from Android Jetpack:

One feature of Decompose is that it works well not only with Compose but also with SwiftUI and other UI frameworks, but I think we do not need that. But it means that Decompose is designed to be more of a general framework rather than being made for or tied to any specific UI framework. Instead, it is written that Decompose goes well with MVIKotlin, another framework by the same author.

The project exists since 2020 (thus the oldest) and has been continuously very active (most active of those compared) ever since, 1.7k+ people starred it on GitHub. They are working on a new major version since at least December 2023. Striking is the extremely low number of open issues.

Conclusion

To be honest, I didn't really grasp the concepts of Decompose and the independency from Compose Multiplatform is nothing we would require. The migration to ViewModels + Composables + Navigation seems to me is already enough to chew, it does not help to then learn even new (even if related) concepts.

This is why I currently slightly favour Voyager, because just as PreCompose, it sticks quite closely to the concepts from Android Jetpack while at the same time is more popular and has more documentation + useful components than PreCompose has to offer, apparently.

The final decision on this lies somewhat in the future, though. The goal of this preliminary research has been to first ascertain whether the concepts of ViewModel + Navigation can be transferred to multiplatform too, so that the iterative steps 1 & 2 (before 3 & 4) are possible. The outcome: Yes, it's possible, frameworks exist whose concepts and APIs are quite similar to what we have with Android Jetpack.

susrisha commented 8 months ago

we on the other hand are also trying to make an iOS native version with my team https://github.com/TaskarCenterAtUW/GoInfoGame-iOS

westnordost commented 8 months ago

Interesting, first time I heard about "GoInfoGame". It looks like an abandoned fork of SC from 7 years ago that was created during a hackathon. So, your plan is to port that? Well.

Note that back then, SC was - architecturally, and what it could do - much more basic than now. Basically, one overpass query per quest type, display them on the map, answers are converted to OSM tags and uploaded. I mentioned this old architecture in my talk on the SotM 2021 in the first few minutes, before I continued to talk about the current architecture. (Actually, that old architecture was already the next iteration from the overpass queries from 7 years ago, but nevermind)

People tend to wrongfully assume a project is simple because its UI is simple. So to your attempt, I would reply the same as I did before in reply to someone else and add that GoMap!! (to my knowledge) already includes some basic StreetComplete-inspired quests and this is as far as one can go without, well, going towards replicating all of StreetComplete functionality eventually. Of course, it goes without saying that trying to re-code something from scratch that has been continuously developed and refined over the course of 8+ years seems to be a futile endeavor, much less if there is already a plan well underway towards making it platform independent.

Anyway, if you made up your mind, does this mean that you do not anymore plan on helping with porting the osmfeatures library to kotlin kmp?

(I will hide these comments as off topic as they are not related to planning an iOS port for this app)

susrisha commented 8 months ago

That was just an attempt we did and still pursuing for usage elsewhere. we still are pretty much inclined to helping with the port to kmp. Yeah the logic seems a bit old and we intended to do it as a separate endeavor.

westnordost commented 4 months ago

Step 4. Multiplatform ViewModel / Navigation

Also,

To just keep using the AndroidX thingies would of course be the least effort.

louwers commented 2 months ago

Thanks for the great discussions at State of the Map EU. With regards to MapLibre Native for Compose Multiplatform, it seems someone already got it to work, someone else is building a library. πŸ‘

https://github.com/maplibre/maplibre-native/issues/2638

Doomsdayrs commented 1 month ago

Not exactly sure where to place, but this might be useful:

Lifecycle path to Multiplatform

westnordost commented 3 weeks ago

The funding through Prototype Fund ended, time for some progress update, retrospective and outlook!

Progress Update

The work that has to be done can chiefly be grouped into two areas:

  1. Replacing dependencies to Java, Java-Libraries and Android with Kotlin Multiplatform (KMP) Libraries or own implementations on iOS
  2. Re-Implementing the whole UI in Compose, also using ViewModels and Navigation.

1. Replacing dependencies to Java

The biggest tasks completed within the funding period in this regard were replacing the Java Http Client with the KMP library Ktor-Client, porting the osmfeatures library to Kotlin Multiplatform and creating the osm-opening-hours Kotlin Multiplatform library. In detail:

progress-dependencies

(blue = already done before the funding period started)

What remains? Mostly fitzelkram and glue. But, well, that kind of stuff is often what takes longer than expected. One of the larger things in terms of lines of code that still needs to be done are

1. Re-Implementing the UI in Compose

...and introducing ViewModels + Navigation. Approximately, the absolute effort for this is about double that of the above, so this makes up the bulk of the to-be-done work.

MapLibre

An essential and large part of this undertaking was to migrate from the abandoned map renderer tangram-es to MapLibre-native, done by @Helium314 and me. It was merged to master recently because we finally found a workaround for a critical error in MapLibre.

Compose

I first focused on implementing all screens except the main screen. Recently, I broke forth into the main screen though, but mostly just the controls, dialogs etc. In detail:

progress-ui

So, what's still to do are all the quest and overlay forms (and you know, they are often custom, that's why it is a lot of UI code) and abstracting the Android-specific implementation of MapLibre, i.e. use MapLibre in Compose.

Now, I would love to not have to do the latter myself, but a library for this does not exist yet. There is, however, some developer interest in this. It looks like a group is forming to implement a library, but as it stands, there are currently 3-4 competing projects, so I hope there will be some consolidation soon: https://github.com/maplibre/maplibre-native/issues/2638

Retrospective

The time estimations made for a full iOS-version of the app (one year full-time for one developer) are still quite on point.

The MapLibre integration took about 6 weeks more than expected even though @Helium314 did preliminary work on it before and then substantially helped me complete it. Re-Implementing the UI in Compose on the other hand turned out to be more straightforward and faster than expected, so that somewhat made up for the extra time spent on MapLibre.

Other than what has been mentioned, there were unfortunately no contributions made to this effort from other developers since I began to work full-time on it. So, now that I have worked on it for half a year, it would be roughly half a year more to complete this effort. (Plus, realistically, some generous buffer for unforeseen extra effort.)

Outlook

My to dos

Despite the funding having ended, there are a few things I still want to complete in order to get to a round conclusion of the work done so far. The time frame is 1-2 months (with less speed, need also some time out, really)

Continuing

Extrapolating from the last half a year, I am now less hopeful about that it would be possible to complete this project by community contributions alone. The remaining work is also still substantial. I will look into possible follow-up funding, but the outlook does not look too good: Another Prototype Fund will not be possible, last time I checked, OpenStreetMap has no money and the funding programme by the NLNet foundation is being dissolved in favor of some AI bullshit, too. Sponsors through Liberapay, Github sponsors and Patreon also didn't increase since this effort was announced, so crowd funding won't work either. Let's see. Suggestions welcome.

Contributing

Of course, dear interested developer, any contribution done to this effort will just make completing it more feasible and no work towards that goal is lost, because we continuously integrate it into new versions of the app.

If you are inclined to help, the next steps in this process are to, bit by bit, reimplement the current UI in (Jetpack) Compose and use ViewModels. The migration to Compose happens in a bottom-up sort of fashion (widgets first, then parts of the screen, at the end, the whole screen). So, a most sustainable contribution would be to start with reimplementing widgets, such as the opening hours form (big effort), the hydrant diameter form (medium effort) or the building levels input form (small effort) (or many more).

Now, there could theoretically be a version of the app for iOS with limited functionality before we reached the 100%, by excluding those quests whose UI haven't been migrated yet. Tasks that have to definitely be done before that, however, are,


As required by the Prototype Fund, I also created a report that will vanish in some archive that noone will read. Maybe you'd like to read it instead, however, it is in German and contains mostly the same stuff I wrote here: Schlussbericht StreetComplete fΓΌr iOS

mcliquid commented 3 weeks ago

Many thanks for the summary. The PDF is also very interesting to read. I just did some quick research on various FOSS fundings. There seem to be at least a few relevant ones at first glance. Some questions from my side to check how to support as a non-developer:

  1. Is there an overview of funds you have already applied to so that one would not submit a duplicate?
  2. Would it even be possible to apply for funding in your name or is it a bad idea?
  3. If I have it right in my head, Google would reject a version that asks for microdonations at app startup, right?
  4. Have you already asked OSM affiliated companies such as TomTom, Komoot, Mapbox, etc. for financial support?
  5. Have you already been in contact with FOSSGIS to use their network? Any other ideas to help?

Thanks for your reply.

westnordost commented 3 weeks ago

Oh, well, I didn't do that search yet because I first wanted to "clean up" by taking on the mentioned tasks, draw some breath and mind some other RL stuff that I didn't have the time to worry about in the last months. You know, enjoy the summer while it still lasts. Also, I do receive quite a bit of crowd funding IMO (for generally working on SC), so I really should do some quality of life improvements on the app again after I solely focused on this effort for so long before I look how this effort can continue.

There are a few organizations that seemed interested in pitching into sponsoring specifically an iOS version of the app, but IIRC each before I mentioned the scale of such an effort. Now that the scale has been reduced to about half, maybe I should ping them again, see what they think. I have never asked OSM affiliated companies for financial support. I also can't imagine that they'd give out that kind of money like that, they'd rather give this directly to the foundation, I presume. I mean, they'd need it, too, OSM has very little money to spare.

Google would only allow for donations if it was done through their payment system. The app would then be marked as having "in-app payments", a very undesirable badge to have as a FOSS app. To aggressively ask Android users to donate for development of essentially an iOS version also sounds... not ideal. πŸ˜…

Applying for funding in my name would be a bad idea. But of course, know that you could apply in your name, too. Someone else with the necessary knowledge and skills could also implement it - I always encourage contributions. Or as a team. Anyway, I already got some potential funding sources bookmarked, I'd be happy for if you want to email me further suggestions.

erwin commented 3 weeks ago

Google would only allow for donations if it was done through their payment system. The app would then be marked as having "in-app payments", a very undesirable badge to have as a FOSS app

Why is that undesirable?

Anyone who's more serious about free software can use F-Droid or directly download the APK.

You can write on the Google Play Install page that:

"In App Payments are for donations that fund development".

It's not your fault Apple charges developers to distribute a product to Apple hardware.

I don't think anyone who joins the Apple ecosystem can expect that their software should be cost free.

If you would enjoy writing the iOS version, then why not just charge $1 or $2 for the iOS version?

mcliquid commented 3 weeks ago

Oh, well, I didn't do that search yet because I first wanted to "clean up" by taking on the mentioned tasks, draw some breath and mind some other RL stuff that I didn't have the time to worry about in the last months. You know, enjoy the summer while it still lasts.

I understand, so there is basically no lack of follow-up financing in the first place, I had understood that differently at first. There should be a few possibilities for financing, but of course the commitment for further development must also be there at all.

But of course, know that you could apply in your name, too.

Why should it make a difference whether I ask for support in your name or someone else's? An third-party request remains a third-party request or not? I guess that the person who wants to invest the time in programming probably has to do it themselves.

matkoniecz commented 3 weeks ago

Why should it make a difference whether I ask for support in your name or someone else's?

This kind of application for funding is basically "I promise to do XYZ work in exchange for receiving M amount of funding".

I would not want either for someone to make such promises on my behalf!

Though if someone spotted potential well matching funding sources... Then I would be happy to learn about it! (Maybe in discussion section at this repository if it is extremely well fitting?)

matkoniecz commented 3 weeks ago

Why is that undesirable?

Apps with such note are typically requiring payment to work, are blatant cash grabs or otherwise problematic. (With exception of ones where paying for something is whole point of app)

(I marked it as offtopic after writing, maybe it should be discussed separately if at all)

matkoniecz commented 3 weeks ago

I have never asked OSM affiliated companies for financial support. I also can't imagine that they'd give out that kind of money like that, they'd rather give this directly to the foundation, I presume.

I think that at least in some cases there may be interest from them in funding very specific project. I would not assume that all such funding needs to be routed through OSMF.

westnordost commented 3 weeks ago

This is getting a bit off-topic, the ticket is about technical planning, not funding discussion, after all. It is not possible to transfer posts to a discussion on GitHub, so I'll just hide them here as off-topic. (We could continue in the discussions area.)

Charging for StreetComplete is off the table for me. StreetComplete is no convenience app that does anything for the user, like e.g. a general purpose map app would. It may make it more convenient and fun to contribute to OSM, but contributing to OSM is still honorary engagement. It doesn't feel right to charge for the ability to do essentially unpaid work. Also, with projected usage numbers vs implementation effort, 1$ per user wouldn't cut it, by far.

@mcliquid, well, now I think you misunderstood me. I would be grateful for pointers to possible follow-up fundings. It happened plenty of times in the past that some people/orgs expressed interest in sponsoring and nothing came of it. I won't bet on that and I don't blame them when the extent is unclear - certain features could be done in days, this effort takes a year. You can imagine the difference in costs involved. Anyway, what I meant with "you could apply": Others, like @matkoniecz , already applied for grants that had as its topic that they work on certain StreetComplete-stuff. I am fine with that.

rugk commented 3 weeks ago

First of all, thanks a lot for your efforts in any case!

there were unfortunately no contributions made to this effort from other developers since I began to work full-time on it

One speculation I'd like to make about this: Most users in the community here (aka StreetComplete) likely are Android users. So while I guess most of them see the point in a cross-platform app and supporting that case, fewer actually feel like contributing or would actively see the benefit (for themselves)? And yeah, I know, no iOS device is needed in theory but yeah... people also want to see their efforts ideally. And for a kinds "low-level" project like this one, you just don't see the result (or only at the end, which is harder to reach).

As such, I agree having an MVP and a published iOS app would likely attackt iOS users and thus maybe also iOS developers or at least developers intended to supporting "their" "own" platform. After all, having an app to use by yourself is probably the best motivation for building it (in OSS world IMHO).

Edit: BTW, I've tried to spread the word about this on Mastodon.

rugk commented 3 weeks ago

Another note on funding, and yes, I try to keep it short. Regarding:

the funding programme by the NLNet foundation is being dissolved in favor of some AI bullshit, too

Note if I understand it correctly, this is because the EU commission made the decicion to stop funding Generation Internet initiative (NGI) resulting β€œin a loss of €27 million for software freedom.”

That said, you – yes, you who are reading this and possibly using SC – can do something about it, because they actually offer a public consultation, where you can criticize that decision! (And yes, non-EU citizens, too!)

More information and arguments here: https://fsfe.org/news/2024/news-20240911-02.html (Deadline is: 20 September 2024)

Only thing I want to add is: The target audience searched there that is likely most relevant here are "members of the public (e.g. pupils and students interested in an ICT-related career or employees in the digital field).". Especially, you can mention this app here or others and explain it has been funded by that specific fund, actually.

So make your voices heard!