-
Notifications
You must be signed in to change notification settings - Fork 1
Merge release/3.1.0 into develop
#153
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
* โจ [feat] Hotel Tickets ๊ตํ ํตํ UI ์ง์ ์ถ๊ฐ - Item์ canExchangeHotelTickets computed property ์ถ๊ฐ - ItemBellsView์ hotelTickets ๋ชจ๋ ์ถ๊ฐ (์์ด์ฝ: icon-hoteltickets) - ItemDetailInfoView์ Hotel Tickets ๊ตํ ๊ฐ๊ฒฉ ํ์ ๋ก์ง ์ถ๊ฐ * ๐ง [chore] ์๋ก์ด ํ๊ทธ ๋ค๊ตญ์ด ์ถ๊ฐ
* โจ [feat] Variant ์์ง ์ถ์ ์ ์ํ CoreData ์คํค๋ง ๋ฐ Storage ๋ ์ด์ด ์ถ๊ฐ
- VariantCollectionEntity ์ถ๊ฐ (variantId, itemName ์ ์ฅ)
- VariantsStorage ํ๋กํ ์ฝ ์ ์ (fetch, add, remove, removeAll)
- CoreDataVariantsStorage ๊ตฌํ (RxSwift ๊ธฐ๋ฐ)
- CoreDataStorage์ ์๋ ๋ณํฉ ์ค์ ์ถ๊ฐ (automaticallyMergesChangesFromParent)
- CoreDataItemsStorage์ viewContext ์ฆ์ ์ ์ฅ ๋ก์ง ์ถ๊ฐ (๋ฐ์ดํฐ ์์ค ๋ฐฉ์ง)
* ๐ [fix] ์ท ์์ดํ
variants๊ฐ ๋์ผํ variantId๋ฅผ ์ฌ์ฉํ๋ ๋ฒ๊ทธ ์์
- WardrobeVariat.toVariat()์์ ํ๋์ฝ๋ฉ๋ "1_0_0" ๋์ filename๊ณผ internalId๋ฅผ ์กฐํฉํ ๊ณ ์ ID ์์ฑ
- ํ์: "{filename}_{internalId}_0"์ผ๋ก ๋ณ๊ฒฝํ์ฌ ๊ฐ variant๋ง๋ค ๊ณ ์ ์ฑ ๋ณด์ฅ
- variationsWithColor ํํฐ ํต๊ณผ๋ฅผ ์ํด "_0" ์ ๋ฏธ์ฌ ์ ์ง
* โจ [feat] Variant ์์ง ๊ธฐ๋ฅ - Reactor ๋ฐ ๋น์ฆ๋์ค ๋ก์ง ์ถ๊ฐ
- ItemDetailReactor์ variant collection actions/mutations/state ์ถ๊ฐ
- fetchCollectedVariants, toggleVariantCollection ์ก์
- collectedVariantIds ์ํ ๊ด๋ฆฌ
- CatalogCellReactor์ ์์ดํ
์์ง ํด์ ์ variants ์ผ๊ด ์ ๊ฑฐ ๋ก์ง ์ถ๊ฐ
- Items ์ฑ๊ธํด์ variant ๊ด๋ฆฌ ๊ธฐ๋ฅ ์ถ๊ฐ
- collectedVariants BehaviorRelay ์ถ๊ฐ
- updateVariant, getCollectedVariants ๋ฉ์๋ ์ถ๊ฐ
- CoreDataVariantsStorage ์ด๊ธฐํ ๋ฐ ๋ก๋
- ์์ดํ
์ฒดํฌ ํด์ ์ ํด๋น ์์ดํ
์ ๋ชจ๋ variants ์๋ ์ ๊ฑฐ
- Variant ์ฒดํฌ ์ ์์ดํ
๋ ์๋ ์์ง ์ฒ๋ฆฌ
* โจ [feat] Variant ์์ง ๊ธฐ๋ฅ - UI ๋ ์ด์ด ๊ตฌํ
- VariantCell์ ์ฒดํฌ๋ฐ์ค UI ์ถ๊ฐ
- checkButton (checkmark.seal ์์ด์ฝ)
- showCheckbox ํ๋๊ทธ๋ก ๋ฆฌํผ ๊ฐ๋ฅ ์์ดํ
ํํฐ๋ง
- checkboxObservable๋ก ์ฒดํฌ ์ด๋ฒคํธ ์ ๋ฌ
- ItemVariantsView์ ์์ง ์ํ ๊ด๋ฆฌ ์ถ๊ฐ
- collectedVariantIds BehaviorRelay๋ก reactive ์
๋ฐ์ดํธ
- isReformable ํ๋๊ทธ๋ก ์ฒดํฌ๋ฐ์ค ํ์ ์ ์ด
- didToggleVariantCollection Observable ์ถ๊ฐ
- ItemDetailViewController ๋ฐ์ธ๋ฉ ์ถ๊ฐ
- viewDidAppear์์ ์์ง ์ํ ์๋ก๊ณ ์นจ
- reactor์ ์๋ฐฉํฅ ๋ฐ์ธ๋ฉ (collectedVariantIds โ UI)
- setUpVariant์์ ๋ฆฌํผ ๊ฐ๋ฅ ์ฌ๋ถ ์ ๋ฌ
* ๐ง [chore] ์ฑ ์ข
๋ฃ ์ CoreData ์ ์ฅ ๋ณด์ฅ ๋ก์ง ์ถ๊ฐ
- sceneDidEnterBackground์์ background task ์์
- ๋น๋๊ธฐ CoreData ์์
์๋ฃ๋ฅผ ์ํด 2์ด ๋๊ธฐ ํ viewContext ์ ์ฅ
- ์ฑ ๊ฐ์ ์ข
๋ฃ ์ ๋ฐ์ดํฐ ์์ค ๋ฐฉ์ง
๐ WalkthroughWalkthroughAdds variant-collection support (model, storage, UI, reactor/viewmodel wiring), hotel-tickets exchange support (model, UI, asset, localization), Core Data migration/config tweaks, Rx wrapper for networking, and minor scene/background and layout adjustments. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant VariantCell
participant ItemVariantsView
participant Reactor as ItemDetailReactor
participant VariantsStorage as CoreDataVariantsStorage
participant Items as Items
User->>VariantCell: Tap checkbox
VariantCell->>ItemVariantsView: emit checkboxObservable(variant, isCollected)
ItemVariantsView->>Reactor: dispatch Action.toggleVariantCollection(variant)
Reactor->>VariantsStorage: add/remove(variantId, itemName)
VariantsStorage->>VariantsStorage: persist change (background context)
Reactor->>Items: updateVariant(variantId, itemName, isAdding)
Items->>ItemVariantsView: collectedVariantIds observable updates
ItemVariantsView->>VariantCell: refresh checkbox state
Estimated code review effort๐ฏ 4 (Complex) | โฑ๏ธ ~45 minutes Possibly related PRs
Suggested labels
Poem
๐ฅ Pre-merge checks | โ 1 | โ 2โ Failed checks (2 warnings)
โ Passed checks (1 passed)
โ๏ธ Tip: You can configure your own custom pre-merge checks in the settings. โจ Finishing touches
๐งช Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
Caution
Some comments are outside the diff and canโt be posted inline due to platform limitations.
โ ๏ธ Outside diff range comments (1)
Animal-Crossing-Wiki/Projects/App/Sources/Presentation/Catalog/Views/VariantCell.swift (1)
39-48:โ ๏ธ Potential issue | ๐ก MinorMinor: Checkbox briefly visible during reuse for non-checkbox cells.
Setting
checkButton.isHidden = falseinprepareForReusemeans cells that should hide the checkbox will briefly show it untilsetUp(showCheckbox: false)is called. Consider defaulting to hidden inprepareForReuse:๐ง Suggested fix
override func prepareForReuse() { super.prepareForReuse() imageView.kf.cancelDownloadTask() imageView.image = nil titleLabel.text = nil isCollected = false - checkButton.isHidden = false + checkButton.isHidden = true updateCheckboxImage() disposeBag = DisposeBag() }
๐ค Fix all issues with AI agents
In
`@Animal-Crossing-Wiki/Projects/App/Sources/CoreDataStorage/CoreDataStorage.xcdatamodeld/UserCollectionEntity.xcdatamodel/contents`:
- Around line 91-99: The NSPersistentCloudKitContainer initialization in
CoreDataStorage.swift must enable automatic lightweight migration so adding
VariantCollectionEntity doesn't crash existing stores; update the container
configuration (the container.persistentStoreDescriptions handling) to set
NSMigratePersistentStoresAutomaticallyOption and
NSInferMappingModelAutomaticallyOption to true for each
persistentStoreDescription before loading stores, or alternatively add explicit
.xcmappingmodel files if lightweight migration is insufficient. Ensure the
changes are applied prior to calling loadPersistentStores on the
NSPersistentCloudKitContainer.
In
`@Animal-Crossing-Wiki/Projects/App/Sources/Presentation/Catalog/ViewModels/ItemDetailReactor.swift`:
- Around line 105-111: fetchCollectedVariants currently snapshots
Items.shared.variantList with .take(1) and can race with async hydration from
Items.setUpUserCollection(); instead, read the authoritative source directly or
wait for hydration: replace the Items.shared.variantList.take(1) usage in the
.fetchCollectedVariants case with a direct synchronous/awaited fetch from
VariantsStorage (e.g. call the VariantsStorage method that returns collected
variant ids or await its hydrated/completion publisher) or subscribe to a
VariantsStorage.hydrated publisher before mapping to
Mutation.setCollectedVariantIds โ reference fetchCollectedVariants,
Items.shared.variantList, Items.setUpUserCollection(), and VariantsStorage to
locate and update the logic so the action uses the hydrated storage rather than
a potentially-empty snapshot.
In
`@Animal-Crossing-Wiki/Projects/App/Sources/Presentation/Catalog/Views/ItemVariantsView.swift`:
- Line 71: Replace the single-line guard used in the closure with a multi-line
guard block to satisfy SwiftLint's conditional_returns_on_newline rule: change
the line "guard let self = self else { return }" to a block-style guard (guard
let self = self else { newline return newline }) so the return is on its own
line; update the occurrence inside ItemVariantsView (the closure capturing self)
accordingly.
- Around line 79-84: The subscription on cell.checkboxObservable currently uses
take(1) which unsubscribes after the first toggle; remove the take(1) so the
cell can emit multiple toggles before reuse, i.e. subscribe directly to
cell.checkboxObservable and keep disposing into cell.disposeBag; ensure
VariantCell.prepareForReuse() continues to reset disposeBag so subscriptions are
cleared on reuse and the handler variantCollectionToggled.accept((item,
isCollected)) remains the callback.
In `@Animal-Crossing-Wiki/Projects/App/Sources/SceneDelegate.swift`:
- Around line 52-67: Remove the arbitrary 2-second delay and perform the Core
Data save immediately when entering background: call beginBackgroundTask to
obtain backgroundTaskID, save using
CoreDataStorage.shared.persistentContainer.viewContext synchronously (try
viewContext.save()), and then end the task with
UIApplication.shared.endBackgroundTask(backgroundTaskID). Also update the
expiration handler passed to UIApplication.shared.beginBackgroundTask to capture
the same backgroundTaskID and safely call endBackgroundTask only if
backgroundTaskID != .invalid, then set backgroundTaskID = .invalid after ending
to avoid double-ending. Ensure you remove the DispatchQueue.main.asyncAfter
usage and guard against calling endBackgroundTask on an already-ended task.
๐งน Nitpick comments (3)
Animal-Crossing-Wiki/Projects/App/Sources/Presentation/Catalog/Views/VariantCell.swift (1)
17-19: Consider makingdisposeBagprivate.The
disposeBagproperty is exposed publicly but appears to only be used internally for subscription cleanup. Making itprivatewould better encapsulate the cell's implementation.โป๏ธ Suggested change
- var disposeBag = DisposeBag() + private var disposeBag = DisposeBag()Animal-Crossing-Wiki/Projects/App/Sources/CoreDataStorage/VariantsStorage/CoreDataVariantsStorage.swift (1)
110-134: UsesaveViewContext()helper for consistency.The
remove()method has inline view context save logic (lines 121-128) that duplicates thesaveViewContext()helper. Theadd()andremoveAll()methods already use the helper.โป๏ธ Proposed refactor
func remove(_ variantId: String) { coreDataStorage.performBackgroundTask { context in do { let object = try self.coreDataStorage.getUserCollection(context) let variants = object.variants?.allObjects as? [VariantCollectionEntity] ?? [] if let variant = variants.first(where: { $0.variantId == variantId }) { object.removeFromVariants(variant) context.saveContext() - - DispatchQueue.main.async { - let viewContext = self.coreDataStorage.persistentContainer.viewContext - do { - try viewContext.save() - } catch { - debugPrint(error) - } - } + self.saveViewContext() } } catch { debugPrint(error) } } }Animal-Crossing-Wiki/Projects/App/Sources/Presentation/Catalog/ViewControllers/ItemDetailViewController.swift (1)
40-45: Avoid double fetch on first appearance.
viewDidLoadalready dispatches fetch actions;viewDidAppearwill fire immediately after and repeat them. Consider gating the first appearance or moving the refresh to subsequent appearances only.โป๏ธ One way to gate the first appearance
- private var reactor: ItemDetailReactor? + private var reactor: ItemDetailReactor? + private var didInitialFetch = false @@ override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - // ํ๋ฉด์ด ๋ค์ ๋ํ๋ ๋ ์์ง ์ํ ๊ฐฑ์ - reactor?.action.onNext(.fetch) - reactor?.action.onNext(.fetchCollectedVariants) + // ํ๋ฉด์ด ๋ค์ ๋ํ๋ ๋ ์์ง ์ํ ๊ฐฑ์ + if didInitialFetch { + reactor?.action.onNext(.fetch) + reactor?.action.onNext(.fetchCollectedVariants) + } else { + didInitialFetch = true + } }Also applies to: 73-83
...urces/CoreDataStorage/CoreDataStorage.xcdatamodeld/UserCollectionEntity.xcdatamodel/contents
Show resolved
Hide resolved
...l-Crossing-Wiki/Projects/App/Sources/Presentation/Catalog/ViewModels/ItemDetailReactor.swift
Outdated
Show resolved
Hide resolved
Animal-Crossing-Wiki/Projects/App/Sources/Presentation/Catalog/Views/ItemVariantsView.swift
Outdated
Show resolved
Hide resolved
Animal-Crossing-Wiki/Projects/App/Sources/Presentation/Catalog/Views/ItemVariantsView.swift
Show resolved
Hide resolved
- automaticallyMergesChangesFromParent๋ก ์๋ ๋ณํฉ๋๋ฏ๋ก ๋ช ์์ viewContext.save() ๋ถํ์ - CoreDataItemsStorage, CoreDataVariantsStorage์์ saveViewContext() ๋ฉ์๋ ์ ๊ฑฐ - SceneDelegate์์ ๋ถํ์ํ viewContext.save() ์ ๊ฑฐ (2์ด ๋๊ธฐ๋ง ์ ์ง) - VariantsStorage์์ removeFromVariants ํ context.delete() ์ถ๊ฐ (orphaned entities ๋ฐฉ์ง) - ItemVariantsView์์ .take(1) ์ ๊ฑฐ (disposeBag์ด prepareForReuse์์ ๋ฆฌ์ ๋๋ฏ๋ก ๋ถํ์)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
๐ค Fix all issues with AI agents
In
`@Animal-Crossing-Wiki/Projects/App/Sources/Presentation/Catalog/Views/ItemVariantsView.swift`:
- Around line 65-84: In setUpItems(by:), ensure the UI binding runs on the main
thread by inserting an observe(on: MainScheduler.instance) into the
collectedVariantIds observable chain before binding to collectionView.rx.items;
specifically, after the collectedVariantIds.map { _ in variations } call and
before .bind(to: collectionView.rx.items(...)), add the observe operator so the
subscription that creates VariantCell via collectionView.rx.items (and uses
cell.setUp and cell.checkboxObservable) receives events on the main scheduler.
In `@Animal-Crossing-Wiki/Projects/App/Sources/SceneDelegate.swift`:
- Around line 52-61: Remove the no-op background task and its 2-second delayed
end: delete the backgroundTaskID variable and the calls to
UIApplication.shared.beginBackgroundTask /
UIApplication.shared.endBackgroundTask as well as the
DispatchQueue.main.asyncAfter block; the app already uses background contexts
via performBackgroundTask with immediate saveContext() and the viewContext
merges changes, so no explicit backgroundTask management is needed.
๐งน Nitpick comments (2)
Animal-Crossing-Wiki/Projects/App/Sources/CoreDataStorage/VariantsStorage/CoreDataVariantsStorage.swift (2)
35-44: Avoid loading all variants for per-item fetches.
Filtering in-memory scales poorly as the collection grows. Prefer a predicate-based fetch for just the itemโs variants.โป๏ธ Proposed refactor
- let variantEntities = object.variants?.allObjects as? [VariantCollectionEntity] ?? [] - let variantIds = Set(variantEntities - .filter { $0.itemName == itemName } - .compactMap { $0.variantId }) + let request = VariantCollectionEntity.fetchRequest() + request.predicate = NSPredicate(format: "itemName == %@", itemName) + request.fetchBatchSize = 200 + let variantEntities = try context.fetch(request) + let variantIds = Set(variantEntities.compactMap { $0.variantId })
75-129: Surface write failures instead of debugPrint-only.
Add/remove/removeAll can fail silently; consider returning aCompletable(or similar) so callers can handle errors and keep UI state consistent.
Animal-Crossing-Wiki/Projects/App/Sources/Presentation/Catalog/Views/ItemVariantsView.swift
Show resolved
Hide resolved
- CoreData ์๋ ๋ง์ด๊ทธ๋ ์ด์ ์ค์ ์ถ๊ฐ (๊ธฐ์กด ์ฌ์ฉ์ ์ฑ ์ ๋ฐ์ดํธ ์ ํฌ๋์ ๋ฐฉ์ง) - NSMigratePersistentStoresAutomaticallyOption ํ์ฑํ - NSInferMappingModelAutomaticallyOption ํ์ฑํ - ItemDetailReactor.fetchCollectedVariants race condition ์์ - Items.shared.variantList ๋์ variantsStorage.fetchByItem() ์ง์ ํธ์ถ - ๋น๋๊ธฐ ์ด๊ธฐํ ์๋ฃ ์ ๋น ๊ฐ ๋ฐํ ๋ฌธ์ ํด๊ฒฐ - ItemVariantsView UI ๋ฐ์ธ๋ฉ์ MainScheduler ๋ช ์ - .observe(on: MainScheduler.instance) ์ถ๊ฐ๋ก ๋ฉ์ธ ์ค๋ ๋ ๋ณด์ฅ
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
๐ค Fix all issues with AI agents
In
`@Animal-Crossing-Wiki/Projects/App/Sources/Presentation/Catalog/ViewModels/ItemDetailReactor.swift`:
- Around line 112-137: The toggleVariantCollection handler currently calls
variantsStorage.add/remove (and related Items.shared.updateVariant /
storage.update / Items.shared.updateItem) which each performBackgroundTask on
separate private contexts and can complete out of order; change these calls so
writes are serialized: either update variantsStorage to expose a single shared
background context or serial DispatchQueue and perform the add/remove on that
single queue/context, or change variantsStorage.add/remove to return a
Completable/Single and chain those futures so toggleVariantCollection waits for
the prior write before issuing the next; ensure the same serialization is
applied to Items.shared.updateVariant and storage.update calls so the final
toggle action wins and mutations return only after the serialized write
completes.
| case .toggleVariantCollection(let variant): | ||
| HapticManager.shared.impact(style: .medium) | ||
| let isCurrentlyCollected = currentState.collectedVariantIds.contains(variant.variantId) | ||
| let newState = !isCurrentlyCollected | ||
|
|
||
| if newState { | ||
| variantsStorage.add(variant.variantId, itemName: currentState.item.name) | ||
| Items.shared.updateVariant(variant.variantId, itemName: currentState.item.name, isAdding: true) | ||
|
|
||
| // ์์ดํ ์ด ์์ง๋์ง ์์๋ค๋ฉด ํจ๊ป ์์ง ์ฒ๋ฆฌ | ||
| if !currentState.isAcquired { | ||
| Items.shared.updateItem(currentState.item) | ||
| storage.update(currentState.item) | ||
| } | ||
| } else { | ||
| variantsStorage.remove(variant.variantId) | ||
| Items.shared.updateVariant(variant.variantId, itemName: currentState.item.name, isAdding: false) | ||
| } | ||
|
|
||
| // Variant ์ฒดํฌ ์ ์์ดํ ๋ ํจ๊ป ์ฒดํฌ๋ ๊ฒฝ์ฐ ๋ mutation ๋ฐํ | ||
| var mutations: [Mutation] = [.updateVariantCollection(variantId: variant.variantId, isCollected: newState)] | ||
| if newState && !currentState.isAcquired { | ||
| mutations.append(.updateAcquired) | ||
| } | ||
|
|
||
| return .from(mutations) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
๐งฉ Analysis chain
๐ Script executed:
find . -name "CoreDataVariantsStorage.swift" -type fRepository: leeari95/ACNH-wiki
Length of output: 167
๐ Script executed:
fd "VariantsStorage" --type fRepository: leeari95/ACNH-wiki
Length of output: 261
๐ Web query:
NSPersistentContainer performBackgroundTask execution order guarantee concurrent
๐ก Result:
NSPersistentContainer.performBackgroundTask(_:) does not guarantee execution order across multiple calls, and blocks may run concurrently.
- Each call creates a new
NSManagedObjectContextconfigured with.privateQueueConcurrencyType(i.e., its own private queue). Because these are separate contexts/queues, twoperformBackgroundTaskblocks can overlap in time and complete in any order. Apple documents the concurrency types and thatperformBackgroundTaskuses private-queue contexts, but does not specify any FIFO/serial ordering guarantee across tasks. [1] - In contrast, ordering is guaranteed per context: multiple
context.perform { ... }calls on the sameNSManagedObjectContextare queued and executed serially (FIFO) on that contextโs private queue. [1][2]
If you need strict ordering / no concurrent writers:
- Create one background context (
container.newBackgroundContext()), and submit work viabgContext.perform { ... }(orperformAndWait). This serializes work on that single contextโs queue. [1][2] - Or, keep using
performBackgroundTask, but add your own serialDispatchQueue/OperationQueueto enqueue tasks and only start the next when the previous one finishes (often via completion orperformBackgroundTask+DispatchGroup). [2]
Implication: if you fire many performBackgroundTask writes, you should assume concurrent saves are possible, and handle conflicts (merge policy, re-fetch by objectID, etc.) rather than relying on an internal serial queue. [2]
Sources:
[1] Apple Core Data Concurrency Programming Guide (archived): private-queue contexts, and use of performBlock/performBlockAndWait; NSPersistentContainer background contexts are .privateQueueConcurrencyType. (developer.apple.com)
[2] Discussion/confirmation that performBackgroundTask calls can overlap, while perform on the same context is serialized. (stackoverflow.com)
Citations:
- 1: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CoreData/Concurrency.html?utm_source=openai
- 2: https://stackoverflow.com/questions/53633366/coredata-whats-the-difference-between-performbackgroundtask-and-newbackgroundc?utm_source=openai
๐ Script executed:
cat -n "Animal-Crossing-Wiki/Projects/App/Sources/CoreDataStorage/VariantsStorage/CoreDataVariantsStorage.swift" | sed -n '74,111p'Repository: leeari95/ACNH-wiki
Length of output: 1723
๐ Script executed:
cat -n "Animal-Crossing-Wiki/Projects/App/Sources/CoreDataStorage/VariantsStorage/CoreDataVariantsStorage.swift" | head -120Repository: leeari95/ACNH-wiki
Length of output: 5507
๐ Script executed:
cat -n "Animal-Crossing-Wiki/Projects/App/Sources/Presentation/Catalog/ViewModels/ItemDetailReactor.swift" | sed -n '112,137p'Repository: leeari95/ACNH-wiki
Length of output: 1492
๐ Script executed:
grep -A 5 -B 5 "variantsStorage.add\|variantsStorage.remove" "Animal-Crossing-Wiki/Projects/App/Sources/Presentation/Catalog/ViewModels/ItemDetailReactor.swift"Repository: leeari95/ACNH-wiki
Length of output: 1621
Serialize variant write operations to avoid out-of-order persistence.
variantsStorage.add/remove use performBackgroundTask without ordering guaranteesโeach call creates its own private-queue context and can complete in any order. Rapid toggles (collect โ uncollect โ recollect) may persist the wrong final state. Serialize writes using a single background context with context.perform { ... } or return Completable/Single to chain operations so the last action wins.
๐ค Prompt for AI Agents
In
`@Animal-Crossing-Wiki/Projects/App/Sources/Presentation/Catalog/ViewModels/ItemDetailReactor.swift`
around lines 112 - 137, The toggleVariantCollection handler currently calls
variantsStorage.add/remove (and related Items.shared.updateVariant /
storage.update / Items.shared.updateItem) which each performBackgroundTask on
separate private contexts and can complete out of order; change these calls so
writes are serialized: either update variantsStorage to expose a single shared
background context or serial DispatchQueue and perform the add/remove on that
single queue/context, or change variantsStorage.add/remove to return a
Completable/Single and chain those futures so toggleVariantCollection waits for
the prior write before issuing the next; ensure the same serialization is
applied to Items.shared.updateVariant and storage.update calls so the final
toggle action wins and mutations return only after the serialized write
completes.
- topAnchor constant๋ฅผ -40์์ 0์ผ๋ก ๋ณ๊ฒฝํ์ฌ ๋ ์ด์์ ๊ฐ์
- APIProvider์ RxSwift Single ๋ฐํ ๋ฉ์๋ ์ถ๊ฐ - DefaultAPIProvider์์ Alamofire๋ฅผ RxSwift๋ก ๋ํ - Items.swift์์ retry(when:) ์คํผ๋ ์ดํฐ ์ฌ์ฉ - ์ง์ ๋ฐฑ์คํ ์ ๋ต ์ ์ฉ (2์ด, 4์ด, 6์ด ๊ฐ๊ฒฉ, ์ต๋ 3ํ) - ์ ์ธ์ ์ฝ๋๋ก ๊ฐ์ (์๋ ์ฌ๊ท ํธ์ถ ์ ๊ฑฐ)
๐ ์์ ์ ํ
๐ ์ฒดํฌ๋ฆฌ์คํธ
Summary by CodeRabbit
New Features
Localization
Chores
Stability
โ๏ธ Tip: You can customize this high-level summary in your review settings.