Running the app (iOS Simulator & XCode Canvas)
Firstly, in MockAPIService.swift is where you'll be able to dictate whether the app is being mocked, through the isMocked variable, as you can see here:
This should be set to true if you're working on a UI-specific feature that will be previewed within XCode often for making UI tweaks.
On that topic, to preview within XCode, you can toggle that through toggling "Edit" -> "Canvas"
However, you'll only be able to preview SwiftUI files that include this section here (typically at the bottom of the file):
More complicated case, to supply initial state and init() parameters:
API Calls
-
In our codebase, we do these API calls from within
ViewModels, which leverage theIAPIServiceinterface methods, implemented inAPIService.swiftand implemented as mocks inMockAPIService.swift- An example of this is here:

- As you can see, the method is marked
async - It interfaces with our back-end API, as the URLs match up with our endpoints in the back-end
Controllers/directory - We surround all code with a
do-catch, similar to other languages'try-catchblocks.
- An example of this is here:
-
APIService.swift- This makes actual GET, POST, PUT, and DELETE requests to our back-end API
DecodableandEncodableare used to serialize and deserialize JSON data, so we need ourModels/classes to conform (implement) these protocols- The
parametersargument is used for argument parameters, like in a URL as you'd see/events?requestingUserId=1for example -> we then construct afinalURL
- The
URLSession.shared.dataTaskmethod is used to make the actual request
- Then, we
handleAuthTokens()to deal with JWTs (JSON Web Tokens) sent from the back-end, which comprises the access token and refresh token - Afterward, we ensure:
- The status code is what we expect, like 204 for a successful DELETE request or 200 for a successful GET request
- The data can be decoded into what we expect, like a
Userobject for a GET request to/users/1 - If there's an error in any capacity, we throw it so that the calling
ViewModelclass can catch it and deal with it through UI or state updates accordingly
-
MockAPIService.swift
Asynchrony in SwiftUI
onAppear{}is a way to run a function when a view appears- The
Task{}closure, is a way to run an asynchronous functions in SwiftUI- This ensures that this piece of code is ran asynchronously, and that the UI is not blocked on the main thread, since anything in
Task{}runs on the background thread.
- This ensures that this piece of code is ran asynchronously, and that the UI is not blocked on the main thread, since anything in
MainActor.run{}is a way to run a function on the main thread, and is used to update the UI- This is similar to React's
setState()method - This is essentially the inverse of
Task{}in that it runs on the main thread, and is used to update the UI, from within a background thread - One application would be when you're fetching data from within a ViewModel class (which is on a background thread), and you want to update the UI with that data, you would use
MainActor.run{}to update the UI with that data
- This is similar to React's
SwiftUI Syntax
@Stateis a mutable variable, and works similarly to React state variables, except withoutsetState()methods@Bindingis a way to pass a@Statevariable from a parent view to a child view
@ObservedObjectis a way to observe changes in an object, and is used for observing changes in aViewModelobject
var body: some View{}is the main body of a SwiftUI view, and is where the UI is defined- This is similar to React's return statement in a functional component
HStack,VStack, andZStackare ways to group components (like divs in HTML) across dimensions: horizontally, vertically, and in the Z-dimension
Button{}is a way to create a button in SwiftUINavigationLink{}is a way to navigate to another view in SwiftUI, as a button
- Unwrapping Optionals (4 ways):
In this example, app.users is an optional value, a.k.a. it might be nil (null in other languages). Thus, we need to 'unwrap' it to get the actual value, and here are the 4 ways to do this in Swift:
let appUsers = appPortfolio.map {app in
// Force-Unwrap
return app.users! // leads to error if app.users is nil -> very dangerous
// guard unwrap, for early-exits
guard let userCount = app.users else {
return 0
}
return userCount
// if unwrap, for quick usage, but without persistence of guard
if let userCount = app.users {
return userCount
} else {
return 0
}
// nil coalescing to provide default value
// example of good default would be "Not Given" for a user's username
return app.users ?? 0
}











