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:
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user