iOS SDK - Complete Integration Guide
Comprehensive guide for integrating GetTranslated.AI into your iOS applications
Table of Contents
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:
- GetTranslated - Main SDK class with static methods
- Logger - Configurable logging system with OSLog integration
- StorageKeys - UserDefaults key management
- LanguageDetection - Device language detection and matching
- 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:
- Server language override (if provided by the server)
- Saved user language preference (if previously set)
- Device language (with smart matching to supported languages)
- 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 createdlogLevel- 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 authenticationcallback- 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 implementingLanguageChangeCallbackprotocol
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 translateonReady- Closure called when translation is availableonError- 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 translatecallback- Callback object implementingTranslationCallbackprotocol
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 failed400- Bad request - invalid parameters401- Unauthorized - invalid API key403- Permission denied - API key lacks required permissions404- Not found - endpoint or resource not found500- Internal server error503- 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) }
}
}