An iOS Experience That Had to Continue Before Opening the App
How widgets, notifications, deep links, and local cache kept the same park state connected.
The first thing I learned while building the iOS app was that an app is not used only inside its icon.
At first, I thought one app screen and one widget would be enough. Once I tried to use it myself, the work on the iOS side grew quickly.
Users right before departure do not calmly explore an app. They are often about to leave home, standing by the elevator, or already sitting in the car. If the app, widget, notification, and map-app handoff break apart in that short moment, the user goes back to the old way.
The app had to show park lists and parking information. The widget had to show the current status without opening the app. Favorites had to remain on the device. Notification settings had to match the server. Navigation had to hand off to the map app the user already used, such as Naver Map, KakaoMap, TMAP, Apple Maps, or Google Maps.
I built it with SwiftUI, and internally separated state, API calls, local cache, and widget snapshots so they would not blur together. SwiftData stored park and parking status plus updated times, and the app and WidgetKit extension read the same values through an App Group.
When a user entered the app from a widget or navigation flow, it continued to the park and screen they had been viewing.
Information that can be seen only after opening the app is only half useful.
For car decisions and widgets, checking before departure was especially important. Users should be able to see the status of a frequent park without deliberately opening the app. So I did not treat widgets as extras. I made them surfaces that are viewed immediately outside the app. I kept revising what to keep and what to discard in small, medium, and large widgets.

The general widget needed information that helps answer, “Is this park a good choice today?”, such as congestion, weather, and events.

The car widget had to show recommended parking lots and remaining spaces first.
A mobile app is not complete only inside its screens. It has to continue through widgets, notifications, deep links, cache, settings, permissions, and external map apps before it becomes useful. The job on iOS was to let several entry points read the same information quickly, but only as much as each moment needed.
Early on, I thought first about how to split screens. In practice, the more important question was where the answer had to appear in each moment.
| Touchpoint | Job | Risk to watch |
|---|---|---|
| App | Compare, check details, search facilities, change settings | Do not blur the first-screen decision |
| Widget | Quick check before leaving | Do not make stale values look current |
| Notification | Changes in user-defined conditions | Do not alert on every small change |
| Deep link | Continue to the park and screen being viewed | Do not send the user back to the first screen |
| External map app | Start actual movement | Link near the parking lot or access point, not only the whole park |
Only after separating these roles could I explain why SwiftUI screens, SwiftData storage, App Group, WidgetKit, and deep links were each necessary.
Looking backward from the departure moment showed where the boundaries needed to be.
Widgets were not a place to put more
The biggest widget question was not how much information to add. It was what to remove.
In the app, a user can scroll, tap, and enter detail screens. A widget does not have that time. The user glances once and either stops or leaves. So the car widget focused on “which parking lot is better right now,” and the general widget focused on “is this park okay today?”
This difference affected data storage as well. Even if the app and widget use the same source data, the widget needed its own snapshot. The app’s last observed status, updated time, selected park, and widget type had to be read together reliably. That is why I used an App Group to pass values between the app and WidgetKit extension.
Deep links also mattered. Opening a park from a widget should not land on a generic first screen. It should continue naturally to that park and that mode. The navigation button also needed to open the user’s preferred map app.
The app, widget, notifications, external map apps, and local cache need to stay connected before the app becomes practical.
From this point on, iOS architecture was no longer just screen layout. The problem became what each touchpoint should say immediately, and how the same values could be read together.
I asked for location permission only when needed
I also did not want to ask for location permission at the start. Hangangjari should be usable without knowing the user’s location. If users can manually choose parks and favorites, sorting nearby parks is a convenience, not an entry requirement.
So I treated location not as a fee users must pay to use the app, but as an option for finding nearby parks and parking lots more easily. The app should not collapse if permission is denied, and even when permission is granted, I wanted to keep the position on the device rather than send coordinates to the server.
Asking later changed the permission copy. A permission prompt should appear not when the app is technically allowed to ask, but when the user can understand why it helps. It felt more natural to ask when the user wanted nearby parks, and when the app could explain how sorting by current location would help.
Empty values needed reasons
On iOS, the screens that took the most care were often failure screens rather than success screens. The app needed distinct states for data not loaded yet, network failure, stale last values, and information that did not exist for a specific park.
I wanted to finish the success screens first and deal with these cases later. But public-data apps encounter empty states often. Each source updates at a different speed, and some information exists only for some parks. If an empty screen has no reason, users feel that the app is broken.
So neither the app nor the widget treated “no value” as one state. Loading, failed fetches, unavailable information for that park, and stale last-success values all needed different guidance. Users use those differences to decide whether to wait, look at another park, or leave anyway.
I kept app, widget, and notification roles separate
The app, widget, and notifications look at the same data, but they do not do the same job.
- App: compare, check details, find nearby facilities, and change settings.
- Widget: check quickly before leaving.
- Notification: tell only the changes in conditions the user chose.
- External map app: move into the actual trip.
Without this separation, one screen and one notification get too many jobs. The first app screen wants every piece of information. The widget shows so much that it becomes hard to read. Notifications fire on small changes. More information then creates more fatigue.
Widgets needed particular care. Putting many values in a small widget can look smart but becomes hard to read. Reducing too much fails to answer, “So can I go?” The general widget therefore answers “Is this park okay today?”, and the car widget answers “Can I take the car right now?”
Device-local values aligned the app and widget
SwiftData and App Group were not just temporary storage for network responses. They became the standard that let the app and widget read the same values with the same meaning.
The app needed to know the user’s last selected park, favorites, viewed screen, and last updated time. The widget needed to read that information safely even when the app was closed. If the network briefly failed, showing the last verified value and its time was better than showing an entirely empty screen.
A stale snapshot becomes dangerous if it looks current. That is why updated time and state guidance mattered as much as the value itself in the widget.
A park seen in the widget opened directly in the app
If opening the app from a widget starts again from the park list, the context breaks. The user was already looking at a specific park in the widget. The app needs to receive that park and screen.
External map apps are the same. When a user sees a parking lot and taps directions, it should move directly into the map app they usually use. Asking again or sending only the whole park address makes them wander again near the parking-lot entrance.
Hangangjari is used in short moments before and after movement, so small breaks quickly become fatigue.
In the end, the important thing in the iOS app was not the number of screens. It was keeping the park and choice the user had already seen connected inside and outside the app. That is why I treated widgets, notifications, and deep links not as separate feature-list items, but as one continuous checking process.
Share
No comments yet. You can leave the first one.
Pending review