feat: update bundle ID config, CloudKit container, and add landing page
- Update CloudKit container ID to iCloud.com.88oakapps.SportsTime across all services - Update IAP product IDs to match new bundle ID (com.88oakapps.SportsTime) - Add app landing page with light, welcoming design matching app aesthetic - Update entitlements and project configuration Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -27,7 +27,8 @@
|
|||||||
"Bash(npx tsc:*)",
|
"Bash(npx tsc:*)",
|
||||||
"Bash(timeout 10 npx remotion:*)",
|
"Bash(timeout 10 npx remotion:*)",
|
||||||
"Bash(npx remotion:*)",
|
"Bash(npx remotion:*)",
|
||||||
"Bash(xcrun xcresulttool:*)"
|
"Bash(xcrun xcresulttool:*)",
|
||||||
|
"Bash(sips:*)"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ TripCreationView → TripCreationViewModel → PlanningRequest
|
|||||||
- ViewModels use `@Observable` (not ObservableObject)
|
- ViewModels use `@Observable` (not ObservableObject)
|
||||||
- All planning engine components are `actor` types for thread safety
|
- All planning engine components are `actor` types for thread safety
|
||||||
- Domain models are pure Codable structs; SwiftData models wrap them via encoded `Data` fields
|
- Domain models are pure Codable structs; SwiftData models wrap them via encoded `Data` fields
|
||||||
- CloudKit container ID: `iCloud.com.sportstime.app`
|
- CloudKit container ID: `iCloud.com.88oakapps.SportsTime`
|
||||||
- `PDFGenerator` and `ExportService` are `@MainActor final class` (not actors) because they access MainActor-isolated UI properties and use UIKit drawing
|
- `PDFGenerator` and `ExportService` are `@MainActor final class` (not actors) because they access MainActor-isolated UI properties and use UIKit drawing
|
||||||
|
|
||||||
### Themed Background System
|
### Themed Background System
|
||||||
|
|||||||
@@ -304,10 +304,10 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
CODE_SIGN_ENTITLEMENTS = SportsTime/SportsTime.entitlements;
|
CODE_SIGN_ENTITLEMENTS = SportsTime/SportsTimeDebug.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_TEAM = V3PF3M6B6U;
|
DEVELOPMENT_TEAM = QND55P4443;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = SportsTime/Info.plist;
|
INFOPLIST_FILE = SportsTime/Info.plist;
|
||||||
@@ -322,7 +322,7 @@
|
|||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "com.t-t.SportsTime";
|
PRODUCT_BUNDLE_IDENTIFIER = com.88oakapps.SportsTime.Debug;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||||
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||||
@@ -342,7 +342,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = SportsTime/SportsTime.entitlements;
|
CODE_SIGN_ENTITLEMENTS = SportsTime/SportsTime.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_TEAM = V3PF3M6B6U;
|
DEVELOPMENT_TEAM = QND55P4443;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = SportsTime/Info.plist;
|
INFOPLIST_FILE = SportsTime/Info.plist;
|
||||||
@@ -357,7 +357,7 @@
|
|||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "com.t-t.SportsTime";
|
PRODUCT_BUNDLE_IDENTIFIER = com.88oakapps.SportsTime;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||||
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||||
@@ -500,7 +500,7 @@
|
|||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 26.2;
|
IPHONEOS_DEPLOYMENT_TARGET = 26.2;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "com.t-t.SportsTimeTests";
|
PRODUCT_BUNDLE_IDENTIFIER = com.88oakapps.SportsTimeTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
||||||
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||||
@@ -542,7 +542,7 @@
|
|||||||
DEVELOPMENT_TEAM = V3PF3M6B6U;
|
DEVELOPMENT_TEAM = V3PF3M6B6U;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "com.t-t.SportsTimeUITests";
|
PRODUCT_BUNDLE_IDENTIFIER = com.88oakapps.SportsTimeUITests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
||||||
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||||
|
|||||||
@@ -93,7 +93,7 @@
|
|||||||
"locale" : "en_US"
|
"locale" : "en_US"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"productID" : "com.sportstime.pro.monthly",
|
"productID" : "com.88oakapps.SportsTime.pro.monthly",
|
||||||
"recurringSubscriptionPeriod" : "P1M",
|
"recurringSubscriptionPeriod" : "P1M",
|
||||||
"referenceName" : "Pro Monthly",
|
"referenceName" : "Pro Monthly",
|
||||||
"subscriptionGroupID" : "21514523",
|
"subscriptionGroupID" : "21514523",
|
||||||
@@ -127,7 +127,7 @@
|
|||||||
"locale" : "en_US"
|
"locale" : "en_US"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"productID" : "com.sportstime.pro.annual",
|
"productID" : "com.88oakapps.SportsTime.pro.annual2",
|
||||||
"recurringSubscriptionPeriod" : "P1Y",
|
"recurringSubscriptionPeriod" : "P1Y",
|
||||||
"referenceName" : "Pro Annual",
|
"referenceName" : "Pro Annual",
|
||||||
"subscriptionGroupID" : "21514523",
|
"subscriptionGroupID" : "21514523",
|
||||||
|
|||||||
@@ -18,10 +18,10 @@ final class BackgroundSyncManager {
|
|||||||
// MARK: - Task Identifiers
|
// MARK: - Task Identifiers
|
||||||
|
|
||||||
/// Background app refresh task - runs periodically (system decides frequency)
|
/// Background app refresh task - runs periodically (system decides frequency)
|
||||||
static let refreshTaskIdentifier = "com.sportstime.app.refresh"
|
static let refreshTaskIdentifier = "com.88oakapps.SportsTime.refresh"
|
||||||
|
|
||||||
/// Background processing task - runs during optimal conditions (plugged in, overnight)
|
/// Background processing task - runs during optimal conditions (plugged in, overnight)
|
||||||
static let processingTaskIdentifier = "com.sportstime.app.db-cleanup"
|
static let processingTaskIdentifier = "com.88oakapps.SportsTime.db-cleanup"
|
||||||
|
|
||||||
// MARK: - Singleton
|
// MARK: - Singleton
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ final class BackgroundSyncManager {
|
|||||||
|
|
||||||
private var modelContainer: ModelContainer?
|
private var modelContainer: ModelContainer?
|
||||||
private var currentCancellationToken: BackgroundTaskCancellationToken?
|
private var currentCancellationToken: BackgroundTaskCancellationToken?
|
||||||
private let logger = Logger(subsystem: "com.sportstime.app", category: "BackgroundSyncManager")
|
private let logger = Logger(subsystem: "com.88oakapps.SportsTime", category: "BackgroundSyncManager")
|
||||||
|
|
||||||
private init() {}
|
private init() {}
|
||||||
|
|
||||||
@@ -332,16 +332,16 @@ final class BackgroundSyncManager {
|
|||||||
#if DEBUG
|
#if DEBUG
|
||||||
extension BackgroundSyncManager {
|
extension BackgroundSyncManager {
|
||||||
/// Manually trigger refresh task for testing.
|
/// Manually trigger refresh task for testing.
|
||||||
/// Run in debugger: `e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.sportstime.app.refresh"]`
|
/// Run in debugger: `e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.88oakapps.SportsTime.refresh"]`
|
||||||
func debugTriggerRefresh() {
|
func debugTriggerRefresh() {
|
||||||
print("Debug: Use lldb command to trigger background refresh:")
|
print("Debug: Use lldb command to trigger background refresh:")
|
||||||
print("e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@\"com.sportstime.app.refresh\"]")
|
print("e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@\"com.88oakapps.SportsTime.refresh\"]")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Manually trigger processing task for testing.
|
/// Manually trigger processing task for testing.
|
||||||
func debugTriggerProcessing() {
|
func debugTriggerProcessing() {
|
||||||
print("Debug: Use lldb command to trigger background processing:")
|
print("Debug: Use lldb command to trigger background processing:")
|
||||||
print("e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@\"com.sportstime.app.db-cleanup\"]")
|
print("e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@\"com.88oakapps.SportsTime.db-cleanup\"]")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ actor CloudKitService {
|
|||||||
private let recordsPerPage = 400
|
private let recordsPerPage = 400
|
||||||
|
|
||||||
private init() {
|
private init() {
|
||||||
self.container = CKContainer(identifier: "iCloud.com.sportstime.app")
|
self.container = CKContainer(identifier: "iCloud.com.88oakapps.SportsTime")
|
||||||
self.publicDatabase = container.publicCloudDatabase
|
self.publicDatabase = container.publicCloudDatabase
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import CloudKit
|
|||||||
actor ItineraryItemService {
|
actor ItineraryItemService {
|
||||||
static let shared = ItineraryItemService()
|
static let shared = ItineraryItemService()
|
||||||
|
|
||||||
private let container = CKContainer(identifier: "iCloud.com.sportstime.app")
|
private let container = CKContainer(identifier: "iCloud.com.88oakapps.SportsTime")
|
||||||
private var database: CKDatabase { container.privateCloudDatabase }
|
private var database: CKDatabase { container.privateCloudDatabase }
|
||||||
|
|
||||||
private let recordType = "ItineraryItem"
|
private let recordType = "ItineraryItem"
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ final class NetworkMonitor {
|
|||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
|
|
||||||
private let monitor = NWPathMonitor()
|
private let monitor = NWPathMonitor()
|
||||||
private let queue = DispatchQueue(label: "com.sportstime.networkmonitor")
|
private let queue = DispatchQueue(label: "com.88oakapps.SportsTime.networkmonitor")
|
||||||
private let logger = Logger(subsystem: "com.sportstime.app", category: "NetworkMonitor")
|
private let logger = Logger(subsystem: "com.88oakapps.SportsTime", category: "NetworkMonitor")
|
||||||
|
|
||||||
/// Current network status
|
/// Current network status
|
||||||
private(set) var isConnected: Bool = false
|
private(set) var isConnected: Bool = false
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ actor PollService {
|
|||||||
private var pollSubscriptionID: CKSubscription.ID?
|
private var pollSubscriptionID: CKSubscription.ID?
|
||||||
|
|
||||||
private init() {
|
private init() {
|
||||||
self.container = CKContainer(identifier: "iCloud.com.sportstime.app")
|
self.container = CKContainer(identifier: "iCloud.com.88oakapps.SportsTime")
|
||||||
self.publicDatabase = container.publicCloudDatabase
|
self.publicDatabase = container.publicCloudDatabase
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ final class SyncLogger {
|
|||||||
|
|
||||||
private let fileURL: URL
|
private let fileURL: URL
|
||||||
private let maxLines = 500
|
private let maxLines = 500
|
||||||
private let queue = DispatchQueue(label: "com.sportstime.synclogger")
|
private let queue = DispatchQueue(label: "com.88oakapps.SportsTime.synclogger")
|
||||||
|
|
||||||
private init() {
|
private init() {
|
||||||
let docs = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
|
let docs = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ final class VisitPhotoService {
|
|||||||
|
|
||||||
init(modelContext: ModelContext) {
|
init(modelContext: ModelContext) {
|
||||||
self.modelContext = modelContext
|
self.modelContext = modelContext
|
||||||
self.container = CKContainer(identifier: "iCloud.com.sportstime.app")
|
self.container = CKContainer(identifier: "iCloud.com.88oakapps.SportsTime")
|
||||||
self.privateDatabase = container.privateCloudDatabase
|
self.privateDatabase = container.privateCloudDatabase
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ final class StoreManager {
|
|||||||
// MARK: - Constants
|
// MARK: - Constants
|
||||||
|
|
||||||
static let proProductIDs: Set<String> = [
|
static let proProductIDs: Set<String> = [
|
||||||
"com.sportstime.pro.monthly",
|
"com.88oakapps.SportsTime.pro.monthly",
|
||||||
"com.sportstime.pro.annual"
|
"com.88oakapps.SportsTime.pro.annual2"
|
||||||
]
|
]
|
||||||
|
|
||||||
static let freeTripLimit = 1
|
static let freeTripLimit = 1
|
||||||
@@ -59,11 +59,11 @@ final class StoreManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var monthlyProduct: Product? {
|
var monthlyProduct: Product? {
|
||||||
products.first { $0.id == "com.sportstime.pro.monthly" }
|
products.first { $0.id == "com.88oakapps.SportsTime.pro.monthly" }
|
||||||
}
|
}
|
||||||
|
|
||||||
var annualProduct: Product? {
|
var annualProduct: Product? {
|
||||||
products.first { $0.id == "com.sportstime.pro.annual" }
|
products.first { $0.id == "com.88oakapps.SportsTime.pro.annual2" }
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Initialization
|
// MARK: - Initialization
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ final class ShareService {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Open Instagram Stories
|
// Open Instagram Stories
|
||||||
let urlString = "instagram-stories://share?source_application=com.sportstime.app"
|
let urlString = "instagram-stories://share?source_application=com.88oakapps.SportsTime"
|
||||||
if let url = URL(string: urlString) {
|
if let url = URL(string: urlString) {
|
||||||
UIApplication.shared.open(url)
|
UIApplication.shared.open(url)
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import Foundation
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import os.log
|
import os.log
|
||||||
|
|
||||||
private let logger = Logger(subsystem: "com.sportstime.app", category: "ScheduleViewModel")
|
private let logger = Logger(subsystem: "com.88oakapps.SportsTime", category: "ScheduleViewModel")
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
@Observable
|
@Observable
|
||||||
|
|||||||
@@ -2,16 +2,16 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
||||||
|
<array>
|
||||||
|
<string>com.88oakapps.SportsTime.refresh</string>
|
||||||
|
<string>com.88oakapps.SportsTime.db-cleanup</string>
|
||||||
|
</array>
|
||||||
<key>UIBackgroundModes</key>
|
<key>UIBackgroundModes</key>
|
||||||
<array>
|
<array>
|
||||||
<string>remote-notification</string>
|
<string>remote-notification</string>
|
||||||
<string>fetch</string>
|
<string>fetch</string>
|
||||||
<string>processing</string>
|
<string>processing</string>
|
||||||
</array>
|
</array>
|
||||||
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
|
||||||
<array>
|
|
||||||
<string>com.sportstime.app.refresh</string>
|
|
||||||
<string>com.sportstime.app.db-cleanup</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<string>development</string>
|
<string>development</string>
|
||||||
<key>com.apple.developer.icloud-container-identifiers</key>
|
<key>com.apple.developer.icloud-container-identifiers</key>
|
||||||
<array>
|
<array>
|
||||||
<string>iCloud.com.sportstime.app</string>
|
<string>iCloud.com.88oakapps.SportsTime</string>
|
||||||
</array>
|
</array>
|
||||||
<key>com.apple.developer.icloud-services</key>
|
<key>com.apple.developer.icloud-services</key>
|
||||||
<array>
|
<array>
|
||||||
|
|||||||
16
SportsTime/SportsTimeDebug.entitlements
Normal file
16
SportsTime/SportsTimeDebug.entitlements
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>aps-environment</key>
|
||||||
|
<string>development</string>
|
||||||
|
<key>com.apple.developer.icloud-container-identifiers</key>
|
||||||
|
<array>
|
||||||
|
<string>iCloud.com.88oakapps.SportsTime.Debug</string>
|
||||||
|
</array>
|
||||||
|
<key>com.apple.developer.icloud-services</key>
|
||||||
|
<array>
|
||||||
|
<string>CloudKit</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
1197
landing_page/index.html
Normal file
1197
landing_page/index.html
Normal file
File diff suppressed because it is too large
Load Diff
BIN
screens/flow.png
Normal file
BIN
screens/flow.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 MiB |
BIN
screens/phone.png
Normal file
BIN
screens/phone.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 521 KiB |
Reference in New Issue
Block a user