Skip to main content

Recipes

A custom modifier to easily apply string keys to Text views.
struct StringbootKey: ViewModifier {
    let key: String
    let params: [String: Any]
    @State private var text: String = ""

    func body(content: Content) -> some View {
        Text(text.isEmpty ? key : text)
            .onAppear { loadString() }
            .onReceive(Stringboot.shared.stringPublisher(for: key)) { newText in
                self.text = newText
            }
    }
    
    private func loadString() {
        self.text = Stringboot.shared.string(forKey: key, params: params)
    }
}

extension View {
    func stringboot(_ key: String, params: [String: Any] = [:]) -> some View {
        modifier(StringbootKey(key: key, params: params))
    }
}
Showing localized alerts from a ViewModel.
class ProfileViewModel: ObservableObject {
    @Published var showError = false
    @Published var errorMessage = ""
    
    func saveProfile() {
        let errorKey = "error_save_failed"
        self.errorMessage = Stringboot.shared.string(forKey: errorKey)
        self.showError = true
    }
}
Formatting dates based on the active Stringboot locale.
func localizedDate(_ date: Date) -> String {
    let formatter = DateFormatter()
    formatter.locale = Stringboot.shared.currentLocale
    formatter.dateStyle = .long
    formatter.timeStyle = .short
    return formatter.string(from: date)
}

Plug-and-Play Components

A ready-to-use sheet for language selection.
struct LanguagePickerView: View {
    @Environment(\.dismiss) var dismiss
    @State private var languages: [Language] = []
    
    var body: some View {
        NavigationView {
            List(languages, id: \.code) { lang in
                Button(action: {
                    Stringboot.shared.setLocale(lang.code)
                    dismiss()
                }) {
                    HStack {
                        Text(lang.name)
                            .foregroundColor(.primary)
                        Spacer()
                        if lang.code == Stringboot.shared.currentLocale {
                            Image(systemName: "checkmark")
                                .foregroundColor(.blue)
                        }
                    }
                }
            }
            .navigationTitle("Select Language")
            .toolbar {
                ToolbarItem(placement: .cancellationAction) {
                    Button("Close") { dismiss() }
                }
            }
        }
        .onAppear {
            self.languages = Stringboot.shared.availableLanguages
        }
    }
}
A searchable FAQ browser.
struct FAQSheet: View {
    let tag: String
    @State private var faqs: [FAQ] = []
    @Environment(\.dismiss) var dismiss
    
    var body: some View {
        NavigationView {
            List(faqs) { faq in
                DisclosureGroup(
                    content: { Text(faq.answer).padding(.vertical) },
                    label: { Text(faq.question).font(.headline) }
                )
            }
            .navigationTitle("Help & Support")
            .toolbar {
                ToolbarItem(placement: .confirmationAction) {
                    Button("Done") { dismiss() }
                }
            }
        }
        .onAppear {
            Task {
                self.faqs = await FAQProvider.shared.getFAQs(tag: tag)
            }
        }
    }
}