Skip to content
4 min read Guides Swift

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, .currentSwiftlyFeedback.config, .theme, .shared
Showcase cards by default, listStyle to switchOne list style, the one 1.3 calls Classic. There is no listStyle
FeedbackListView(embedsNavigationStack: false) inside your own stackNo 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 widthOne stack on every size
Status and Category pickers, filtering on the deviceA Status picker that fails (see below), and no Category picker
Submit and Cancel at the bottom of the form on iPhone and iPadBoth in the toolbar
The package asks you to trust a build pluginNo 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

Users vote and comment inside your app. A new request starts with its submitter's vote.

Moving to 1.3

  1. Change the package rule to Up to Next Major Version from 1.3.0.
  2. Build. The old names config, theme and shared still compile, with warnings that name the new ones: configuration, appearance and current.
  3. Remove the workarounds above if you like. Filters work, a custom id applies at once, and logs carry no personal data.
  4. Add a default to any exhaustive switch over SwiftlyFeedbackError, which has new cases.
  5. 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.

Keep reading

Collect feedback in your own app

FeedbackKit ships SDKs for Swift, Kotlin, React Native, Flutter and JavaScript, and syncs what your users ask for into the tracker your team already uses.