Pinned to Swift SDK 1.2? Notes for Your Feedback Board
These notes cover Swift SDK 1.2. If you are starting today, use the current SwiftUI tutorial, which covers Swift SDK 1.3. Version 1.2 was tagged on 14 October 2026 and 1.3 followed under an hour later, so there is no full 1.2 tutorial: a new integration should not start on a release that was replaced the same evening. This page is for apps that resolved 1.2 and need to stay there for a while.
Version 1.2.0 was originally tagged 2.0.0 and renumbered on 17 October 2026. It contains source-breaking changes from 1.1.1 despite the minor version number, and so does 1.3.0: the migration notes list each one.
The short version
- To stay on 1.2, use Up to Next Minor Version from 1.2.0. An Up to Next Major rule from 1.2.0 moves to 1.3.0 on the next package update, and 1.3.0 is source-breaking.
- Follow the current tutorial, with the differences in the table below.
- Work around three defects: the Status picker, a custom user id set mid-session, and request bodies in the log.
- When you can, move to 1.3. Most of the change is renaming three properties.
How 1.2 differs from the current tutorial
| Current tutorial (1.3) | On 1.2 |
|---|---|
SwiftlyFeedback.configuration, .appearance, .current | SwiftlyFeedback.config, .theme, .shared |
Showcase cards by default, listStyle to switch | One list style, the one 1.3 calls Classic. There is no listStyle |
FeedbackListView(embedsNavigationStack: false) inside your own stack | No such parameter. The list always brings its own NavigationStack, so present it in a sheet or a tab rather than pushing it |
| Split view on iPad at regular width | One stack on every size |
| Status and Category pickers, filtering on the device | A Status picker that fails (see below), and no Category picker |
| Submit and Cancel at the bottom of the form on iPhone and iPad | Both in the toolbar |
| The package asks you to trust a build plugin | No plugin, and the public Strings enum still exists |
Everything else in the current tutorial applies: the package URL, configure(environment: .production, key:), the submit form, the detail screen, voting, MRR, status emails and the vote dialog. Search, the loading and error screens, the remembered sort and the rebuilt detail screen are all in 1.2 already, as What's New in Swift SDK 1.2 describes.
Three defects and their workarounds
The Status picker and filtered reads
On 1.2, getFeedback(status:) and getFeedback(category:) with any filter fail with SwiftlyFeedbackError.notFound. The SDK appends the query string to the path, the ? is escaped, and the server sees a route that does not exist. The Status picker in the list's menu uses the same call, so choosing a status shows an error. Hide the picker, and filter your own reads in memory:
struct MostWantedRow: View {
@State private var mostWanted: Feedback?
var body: some View {
Group {
if let mostWanted {
NavigationLink(mostWanted.title) {
FeedbackDetailView(feedback: mostWanted)
}
}
}
.task {
guard let client = SwiftlyFeedback.shared else { return }
let all = (try? await client.getFeedback()) ?? []
mostWanted = all
.filter { $0.status == .approved }
.max { $0.voteCount < $1.voteCount }
}
}
}
A custom user id set after launch
updateUser(customID:) saves the id to the Keychain, but the running client keeps the id it started with until the next launch. Votes and comments made straight after sign-in still belong to the anonymous id. When you know who is signed in at launch, set the id before configure, which reads it from the Keychain.
Request bodies in the log
With logging on, which is the default, 1.2 writes request bodies to the system log, and those carry user ids, emails and MRR. Turn logging off outside DEBUG builds.
All three in the App initialiser
init() {
if let userID = UserDefaults.standard.string(forKey: "trailmateUserID") {
SwiftlyFeedback.updateUser(customID: userID)
}
SwiftlyFeedback.configure(environment: .production, key: "sf_your_api_key")
SwiftlyFeedback.config.buttons.statusFilter.display = false
#if !DEBUG
SwiftlyFeedback.config.loggingEnabled = false
#endif
}
Theming on 1.2
Status and category colours are ThemeColor values on 1.2, as on 1.3, but you reach them through theme:
SwiftlyFeedback.theme.primaryColor = .adaptive(light: .green, dark: .mint)
SwiftlyFeedback.theme.statusColors.testflight = .color(.teal)
How FeedbackKit fits in
None of this changes what your users see when they vote, or what your team sees in the dashboard and the admin apps. Here is how voting looks in FeedbackKit, with sample data from a fictional hiking app:
Votes from inside the app
Moving to 1.3
- Change the package rule to Up to Next Major Version from 1.3.0.
- Build. The old names
config,themeandsharedstill compile, with warnings that name the new ones:configuration,appearanceandcurrent. - Remove the workarounds above if you like. Filters work, a custom id applies at once, and logs carry no personal data.
- Add a
defaultto any exhaustiveswitchoverSwiftlyFeedbackError, which has new cases. - Decide whether you want the new Showcase cards or
listStyle = .classic.
After the move, the same settings read like this:
SwiftlyFeedback.configuration.buttons.statusFilter.display = true
SwiftlyFeedback.appearance.statusColors.testflight = .color(.teal)
SwiftlyFeedback.configuration.listStyle = .showcase
The details are in What's New in Swift SDK 1.3, and the reference is in the Swift SDK docs.
Every plan includes the Swift SDK. Start free, and put the board on 1.3 in your next build.