June 26, 2024

Exploring SwiftUI: Building a Map App with MVVM Architecture

A young boy wearing a white VR headset

SwiftUI as a framework introduced by Apple for building user interfaces across all Apple platforms. It's designed to simplify and streamline the UI development process, offering a declarative syntax that allows developers to describe the desired interface and its behaviour concisely.

Unlike UIKit, which relies heavily on imperative programming, SwiftUI embraces a more modern approach, enabling developers to create dynamic and interactive interfaces with less code. With SwiftUI, developers can enjoy features like live previews, automatic layout adjustments, and seamless integration with Swift code.

To get better understanding SwiftUI, we’ll make case study called Map App, we will import a bunch of real-world locations, such as the Colosseum and the Eiffel Tower, and then display them on the map with a nice UI. The focus of this project is to (1) better understand MVVM architecture, and (2) to get more comfortable using SwiftUI frames, alignments, animations, and transitions.

Map app demo

Start SwiftUI Development with Xcode

Setup project

  • Open Xcode (my version is 15.3 (15E204a))
  • File > New > Project
  • Choose iOS > App

Enter the product name “App”, and leave storage as none for now.

Setup assets

  • Download the assets here https://www.swiftful-thinking.com/downloads. Choose  “SwiftUI Map App - Resources”.
  • Remove folder Assets.xcassets on our project directory, then paste it with new download resources folder called Assets.xcassets to our project directory.
  • Create new folder named DataServices, and paste LocationsDataService.swift inside.

Models/Location.swift

Let’s create new model called Location.

This model encapsulates data related to a geographical location. The struct contains properties such as name, cityName, coordinates, etc. Additionally, the Identifiable protocol is adopted to enable unique identification of instances through the id property, which concatenates the name and cityName. The Equatable protocol is also implemented to support comparison of Location instances. Overall, this struct serves as a data model facilitating the management and manipulation of location data within map-based applications.

ViewModels/LocationViewModel.swift

LocationViewModel class, which serves as the view model for managing location-related data and interactions within the map app. Let's dissect its functionalities:

  • ObservableObject: LocationViewModel adopts the ObservableObject protocol, indicating that it can publish changes to its properties, allowing SwiftUI views to react and update accordingly.
  • Published Properties:
    • locations: An array of Locations objects, published to notify views of any changes.
    • mapLocation: Represents the currently selected location on the map. Whenever it changes, the updateMapRegion method is called to update the map's region accordingly.
    • mapRegion: Represents the visible region of the map.
    • showLocationList: A boolean flag indicating whether to display a list of locations.
    • sheetLocation: Represents a location to be displayed in a sheet view.
  • Constants:
    • mapSpan: Specifies the span (the width and height) of the map region to be displayed.
  • Initialisation: In the init method locations are loaded from LocationsDataService, and the first location in the list is set as the initial mapLocation. The map region is then updated accordingly.
  • Methods:
    • updateMapRegion: Updates the mapRegion based on the given location's coordinates.
    • toggleLocationList: Toggles the visibility of the locations list.
    • showNextLocationList: Update the mapLocation and hides the locations list, typically triggered when selecting a location from the list.
    • clickNextLocation: Advances to the next location in the locations array, updating mapLocation and hiding the locations list accordingly.

Overall, LocationViewModel manages the state of location-related data and interactions, facilitating communication between SwiftUI views and data sources within the map app.

MapApp.swift

The LocationViewModel instance vm is created using the @StateObject property wrapper. This ensures that the view model is preserved across view updates and is only created once during the app's lifecycle.

Views/LocationsListView.swift

Responsible for displaying a list of locations fetched from the LocationViewModel. Each location is represented as a button within a List view, triggering the presentation of detailed information when tapped. The design utilises a custom listRowView unction to render each list item with an image, location name, and city name in a horizontal layout. The LocationListView is styled with a plain list style, ensuring a clean and minimalistic appearance. Additionally, the code includes a preview for the ListLocationView, showcasing its appearance with sample data.

Views/LocationPreviewView.swift

This view designed to provide a compact preview of a location's details. It displays an image of the location followed by its name, with buttons for learning more about the location and navigating to the next one. The design employs a horizontal layout with aligned vertical stacks, ensuring an organised presentation of information. Additionally, the view features rounded corners and a subtle background, enhancing its visual appeal. The preview showcases the appearance of the LocationPreviewView with sample data from the LocationDataService.

Views/LocationDetailView.swift

This display detailed information about a specific location. It presents an image banner showcasing multiple images of the location in a scrollable TabView. Below the image banner, the view includes sections for the location's name, city, description, and a link to learn more about the location on Wikipedia if available. Additionally, it incorporates a Map view displaying the location's coordinates and a back button to dismiss the detail view. The design emphasises a clean and organised layout, with each section appropriately spaced and aligned for optimal readability. The preview demonstrates the appearance of the LocationDetailView with sample data from LocationDataService and an environment object of LocationViewModel.

Views/LocationView.swift

This is the main interface for the map app. It displays a map with annotations for each location stored in the LocationViewModel. Tapping on an annotation triggers the display of a preview for the selected location. Additionally, there's a header section displaying the name and city of the current location, with a button to toggle the visibility of a list of locations. The view also includes a sheet presentation for detailed information about a selected location. The design employs a ZStack to overlay the map with UI elements and incorporates transitions for smooth interface changes. Finally, the code includes a preview for the LocationVIew and a custom MapMaker view used for map annotations.

Summary

This case studies of map app using the Model-View-ViewModel (MVVM) architecture with SwiftUI. The MVVM pattern facilitates the organisation, efficiency, and scalability of the app by separating concerns into distinct layers: the Model (represented by the Location struct), the View (comprised of LocationView, LocationListView, and LocationDetailView), and the ViewModel LocationViewModel.

The ViewModel manages the app's state and logic, including data loading, navigation, and user interactions, while the Views are responsible for presenting the UI elements and responding to changes in the ViewModel. Through this structured approach, the code achieves a clear separation of concerns, promoting maintainability and extensibility of the map app.

References

https://developer.apple.com/xcode/swiftui/

https://www.youtube.com/playlist?list=PLwvDm4Vfkdphbc3bgy_LpLRQ9DDfFGcFu

We can help with your mobile app development, let's chat.

Written by:

Alvin Amri [Fullstack developer at 42 Interactive]

- As a part of 42 Interactive training and research programme -