В моем приложении есть две сущности: «Карта» и «CardRecord», они имеют отношение 1:n, одна карта имеет много записей. У меня есть представление, которое должно показать, сколько записей содержится в каждой карточке, поэтому вот мой код:
@SectionedFetchRequest
private var sectionRecords: SectionedFetchResults<Card, CardRecord>
init(month: Int32) {
let predicate = NSPredicate(format: "month = %i", month)
_sectionRecords = SectionedFetchRequest<Card, CardRecord>(
entity: CardRecord.entity(),
sectionIdentifier: \CardRecord.card!,
sortDescriptors: [],
predicate: predicate
)
}
var body: some View {
VStack(alignment: .leading) {
ForEach(sectionRecords) { section in
CardExpansionView(card: section.id, num: Int32(section.count))
}
}
}
Мне нужно только зациклить разделRecords, потому что мне нужен только номер каждого раздела, и мне нужно показать карту @ObservedObject, поэтому я использую карту объекта связи в качестве идентификатора раздела.
Когда я запускаю приложение в симуляторе, произошла ошибка:
ForEach<SectionedFetchResults<Card, CardRecord>, Card, CardExpansionView>: the ID <Card: 0x6000025fdf40> (entity: Card; id: 0xe565b4b98e84a111 <x-coredata://292B0160-058F-48AE-BFCC-11196BF00552/Card/p24>; data: {
cid = "6E3C25B7-2DC2-4538-B3D9-950A387C13AD";
color = CardBrown;
createTime = "2024-04-04 07:47:22 +0000";
icon = "cup.and.saucer";
name = "NO COFFEE";
records = "<relationship fault: 0x60000063f6e0 'records'>";
}) occurs multiple times within the collection, this will give undefined results!
При этом возникает еще один вопрос, количество разделов на странице указано неверно и имеются дубликаты карточек.
Затем я добавляю атрибут cardId в сущность CardRecord и меняю идентификатор раздела на cardId.
sectionIdentifier: \CardRecord.cardId!
Когда я снова запускаю приложение... Ошибка исчезла, но количество разделов по-прежнему неверное, а дубликаты все еще существуют.
Итак, в чем проблема и что мне нужно с этим делать.
И напоследок вопрос: как отсортировать "sectionRecords" по "section.count"
И спасибо.
Спасибо за ваш ответ. Я внимательно прочитал эту статью, но, похоже, она работает не так, как я хочу, теперь я пытаюсь решить эту проблему, используя другой подход. Вот мой код:
Я покажу свой код в следующем ответе.
Сейчас он работает хорошо, и я думаю, что его можно оптимизировать лучше (но я понятия не имею об этом :)).





var body: some View {
VStack(alignment: .leading) {
ForEach(ranksFetcher.ranks, id: \.id) { rank in
CardExpansionView(card: rank.card, num: rank.count, maxWidth: rank.width)
}
}
.frame(maxWidth: .infinity, alignment: .leading)
.onAppear() {
if fetch {
ranksFetcher.moc = moc
ranksFetcher.maxWidth = viewWidth()
ranksFetcher.fetch(yearmonth: yearmonth)
fetch.toggle()
}
}
}
class RanksFetcher: NSObject, ObservableObject {
var moc: NSManagedObjectContext?
var maxWidth: CGFloat?
@Published
private(set) var ranks: [CardRankModel] = []
func fetch(yearmonth: Int32) {
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "CardRecord")
fetchRequest.predicate = NSPredicate(format: "month = %i", yearmonth)
fetchRequest.propertiesToGroupBy = ["cardId"]
fetchRequest.resultType = .dictionaryResultType
// Defining column 'count'
let expressDescription = NSExpressionDescription()
expressDescription.resultType = .integer32
let name = "count"
expressDescription.name = name
// Count witch column
let cardId = NSExpression(forKeyPath: \CardRecord.cardId)
let express = NSExpression(forFunction: "count:", arguments: [cardId])
expressDescription.expression = express
fetchRequest.propertiesToFetch = ["cardId", expressDescription]
var groups = (try? moc!.fetch(fetchRequest) as? [[String: Any]]) ?? []
groups.sort { e1, e2 in
let i1 = e1["count"] as? Int32 ?? 0
let i2 = e2["count"] as? Int32 ?? 0
return i1 > i2
}
var cardIds = [String]()
for gp in groups {
cardIds.append(gp["cardId"] as! String)
}
let cardsFetchRequest = NSFetchRequest<Card>(entityName: "Card")
cardsFetchRequest.predicate = NSPredicate(format: "cid in (%@)", cardIds)
let cards = (try? moc!.fetch(cardsFetchRequest)) ?? []
let cardMap = Dictionary.init(cards.map { ($0.cid, $0) }) { $1 }
let maxWidth = maxWidth!
let maxCount = groups.first?["count"] as? Int32
var data: [CardRankModel] = []
for gp in groups {
let cardId = gp["cardId"] as! String
let count = gp["count"] as? Int32 ?? 0
if let card = cardMap[cardId] {
let cardRankModel = CardRankModel(id: card.cid!, card: card, count: count, width: maxWidth * (CGFloat(count) / CGFloat(maxCount!)))
data.append(cardRankModel)
}
}
ranks = data
}
}
Я не удивлен, что вы получаете сообщение об ошибке, поскольку каждая карта с более чем одной записью CardRecord будет использоваться несколько раз. Тем не менее, я думаю, вам следует полностью отказаться от этой идеи и вместо этого взглянуть на производные атрибуты. Если вы добавите производный атрибут count в Card, вы можете решить эту проблему с помощью обычного FetchRequest. Вот одна статья на эту тему.