Можно ли объединить две модели SwiftData в один список?

У меня есть три модели 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.

Какие-либо предложения?

Обратите внимание: никогда не используйте ! в своем коде. Также нет необходимости в Identifiable, @Model уже это обеспечивает. Чтобы объединить массивы двух разных типов (item1 и item2), вы можете использовать zip. Затем вы можете использовать .map для преобразования в массив кортежей.

workingdog support Ukraine 30.06.2024 03:55

Кажется, что вы могли бы создать один класс Item, просто сделав address и notes необязательными. Кроме того, вместо инициализации всех свойств как пустых строк используйте соответствующий инициализатор.

Paulw11 30.06.2024 06:59

Не по теме, но sql JOIN сильно отличается от того, что вы пытаетесь сделать здесь, это больше похоже на использование UNION.

Joakim Danielson 30.06.2024 10:36
Структурированный массив Numpy
Структурированный массив Numpy
Однако в реальных проектах я чаще всего имею дело со списками, состоящими из нескольких типов данных. Как мы можем использовать массивы numpy, чтобы...
T - 1Bits: Генерация последовательного массива
T - 1Bits: Генерация последовательного массива
По мере того, как мы пишем все больше кода, мы привыкаем к определенным способам действий. То тут, то там мы находим код, который заставляет нас...
Что такое деструктуризация массива в JavaScript?
Что такое деструктуризация массива в JavaScript?
Деструктуризация позволяет распаковывать значения из массивов и добавлять их в отдельные переменные.
2
3
87
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы не можете объединить два @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))
    }
}

Идеальный. Большое спасибо!

SwiftBleu 30.06.2024 21:01

Другие вопросы по теме