- Three-scenario planning engine (A: date range, B: selected games, C: directional routes) - GeographicRouteExplorer with anchor game support for route exploration - Shared ItineraryBuilder for travel segment calculation - TravelEstimator for driving time/distance estimation - SwiftUI views for trip creation and detail display - CloudKit integration for schedule data - Python scraping scripts for sports schedules 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
146 lines
4.3 KiB
Markdown
146 lines
4.3 KiB
Markdown
# CloudKit Setup Guide for SportsTime
|
|
|
|
## 1. Configure Container in Apple Developer Portal
|
|
|
|
1. Go to [Apple Developer Portal](https://developer.apple.com/account)
|
|
2. Navigate to **Certificates, Identifiers & Profiles** > **Identifiers**
|
|
3. Select your App ID or create one for `com.sportstime.app`
|
|
4. Enable **iCloud** capability
|
|
5. Click **Configure** and create container: `iCloud.com.sportstime.app`
|
|
|
|
## 2. Configure in Xcode
|
|
|
|
1. Open `SportsTime.xcodeproj` in Xcode
|
|
2. Select the SportsTime target
|
|
3. Go to **Signing & Capabilities**
|
|
4. Ensure **iCloud** is added (should already be there)
|
|
5. Check **CloudKit** is selected
|
|
6. Select container `iCloud.com.sportstime.app`
|
|
|
|
## 3. Create Record Types in CloudKit Dashboard
|
|
|
|
Go to [CloudKit Dashboard](https://icloud.developer.apple.com/dashboard)
|
|
|
|
### Record Type: `Stadium`
|
|
|
|
| Field | Type | Notes |
|
|
|-------|------|-------|
|
|
| `stadiumId` | String | Unique identifier |
|
|
| `name` | String | Stadium name |
|
|
| `city` | String | City |
|
|
| `state` | String | State/Province |
|
|
| `location` | Location | CLLocation (lat/lng) |
|
|
| `capacity` | Int(64) | Seating capacity |
|
|
| `sport` | String | NBA, MLB, NHL |
|
|
| `teamAbbrevs` | String (List) | Team abbreviations |
|
|
| `source` | String | Data source |
|
|
| `yearOpened` | Int(64) | Optional |
|
|
|
|
**Indexes**:
|
|
- `sport` (Queryable, Sortable)
|
|
- `location` (Queryable) - for radius searches
|
|
- `teamAbbrevs` (Queryable)
|
|
|
|
### Record Type: `Team`
|
|
|
|
| Field | Type | Notes |
|
|
|-------|------|-------|
|
|
| `teamId` | String | Unique identifier |
|
|
| `name` | String | Full team name |
|
|
| `abbreviation` | String | 3-letter code |
|
|
| `sport` | String | NBA, MLB, NHL |
|
|
| `city` | String | City |
|
|
|
|
**Indexes**:
|
|
- `sport` (Queryable, Sortable)
|
|
- `abbreviation` (Queryable)
|
|
|
|
### Record Type: `Game`
|
|
|
|
| Field | Type | Notes |
|
|
|-------|------|-------|
|
|
| `gameId` | String | Unique identifier |
|
|
| `sport` | String | NBA, MLB, NHL |
|
|
| `season` | String | e.g., "2024-25" |
|
|
| `dateTime` | Date/Time | Game date and time |
|
|
| `homeTeamRef` | Reference | Reference to Team |
|
|
| `awayTeamRef` | Reference | Reference to Team |
|
|
| `venueRef` | Reference | Reference to Stadium |
|
|
| `isPlayoff` | Int(64) | 0 or 1 |
|
|
| `broadcastInfo` | String | TV channel |
|
|
| `source` | String | Data source |
|
|
|
|
**Indexes**:
|
|
- `sport` (Queryable, Sortable)
|
|
- `dateTime` (Queryable, Sortable)
|
|
- `homeTeamRef` (Queryable)
|
|
- `awayTeamRef` (Queryable)
|
|
- `season` (Queryable)
|
|
|
|
## 4. Import Data
|
|
|
|
After creating record types:
|
|
|
|
```bash
|
|
# 1. First scrape the data
|
|
cd Scripts
|
|
python3 scrape_schedules.py --sport all --season 2025 --output ./data
|
|
|
|
# 2. Run the import script (requires running from Xcode or with proper entitlements)
|
|
# The Swift script cannot run standalone - use the app or create a macOS command-line tool
|
|
```
|
|
|
|
### Alternative: Import via App
|
|
|
|
Add this to your app for first-run data import:
|
|
|
|
```swift
|
|
// In AppDelegate or App init
|
|
Task {
|
|
let importer = CloudKitImporter()
|
|
|
|
// Load JSON from bundle or downloaded file
|
|
if let stadiumsURL = Bundle.main.url(forResource: "stadiums", withExtension: "json"),
|
|
let gamesURL = Bundle.main.url(forResource: "games", withExtension: "json") {
|
|
// Import stadiums first
|
|
let stadiumsData = try Data(contentsOf: stadiumsURL)
|
|
let stadiums = try JSONDecoder().decode([ScrapedStadium].self, from: stadiumsData)
|
|
let count = try await importer.importStadiums(from: stadiums)
|
|
print("Imported \(count) stadiums")
|
|
}
|
|
}
|
|
```
|
|
|
|
## 5. Security Roles (CloudKit Dashboard)
|
|
|
|
For the **Public Database**:
|
|
|
|
| Role | Stadium | Team | Game |
|
|
|------|---------|------|------|
|
|
| World | Read | Read | Read |
|
|
| Authenticated | Read | Read | Read |
|
|
| Creator | Read/Write | Read/Write | Read/Write |
|
|
|
|
Users should only read from public database. Write access is for your admin imports.
|
|
|
|
## 6. Testing
|
|
|
|
1. Build and run the app on simulator or device
|
|
2. Check CloudKit Dashboard > **Data** to see imported records
|
|
3. Use **Logs** tab to debug any issues
|
|
|
|
## Troubleshooting
|
|
|
|
### "Container not found"
|
|
- Ensure container is created in Developer Portal
|
|
- Check entitlements file has correct container ID
|
|
- Clean build and re-run
|
|
|
|
### "Permission denied"
|
|
- Check Security Roles in CloudKit Dashboard
|
|
- Ensure app is signed with correct provisioning profile
|
|
|
|
### "Record type not found"
|
|
- Create record types in Development environment first
|
|
- Deploy schema to Production when ready
|