20 Open a Project
Start a new app project by:
- opening Xcode and choosing create a new project
- select a single view iOS app, choose a project name
- select Swift as the language and SwiftUI as the user interface
Follow https://developer.apple.com/tutorials/swiftui for tutorials to help get started. I did and these notes are things I distilled out of that experience and want to remember.
Data Models
Apple approaches from the UI side, however I really like to start by thinking about data and how to organize it. Xcode creates an application framework with no data models at all. Start by choosing File / New / Group to create groups to organize your files, maybe Screens and Subviews to hold views and put the code files in the application root group. Then click on the application folder and choose File / New / File to create a Swift File with a name like “UserSettings.swift”.
- define your global structs, classes, and variables in UserData.swift or somewhere else global so they can be published to views and modified by external code like a location manager.
- in
Mapper
the classUserSettings
follows theObservableObject
protocol, so it can be attached as an environment element. The instanceuserSettings
is a global variable that can have values assigned by any function, anywhere, e.g. in the location manager. - in
SceneDelegate.swift
underwillConnectTo session:
theuserSettings
instance is attached toContentView
as an environment object.let contentView = ContentView().environmentObject(userSettings)
- in
ContentView
and subsidiary views, the namesettings
is assigned to the environment object for local referencing. This makes me think there can only be one object of a given class in the environment.@EnvironmentObject var settings: UserSettings
- in previews, be sure to include the environment so the preview has some data to work with. This doesn’t need to be done explicitly when views are called as subsidiary views within a parent view — they inherit the environment from the parent.
ContentView().environmentObject(UserSettings())
- in
-
structs
are value types, so assignment moves the current value into another copy of the object -
classes
are reference types, so assignment provides another pointer to the same storage space
Now you are ready to start defining some data and some logic for manipulating and displaying that data. When you change the value of a published environment variable, all the views that depend on it will update automatically.
Identifiable Objects for Lists
In order to take advantage of the SwiftUI list capabilities, the items in your list need to follow the Identifiable
protocol. An easy way to do that is to start with a struct
that includes a constant component named id
of type UUID
with each instance of the struct
having a unique id
value. ( let id = UUID( )
)
Timers to Make things Happen
https://www.hackingwithswift.com/articles/117/the-ultimate-guide-to-timer has it all pretty well. Embedding a timer in a view like https://medium.com/better-programming/make-a-simple-countdown-with-timer-and-swiftui-3ce355b54986 creates problems with previews. Consider declaring some timers as global so you can update them or invalidate them from anywhere. This code in UserSettings.swift
// keeps the dateNow up to date in the userSettings -- set in SceneDelegate var fastTimer = Timer() func fastTimerAction(){ userSettings.dateNow = Date() }
is set in motion at the start of execution by this code in SceneDelegate.swift
fastTimer = Timer.scheduledTimer(withTimeInterval: 0.03, repeats: true) {_ in fastTimerAction() }
This scheduled timer will call fastTimerAction()
to update the value of dateNow
, forcing an update in any views that depend on it. You need to make the assignment in code that will run after execution begins, like in SceneDelegate
. This fast timer is good for keeping real time displays up to date. Slower timers can be used to generate events independent of the user.
Background Processing for Location Data
Set the application to allow background location updates under signing and capabilities.
Make sure the authorization request strings are in Info.plist
Set the location manager allowsBackgroundLocationUpdates = true
and pausesLocationUpdatesAutomatically = false
Consider using region monitoring to be sure the app is relaunched or brought to foreground automatically.
An error will show up in debug saying “Can't end BackgroundTask: no background task exists with identifier 1 (0x1), or it may have already been ended. Break in UIApplicationEndBackgroundTaskError() to debug.
” when you switch out of the app, but it seems to be harmless.