iOS SDK
AI Implementation Guide
How to use this page: Copy everything below the horizontal line and paste it into your AI coding assistant (Claude, ChatGPT, GitHub Copilot, Cursor, etc.) to get accurate MovableInk SDK implementation help.
Sample Prompts
After pasting the context below into your AI assistant, try these prompts:
- "Implement MovableInk deeplinking in my SwiftUI app using the context I provided"
- "Add MovableInk deeplinking to my UIKit app"
- "Help me configure Universal Links for MovableInk"
- "Implement behavior events to capture when users view products"
- "Add orderCompleted event tracking to my checkout flow"
- "Help me test my MovableInk integration"
- "Debug why my MovableInk deeplinks aren't working"
Context for AI Assistant
Current SDK Version: 3.0.0
Copy everything inside the code block below and paste into your AI assistant:
# MovableInk iOS SDK Implementation Reference
## Overview
### What is MovableInk?
MovableInk is a marketing personalization platform that helps brands create dynamic, personalized content for email, mobile, and web. The iOS SDK enables two key capabilities:
1. **Deeplinking**: Users tap links in marketing emails → your app opens to specific content
2. **Behavior Events**: Capture user interactions (product views, purchases) to power personalization
### SDK Information
- **Minimum iOS**: 13.0
- **Language**: Swift (Objective-C compatible)
- **GitHub**: https://github.com/movableink/ios-sdk
## Key Terminology
- **MIU (MovableInk User ID)**: Unique, non-PII identifier linking app users to marketing profiles (typically a UUID, NOT an email)
- **MovableInk Link**: URL format `https://mi.yourcompany.com/p/...` in marketing emails
- **AASA**: Apple App Site Association file at `https://mi.yourcompany.com/.well-known/apple-app-site-association` (configured by MovableInk team)
- **Universal Links**: iOS deep linking using https URLs (requires Associated Domains capability)
## Configuration for Deeplinking
### Step 1: Add Associated Domains Capability
1. In Xcode, select your app target
2. Go to **Signing & Capabilities** tab
3. Click **+ Capability** → Add **Associated Domains**
4. Add entry: `applinks:mi.yourcompany.com`
**Important**: Replace `mi.yourcompany.com` with actual MovableInk subdomain (no `https://`)
### Step 2: Update Info.plist
Add this to your `Info.plist`:
```xml
<key>movable_ink_universal_link_domains</key>
<array>
<string>mi.yourcompany.com</string>
</array>
```
**Important**: Replace `mi.yourcompany.com` with actual MovableInk subdomain
## Deeplinking Implementation
### SwiftUI Implementation
```swift
import SwiftUI
import MovableInk
@main
struct YourApp: App
MIClient.resolveURL(url)
}
}
}
}
private func handleDeeplink(_ url: URL)
}
}
```
### UIKit Implementation (AppDelegate)
```swift
import UIKit
import MovableInk
class AppDelegate: UIResponder, UIApplicationDelegate
MIClient.resolveURL(url)
}
return true
}
private func handleDeeplink(_ url: URL)
}
```
### UIKit Implementation (SceneDelegate - iOS 13+)
```swift
import UIKit
import MovableInk
class SceneDelegate: UIResponder, UIWindowSceneDelegate
MIClient.resolveURL(url)
}
}
private func handleDeeplink(_ url: URL)
}
```
## Behavior Events Implementation (Optional)
### Setup
Initialize the SDK with your API key (required only for behavior events, not deeplinking):
```swift
import MovableInk
// In AppDelegate.didFinishLaunchingWithOptions or App.init
MIClient.start(apiKey: "YOUR_API_KEY", region: .us)
```
**Regions**: `.us` or `.eu` (default is `.us` if not specified)
### Set User ID (MIU)
Set the MIU as soon as user is identified (typically after login):
```swift
MIClient.setMIU("USER_UNIQUE_ID")
```
**Important**:
- MIU must be non-PII (use UUID, NOT email address)
- Must match what marketing team uses in email campaigns
- Should be URL-friendly string
### Product Searched
```swift
MIClient.productSearched(
properties: .init(
query: "running shoes",
url: URL(string: "https://yourapp.com/search?q=running+shoes"),
meta: [
"resultsCount": 42
]
)
)
```
### Product Viewed
```swift
MIClient.productViewed(
properties: .init(
id: "PROD-12345",
title: "Running Shoes",
price: "89.99",
currency: .USD,
url: URL(string: "https://yourapp.com/products/12345"),
categories: [
ProductCategory(id: "shoes", url: URL(string: "https://yourapp.com/categories/shoes")),
ProductCategory(id: "running", url: URL(string: "https://yourapp.com/categories/running"))
],
meta: [
"inStock": true,
"rating": 4.5
]
)
)
```
**Important**: Product ID must not be empty string
### Product Added (to Cart)
```swift
MIClient.productAdded(
properties: .init(
id: "PROD-12345",
title: "Running Shoes",
price: "89.99",
currency: .USD,
url: URL(string: "https://yourapp.com/products/12345"),
categories: [
ProductCategory(id: "shoes"),
ProductCategory(id: "running")
],
meta: [
"size": "10",
"color": "blue"
]
)
)
```
### Product Removed (from Cart)
```swift
MIClient.productRemoved(
properties: .init(
id: "PROD-12345",
title: "Running Shoes",
price: "89.99",
currency: .USD,
url: URL(string: "https://yourapp.com/products/12345")
)
)
```
### Category Viewed
```swift
MIClient.categoryViewed(
category: .init(
id: "shoes",
title: "Shoes",
url: URL(string: "https://yourapp.com/categories/shoes"),
meta: [
"itemCount": 156
]
)
)
```
### Order Completed
```swift
MIClient.orderCompleted(
properties: .init(
id: "ORDER-789",
revenue: "179.98",
currency: .USD,
products: [
.init(
id: "PROD-12345",
title: "Running Shoes",
price: "89.99",
url: URL(string: "https://yourapp.com/products/12345"),
quantity: 2,
meta: [
"size": "10",
"color": "blue"
]
)
]
)
)
```
**Important**:
- Order ID and Product IDs must not be empty strings
- Revenue and price should be in dollars and cents (e.g., "15.99")
- Can include up to 100 products per order
### Currency Support
Available currency values:
- `.USD`, `.EUR`, `.GBP`, `.CAD`, `.AUD`, `.JPY`, etc.
- Default is `.USD` if not specified
- Introduced in SDK v2
## Testing
### Test Deeplinking
1. Build and run your app on device or simulator (iOS 13+)
2. Put app in background or close it
3. Open a MovableInk test link in Safari: `https://mi.yourcompany.com/p/rp/test`
4. App should open automatically
5. Check Xcode console for: `Deeplink resolved to: <final URL>`
### Test Behavior Events
1. Set MIU to test value: `MIClient.setMIU("00000000-0000-0000-0000-000000000000")`
2. Trigger an event (e.g., view a product)
3. Check Xcode console for event logs (filter by "MI SDK")
4. You should see success message with event name
5. Coordinate with MovableInk team to verify events are received
## Common Issues and Solutions
### Deeplinks Not Opening App
**Issue**: Clicking MovableInk links doesn't open the app
**Solutions**:
- Verify AASA file is accessible: `https://mi.yourcompany.com/.well-known/apple-app-site-association`
- Check Associated Domains match exactly (no wildcards, no `https://`)
- Ensure testing on iOS 13+
- Try uninstalling and reinstalling app (clears AASA cache)
- Verify Bundle ID matches what's in AASA file
- Wait 24-48 hours after AASA changes for Apple CDN to update
### Events Not Sending
**Issue**: Behavior events not appearing in MovableInk platform
**Solutions**:
- Verify API key is set: `MIClient.start(apiKey: "...", region: .us)`
- Ensure MIU is set before sending events: `MIClient.setMIU("...")`
- Check console for "MI SDK" logs showing success/failure
- Verify region is correct (`.us` or `.eu`)
- Confirm API key is for correct environment (staging/production)
### MIU Mismatch
**Issue**: Events not associating with correct users in campaigns
**Solutions**:
- Verify MIU matches exactly what marketing team uses in ESP
- Ensure MIU is set after user logs in
- MIU should be UUID format, not email address
- Call `MIClient.identifyUser()` after setting MIU for guest users who log in
### Build Errors
**Issue**: SDK not found or import errors
**Solutions**:
- For SPM: Ensure package is added in Xcode > Package Dependencies
- For CocoaPods: Run `pod install` and open `.xcworkspace` (not `.xcodeproj`)
- Clean build folder: Product > Clean Build Folder
- Check deployment target is iOS 13.0+
## Important Notes
- **Replace placeholders**: Always replace `mi.yourcompany.com` with actual MovableInk subdomain
- **MIU requirements**: Must be non-PII, URL-friendly, match marketing team's identifier
- **API Key**: Required only for behavior events, NOT for deeplinking
- **Deeplinking works without API key**: Basic deeplinking functionality requires only Universal Links configuration
- **Region compliance**: Use `.eu` for European users if required for data compliance
- **Price format**: Always use string format for prices ("15.99", not 15.99)
- **Product IDs**: Must not be empty strings - will cause event to fail
- **Testing**: Use UUID `00000000-0000-0000-0000-000000000000` for testing with MovableInk team
- **Guest users**: Call `identifyUser()` after setting MIU for users who made actions while logged out
## Full Example App
Minimal working example:
```swift
import SwiftUI
import MovableInk
@main
struct ShoppingApp: App
var body: some Scene
MIClient.resolveURL(url)
}
}
.onAppear
}
}
}
}
struct ProductDetailView: View
.onAppear
}
}
```
## Push Notifications (Optional)
### Overview
Track when users open MovableInk push notifications for campaign attribution and conversion measurement. This feature is particularly useful for DaVinci customers.
### Prerequisites
This assumes you have already set up push notifications in your app using the UserNotifications framework or UIApplicationDelegate.
### Using UserNotifications Framework (Recommended)
Implement the delegate method to track notification opens:
```swift
import UserNotifications
import MovableInk
class NotificationDelegate: NSObject, UNUserNotificationCenterDelegate
}
```
### Using UIApplicationDelegate
If not using UserNotifications framework:
```swift
import MovableInk
func application(
_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any]
)
```
### Manual Tracking Without SDK
Extract the tracking URL from the notification payload and make a request:
```swift
import UserNotifications
func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void
)
var urlString: String?
// Check for "mi" container format
if let miContainer = userInfo["mi"] as? [String: Any]
// Check for "data" container format
if urlString == nil, let dataContainer = userInfo["data"] as? [String: Any]
// Make tracking request
if let urlString = urlString, let url = URL(string: urlString)
}
completionHandler()
}
```
### Payload Formats
MovableInk push notifications will have one of these payload structures:
**Standard Format:**
```json
,
"sound": "default"
},
"mi":
}
```
**DaVinci Format:**
```json
,
"sound": "default"
},
"data":
}
```
## Additional Resources
- Full Documentation: https://sdk-mobile.movableink.com/
- GitHub Repository: https://github.com/movableink/ios-sdk
- Contact MovableInk team for: API keys, AASA configuration, MIU strategy, testing support