GetTranslated.AI

iOS SDK - Complete Integration Guide

Comprehensive guide for integrating GetTranslated.AI into your iOS applications

Overview

The GetTranslated iOS SDK provides real-time, AI-powered translations for iOS applications. It offers seamless integration with automatic language detection, offline caching, and support for both anonymous and authenticated users.

Key Features

  • Real-time Translation: Get translations on-demand with automatic caching
  • Anonymous User Support: Automatic user ID generation with seamless login transition
  • Language Management: Automatic language detection based on system preferences and overrides (manual setting optional for custom selectors)
  • Language Change Callbacks: Reactive programming support for SwiftUI/Combine integration
  • Offline Caching: Persistent translation cache using UserDefaults
  • Initialization State Checking: Check SDK initialization status with isInitialized()
  • Swift Package Manager: Easy integration via SPM
  • Cross-platform: Consistent API with other GetTranslated SDKs
  • Comprehensive Logging: Configurable logging levels for debugging

Architecture

The SDK follows a singleton pattern with the following key components:

  1. GetTranslated - Main SDK class with static methods
  2. Logger - Configurable logging system with OSLog integration
  3. StorageKeys - UserDefaults key management
  4. LanguageDetection - Device language detection and matching
  5. Constants - SDK configuration and endpoint definitions

Installation

Prerequisites

  • iOS 13.0+ / macOS 10.15+ / tvOS 13.0+ / watchOS 6.0+
  • Swift 5.7+
  • Xcode 14.0+

Option A: Swift Package Manager (Recommended)

1. In Xcode, go to File → Add Packages...

2. Enter the repository URL: https://github.com/get-translated/ios-sdk.git

3. Select version: 1.0.0 or Up to Next Major Version

4. Add to your target

// Or add to your Package.swift
dependencies: [
    .package(url: "https://github.com/get-translated/ios-sdk.git", from: "1.0.0")
]

Option B: CocoaPods

Add to your Podfile:

pod 'GetTranslatedSDK', '~> 1.0.0'

Then run:

pod install

Option C: Manual Installation

1. Download the SDK source code

2. Add the GetTranslatedSDK folder to your Xcode project

3. Link the framework to your target

Quick Start

Basic Initialization

import GetTranslatedSDK

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Initialize GetTranslated SDK
    GetTranslated.initialize(key: "your-ck-api-key")
    
    return true
}

Simple Translation

// Synchronous translation
let translated = GetTranslated.getDynamicString("Hello, World!")

// Asynchronous translation with callback
GetTranslated.getDynamicString("Hello, World!") { translation in
    // Update UI on main thread
    DispatchQueue.main.async {
        label.text = translation
    }
} onError: { error in
    // Handle error
    print("Translation error: \(error)")
}

Language Management

Important: The SDK automatically sets the application language based on system preferences and any saved user overrides during initialization. You do not need to manually set the language unless you want to implement a custom language selector in your app.

The SDK's automatic language resolution follows this priority:

  1. Server language override (if provided by the server)
  2. Saved user language preference (if previously set)
  3. Device language (with smart matching to supported languages)
  4. Base language (fallback)
// Get supported languages
let languages = GetTranslated.getLanguages()

// Get current language (automatically set by SDK)
let currentLang = GetTranslated.getCurrentLanguage()

// Optional: Manually set language (only if implementing a language selector)
GetTranslated.setLanguage("es")

API Reference

Static Methods

initialize(key:userId:logLevel:callback:)

Initializes the GetTranslated SDK.

Parameters:

  • key - API key for authentication (required)
  • userId - Optional user ID. If not provided, an anonymous user will be created
  • logLevel - Optional log level for debugging (default: .warn)
  • callback - Optional callback to receive initialization status
// Anonymous user (simple)
GetTranslated.initialize(key: "your-ck-api-key")

// With user ID
GetTranslated.initialize(key: "your-ck-api-key", userId: "user-123")

// With custom logging
GetTranslated.initialize(key: "your-ck-api-key", userId: "user-123", logLevel: .debug)

// With callback (recommended)
class AppDelegate: UIResponder, UIApplicationDelegate, InitCallback {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        GetTranslated.initialize(key: "your-ck-api-key", callback: self)
        return true
    }
    
    func onInitSuccess() {
        print("SDK initialized successfully")
    }
    
    func onInitError(_ errorCode: Int, _ errorMessage: String) {
        print("Initialization failed: \(errorCode) - \(errorMessage)")
    }
}

isInitialized()

Returns whether the SDK has been successfully initialized.

Returns: Bool - true if the SDK is initialized and ready to use, false otherwise

// Check if SDK is initialized
if GetTranslated.isInitialized() {
    // SDK is ready to use
    let languages = GetTranslated.getLanguages()
} else {
    // Wait for initialization or initialize
    GetTranslated.initialize(key: "your-ck-api-key")
}

login(userId:callback:)

Login with a specific user ID (transitions from anonymous to authenticated).

Parameters:

  • userId - User ID for authentication
  • callback - Optional callback to receive re-initialization status
// Simple login
GetTranslated.login(userId: "user-123")

// Login with callback
class MyViewController: UIViewController, InitCallback {
    func loginUser() {
        GetTranslated.login(userId: "user-123", callback: self)
    }
    
    func onInitSuccess() {
        print("Login successful")
        updateUI()
    }
    
    func onInitError(_ errorCode: Int, _ errorMessage: String) {
        print("Login failed: \(errorCode) - \(errorMessage)")
    }
}

logout(callback:)

Logout and return to anonymous user.

Parameters:

  • callback - Optional callback to receive re-initialization status
// Simple logout
GetTranslated.logout()

// Logout with callback
GetTranslated.logout(callback: self)

setLanguage(_:)

Set language override programmatically. Note: This is optional - the SDK automatically sets the language based on system preferences and saved overrides during initialization. Only use this method if you want to implement a custom language selector in your app.

Parameters:

  • languageCode - ISO 639-1 language code (e.g., "en", "es", "fr")
// Optional: Only needed if implementing a language selector
GetTranslated.setLanguage("es")

onLanguageChange(_:)

Register a callback for language changes. Useful for SwiftUI/Combine integration and reactive programming patterns.

Parameters:

  • callback - Callback object implementing LanguageChangeCallback protocol
class MyLanguageCallback: LanguageChangeCallback {
    func onLanguageChanged(_ languageCode: String) {
        print("Language changed to: \(languageCode)")
        // Update UI, refresh translations, etc.
    }
}

let callback = MyLanguageCallback()
GetTranslated.onLanguageChange(callback)

offLanguageChange(_:)

Unregister a language change callback.

Parameters:

  • callback - Callback object to remove
// Unregister callback
GetTranslated.offLanguageChange(callback)

getLanguages()

Returns the set of languages supported by the current project.

Returns: Set<String> - Set of supported language codes, or empty set if not initialized

let languages = GetTranslated.getLanguages()
// Returns: ["en", "es", "fr", "de", ...]

getCurrentLanguage()

Returns the current language code being used by the SDK.

Returns: String - Current language code (e.g., "en", "es", "fr"), or "en" if not initialized

let currentLang = GetTranslated.getCurrentLanguage()
// Returns: "en" or "es" etc.

getDynamicString(_:)

Get translation synchronously (checks cache first, returns original if not cached).

Parameters:

  • text - Text to translate

Returns: String - Translated text or original text if not cached

let translation = GetTranslated.getDynamicString("Hello, World!")

getDynamicString(_:onReady:onError:)

Get translation with callback for async notifications.

Parameters:

  • text - Text to translate
  • onReady - Closure called when translation is available
  • onError - Closure called if translation fails

Returns: String - Translated text or original text if not cached

GetTranslated.getDynamicString("Hello, World!") { translation in
    // Handle successful translation
    label.text = translation
} onError: { error in
    // Handle error
    print("Error: \(error)")
}

getDynamicString(_:callback:)

Get translation with protocol-based callback (for compatibility with Android pattern).

Parameters:

  • text - Text to translate
  • callback - Callback object implementing TranslationCallback protocol
class MyTranslationCallback: TranslationCallback {
    func onTranslationReady(_ translation: String) {
        label.text = translation
    }
    
    func onTranslationError(_ errorMessage: String) {
        print("Error: \(errorMessage)")
    }
}

let callback = MyTranslationCallback()
GetTranslated.getDynamicString("Hello, World!", callback: callback)

InitCallback Protocol

Protocol for receiving SDK initialization status and errors.

public protocol InitCallback: AnyObject {
    /// Called when SDK initialization succeeds
    func onInitSuccess()
    
    /// Called if there is an error during SDK initialization
    /// - Parameters:
    ///   - errorCode: The HTTP error code (e.g., 401, 403, 500), or 0 for network/parsing errors
    ///   - errorMessage: A description of the error
    func onInitError(_ errorCode: Int, _ errorMessage: String)
}

Error Codes:

  • 0 - Network error or connection failed
  • 400 - Bad request - invalid parameters
  • 401 - Unauthorized - invalid API key
  • 403 - Permission denied - API key lacks required permissions
  • 404 - Not found - endpoint or resource not found
  • 500 - Internal server error
  • 503 - Service unavailable

TranslationCallback Protocol

public protocol TranslationCallback: AnyObject {
    /// Called when the requested translation is available.
    /// - Parameter translation: The requested translation
    func onTranslationReady(_ translation: String)
    
    /// Called if there is an error completing the translation request.
    /// - Parameter errorMessage: A description of the error
    func onTranslationError(_ errorMessage: String)
}

LanguageChangeCallback Protocol

Protocol for receiving language change notifications. Useful for SwiftUI/Combine integration.

public protocol LanguageChangeCallback: AnyObject {
    /// Called when the language changes
    /// - Parameter languageCode: The new language code
    func onLanguageChanged(_ languageCode: String)
}

Logger Class

// Log Levels
public enum LogLevel: Int, Comparable {
    case error = 0    // Only error messages
    case warn = 1     // Warning and error messages (default)
    case info = 2     // Informational, warning, and error messages
    case debug = 3    // Debug, info, warning, and error messages
    case verbose = 4  // All messages
}

// Initialize logger
Logger.initialize(level: .debug, enableConsole: true)

// Log messages
Logger.getInstance().info("Your message")
Logger.getInstance().error("Error occurred", error)
Logger.getInstance().debug("Debug information", data)
Logger.getInstance().warn("Warning message")
Logger.getInstance().verbose("Verbose information")

// Convenience functions
Log.info("Your message")
Log.error("Error occurred", error)
Log.debug("Debug information", data)
Log.warn("Warning message")
Log.verbose("Verbose information")

Advanced Usage

Initialization Callbacks

Use InitCallback to handle initialization status and errors:

class AppDelegate: UIResponder, UIApplicationDelegate, InitCallback {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        GetTranslated.initialize(key: "your-ck-api-key", callback: self)
        return true
    }
    
    func onInitSuccess() {
        // SDK is ready to use
        let languages = GetTranslated.getLanguages()
        print("SDK initialized with \(languages.count) languages")
    }
    
    func onInitError(_ errorCode: Int, _ errorMessage: String) {
        switch errorCode {
        case 401:
            print("Invalid API key - check your key in your dashboard")
        case 403:
            print("Permission denied - check API key permissions in your dashboard")
        case 0:
            print("Network error - check your internet connection")
        default:
            print("Initialization error: \(errorCode) - \(errorMessage)")
        }
    }
}

Custom Logging Configuration

// Initialize with custom logging
Logger.initialize(level: .verbose, enableConsole: true)

// Or configure after initialization
let logger = Logger.getInstance()
logger.setLevel(.debug)

Error Handling Strategies

1. Graceful Fallback

func getTranslationSafely(_ text: String) -> String {
    do {
        return GetTranslated.getDynamicString(text)
    } catch {
        Logger.getInstance().error("Translation failed", error)
        return text // Return original text as fallback
    }
}

2. Callback with Retry

func translateWithRetry(_ text: String, maxRetries: Int) {
    GetTranslated.getDynamicString(text) { translation in
        // Success - update UI
        updateUI(translation)
    } onError: { error in
        if maxRetries > 0 {
            // Retry with exponential backoff
            DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(4 - maxRetries)) {
                translateWithRetry(text, maxRetries: maxRetries - 1)
            }
        } else {
            // Final failure - show fallback
            updateUI(text)
        }
    }
}

User Management Patterns

1. Anonymous to Authenticated Flow

class UserManager {
    private let userIdKey = "user_id"
    
    func loginUser(_ userId: String) {
        do {
            GetTranslated.login(userId: userId)
            saveUserId(userId)
            notifyLoginSuccess()
        } catch {
            Logger.getInstance().error("Login failed", error)
            notifyLoginError(error.localizedDescription)
        }
    }
    
    func logoutUser() {
        do {
            GetTranslated.logout()
            clearUserId()
            notifyLogoutSuccess()
        } catch {
            Logger.getInstance().error("Logout failed", error)
        }
    }
    
    private func saveUserId(_ userId: String) {
        UserDefaults.standard.set(userId, forKey: userIdKey)
    }
    
    private func clearUserId() {
        UserDefaults.standard.removeObject(forKey: userIdKey)
    }
}

Performance Optimization

1. Batch Translation Loading

class TranslationLoader {
    private var pendingTranslations: [String] = []
    private var batchWorkItem: DispatchWorkItem?
    
    func loadTranslation(_ text: String) {
        pendingTranslations.append(text)
        
        // Cancel previous batch if exists
        batchWorkItem?.cancel()
        
        // Schedule batch processing
        batchWorkItem = DispatchWorkItem { [weak self] in
            self?.processBatch()
        }
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: batchWorkItem!)
    }
    
    private func processBatch() {
        let batch = pendingTranslations
        pendingTranslations.removeAll()
        
        for text in batch {
            GetTranslated.getDynamicString(text) { translation in
                notifyTranslationReady(text, translation)
            } onError: { error in
                notifyTranslationError(text, error)
            }
        }
    }
}

Best Practices

1. Initialization

  • Initialize early: In AppDelegate or SceneDelegate
  • Handle errors: Wrap initialization in do-catch
  • Use appropriate log level: Use .warn for production, .debug for development
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    do {
        #if DEBUG
        GetTranslated.initialize(key: "your-ck-api-key", logLevel: .debug)
        #else
        GetTranslated.initialize(key: "your-ck-api-key", logLevel: .warn)
        #endif
    } catch {
        Logger.getInstance().error("SDK initialization failed", error)
    }
    return true
}

2. Translation Usage

  • Use callbacks for UI updates: Prevents blocking the main thread
  • Provide fallbacks: Always have a fallback for failed translations
  • Cache translations: The SDK caches automatically, but you can add your own layer
func updateText(_ originalText: String) {
    GetTranslated.getDynamicString(originalText) { translation in
        DispatchQueue.main.async {
            label.text = translation
        }
    } onError: { error in
        DispatchQueue.main.async {
            label.text = originalText // Fallback
        }
    }
}

3. Language Management

Automatic Language Detection: The SDK automatically sets the application language during initialization based on system preferences and saved overrides. You only need to manually set the language if implementing a custom language selector.

  • Check language support: Verify language is supported before setting
  • Persist user preferences: Save language choices in UserDefaults
  • Handle language changes: Update UI when language changes
  • Use language change callbacks: Register callbacks to react to language changes automatically
// Example: Implementing a custom language selector (optional)
func setLanguageSafely(_ languageCode: String) {
    let supportedLanguages = GetTranslated.getLanguages()
    if supportedLanguages.contains(languageCode) {
        GetTranslated.setLanguage(languageCode)
        saveLanguagePreference(languageCode)
    } else {
        Logger.getInstance().warn("Language not supported: \(languageCode)")
    }
}

// Example: Using language change callbacks for reactive UI updates
class LanguageAwareViewController: UIViewController, LanguageChangeCallback {
    override func viewDidLoad() {
        super.viewDidLoad()
        GetTranslated.onLanguageChange(self)
        loadTranslations()
    }
    
    deinit {
        GetTranslated.offLanguageChange(self)
    }
    
    func onLanguageChanged(_ languageCode: String) {
        // Automatically update UI when language changes
        loadTranslations()
        updateUI()
    }
    
    private func loadTranslations() {
        // Reload all translations
    }
}

Troubleshooting

Common Issues

❌ "Not initialized" Error

Problem: Calling SDK methods before initialization

Solution: Make sure to call GetTranslated.initialize() before using other methods. Use the callback version to ensure initialization completes. You can also check initialization status with GetTranslated.isInitialized().

❌ Initialization Errors (401, 403, etc.)

Problem: SDK initialization fails with error code

Solution: Use InitCallback to handle errors. Check error code: 401 = invalid API key, 403 = permission denied, 0 = network error

❌ Translations Not Updating

Problem: Not handling language changes properly

Solution: Update UI after language change and re-translate texts

❌ Network Errors

Problem: Not handling network failures

Solution: Implement proper error handling with fallbacks and retry logic

❌ Swift Package Manager Issues

Problem: Package not found or build errors

Solution: Ensure you're using the correct repository URL and version. Try cleaning build folder (Cmd+Shift+K) and rebuilding

Debug Mode

Enable debug logging to troubleshoot issues:

// Enable debug logging
GetTranslated.initialize(key: "your-ck-api-key", logLevel: .debug)

// Or configure logging separately
Logger.initialize(level: .verbose, enableConsole: true)

Examples

Complete App Example

import UIKit
import GetTranslatedSDK

class ViewController: UIViewController {
    @IBOutlet weak var welcomeLabel: UILabel!
    @IBOutlet weak var descriptionLabel: UILabel!
    @IBOutlet weak var languageButton: UIButton!
    @IBOutlet weak var translateButton: UIButton!
    @IBOutlet weak var customTextInput: UITextField!
    @IBOutlet weak var customTranslationLabel: UILabel!
    
    private let sampleTexts = [
        "Welcome to our app!",
        "This is a sample translation app.",
        "Tap the buttons to test translations."
    ]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupUI()
        loadTranslations()
    }
    
    private func setupUI() {
        languageButton.addTarget(self, action: #selector(switchLanguage), for: .touchUpInside)
        translateButton.addTarget(self, action: #selector(translateCustomText), for: .touchUpInside)
    }
    
    private func loadTranslations() {
        // Load sample texts
        GetTranslated.getDynamicString(sampleTexts[0]) { translation in
            DispatchQueue.main.async {
                self.welcomeLabel.text = translation
            }
        } onError: { error in
            Logger.getInstance().error("Translation failed for: \(self.sampleTexts[0])", error)
        }
        
        GetTranslated.getDynamicString(sampleTexts[1]) { translation in
            DispatchQueue.main.async {
                self.descriptionLabel.text = translation
            }
        } onError: { error in
            Logger.getInstance().error("Translation failed for: \(self.sampleTexts[1])", error)
        }
    }
    
    @objc private func switchLanguage() {
        let currentLang = GetTranslated.getCurrentLanguage()
        let newLang = currentLang == "en" ? "es" : "en"
        
        GetTranslated.setLanguage(newLang)
        loadTranslations() // Re-translate all texts
        updateLanguageButton()
    }
    
    private func updateLanguageButton() {
        let currentLang = GetTranslated.getCurrentLanguage()
        let buttonText = currentLang == "en" ? "Switch to Spanish" : "Switch to English"
        languageButton.setTitle(buttonText, for: .normal)
    }
    
    @objc private func translateCustomText() {
        guard let text = customTextInput.text?.trimmingCharacters(in: .whitespaces),
              !text.isEmpty else {
            showError("Please enter text to translate")
            return
        }
        
        customTranslationLabel.text = "Translating..."
        
        GetTranslated.getDynamicString(text) { translation in
            DispatchQueue.main.async {
                self.customTranslationLabel.text = translation
            }
        } onError: { error in
            DispatchQueue.main.async {
                self.customTranslationLabel.text = "Translation failed: \(error)"
            }
        }
    }
    
    private func showError(_ message: String) {
        let alert = UIAlertController(title: "Error", message: message, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .default))
        present(alert, animated: true)
    }
}

Translation Service Example

class TranslationService {
    static let shared = TranslationService()
    
    private var cache: [String: String] = [:]
    private var listeners: [TranslationListener] = []
    
    protocol TranslationListener: AnyObject {
        func onTranslationReady(original: String, translation: String)
        func onTranslationError(original: String, error: String)
    }
    
    func addListener(_ listener: TranslationListener) {
        listeners.append(listener)
    }
    
    func removeListener(_ listener: TranslationListener) {
        listeners.removeAll { $0 === listener }
    }
    
    func translate(_ text: String) {
        if let cached = cache[text] {
            notifyTranslationReady(text, cached)
            return
        }
        
        GetTranslated.getDynamicString(text) { translation in
            self.cache[text] = translation
            self.notifyTranslationReady(text, translation)
        } onError: { error in
            self.notifyTranslationError(text, error)
        }
    }
    
    func translateBatch(_ texts: [String]) {
        for text in texts {
            translate(text)
        }
    }
    
    func clearCache() {
        cache.removeAll()
    }
    
    private func notifyTranslationReady(_ original: String, _ translation: String) {
        listeners.forEach { $0.onTranslationReady(original: original, translation: translation) }
    }
    
    private func notifyTranslationError(_ original: String, _ error: String) {
        listeners.forEach { $0.onTranslationError(original: original, error: error) }
    }
}