Skip to main content

Overview

Dynamic strings allow you to update your iOS app’s text content instantly without submitting a new version to the App Store. Change marketing copy, fix typos, run time-sensitive campaigns, or adjust messaging—all from the Stringboot Dashboard.

Key Benefits

Instant Updates

Change text content without app releases or user updates

Offline-First

Strings cached locally—works without internet connection

SwiftUI Native

Use SBText for automatic string injection

Reactive UI

Combine publishers for automatic UI updates

Quick Start

1. Add String to Dashboard

Go to Stringboot DashboardStringsAdd New String:
  • Key: welcome_message
  • Value: "Welcome to our app!"
  • Language: en

2. Use in Your App

3. Update from Dashboard

Go to Strings → Edit welcome_message → Change to "Welcome back!"Save Your app automatically shows the new text next time it syncs (happens automatically on app launch).

String Retrieval Methods

The simplest method for SwiftUI apps: auto-updating text views.
ProductView.swift
import SwiftUI
import StringbootSDK

struct ProductView: View {
    var body: some View {
        VStack(alignment: .leading, spacing: 16) {
            SBText("product_title")
                .font(.title)
                .fontWeight(.bold)

            SBText("product_description")
                .font(.body)
                .foregroundColor(.secondary)

            Button(action: buyNow) {
                SBText("button_buy_now")
                    .font(.headline)
            }
            .buttonStyle(.borderedProminent)
        }
        .padding()
    }

    func buyNow() {
        // Purchase logic
    }
}
Benefits:
  • Zero boilerplate code
  • Automatically updates when language changes
  • Perfect for static layouts
  • Handles missing strings gracefully
When to use:
  • Most Text views in SwiftUI
  • Marketing pages and static content
  • Forms with static labels

2. Async/Await

Get strings asynchronously with Swift’s modern concurrency.
let text = await StringProvider.shared.get("welcome_message")
Full Signature:
let text = await StringProvider.shared.get(
    _ key: String,
    lang: String? = nil  // Optional: defaults to current locale
)
Example: Dialog Content
private func showWelcomeDialog() async {
    let title = await StringProvider.shared.get("dialog_welcome_title")
    let message = await StringProvider.shared.get("dialog_welcome_message")
    let buttonText = await StringProvider.shared.get("button_ok")

    let alert = UIAlertController(
        title: title,
        message: message,
        preferredStyle: .alert
    )

    alert.addAction(UIAlertAction(title: buttonText, style: .default))
    present(alert, animated: true)
}
When to use:
  • Dialogs and alerts
  • Non-critical string retrieval
  • When you need strings before UI renders

3. Combine Publishers

Use Combine for reactive string updates.
import Combine
import StringbootSDK

class ViewModel: ObservableObject {
    @Published var statusMessage = ""
    private var cancellables = Set<AnyCancellable>()

    init() {
        StringProvider.shared.getPublisher(for: "status_message")
            .receive(on: DispatchQueue.main)
            .assign(to: &$statusMessage)
    }
}

struct ContentView: View {
    @StateObject private var viewModel = ViewModel()

    var body: some View {
        Text(viewModel.statusMessage)
            .font(.headline)
    }
}
Benefits:
  • Automatically updates when string changes
  • Updates when network sync completes
  • Perfect for dynamic content
  • Integrates with existing Combine code
When to use:
  • Content that changes based on user actions
  • Status messages and live updates
  • Templates with dynamic formatting
  • MVVM architectures

Advanced Patterns

Get Multiple Strings at Once

Fetch multiple strings efficiently in a single query.
Task {
    let keys = ["title", "subtitle", "description", "call_to_action"]
    let strings = await StringProvider.shared.getMultiple(keys, lang: "en")

    titleLabel.text = strings["title"] ?? "Default Title"
    subtitleLabel.text = strings["subtitle"] ?? "Default Subtitle"
    descriptionLabel.text = strings["description"] ?? "Default Description"
    ctaButton.setTitle(strings["call_to_action"] ?? "Get Started", for: .normal)
}
Benefits:
  • Single database query instead of multiple
  • More efficient for bulk retrieval
  • Returns a [String: String] dictionary

String Templates with Formatting

Use string templates with dynamic values. Dashboard String:
Key: welcome_user
Value: "Welcome back, %@! You have %d new messages."
Code:
let template = await StringProvider.shared.get("welcome_user")
let formattedText = String(format: template, userName, messageCount)
welcomeLabel.text = formattedText
// Output: "Welcome back, John! You have 5 new messages."
Example: Dynamic Countdown Dashboard:
Key: offer_expires
Value: "Offer expires in %d days!"
Code:
class CountdownViewModel: ObservableObject {
    @Published var countdownText = ""

    init() {
        StringProvider.shared.getPublisher(for: "offer_expires")
            .map { template in
                let daysRemaining = self.calculateDaysRemaining()
                return String(format: template, daysRemaining)
            }
            .assign(to: &$countdownText)
    }

    func calculateDaysRemaining() -> Int {
        // Calculate days until offer expires
        return 7
    }
}

Preloading Strings for Performance

Preload frequently used strings into memory cache for instant access.
override func viewDidLoad() {
    super.viewDidLoad()

    Task {
        // Preload strings for current language
        await StringProvider.shared.preloadLanguage(
            lang: "en",
            maxStrings: 500
        )

        // Now show UI
        await MainActor.run {
            setupUI()
        }
    }
}
When to use:
  • App startup
  • Before showing a complex screen with many strings
  • After language switching
Benefits:
  • Instant string retrieval
  • Eliminates database queries
  • Reduces perceived lag

Refresh Strings from Network

Manually trigger a network sync to get latest strings.
@IBAction func refreshContent(_ sender: UIButton) {
    sender.isEnabled = false
    activityIndicator.startAnimating()

    Task {
        let success = await StringProvider.shared.refreshFromNetwork(lang: "en")

        await MainActor.run {
            if success {
                showToast("Content updated!")
            } else {
                showToast("Using cached content")
            }

            sender.isEnabled = true
            activityIndicator.stopAnimating()
        }
    }
}
Behavior:
  • Fetches latest strings from server
  • Updates local Core Data database
  • Clears memory cache
  • Publishers automatically emit new values

List/Collection Integration

SwiftUI List with SBText

ProductListView.swift
import SwiftUI
import StringbootSDK

struct ProductListView: View {
    let products: [Product]

    var body: some View {
        List(products) { product in
            VStack(alignment: .leading) {
                Text(product.name)
                    .font(.headline)

                SBText("product_price_label")
                    .font(.subheadline)
                    .foregroundColor(.secondary)
                +
                Text(" $\(product.price, specifier: "%.2f")")
                    .font(.subheadline)
            }
        }
    }
}

UIKit TableView

ProductCell.swift
import UIKit
import StringbootSDK

class ProductCell: UITableViewCell {
    let productNameLabel = UILabel()
    let priceLabel = SBLabel(key: "product_price_label")

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        setupViews()
    }

    func configure(with product: Product) {
        productNameLabel.text = product.name

        Task {
            let template = await StringProvider.shared.get("product_price_label")
            await MainActor.run {
                priceLabel.text = String(format: template, product.price)
            }
        }
    }
}

Handling Missing Strings

Fallback Behavior

When a string key doesn’t exist, Stringboot returns the key itself:
let text = await StringProvider.shared.get("non_existent_key")
// Returns: "non_existent_key"

Provide Default Values

func getString(_ key: String, fallback: String) async -> String {
    let text = await StringProvider.shared.get(key)
    return text == key ? fallback : text
}

// Usage
let title = await getString("product_title", fallback: "Product")

Check if String Exists

func stringExists(_ key: String) async -> Bool {
    let text = await StringProvider.shared.get(key)
    return text != key
}

if await stringExists("special_offer_title") {
    specialOfferView.isHidden = false
    let title = await StringProvider.shared.get("special_offer_title")
    specialOfferLabel.text = title
}

Best Practices

Recommended:
SBText("welcome_message")
    .font(.title)
Avoid:
@State private var text = ""

Text(text)
    .task {
        text = await StringProvider.shared.get("welcome_message")
    }
SBText automatically handles updates and lifecycle.
Recommended:
StringProvider.shared.getPublisher(for: "status_message")
    .assign(to: &$statusMessage)
Avoid:
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
    Task {
        statusMessage = await StringProvider.shared.get("status_message")
    }
}
Publishers automatically update when content changes.
override func viewDidLoad() {
    super.viewDidLoad()

    Task {
        await StringProvider.shared.preloadLanguage("en", maxStrings: 500)

        await MainActor.run {
            loadingView.isHidden = true
            contentView.isHidden = false
        }
    }
}
Eliminates database queries during UI rendering.
Task {
    let text = await StringProvider.shared.get("message")

    await MainActor.run {
        label.text = text
    }
}
Or mark your method with @MainActor:
@MainActor
func updateUI() async {
    let text = await StringProvider.shared.get("message")
    label.text = text  // Already on main actor
}

Common Use Cases

Marketing Campaigns

Update promotional messages instantly without app updates. Dashboard Strings:
  • campaign_banner_title: “50% Off All Items!”
  • campaign_banner_subtitle: “Limited time offer - ends Friday”
  • campaign_cta_button: “Shop Now”
SwiftUI Code:
VStack(spacing: 12) {
    SBText("campaign_banner_title")
        .font(.title)
        .fontWeight(.bold)

    SBText("campaign_banner_subtitle")
        .font(.subheadline)

    Button(action: shopNow) {
        SBText("campaign_cta_button")
    }
    .buttonStyle(.borderedProminent)
}
Update campaign text from dashboard as needed—no code changes required!

Seasonal Content

struct SeasonalGreetingView: View {
    @State private var greeting = ""

    var body: some View {
        Text(greeting)
            .font(.largeTitle)
            .task {
                for await text in StringProvider.shared.getPublisher(for: "seasonal_greeting").values {
                    greeting = text
                }
            }
    }
}
Dashboard (update as seasons change):
  • December: “seasonal_greeting” = “Happy Holidays!”
  • January: “seasonal_greeting” = “Happy New Year!”
  • Spring: “seasonal_greeting” = “Spring Sale!”

Fix Typos Instantly

Found a typo in production? Fix it immediately from the dashboard. Before:
"Welcom to our app!"  ❌
Fix: Edit string in dashboard → Save After:
"Welcome to our app!"  ✅
Users see the fix next time they open the app—no App Store review needed!

Next Steps


API Reference

StringProvider Methods

MethodDescriptionReturns
get(_ key, lang?)Get string asynchronouslyasync String
getPublisher(for:lang:)Get Combine publisher for stringAnyPublisher<String, Never>
getMultiple(_ keys, lang?)Get multiple stringsasync [String: String]
preloadLanguage(lang, maxStrings?)Preload strings into cacheasync Void
refreshFromNetwork(lang:)Sync latest strings from serverasync Bool

SwiftUI Components

ComponentDescription
SBText(_ key)Auto-updating Text view
SBLabel(key:)UIKit auto-updating label

Troubleshooting

Causes:
  1. String key doesn’t exist in dashboard
  2. Network sync hasn’t happened yet
  3. String not in cache or database
Solutions:
  • Verify key exists in Stringboot Dashboard
  • Call await StringProvider.shared.refreshFromNetwork() to sync
  • Check logs with StringbootLogger.isLoggingEnabled = true
For SBText: SBText automatically updates—check if SDK initialized correctly.For manual updates:
// Trigger manual sync
await StringProvider.shared.refreshFromNetwork("en")
// SBText and Publishers auto-update
Use preloading:
await StringProvider.shared.preloadLanguage("en", maxStrings: 1000)
Batch retrieve:
let strings = await StringProvider.shared.getMultiple([
    "key1", "key2", "key3"
])

Support