Writing the new iFood for Partners — Part 3: What are we managing now?!

Gildásio Filho
iFood Engineering
Published in
6 min readMay 5, 2022

--

Hello once again, I'm Gildásio, a software engineer from the iFood for Partners app team and this is the third and last part of our journey on turning this into a super-app in Flutter.

After updating from Flutter 1.22.6 to 2.0 almost a year ago, we been seamlessly updating each new major version after a month of its release (so we wait for all the bug fixes first before jumping in), so that's been really smooth and thanks to the Flutter team since they keep each update with minimal breaking changes!

At the same time, we decided on a new architecture for our UI and state management, and been with it since then, making sure it serves each new feature with ease for every new developer. We already changed a few older features to use it, and aim to have the whole app "converted" during the year, so that's another challenge for us.

But today we're talking about something even bigger: merging with another app and not even a Flutter one, unfortunately. iFood's Order Manager is currently only available for Android, had a smaller team and it was decided back in September/2021 that we were to "absorb" it into our Flutter app, making it our new months-long journey to make a new module that could well be another app, then keep adding features until we can do everything the current native app does, but in Flutter! Below, you'll ride along with me as I talk a bit broadly about all the biggest topics we had to tackle, shall we?

The Order Manager

It's the brain of the whole operation, it's where the restaurants in our platform can accept, reject, dispatch, request delivery, manage cancellations and everything related to each order. Just talking about it like this makes it seem important already, and we needed to nail this new implementation as soon as we could.

Every partnered restaurant has to keep an instance of the Order Manager open to be considered open in the consumer app, be it the desktop, web or the previous app. Now that we’re moving from the previous app to ours, we need to guarantee that we can keep polling events, and that has a target of every 2min. If no polling has been done in that time after the last one, and there isn’t any other Order Manager instance open, that restaurant will appear as closed and obviously, everyone loses if that happens.

If you're an old Flutter developer, you know that the matter of background tasks has always been complicated to work with, fortunately, when we needed them it was easier than ever with background_fetch, but only until it wasn't. Those fetch events only happen roughly every 15min, sometimes even more, and that's way more than our polling window of 2min. For Android, we had to give up on the lib and write our own service based on AlarmManager and wrap that up in our own plugin since we had that option to make it more frequent. For iOS, we discovered that it would take days until the system recognizes that your app can start getting into the background fetch windows, and unfortunately writing a service similar to what we did on Android wasn't an option.

Our main mission was to guarantee the same experience that the users already had on Android, but now on iOS, and unfortunately no workarounds have been found for the background fetch timing and we decided to rely on push notifications that wake the app, and also telling the user to keep the app open as long as they can to maintain the foreground polling service.

Two-in-one

We took the "absorbing another app" as literal as we could, you can toggle between both "Order Manager" and "iFood for Partners" one with a button in the top right corner of the main screen of each of those, but those names are hard to keep repeating for both the users and us, so we changed those names to "Orders" and "Store", since the features in the Store side are changing your menu, opening hours, and other non-critical things that you need to keep an eye on.

This came with a lot of challenges: first, we needed to make sure we could toggle the new half with feature flags, and also make it so the account and restaurant permissions are handled separately for each half of the app. We shouldn't be checking permissions only related to the Orders side to make changes in the Store side, and vice-versa.

We have an internal product that handles push notifications, remote configs and the feature flags we needed called Faster, and for now it only has iOS and Android-only SDKs, which meant we had to write our own Flutter version using those. It's in our plans to write a Dart-only version of it, but that will have to wait a bit.

A whole new API

All the while we were writing the first versions, another project was creeping under our backs called merchant-api, which is a new version of the pos-api available for any developers that want to create or integrate their own "Order Managers", you can read more about it here. Which meant that while we were actually making progress and making the Orders module work as it should, we had to be prepared to change endpoints, models and even some rules in the UI because the whole API was going to change.

One of those big changes was the token management. For the Store side, we work with our own BFF (backend-for-frontend), we authenticate through it to receive a token (JWT) that is then used to get a merchant-api one for the Orders side. How did we manage to have two separate API clients at the same time, with their own refresh routine? Abstractions!

We have a network abstraction just called Network that has the REST (GET, POST, etc.) methods as a contract, and a way to add an interceptors. Each new repository that works with any API receives one of those Network objects, and we just had to make one that points to our BFF, and another to the merchant-api, so every Orders repository receives the "OrdersNetwork" implementation with everything they need, and every Store repository receives the "StoreNetwork". The interceptors are also separated, since they are the ones that inject their respective tokens, and take care of refreshing them once needed.

Fortunately, changing endpoints and adding a new network implementation was the easy part, but changing all the models and their fields to work with the new stuff gave us some trouble along the way.

Events, events, events!

Now a bit of explanation on what exactly makes the Order Manager, the Order Manager: event management. I told you before that the most complicated part was making the polling of the events work in the background, but what exactly are we doing with these?

Each Order is created only once, and it can't be changed, meaning any status updates come from events instead. Each event can have some sort of metadata that allows some action to happen, or has information to be shown to the user. As an example, the CONSUMER_CANCELLATION_REQUESTED event contains the reason and internal code of the cancellation, and we need that to show the cancellation screen where the restaurant accepts or declines the request.

With that in mind, we developed a way that changes the Order object internally and locally for the events that do have some kind of change/metadata to it, and plan to keep improving it so it's easier to maintain and add more events as they appear. We're relying heavily on Dart's extension methods to not bloat the already big enough Order class, separating many files with extensions for some rules and fields, it really helps us keep everything organized!

And it's only the beginning!

So far so good, after many challenges along the way, issues solved, engineering discussions on new features that weren't even available in the older app, we managed to launch the Orders module to more than 100k restaurants currently using our app, and plan to deactivate the older Android app, migrating those users to ours as soon as we can. It's rewarding seeing the many reviews in the App Store that asked for the "Order Manager for iOS" being changed to "Finally!" as we make it available for more accounts, and we hope to provide an even better experience for all the new features, and for all the new old users. If you'd like any in-depth article about any of the topics I passed through here, please let us know!

Check the vacancies available on iFood, learn more about the selection process and join the team.

--

--