refactor(itinerary): replace CustomItineraryItem with ItineraryItem across codebase

- Update CKModels.swift to remove deleted type references
- Migrate LocalCustomItem to LocalItineraryItem in SavedTrip.swift
- Update AppDelegate to handle subscription removal
- Refactor AddItemSheet to create ItineraryItem with CustomInfo
- Update ItineraryTableViewController and Wrapper for new model
- Refactor TripDetailView state, methods and callbacks
- Fix TripMapView to display custom items with new model structure

This completes the migration from the legacy CustomItineraryItem/TravelDayOverride
model to the unified ItineraryItem model with ItemKind enum.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-01-17 21:51:32 -06:00
parent b008af1c71
commit cd00384010
7 changed files with 273 additions and 354 deletions

View File

@@ -20,8 +20,7 @@ enum CKRecordType {
static let stadiumAlias = "StadiumAlias"
static let tripPoll = "TripPoll"
static let pollVote = "PollVote"
static let customItineraryItem = "CustomItineraryItem"
static let travelDayOverride = "TravelDayOverride"
static let itineraryItem = "ItineraryItem"
}
// MARK: - CKTeam
@@ -625,155 +624,3 @@ struct CKPollVote {
}
}
// MARK: - CKCustomItineraryItem
struct CKCustomItineraryItem {
static let itemIdKey = "itemId"
static let tripIdKey = "tripId"
static let categoryKey = "category"
static let titleKey = "title"
static let dayKey = "day" // NEW: replaces anchorDay
static let sortOrderDoubleKey = "sortOrderDouble" // NEW: Double instead of Int
static let createdAtKey = "createdAt"
static let modifiedAtKey = "modifiedAt"
// Location fields for mappable items
static let latitudeKey = "latitude"
static let longitudeKey = "longitude"
static let addressKey = "address"
// DEPRECATED - kept for migration reads only
static let anchorTypeKey = "anchorType"
static let anchorIdKey = "anchorId"
static let anchorDayKey = "anchorDay"
static let sortOrderKey = "sortOrder"
let record: CKRecord
init(record: CKRecord) {
self.record = record
}
init(item: CustomItineraryItem) {
let record = CKRecord(
recordType: CKRecordType.customItineraryItem,
recordID: CKRecord.ID(recordName: item.id.uuidString)
)
record[CKCustomItineraryItem.itemIdKey] = item.id.uuidString
record[CKCustomItineraryItem.tripIdKey] = item.tripId.uuidString
record[CKCustomItineraryItem.categoryKey] = item.category.rawValue
record[CKCustomItineraryItem.titleKey] = item.title
record[CKCustomItineraryItem.dayKey] = item.day
record[CKCustomItineraryItem.sortOrderDoubleKey] = item.sortOrder
record[CKCustomItineraryItem.createdAtKey] = item.createdAt
record[CKCustomItineraryItem.modifiedAtKey] = item.modifiedAt
// Location fields (nil values are not stored in CloudKit)
record[CKCustomItineraryItem.latitudeKey] = item.latitude
record[CKCustomItineraryItem.longitudeKey] = item.longitude
record[CKCustomItineraryItem.addressKey] = item.address
self.record = record
}
func toItem() -> CustomItineraryItem? {
guard let itemIdString = record[CKCustomItineraryItem.itemIdKey] as? String,
let itemId = UUID(uuidString: itemIdString),
let tripIdString = record[CKCustomItineraryItem.tripIdKey] as? String,
let tripId = UUID(uuidString: tripIdString),
let categoryString = record[CKCustomItineraryItem.categoryKey] as? String,
let category = CustomItineraryItem.ItemCategory(rawValue: categoryString),
let title = record[CKCustomItineraryItem.titleKey] as? String,
let createdAt = record[CKCustomItineraryItem.createdAtKey] as? Date,
let modifiedAt = record[CKCustomItineraryItem.modifiedAtKey] as? Date
else { return nil }
// Read new fields, with migration fallback from old fields
let day: Int
if let newDay = record[CKCustomItineraryItem.dayKey] as? Int {
day = newDay
} else if let oldDay = record[CKCustomItineraryItem.anchorDayKey] as? Int {
// Migration: use old anchorDay
day = oldDay
} else {
return nil
}
let sortOrder: Double
if let newSortOrder = record[CKCustomItineraryItem.sortOrderDoubleKey] as? Double {
sortOrder = newSortOrder
} else if let oldSortOrder = record[CKCustomItineraryItem.sortOrderKey] as? Int {
// Migration: convert old Int sortOrder to Double
sortOrder = Double(oldSortOrder)
} else {
sortOrder = 0.0
}
// Location fields (optional - nil if not stored)
let latitude = record[CKCustomItineraryItem.latitudeKey] as? Double
let longitude = record[CKCustomItineraryItem.longitudeKey] as? Double
let address = record[CKCustomItineraryItem.addressKey] as? String
return CustomItineraryItem(
id: itemId,
tripId: tripId,
category: category,
title: title,
day: day,
sortOrder: sortOrder,
createdAt: createdAt,
modifiedAt: modifiedAt,
latitude: latitude,
longitude: longitude,
address: address
)
}
}
// MARK: - CKTravelDayOverride
struct CKTravelDayOverride {
static let overrideIdKey = "overrideId"
static let tripIdKey = "tripId"
static let travelAnchorIdKey = "travelAnchorId"
static let displayDayKey = "displayDay"
static let createdAtKey = "createdAt"
static let modifiedAtKey = "modifiedAt"
let record: CKRecord
init(record: CKRecord) {
self.record = record
}
init(override: TravelDayOverride) {
let record = CKRecord(
recordType: CKRecordType.travelDayOverride,
recordID: CKRecord.ID(recordName: override.id.uuidString)
)
record[CKTravelDayOverride.overrideIdKey] = override.id.uuidString
record[CKTravelDayOverride.tripIdKey] = override.tripId.uuidString
record[CKTravelDayOverride.travelAnchorIdKey] = override.travelAnchorId
record[CKTravelDayOverride.displayDayKey] = override.displayDay
record[CKTravelDayOverride.createdAtKey] = override.createdAt
record[CKTravelDayOverride.modifiedAtKey] = override.modifiedAt
self.record = record
}
func toOverride() -> TravelDayOverride? {
guard let overrideIdString = record[CKTravelDayOverride.overrideIdKey] as? String,
let overrideId = UUID(uuidString: overrideIdString),
let tripIdString = record[CKTravelDayOverride.tripIdKey] as? String,
let tripId = UUID(uuidString: tripIdString),
let travelAnchorId = record[CKTravelDayOverride.travelAnchorIdKey] as? String,
let displayDay = record[CKTravelDayOverride.displayDayKey] as? Int,
let createdAt = record[CKTravelDayOverride.createdAtKey] as? Date,
let modifiedAt = record[CKTravelDayOverride.modifiedAtKey] as? Date
else { return nil }
return TravelDayOverride(
id: overrideId,
tripId: tripId,
travelAnchorId: travelAnchorId,
displayDay: displayDay,
createdAt: createdAt,
modifiedAt: modifiedAt
)
}
}

View File

@@ -101,67 +101,62 @@ final class TripVote {
}
}
// MARK: - Local Custom Item (Cache)
// MARK: - Local Itinerary Item (Cache)
@Model
final class LocalCustomItem {
final class LocalItineraryItem {
@Attribute(.unique) var id: UUID
var tripId: UUID
var category: String
var title: String
var day: Int
var sortOrder: Double
var createdAt: Date
var kindData: Data // Encoded ItineraryItem.Kind
var modifiedAt: Date
var pendingSync: Bool // True if needs to sync to CloudKit
init(
id: UUID = UUID(),
tripId: UUID,
category: CustomItineraryItem.ItemCategory,
title: String,
day: Int,
sortOrder: Double = 0.0,
createdAt: Date = Date(),
kindData: Data,
modifiedAt: Date = Date(),
pendingSync: Bool = false
) {
self.id = id
self.tripId = tripId
self.category = category.rawValue
self.title = title
self.day = day
self.sortOrder = sortOrder
self.createdAt = createdAt
self.kindData = kindData
self.modifiedAt = modifiedAt
self.pendingSync = pendingSync
}
var toItem: CustomItineraryItem? {
guard let category = CustomItineraryItem.ItemCategory(rawValue: category)
var toItem: ItineraryItem? {
let decoder = JSONDecoder()
guard let kind = try? decoder.decode(ItemKind.self, from: kindData)
else { return nil }
return CustomItineraryItem(
return ItineraryItem(
id: id,
tripId: tripId,
category: category,
title: title,
day: day,
sortOrder: sortOrder,
createdAt: createdAt,
kind: kind,
modifiedAt: modifiedAt
)
}
static func from(_ item: CustomItineraryItem, pendingSync: Bool = false) -> LocalCustomItem {
LocalCustomItem(
static func from(_ item: ItineraryItem, pendingSync: Bool = false) -> LocalItineraryItem? {
let encoder = JSONEncoder()
guard let kindData = try? encoder.encode(item.kind)
else { return nil }
return LocalItineraryItem(
id: item.id,
tripId: item.tripId,
category: item.category,
title: item.title,
day: item.day,
sortOrder: item.sortOrder,
createdAt: item.createdAt,
kindData: kindData,
modifiedAt: item.modifiedAt,
pendingSync: pendingSync
)