У меня есть три модели SwiftData — родительская Event
, связанная с двумя другими. Я пытаюсь написать запрос, который объединяет две подмодели и фильтры, чтобы отображать только те результаты, которые соответствуют выбранному событию.
МОДЕЛИ
@Model
class Event: Identifiable {
var id = UUID()
var name: String = ""
var dateStart: Date = Date()
var dateEnd: Date = Date()
@Relationship(deleteRule: .cascade)
var flights: [Item1]?
var events: [Item2]?
}
@Model
class Item1: Identifiable {
var id = UUID()
var name: String = ""
var dateStart: Date = Date()
var dateEnd: Date = Date()
@Relationship(inverse: \Trip.flights) var trip: Trip?
}
@Model
class Item2: Identifiable {
var id = UUID()
var name: String = ""
var address: String = ""
var notes: String = ""
var dateStart: Date = Date()
var dateEnd: Date = Date()
@Relationship(inverse: \Trip.events) var trip: Trip?
}
Все это достаточно просто с помощью одной модели. У меня есть переменная let, определяющая событие, а затем я фокусируюсь на одном из элементов ForEach.
let event: Event
ForEach(event.item1!)
Но я не могу объединить две модели в одну переменную без множества ошибок.
Я попробовал базовый && в ForEach, но это выдает ошибку, что массив не может быть преобразован в ожидаемый тип аргумента Bool.
ForEach(event.item1! && event.item2!)
Я также попробовал пару запросов, но у них есть свои ошибки.
@Query(filter: #Predicate<Item1>{ ($0.trip == trip) }) var item1: [Item1]
@Query(filter: #Predicate<Item2>{ ($0.trip == trip) }) var item2: [Item2]
let mergedEvents = item1 + item2
В каждой строке запроса появляется ошибка: «Член экземпляра «событие» нельзя использовать для типа «EventDetails». Вы хотели вместо этого использовать значение этого типа?» И последняя переменная имеет ошибку: «Бинарный оператор «+» не может быть применен к операндам типа «[Item1]» и «[Item2]»
Конечный результат, к которому я стремлюсь, — это единый список, в котором есть как Item1, так и Item2, отсортированный по общему полю «startDate».
Если бы я мог написать простой sql, я бы написал простой JOIN с предложением WHERE, чтобы ограничить результаты событием, но я понятия не имею, как обработать его в Swift.
Какие-либо предложения?
Кажется, что вы могли бы создать один класс Item
, просто сделав address
и notes
необязательными. Кроме того, вместо инициализации всех свойств как пустых строк используйте соответствующий инициализатор.
Не по теме, но sql JOIN сильно отличается от того, что вы пытаетесь сделать здесь, это больше похоже на использование UNION.
Вы не можете объединить два @Query
или объединить результаты @Query
напрямую, но вы можете создать протокол, содержащий свойства, которые вы хотите показать в своем списке, а затем позволить типам моделей соответствовать протоколу.
protocol EventItem {
var id: UUID { get }
var name: String { get }
var dateStart: Date { get }
var dateEnd: Date { get }
}
extension Item1: EventItem {}
extension Item2: EventItem {}
Затем вы можете объединить два свойства flights
и events
в массив типа [any EventItem]
.
Здесь я сделал это как вычисляемое свойство в расширении Event
extension Event {
var allItems: [any EventItem] {
((events ?? []) + (flights ?? [])).sorted { $0.dateStart < $1.dateStart }
}
}
и затем вы можете использовать этот массив в представлении
List(event.allItems, id: \.id) { item in
HStack {
Text(item.name)
Spacer()
Text(item.dateStart.formatted(date: .abbreviated, time: .omitted))
Text(item.dateEnd.formatted(date: .abbreviated, time: .omitted))
}
}
Идеальный. Большое спасибо!
Обратите внимание: никогда не используйте
!
в своем коде. Также нет необходимости вIdentifiable
,@Model
уже это обеспечивает. Чтобы объединить массивы двух разных типов (item1 и item2), вы можете использоватьzip
. Затем вы можете использовать.map
для преобразования в массив кортежей.