Skip to content
This repository has been archived by the owner on May 23, 2023. It is now read-only.
Lukas Romsicki edited this page Jun 19, 2020 · 4 revisions

Welcome to the wiki! Since there isn't much content yet, the core information is contained in this "Home" page.

View Architecture

Views are constructed using a "container" pattern. That is, presentational views are wrapped in Container views that handle the business logic. Why is this important?

  • This app pulls a lot of data from its environment using @EnvironmentObject.
  • If we weren't using the container pattern, the app would experience massive repainting of views throughout the app, since there is no mechanism to tell the view if it should change or not.
  • Unfortunately, we can't simply conform views that use @EnvironmentObject to Equatable. This is well explained in this article.

Best Practices

  • References to the environment should be kept as deep in the view hierarchy as possible. This will prevent from massive subtrees of views from being re-rendered if they don't need to be.
  • Always conform any view to Equatable, and be explicit about which properties must change for the view to re-render.

Folder Structure

If you're a React developer, the folder structure will be familiar to you. Here's a breakdown of what the Xcode project looks like:

  • Application - Anything that pertains to the core application and its environment.
    • This folder is also home to AppDelegate and SceneDelegate.
    • Binders
      • Binders are ViewModifiers that are used to bind ObservableObjects together.
      • For example, you may want to trigger a weather data refresh when the location changes.
    • Controllers
    • Environment - Classes that are used to configure the "environment" that the app is running in.
    • Services
      • Services are usually ObservableObjects that facilitate some sort of interaction with external data.
      • For example, WeatherService is used to communicate with the Nimbus API to fetch the weather.
    • States - ObservableObjects that store the current state of various parts of the user interface.
  • Concepts - Implementation-less classes used to distinguish views based on purpose.
  • Constants - Classes that store global constants.
  • Extensions - Self-explanatory.
  • GraphQL - Stores the models and schemas of the GraphQL APIs.
  • Sections
    • Implementations of each of the main sections of the app, such as Now, Week, Radar are stored here.
    • Views that are specific to their sections are stored in the appropriate folders.
    • The router that handles which section to display is stored at the root and is called CurrentSection.
  • Views - SwiftUI views that can be reused across the app, or are always visible no matter the current section.
    • AppLayout is the main view that renders the frame of the app.
    • Master is the view responsible for rendering the contents of the main coloured sliding panel.

A Single View

A single view follows the following structure. Let's say a view is called ImportantButton:

  • ImportantButton - The core folder.
    • Components - A folder that contains all the subviews, formatted using a similar structure.
    • ImportantButton.swift - The presentational logic.
    • ImportantButtonContainer.swift - The containing view that handles business logic (if required).

This structured can be repeated indefinitely. That is, it can be nested within itself. When in doubt, copy the pattern used in an existing component.

Current Hacks

SwiftUI isn't perfect, and there are still a number of issues that have workarounds:

  • The "pull-to-dismiss" feature for the LocationPicker uses introspection via DismissableScrollViewModifier to dig into the view hierarchy to find the UIScrollView that implements the ScrollView in order to register the appropriate gestures.
  • The DayPicker uses a preference key to size the parent scroll view, since scroll views implemented in UIViewRepresentable currently have a bug in which they don't auto resize when their content changes.
Clone this wiki locally