diff --git a/GdeiAssistant-iOS/Features/Auth/Repositories/MockAuthRepository.swift b/GdeiAssistant-iOS/Features/Auth/Repositories/MockAuthRepository.swift index cd482f6..9e159fc 100644 --- a/GdeiAssistant-iOS/Features/Auth/Repositories/MockAuthRepository.swift +++ b/GdeiAssistant-iOS/Features/Auth/Repositories/MockAuthRepository.swift @@ -2,6 +2,10 @@ import Foundation @MainActor final class MockAuthRepository: AuthRepository { + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + private let validUsername = "gdeiassistant" private let validPassword = "gdeiassistant" diff --git a/GdeiAssistant-iOS/Features/Auth/ViewModels/LoginViewModel.swift b/GdeiAssistant-iOS/Features/Auth/ViewModels/LoginViewModel.swift index 964d3b0..e3633db 100644 --- a/GdeiAssistant-iOS/Features/Auth/ViewModels/LoginViewModel.swift +++ b/GdeiAssistant-iOS/Features/Auth/ViewModels/LoginViewModel.swift @@ -16,6 +16,10 @@ final class LoginViewModel: ObservableObject { self.authManager = authManager } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + var currentDataSourceMode: DataSourceMode { authManager.currentDataSourceMode } diff --git a/GdeiAssistant-iOS/Features/CET/Repositories/MockCETRepository.swift b/GdeiAssistant-iOS/Features/CET/Repositories/MockCETRepository.swift index 9d8e302..60edea5 100644 --- a/GdeiAssistant-iOS/Features/CET/Repositories/MockCETRepository.swift +++ b/GdeiAssistant-iOS/Features/CET/Repositories/MockCETRepository.swift @@ -2,6 +2,10 @@ import Foundation @MainActor final class MockCETRepository: CETRepository { + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func fetchCaptchaImageBase64() async throws -> String { try await Task.sleep(nanoseconds: 120_000_000) return MockSeedData.cetCaptchaBase64 diff --git a/GdeiAssistant-iOS/Features/CET/ViewModels/CETViewModel.swift b/GdeiAssistant-iOS/Features/CET/ViewModels/CETViewModel.swift index d0ea706..5849873 100644 --- a/GdeiAssistant-iOS/Features/CET/ViewModels/CETViewModel.swift +++ b/GdeiAssistant-iOS/Features/CET/ViewModels/CETViewModel.swift @@ -19,6 +19,10 @@ final class CETViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func loadIfNeeded() async { guard dashboard == nil else { return } await refreshCaptcha() diff --git a/GdeiAssistant-iOS/Features/Card/Repositories/MockCardRepository.swift b/GdeiAssistant-iOS/Features/Card/Repositories/MockCardRepository.swift index d80ea02..cbfec2e 100644 --- a/GdeiAssistant-iOS/Features/Card/Repositories/MockCardRepository.swift +++ b/GdeiAssistant-iOS/Features/Card/Repositories/MockCardRepository.swift @@ -2,6 +2,10 @@ import Foundation @MainActor final class MockCardRepository: CardRepository { + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + private var isReportedLoss = false func fetchDashboard(on date: Date) async throws -> CampusCardDashboard { diff --git a/GdeiAssistant-iOS/Features/Card/ViewModels/CardViewModel.swift b/GdeiAssistant-iOS/Features/Card/ViewModels/CardViewModel.swift index d8e8637..9e6e540 100644 --- a/GdeiAssistant-iOS/Features/Card/ViewModels/CardViewModel.swift +++ b/GdeiAssistant-iOS/Features/Card/ViewModels/CardViewModel.swift @@ -15,6 +15,10 @@ final class CardViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func loadIfNeeded() async { guard dashboard == nil else { return } await loadDashboard() diff --git a/GdeiAssistant-iOS/Features/Charge/Repositories/MockChargeRepository.swift b/GdeiAssistant-iOS/Features/Charge/Repositories/MockChargeRepository.swift index 9b2a11d..3fbce7b 100644 --- a/GdeiAssistant-iOS/Features/Charge/Repositories/MockChargeRepository.swift +++ b/GdeiAssistant-iOS/Features/Charge/Repositories/MockChargeRepository.swift @@ -2,6 +2,10 @@ import Foundation @MainActor final class MockChargeRepository: ChargeRepository { + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func fetchCardInfo() async throws -> CampusCardDashboard { try await Task.sleep(for: .milliseconds(250)) return CampusCardDashboard( diff --git a/GdeiAssistant-iOS/Features/Charge/ViewModels/ChargeViewModel.swift b/GdeiAssistant-iOS/Features/Charge/ViewModels/ChargeViewModel.swift index ce83040..b219bdc 100644 --- a/GdeiAssistant-iOS/Features/Charge/ViewModels/ChargeViewModel.swift +++ b/GdeiAssistant-iOS/Features/Charge/ViewModels/ChargeViewModel.swift @@ -36,6 +36,10 @@ final class ChargeViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func loadIfNeeded() { guard !hasLoaded else { return } hasLoaded = true diff --git a/GdeiAssistant-iOS/Features/Collection/Repositories/MockCollectionRepository.swift b/GdeiAssistant-iOS/Features/Collection/Repositories/MockCollectionRepository.swift index 124d22e..a3c7385 100644 --- a/GdeiAssistant-iOS/Features/Collection/Repositories/MockCollectionRepository.swift +++ b/GdeiAssistant-iOS/Features/Collection/Repositories/MockCollectionRepository.swift @@ -2,6 +2,10 @@ import Foundation @MainActor final class MockCollectionRepository: CollectionRepository { + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + private var borrowedBooks: [CollectionBorrowItem] = [ CollectionBorrowItem(id: "c_borrow_1", sn: "sn_1001", code: "code_1001", title: "iOS 架构设计实践", author: "王磊", borrowDate: "2026-03-01", returnDate: "2026-03-22", renewCount: 0), CollectionBorrowItem(id: "c_borrow_2", sn: "sn_1002", code: "code_1002", title: "数据库系统概论", author: "陈小华", borrowDate: "2026-02-24", returnDate: "2026-03-17", renewCount: 1) diff --git a/GdeiAssistant-iOS/Features/Collection/ViewModels/CollectionViewModel.swift b/GdeiAssistant-iOS/Features/Collection/ViewModels/CollectionViewModel.swift index 1cd31b8..717d10c 100644 --- a/GdeiAssistant-iOS/Features/Collection/ViewModels/CollectionViewModel.swift +++ b/GdeiAssistant-iOS/Features/Collection/ViewModels/CollectionViewModel.swift @@ -22,6 +22,10 @@ final class CollectionViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func search() async { let trimmed = FormValidationSupport.trimmed(keyword) guard !trimmed.isEmpty else { diff --git a/GdeiAssistant-iOS/Features/Community/Repositories/MockCommunityRepository.swift b/GdeiAssistant-iOS/Features/Community/Repositories/MockCommunityRepository.swift index 464382e..1772b50 100644 --- a/GdeiAssistant-iOS/Features/Community/Repositories/MockCommunityRepository.swift +++ b/GdeiAssistant-iOS/Features/Community/Repositories/MockCommunityRepository.swift @@ -2,6 +2,10 @@ import Foundation @MainActor final class MockCommunityRepository: CommunityRepository { + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + private var likeCountOverrides: [String: Int] = [:] private var commentCountOverrides: [String: Int] = [:] private var addedComments: [String: [CommunityComment]] = [:] diff --git a/GdeiAssistant-iOS/Features/Community/ViewModels/CommunityFeedViewModel.swift b/GdeiAssistant-iOS/Features/Community/ViewModels/CommunityFeedViewModel.swift index 3bfa350..0330eea 100644 --- a/GdeiAssistant-iOS/Features/Community/ViewModels/CommunityFeedViewModel.swift +++ b/GdeiAssistant-iOS/Features/Community/ViewModels/CommunityFeedViewModel.swift @@ -15,6 +15,10 @@ final class CommunityFeedViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func loadIfNeeded() async { guard !hasLoaded else { return } await loadPosts() diff --git a/GdeiAssistant-iOS/Features/Community/ViewModels/PostDetailViewModel.swift b/GdeiAssistant-iOS/Features/Community/ViewModels/PostDetailViewModel.swift index 8fc4026..d9a270d 100644 --- a/GdeiAssistant-iOS/Features/Community/ViewModels/PostDetailViewModel.swift +++ b/GdeiAssistant-iOS/Features/Community/ViewModels/PostDetailViewModel.swift @@ -19,6 +19,10 @@ final class PostDetailViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func loadIfNeeded() async { guard detail == nil else { return } await loadDetail() diff --git a/GdeiAssistant-iOS/Features/Community/ViewModels/TopicFeedViewModel.swift b/GdeiAssistant-iOS/Features/Community/ViewModels/TopicFeedViewModel.swift index 344f9f4..fa646f0 100644 --- a/GdeiAssistant-iOS/Features/Community/ViewModels/TopicFeedViewModel.swift +++ b/GdeiAssistant-iOS/Features/Community/ViewModels/TopicFeedViewModel.swift @@ -18,6 +18,10 @@ final class TopicFeedViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func loadIfNeeded() async { guard topic == nil && posts.isEmpty else { return } await load() diff --git a/GdeiAssistant-iOS/Features/DataCenter/Repositories/MockDataCenterRepository.swift b/GdeiAssistant-iOS/Features/DataCenter/Repositories/MockDataCenterRepository.swift index 015d686..f61d92d 100644 --- a/GdeiAssistant-iOS/Features/DataCenter/Repositories/MockDataCenterRepository.swift +++ b/GdeiAssistant-iOS/Features/DataCenter/Repositories/MockDataCenterRepository.swift @@ -2,6 +2,10 @@ import Foundation @MainActor final class MockDataCenterRepository: DataCenterRepository { + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func queryElectricity(_ query: ElectricityQuery) async throws -> ElectricityBill { try await Task.sleep(nanoseconds: 180_000_000) guard FormValidationSupport.hasText(query.name), FormValidationSupport.hasText(query.studentNumber) else { diff --git a/GdeiAssistant-iOS/Features/DataCenter/ViewModels/DataCenterViewModel.swift b/GdeiAssistant-iOS/Features/DataCenter/ViewModels/DataCenterViewModel.swift index 8a7765d..b89bb5b 100644 --- a/GdeiAssistant-iOS/Features/DataCenter/ViewModels/DataCenterViewModel.swift +++ b/GdeiAssistant-iOS/Features/DataCenter/ViewModels/DataCenterViewModel.swift @@ -14,6 +14,10 @@ final class ElectricityFeesViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + var availableYears: [Int] { ElectricityQuery.yearOptions } @@ -53,6 +57,10 @@ final class YellowPageViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func loadIfNeeded() async { guard categories.isEmpty else { return } await refresh() diff --git a/GdeiAssistant-iOS/Features/Dating/Repositories/MockDatingRepository.swift b/GdeiAssistant-iOS/Features/Dating/Repositories/MockDatingRepository.swift index dbbeac7..ec55912 100644 --- a/GdeiAssistant-iOS/Features/Dating/Repositories/MockDatingRepository.swift +++ b/GdeiAssistant-iOS/Features/Dating/Repositories/MockDatingRepository.swift @@ -14,6 +14,10 @@ final class MockDatingRepository: DatingRepository { myProfiles = Self.makeMyProfiles() } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func fetchProfiles(filter: DatingFilter) async throws -> [DatingProfile] { hallProfiles.filter { $0.area == filter.area } } diff --git a/GdeiAssistant-iOS/Features/Dating/ViewModels/DatingViewModel.swift b/GdeiAssistant-iOS/Features/Dating/ViewModels/DatingViewModel.swift index 94530d1..f33a692 100644 --- a/GdeiAssistant-iOS/Features/Dating/ViewModels/DatingViewModel.swift +++ b/GdeiAssistant-iOS/Features/Dating/ViewModels/DatingViewModel.swift @@ -14,6 +14,10 @@ final class DatingHallViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func loadIfNeeded() async { guard profiles.isEmpty else { return } await refresh() @@ -66,6 +70,10 @@ final class DatingCenterViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func loadData() async { isLoading = true errorMessage = nil @@ -126,6 +134,10 @@ final class PublishDatingViewModel: ObservableObject { self.profileRepository = profileRepository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + var isFormValid: Bool { !nickname.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && selectedFaculty != ProfileFormSupport.unselectedOption && diff --git a/GdeiAssistant-iOS/Features/Delivery/Repositories/MockDeliveryRepository.swift b/GdeiAssistant-iOS/Features/Delivery/Repositories/MockDeliveryRepository.swift index 863cea9..4fff580 100644 --- a/GdeiAssistant-iOS/Features/Delivery/Repositories/MockDeliveryRepository.swift +++ b/GdeiAssistant-iOS/Features/Delivery/Repositories/MockDeliveryRepository.swift @@ -2,6 +2,10 @@ import Foundation @MainActor final class MockDeliveryRepository: DeliveryRepository { + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + private var orders: [DeliveryOrder] = [ DeliveryOrder(orderID: "delivery_001", displayName: "gdeiassistant", name: mockLocalizedText(simplifiedChinese: "代收", traditionalChinese: "代收", english: "Parcel pickup", japanese: "代理受取", korean: "대리수령"), pickupCode: "51233444567", contactPhone: "13800138000", price: 4.50, company: mockLocalizedText(simplifiedChinese: "菜鸟驿站", traditionalChinese: "菜鳥驛站", english: "Cainiao Station", japanese: "菜鳥ステーション", korean: "차이냐오 스테이션"), address: mockLocalizedText(simplifiedChinese: "南苑 3 栋 508", traditionalChinese: "南苑 3 棟 508", english: "South Court Bldg 3 Room 508", japanese: "南苑3棟508", korean: "남원 3동 508호"), state: .pending, remarks: mockLocalizedText(simplifiedChinese: "课后再送", traditionalChinese: "課後再送", english: "Deliver after class", japanese: "授業後にお願いします", korean: "수업 후 전달 부탁"), orderTime: mockLocalizedText(simplifiedChinese: "10分钟前", traditionalChinese: "10分鐘前", english: "10 min ago", japanese: "10分前", korean: "10분 전")), DeliveryOrder(orderID: "delivery_002", displayName: "20230018", name: mockLocalizedText(simplifiedChinese: "代收", traditionalChinese: "代收", english: "Parcel pickup", japanese: "代理受取", korean: "대리수령"), pickupCode: "40399881234", contactPhone: "13911112222", price: 6.00, company: mockLocalizedText(simplifiedChinese: "东门快递点", traditionalChinese: "東門快遞點", english: "East Gate Parcel Point", japanese: "東門宅配受取所", korean: "동문 택배 보관소"), address: mockLocalizedText(simplifiedChinese: "北苑 6 栋 203", traditionalChinese: "北苑 6 棟 203", english: "North Court Bldg 6 Room 203", japanese: "北苑6棟203", korean: "북원 6동 203호"), state: .delivering, remarks: mockLocalizedText(simplifiedChinese: "到宿舍楼下联系", traditionalChinese: "到宿舍樓下聯繫", english: "Message me when you arrive downstairs", japanese: "寮の下に着いたら連絡してください", korean: "기숙사 아래 도착하면 연락 주세요"), orderTime: mockLocalizedText(simplifiedChinese: "40分钟前", traditionalChinese: "40分鐘前", english: "40 min ago", japanese: "40分前", korean: "40분 전")) diff --git a/GdeiAssistant-iOS/Features/Delivery/ViewModels/DeliveryViewModel.swift b/GdeiAssistant-iOS/Features/Delivery/ViewModels/DeliveryViewModel.swift index c9ed89c..a1e644c 100644 --- a/GdeiAssistant-iOS/Features/Delivery/ViewModels/DeliveryViewModel.swift +++ b/GdeiAssistant-iOS/Features/Delivery/ViewModels/DeliveryViewModel.swift @@ -16,6 +16,10 @@ final class DeliveryViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func loadIfNeeded() async { guard orders.isEmpty else { return } await refresh() @@ -78,6 +82,10 @@ final class PublishDeliveryViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + var isFormValid: Bool { FormValidationSupport.hasText(pickupPlace) && FormValidationSupport.hasText(phone) && diff --git a/GdeiAssistant-iOS/Features/Evaluate/Repositories/MockEvaluateRepository.swift b/GdeiAssistant-iOS/Features/Evaluate/Repositories/MockEvaluateRepository.swift index 02fec49..97cc2c7 100644 --- a/GdeiAssistant-iOS/Features/Evaluate/Repositories/MockEvaluateRepository.swift +++ b/GdeiAssistant-iOS/Features/Evaluate/Repositories/MockEvaluateRepository.swift @@ -2,6 +2,10 @@ import Foundation @MainActor final class MockEvaluateRepository: EvaluateRepository { + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func submit(_ submission: EvaluateSubmission) async throws { try await Task.sleep(nanoseconds: 300_000_000) _ = submission diff --git a/GdeiAssistant-iOS/Features/Evaluate/ViewModels/EvaluateViewModel.swift b/GdeiAssistant-iOS/Features/Evaluate/ViewModels/EvaluateViewModel.swift index 0eff334..86e7005 100644 --- a/GdeiAssistant-iOS/Features/Evaluate/ViewModels/EvaluateViewModel.swift +++ b/GdeiAssistant-iOS/Features/Evaluate/ViewModels/EvaluateViewModel.swift @@ -13,6 +13,10 @@ final class EvaluateViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func requestSubmit() { showConfirm = true } diff --git a/GdeiAssistant-iOS/Features/Express/Repositories/MockExpressRepository.swift b/GdeiAssistant-iOS/Features/Express/Repositories/MockExpressRepository.swift index 2f242ff..534b5f8 100644 --- a/GdeiAssistant-iOS/Features/Express/Repositories/MockExpressRepository.swift +++ b/GdeiAssistant-iOS/Features/Express/Repositories/MockExpressRepository.swift @@ -2,6 +2,10 @@ import Foundation @MainActor final class MockExpressRepository: ExpressRepository { + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + private var posts: [ExpressPost] = [ ExpressPost( id: "express_001", diff --git a/GdeiAssistant-iOS/Features/Express/ViewModels/ExpressViewModel.swift b/GdeiAssistant-iOS/Features/Express/ViewModels/ExpressViewModel.swift index 7870cec..49d1145 100644 --- a/GdeiAssistant-iOS/Features/Express/ViewModels/ExpressViewModel.swift +++ b/GdeiAssistant-iOS/Features/Express/ViewModels/ExpressViewModel.swift @@ -14,6 +14,10 @@ final class ExpressViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func loadIfNeeded() async { guard posts.isEmpty else { return } await refresh() @@ -93,6 +97,10 @@ final class PublishExpressViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + var isFormValid: Bool { FormValidationSupport.hasText(nickname) && FormValidationSupport.hasText(targetName) && diff --git a/GdeiAssistant-iOS/Features/Grade/Repositories/MockGradeRepository.swift b/GdeiAssistant-iOS/Features/Grade/Repositories/MockGradeRepository.swift index 6b899ea..b4f775c 100644 --- a/GdeiAssistant-iOS/Features/Grade/Repositories/MockGradeRepository.swift +++ b/GdeiAssistant-iOS/Features/Grade/Repositories/MockGradeRepository.swift @@ -2,6 +2,10 @@ import Foundation @MainActor final class MockGradeRepository: GradeRepository { + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func fetchGrades(yearIndex: Int?) async throws -> GradeReport { try await Task.sleep(nanoseconds: 280_000_000) let index = yearIndex ?? 3 diff --git a/GdeiAssistant-iOS/Features/Grade/ViewModels/GradeViewModel.swift b/GdeiAssistant-iOS/Features/Grade/ViewModels/GradeViewModel.swift index aadbe45..30ce060 100644 --- a/GdeiAssistant-iOS/Features/Grade/ViewModels/GradeViewModel.swift +++ b/GdeiAssistant-iOS/Features/Grade/ViewModels/GradeViewModel.swift @@ -21,6 +21,10 @@ final class GradeViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func loadIfNeeded() async { if report == nil { await loadGrades(yearIndex: Int(selectedYear)) diff --git a/GdeiAssistant-iOS/Features/GraduateExam/Repositories/MockGraduateExamRepository.swift b/GdeiAssistant-iOS/Features/GraduateExam/Repositories/MockGraduateExamRepository.swift index dd3b55e..391e453 100644 --- a/GdeiAssistant-iOS/Features/GraduateExam/Repositories/MockGraduateExamRepository.swift +++ b/GdeiAssistant-iOS/Features/GraduateExam/Repositories/MockGraduateExamRepository.swift @@ -2,6 +2,10 @@ import Foundation @MainActor final class MockGraduateExamRepository: GraduateExamRepository { + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func queryScore(_ query: GraduateExamQuery) async throws -> GraduateExamScore { try await Task.sleep(nanoseconds: 180_000_000) guard FormValidationSupport.hasText(query.name), FormValidationSupport.hasText(query.examNumber), FormValidationSupport.hasText(query.idNumber) else { diff --git a/GdeiAssistant-iOS/Features/GraduateExam/ViewModels/GraduateExamViewModel.swift b/GdeiAssistant-iOS/Features/GraduateExam/ViewModels/GraduateExamViewModel.swift index 1011581..98b386d 100644 --- a/GdeiAssistant-iOS/Features/GraduateExam/ViewModels/GraduateExamViewModel.swift +++ b/GdeiAssistant-iOS/Features/GraduateExam/ViewModels/GraduateExamViewModel.swift @@ -14,6 +14,10 @@ final class GraduateExamViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func submit() async { guard FormValidationSupport.hasText(query.name), FormValidationSupport.hasText(query.examNumber), FormValidationSupport.hasText(query.idNumber) else { errorMessage = localizedString("graduateExam.formIncomplete") diff --git a/GdeiAssistant-iOS/Features/Home/ViewModels/HomeViewModel.swift b/GdeiAssistant-iOS/Features/Home/ViewModels/HomeViewModel.swift index ae77f58..d87425d 100644 --- a/GdeiAssistant-iOS/Features/Home/ViewModels/HomeViewModel.swift +++ b/GdeiAssistant-iOS/Features/Home/ViewModels/HomeViewModel.swift @@ -17,6 +17,10 @@ final class HomeViewModel: ObservableObject { self.now = now } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func loadIfNeeded() async { if loadedScheduleDay != Self.campusCalendar.startOfDay(for: now()) { await loadTodaySchedule() diff --git a/GdeiAssistant-iOS/Features/Library/Repositories/MockLibraryRepository.swift b/GdeiAssistant-iOS/Features/Library/Repositories/MockLibraryRepository.swift index d0f5ae8..52c6193 100644 --- a/GdeiAssistant-iOS/Features/Library/Repositories/MockLibraryRepository.swift +++ b/GdeiAssistant-iOS/Features/Library/Repositories/MockLibraryRepository.swift @@ -2,6 +2,10 @@ import Foundation @MainActor final class MockLibraryRepository: LibraryRepository { + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + private let validPasswords: Set = ["library123", "123456"] private var renewedRecordIDs: Set = [] diff --git a/GdeiAssistant-iOS/Features/Library/ViewModels/LibraryViewModel.swift b/GdeiAssistant-iOS/Features/Library/ViewModels/LibraryViewModel.swift index 4cdb1fe..e49d7e5 100644 --- a/GdeiAssistant-iOS/Features/Library/ViewModels/LibraryViewModel.swift +++ b/GdeiAssistant-iOS/Features/Library/ViewModels/LibraryViewModel.swift @@ -21,6 +21,10 @@ final class LibraryViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func loadIfNeeded() async { if books.isEmpty { await searchBooks() diff --git a/GdeiAssistant-iOS/Features/LostFound/Repositories/MockLostFoundRepository.swift b/GdeiAssistant-iOS/Features/LostFound/Repositories/MockLostFoundRepository.swift index 233b5e1..507dca4 100644 --- a/GdeiAssistant-iOS/Features/LostFound/Repositories/MockLostFoundRepository.swift +++ b/GdeiAssistant-iOS/Features/LostFound/Repositories/MockLostFoundRepository.swift @@ -9,6 +9,10 @@ final class MockLostFoundRepository: LostFoundRepository { seedPersonalItemsIfNeeded() } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func fetchItems() async throws -> [LostFoundItem] { try await Task.sleep(nanoseconds: 200_000_000) return items diff --git a/GdeiAssistant-iOS/Features/LostFound/ViewModels/LostFoundViewModel.swift b/GdeiAssistant-iOS/Features/LostFound/ViewModels/LostFoundViewModel.swift index 324eeef..a60a7df 100644 --- a/GdeiAssistant-iOS/Features/LostFound/ViewModels/LostFoundViewModel.swift +++ b/GdeiAssistant-iOS/Features/LostFound/ViewModels/LostFoundViewModel.swift @@ -13,6 +13,10 @@ final class LostFoundViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func loadIfNeeded() async { guard items.isEmpty else { return } await refresh() diff --git a/GdeiAssistant-iOS/Features/LostFound/ViewModels/PublishLostFoundViewModel.swift b/GdeiAssistant-iOS/Features/LostFound/ViewModels/PublishLostFoundViewModel.swift index ca6556d..42919a0 100644 --- a/GdeiAssistant-iOS/Features/LostFound/ViewModels/PublishLostFoundViewModel.swift +++ b/GdeiAssistant-iOS/Features/LostFound/ViewModels/PublishLostFoundViewModel.swift @@ -3,6 +3,10 @@ import Combine @MainActor final class PublishLostFoundViewModel: ObservableObject { + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + @Published var title = "" @Published var selectedType: LostFoundType = .lost @Published var selectedItemTypeID = 0 @@ -150,6 +154,10 @@ final class EditLostFoundViewModel: ObservableObject { phone = Self.extractContactValue(from: detail.contactHint, prefix: localizedString("lostFound.mapper.contactPhone")) ?? "" } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + var itemTypeOptions: [String] { LostFoundRemoteMapper.itemTypes } diff --git a/GdeiAssistant-iOS/Features/Marketplace/Repositories/MockMarketplaceRepository.swift b/GdeiAssistant-iOS/Features/Marketplace/Repositories/MockMarketplaceRepository.swift index f910ebb..97fff7b 100644 --- a/GdeiAssistant-iOS/Features/Marketplace/Repositories/MockMarketplaceRepository.swift +++ b/GdeiAssistant-iOS/Features/Marketplace/Repositories/MockMarketplaceRepository.swift @@ -9,6 +9,10 @@ final class MockMarketplaceRepository: MarketplaceRepository { seedPersonalItemsIfNeeded() } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func fetchItems(typeID: Int?) async throws -> [MarketplaceItem] { try await Task.sleep(nanoseconds: 220_000_000) guard let typeID else { diff --git a/GdeiAssistant-iOS/Features/Marketplace/ViewModels/MarketplaceViewModel.swift b/GdeiAssistant-iOS/Features/Marketplace/ViewModels/MarketplaceViewModel.swift index 10724eb..52cd342 100644 --- a/GdeiAssistant-iOS/Features/Marketplace/ViewModels/MarketplaceViewModel.swift +++ b/GdeiAssistant-iOS/Features/Marketplace/ViewModels/MarketplaceViewModel.swift @@ -15,6 +15,10 @@ final class MarketplaceViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + var typeOptions: [(id: Int, title: String)] { MarketplaceRemoteMapper.itemTypes.enumerated().map { (id: $0.offset, title: $0.element) } } diff --git a/GdeiAssistant-iOS/Features/Marketplace/ViewModels/PublishMarketplaceViewModel.swift b/GdeiAssistant-iOS/Features/Marketplace/ViewModels/PublishMarketplaceViewModel.swift index 708625f..0ac6add 100644 --- a/GdeiAssistant-iOS/Features/Marketplace/ViewModels/PublishMarketplaceViewModel.swift +++ b/GdeiAssistant-iOS/Features/Marketplace/ViewModels/PublishMarketplaceViewModel.swift @@ -3,6 +3,10 @@ import Combine @MainActor final class PublishMarketplaceViewModel: ObservableObject { + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + @Published var title = "" @Published var priceText = "" @Published var descriptionText = "" @@ -154,6 +158,10 @@ final class EditMarketplaceViewModel: ObservableObject { ) ?? "" } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + var typeOptions: [String] { MarketplaceRemoteMapper.itemTypes } diff --git a/GdeiAssistant-iOS/Features/Messages/Repositories/MockMessagesRepository.swift b/GdeiAssistant-iOS/Features/Messages/Repositories/MockMessagesRepository.swift index 783072f..a4d5519 100644 --- a/GdeiAssistant-iOS/Features/Messages/Repositories/MockMessagesRepository.swift +++ b/GdeiAssistant-iOS/Features/Messages/Repositories/MockMessagesRepository.swift @@ -2,6 +2,10 @@ import Foundation @MainActor final class MockMessagesRepository: MessagesRepository { + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + private var announcementItems = MockFactory.makeNotifications().filter { $0.destination == .announcement } private var interactionItems = MockFactory.makeNotifications().filter(\.isInteractionItem) private let announcementDetailsByID = MockFactory.makeAnnouncementDetailsByID() diff --git a/GdeiAssistant-iOS/Features/Messages/ViewModels/InteractionMessageListViewModel.swift b/GdeiAssistant-iOS/Features/Messages/ViewModels/InteractionMessageListViewModel.swift index 0a2e6bb..7b809d4 100644 --- a/GdeiAssistant-iOS/Features/Messages/ViewModels/InteractionMessageListViewModel.swift +++ b/GdeiAssistant-iOS/Features/Messages/ViewModels/InteractionMessageListViewModel.swift @@ -19,6 +19,10 @@ final class InteractionMessageListViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func loadIfNeeded() async { guard items.isEmpty else { return } await refresh() diff --git a/GdeiAssistant-iOS/Features/Messages/ViewModels/MessagesViewModel.swift b/GdeiAssistant-iOS/Features/Messages/ViewModels/MessagesViewModel.swift index 9b90bca..ae6d9b9 100644 --- a/GdeiAssistant-iOS/Features/Messages/ViewModels/MessagesViewModel.swift +++ b/GdeiAssistant-iOS/Features/Messages/ViewModels/MessagesViewModel.swift @@ -30,6 +30,10 @@ final class MessagesViewModel: ObservableObject { self.messagesRepository = messagesRepository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + var isInitialLoading: Bool { if hasAnyContent { return false diff --git a/GdeiAssistant-iOS/Features/Messages/ViewModels/SystemNoticeListViewModel.swift b/GdeiAssistant-iOS/Features/Messages/ViewModels/SystemNoticeListViewModel.swift index 7d8e359..db80a42 100644 --- a/GdeiAssistant-iOS/Features/Messages/ViewModels/SystemNoticeListViewModel.swift +++ b/GdeiAssistant-iOS/Features/Messages/ViewModels/SystemNoticeListViewModel.swift @@ -18,6 +18,10 @@ final class SystemNoticeListViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func loadIfNeeded() async { guard items.isEmpty else { return } await refresh() diff --git a/GdeiAssistant-iOS/Features/News/Repositories/MockNewsRepository.swift b/GdeiAssistant-iOS/Features/News/Repositories/MockNewsRepository.swift index 1aaf487..887affd 100644 --- a/GdeiAssistant-iOS/Features/News/Repositories/MockNewsRepository.swift +++ b/GdeiAssistant-iOS/Features/News/Repositories/MockNewsRepository.swift @@ -2,6 +2,10 @@ import Foundation @MainActor final class MockNewsRepository: NewsRepository { + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + private let items = [ NewsItem(id: "news_1", type: 1, title: "学校高质量发展大会暨 2026 年工作会议召开", publishDate: "2026-03-03", content: "学校召开高质量发展大会,系统总结上一阶段重点工作,并部署年度改革发展任务。", sourceURL: "https://www.gdei.edu.cn/1324/1001.htm"), NewsItem(id: "news_2", type: 2, title: "管理学院召开教职工思想教育会议", publishDate: "2026-03-19", content: "管理学院围绕师德师风建设与年度重点工作召开专题会议。", sourceURL: "https://www.gdei.edu.cn/1325/2001.htm"), diff --git a/GdeiAssistant-iOS/Features/News/ViewModels/NewsViewModel.swift b/GdeiAssistant-iOS/Features/News/ViewModels/NewsViewModel.swift index bd01fc2..dcdabf2 100644 --- a/GdeiAssistant-iOS/Features/News/ViewModels/NewsViewModel.swift +++ b/GdeiAssistant-iOS/Features/News/ViewModels/NewsViewModel.swift @@ -18,6 +18,10 @@ final class NewsViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func loadIfNeeded() async { guard items.isEmpty else { return } await refresh() diff --git a/GdeiAssistant-iOS/Features/Photograph/Repositories/MockPhotographRepository.swift b/GdeiAssistant-iOS/Features/Photograph/Repositories/MockPhotographRepository.swift index cd0a4c7..d5d6c59 100644 --- a/GdeiAssistant-iOS/Features/Photograph/Repositories/MockPhotographRepository.swift +++ b/GdeiAssistant-iOS/Features/Photograph/Repositories/MockPhotographRepository.swift @@ -2,6 +2,10 @@ import Foundation @MainActor final class MockPhotographRepository: PhotographRepository { + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + private var posts: [PhotographPost] = MockPhotographRepository.makeInitialPosts() private var comments: [String: [PhotographCommentItem]] = MockPhotographRepository.makeInitialComments() diff --git a/GdeiAssistant-iOS/Features/Photograph/ViewModels/PhotographViewModel.swift b/GdeiAssistant-iOS/Features/Photograph/ViewModels/PhotographViewModel.swift index fe48c95..c84281e 100644 --- a/GdeiAssistant-iOS/Features/Photograph/ViewModels/PhotographViewModel.swift +++ b/GdeiAssistant-iOS/Features/Photograph/ViewModels/PhotographViewModel.swift @@ -19,6 +19,10 @@ final class PhotographViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func loadIfNeeded() async { guard posts.isEmpty else { return } await refresh() @@ -113,6 +117,10 @@ final class PublishPhotographViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + var isFormValid: Bool { FormValidationSupport.hasText(title) && !images.isEmpty && images.count <= 4 } diff --git a/GdeiAssistant-iOS/Features/Profile/Repositories/MockAccountCenterRepository.swift b/GdeiAssistant-iOS/Features/Profile/Repositories/MockAccountCenterRepository.swift index 0c87812..8c64403 100644 --- a/GdeiAssistant-iOS/Features/Profile/Repositories/MockAccountCenterRepository.swift +++ b/GdeiAssistant-iOS/Features/Profile/Repositories/MockAccountCenterRepository.swift @@ -2,6 +2,10 @@ import Foundation @MainActor final class MockAccountCenterRepository: AccountCenterRepository { + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + private var privacySettings = PrivacySettings.default private var phoneStatus = ContactBindingStatus( isBound: true, diff --git a/GdeiAssistant-iOS/Features/Profile/ViewModels/CampusCredentialViewModel.swift b/GdeiAssistant-iOS/Features/Profile/ViewModels/CampusCredentialViewModel.swift index c7d1435..f29ba4e 100644 --- a/GdeiAssistant-iOS/Features/Profile/ViewModels/CampusCredentialViewModel.swift +++ b/GdeiAssistant-iOS/Features/Profile/ViewModels/CampusCredentialViewModel.swift @@ -15,6 +15,10 @@ final class CampusCredentialViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + var canRunAction: Bool { !isLoading && !isActionRunning } diff --git a/GdeiAssistant-iOS/Features/Profile/ViewModels/PrivacySettingsViewModel.swift b/GdeiAssistant-iOS/Features/Profile/ViewModels/PrivacySettingsViewModel.swift index 1fbe1d7..e63e3f6 100644 --- a/GdeiAssistant-iOS/Features/Profile/ViewModels/PrivacySettingsViewModel.swift +++ b/GdeiAssistant-iOS/Features/Profile/ViewModels/PrivacySettingsViewModel.swift @@ -15,6 +15,10 @@ final class PrivacySettingsViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func load() async { isLoading = true errorMessage = nil diff --git a/GdeiAssistant-iOS/Features/Profile/ViewModels/SecurityCenterViewModels.swift b/GdeiAssistant-iOS/Features/Profile/ViewModels/SecurityCenterViewModels.swift index 1bc65d6..7bac319 100644 --- a/GdeiAssistant-iOS/Features/Profile/ViewModels/SecurityCenterViewModels.swift +++ b/GdeiAssistant-iOS/Features/Profile/ViewModels/SecurityCenterViewModels.swift @@ -13,6 +13,10 @@ final class LoginRecordViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func load() async { isLoading = true errorMessage = nil diff --git a/GdeiAssistant-iOS/Features/Profile/ViewModels/SettingsViewModel.swift b/GdeiAssistant-iOS/Features/Profile/ViewModels/SettingsViewModel.swift index 7121d3a..22b481e 100644 --- a/GdeiAssistant-iOS/Features/Profile/ViewModels/SettingsViewModel.swift +++ b/GdeiAssistant-iOS/Features/Profile/ViewModels/SettingsViewModel.swift @@ -13,6 +13,10 @@ final class SettingsViewModel: ObservableObject { self.preferences = preferences } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + var isDebug: Bool { environment.isDebug } diff --git a/GdeiAssistant-iOS/Features/Profile/ViewModels/SupportCenterViewModels.swift b/GdeiAssistant-iOS/Features/Profile/ViewModels/SupportCenterViewModels.swift index b723248..1353337 100644 --- a/GdeiAssistant-iOS/Features/Profile/ViewModels/SupportCenterViewModels.swift +++ b/GdeiAssistant-iOS/Features/Profile/ViewModels/SupportCenterViewModels.swift @@ -23,6 +23,10 @@ final class FeedbackViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + var isFormValid: Bool { FormValidationSupport.hasText(content) } @@ -61,6 +65,10 @@ final class DownloadDataViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func load() async { isLoading = true errorMessage = nil @@ -109,6 +117,10 @@ final class AvatarEditViewModel: ObservableObject { self.sessionState = sessionState } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func load() async { isLoading = true defer { isLoading = false } @@ -173,6 +185,10 @@ final class DeleteAccountViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + var canSubmit: Bool { agreed && FormValidationSupport.hasText(password) } diff --git a/GdeiAssistant-iOS/Features/Schedule/Repositories/MockScheduleRepository.swift b/GdeiAssistant-iOS/Features/Schedule/Repositories/MockScheduleRepository.swift index 0f6d5b2..5f30912 100644 --- a/GdeiAssistant-iOS/Features/Schedule/Repositories/MockScheduleRepository.swift +++ b/GdeiAssistant-iOS/Features/Schedule/Repositories/MockScheduleRepository.swift @@ -2,6 +2,10 @@ import Foundation @MainActor final class MockScheduleRepository: ScheduleRepository { + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func fetchWeeklySchedule(weekIndex: Int) async throws -> WeeklySchedule { try await Task.sleep(nanoseconds: 300_000_000) return MockFactory.makeWeeklySchedule(weekIndex: weekIndex) diff --git a/GdeiAssistant-iOS/Features/Schedule/ViewModels/ScheduleViewModel.swift b/GdeiAssistant-iOS/Features/Schedule/ViewModels/ScheduleViewModel.swift index 5e086e8..cbefe2f 100644 --- a/GdeiAssistant-iOS/Features/Schedule/ViewModels/ScheduleViewModel.swift +++ b/GdeiAssistant-iOS/Features/Schedule/ViewModels/ScheduleViewModel.swift @@ -16,6 +16,10 @@ final class ScheduleViewModel: ObservableObject { self.selectedWeekIndex = initialWeekIndex } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + var todayCourses: [CourseItem] { guard let schedule else { return [] } let weekday = Calendar.current.component(.weekday, from: Date()) diff --git a/GdeiAssistant-iOS/Features/Secret/Repositories/MockSecretRepository.swift b/GdeiAssistant-iOS/Features/Secret/Repositories/MockSecretRepository.swift index 8100674..78eab8e 100644 --- a/GdeiAssistant-iOS/Features/Secret/Repositories/MockSecretRepository.swift +++ b/GdeiAssistant-iOS/Features/Secret/Repositories/MockSecretRepository.swift @@ -2,6 +2,10 @@ import Foundation @MainActor final class MockSecretRepository: SecretRepository { + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + private var posts = MockFactory.makeSecretPosts() private var detailsByID = MockFactory.makeSecretDetailsByID() diff --git a/GdeiAssistant-iOS/Features/Secret/ViewModels/PublishSecretViewModel.swift b/GdeiAssistant-iOS/Features/Secret/ViewModels/PublishSecretViewModel.swift index 73ad2df..7bad169 100644 --- a/GdeiAssistant-iOS/Features/Secret/ViewModels/PublishSecretViewModel.swift +++ b/GdeiAssistant-iOS/Features/Secret/ViewModels/PublishSecretViewModel.swift @@ -3,6 +3,10 @@ import Combine @MainActor final class PublishSecretViewModel: ObservableObject { + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + @Published var mode: SecretDraftMode = .text @Published var selectedThemeID = Int.random(in: 1...12) @Published var content = "" diff --git a/GdeiAssistant-iOS/Features/Secret/ViewModels/SecretViewModel.swift b/GdeiAssistant-iOS/Features/Secret/ViewModels/SecretViewModel.swift index 0686499..debe62a 100644 --- a/GdeiAssistant-iOS/Features/Secret/ViewModels/SecretViewModel.swift +++ b/GdeiAssistant-iOS/Features/Secret/ViewModels/SecretViewModel.swift @@ -17,6 +17,10 @@ final class SecretViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func loadIfNeeded() async { guard posts.isEmpty else { return } await refresh() diff --git a/GdeiAssistant-iOS/Features/Social/Repositories/MockSocialRepository.swift b/GdeiAssistant-iOS/Features/Social/Repositories/MockSocialRepository.swift index cb2f14a..7494beb 100644 --- a/GdeiAssistant-iOS/Features/Social/Repositories/MockSocialRepository.swift +++ b/GdeiAssistant-iOS/Features/Social/Repositories/MockSocialRepository.swift @@ -189,6 +189,10 @@ final class MockSocialRepository: SocialRepository { return page(items, cursor: cursor, limit: limit) } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func fetchConversation(id: String) async throws -> ConversationSummary { try await delay() guard let conversation = conversations[id] else { diff --git a/GdeiAssistant-iOS/Features/Social/ViewModels/ChatThreadViewModel.swift b/GdeiAssistant-iOS/Features/Social/ViewModels/ChatThreadViewModel.swift index e768172..c13e2f6 100644 --- a/GdeiAssistant-iOS/Features/Social/ViewModels/ChatThreadViewModel.swift +++ b/GdeiAssistant-iOS/Features/Social/ViewModels/ChatThreadViewModel.swift @@ -43,6 +43,10 @@ final class ChatThreadViewModel: ObservableObject { self.tokenProvider = tokenProvider } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + var currentUserID: String? { resolvedCurrentUserID } diff --git a/GdeiAssistant-iOS/Features/Social/ViewModels/ConversationListViewModel.swift b/GdeiAssistant-iOS/Features/Social/ViewModels/ConversationListViewModel.swift index b79aa2f..702b5aa 100644 --- a/GdeiAssistant-iOS/Features/Social/ViewModels/ConversationListViewModel.swift +++ b/GdeiAssistant-iOS/Features/Social/ViewModels/ConversationListViewModel.swift @@ -20,6 +20,10 @@ final class ConversationListViewModel: ObservableObject { self.realtimeManager = realtimeManager } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func start() async { await reload() startPolling() diff --git a/GdeiAssistant-iOS/Features/Social/ViewModels/DirectMessagePrivacyViewModel.swift b/GdeiAssistant-iOS/Features/Social/ViewModels/DirectMessagePrivacyViewModel.swift index 0a36b7a..50cce73 100644 --- a/GdeiAssistant-iOS/Features/Social/ViewModels/DirectMessagePrivacyViewModel.swift +++ b/GdeiAssistant-iOS/Features/Social/ViewModels/DirectMessagePrivacyViewModel.swift @@ -15,6 +15,10 @@ final class DirectMessagePrivacyViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func load() async { isLoading = true errorMessage = nil diff --git a/GdeiAssistant-iOS/Features/Social/ViewModels/SocialBlockListViewModel.swift b/GdeiAssistant-iOS/Features/Social/ViewModels/SocialBlockListViewModel.swift index 0d7e699..6a18911 100644 --- a/GdeiAssistant-iOS/Features/Social/ViewModels/SocialBlockListViewModel.swift +++ b/GdeiAssistant-iOS/Features/Social/ViewModels/SocialBlockListViewModel.swift @@ -15,6 +15,10 @@ final class SocialBlockListViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func reload() async { isLoading = true errorMessage = nil diff --git a/GdeiAssistant-iOS/Features/Social/ViewModels/SocialMeSummaryViewModel.swift b/GdeiAssistant-iOS/Features/Social/ViewModels/SocialMeSummaryViewModel.swift index 52a15d5..285f484 100644 --- a/GdeiAssistant-iOS/Features/Social/ViewModels/SocialMeSummaryViewModel.swift +++ b/GdeiAssistant-iOS/Features/Social/ViewModels/SocialMeSummaryViewModel.swift @@ -13,6 +13,10 @@ final class SocialMeSummaryViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func load() async { isLoading = true errorMessage = nil diff --git a/GdeiAssistant-iOS/Features/Social/ViewModels/SocialPublicProfileViewModel.swift b/GdeiAssistant-iOS/Features/Social/ViewModels/SocialPublicProfileViewModel.swift index e655f9d..61a4fd9 100644 --- a/GdeiAssistant-iOS/Features/Social/ViewModels/SocialPublicProfileViewModel.swift +++ b/GdeiAssistant-iOS/Features/Social/ViewModels/SocialPublicProfileViewModel.swift @@ -18,6 +18,10 @@ final class SocialPublicProfileViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func load() async { isLoading = true errorMessage = nil diff --git a/GdeiAssistant-iOS/Features/Social/ViewModels/SocialRelationshipListViewModel.swift b/GdeiAssistant-iOS/Features/Social/ViewModels/SocialRelationshipListViewModel.swift index badade1..882e51f 100644 --- a/GdeiAssistant-iOS/Features/Social/ViewModels/SocialRelationshipListViewModel.swift +++ b/GdeiAssistant-iOS/Features/Social/ViewModels/SocialRelationshipListViewModel.swift @@ -19,6 +19,10 @@ final class SocialRelationshipListViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func reload() async { isLoading = true errorMessage = nil diff --git a/GdeiAssistant-iOS/Features/Social/ViewModels/SocialUserSearchViewModel.swift b/GdeiAssistant-iOS/Features/Social/ViewModels/SocialUserSearchViewModel.swift index 219b12f..ccaab59 100644 --- a/GdeiAssistant-iOS/Features/Social/ViewModels/SocialUserSearchViewModel.swift +++ b/GdeiAssistant-iOS/Features/Social/ViewModels/SocialUserSearchViewModel.swift @@ -18,6 +18,10 @@ final class SocialUserSearchViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func search() { searchTask?.cancel() searchTask = Task { diff --git a/GdeiAssistant-iOS/Features/Spare/Repositories/MockSpareRepository.swift b/GdeiAssistant-iOS/Features/Spare/Repositories/MockSpareRepository.swift index a53d5d5..e93999c 100644 --- a/GdeiAssistant-iOS/Features/Spare/Repositories/MockSpareRepository.swift +++ b/GdeiAssistant-iOS/Features/Spare/Repositories/MockSpareRepository.swift @@ -2,6 +2,10 @@ import Foundation @MainActor final class MockSpareRepository: SpareRepository { + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func queryRooms(_ query: SpareQuery) async throws -> [SpareRoomItem] { try await Task.sleep(nanoseconds: 180_000_000) _ = query diff --git a/GdeiAssistant-iOS/Features/Spare/ViewModels/SpareViewModel.swift b/GdeiAssistant-iOS/Features/Spare/ViewModels/SpareViewModel.swift index c0a4b16..dafe6e6 100644 --- a/GdeiAssistant-iOS/Features/Spare/ViewModels/SpareViewModel.swift +++ b/GdeiAssistant-iOS/Features/Spare/ViewModels/SpareViewModel.swift @@ -14,6 +14,10 @@ final class SpareViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func submitQuery() async { isLoading = true errorMessage = nil diff --git a/GdeiAssistant-iOS/Features/Topic/Repositories/MockTopicRepository.swift b/GdeiAssistant-iOS/Features/Topic/Repositories/MockTopicRepository.swift index 9eefb10..107c3c7 100644 --- a/GdeiAssistant-iOS/Features/Topic/Repositories/MockTopicRepository.swift +++ b/GdeiAssistant-iOS/Features/Topic/Repositories/MockTopicRepository.swift @@ -2,6 +2,10 @@ import Foundation @MainActor final class MockTopicRepository: TopicRepository { + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + private var posts: [TopicPost] = [ TopicPost( id: "topic_001", diff --git a/GdeiAssistant-iOS/Features/Topic/ViewModels/TopicViewModel.swift b/GdeiAssistant-iOS/Features/Topic/ViewModels/TopicViewModel.swift index 2b4c966..d769971 100644 --- a/GdeiAssistant-iOS/Features/Topic/ViewModels/TopicViewModel.swift +++ b/GdeiAssistant-iOS/Features/Topic/ViewModels/TopicViewModel.swift @@ -15,6 +15,10 @@ final class TopicViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + func loadIfNeeded() async { guard posts.isEmpty else { return } await refresh() @@ -87,6 +91,10 @@ final class PublishTopicViewModel: ObservableObject { self.repository = repository } + // No actor-bound cleanup. Avoid the iOS 26.2 MainActor isolated-deinit + // back-deployment crash on synchronous release (swiftlang/swift#87316). + nonisolated deinit {} + var isFormValid: Bool { FormValidationSupport.hasText(topic) && FormValidationSupport.hasText(content) && content.count <= 250 && images.count <= 9 } diff --git a/GdeiAssistant-iOSTests/Container/AppContainerBootstrapTests.swift b/GdeiAssistant-iOSTests/Container/AppContainerBootstrapTests.swift index 1cd2a87..8b6a39f 100644 --- a/GdeiAssistant-iOSTests/Container/AppContainerBootstrapTests.swift +++ b/GdeiAssistant-iOSTests/Container/AppContainerBootstrapTests.swift @@ -297,6 +297,22 @@ final class AppContainerBootstrapTests: XCTestCase { ) } + func testMarketplaceTypesDeclareNonisolatedDeinitAgainstMainActorBackDeployCrash() throws { + let expectations = [ + "GdeiAssistant-iOS/Features/Marketplace/ViewModels/MarketplaceViewModel.swift", + "GdeiAssistant-iOS/Features/Marketplace/ViewModels/PublishMarketplaceViewModel.swift", + "GdeiAssistant-iOS/Features/Marketplace/Repositories/MockMarketplaceRepository.swift" + ] + + for path in expectations { + let source = try sourceFileContents(at: path) + XCTAssertTrue( + source.contains("nonisolated deinit"), + "\(path) must declare nonisolated deinit to avoid the iOS 26.2 MainActor isolated-deinit crash (swiftlang/swift#87316)." + ) + } + } + private func sourceFileContents(at relativePath: String) throws -> String { let url = URL(fileURLWithPath: #filePath) .deletingLastPathComponent() diff --git a/GdeiAssistant-iOSTests/Marketplace/MarketplaceViewModelTests.swift b/GdeiAssistant-iOSTests/Marketplace/MarketplaceViewModelTests.swift index e334cec..9bfd410 100644 --- a/GdeiAssistant-iOSTests/Marketplace/MarketplaceViewModelTests.swift +++ b/GdeiAssistant-iOSTests/Marketplace/MarketplaceViewModelTests.swift @@ -4,7 +4,13 @@ import XCTest @MainActor final class MarketplaceViewModelTests: XCTestCase { - func testInitialStateIsEmpty() async { + /// Sync construction used to crash the runner with + /// `malloc: pointer being freed was not allocated` under + /// `SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor` (iOS 26.2 MainActor + /// isolated-deinit back-deployment, swiftlang/swift#87316). The root + /// fix is `nonisolated deinit` on `MarketplaceViewModel` and + /// `MockMarketplaceRepository`; this sync case stays as a regression probe. + func testInitialStateIsEmpty() { let vm = MarketplaceViewModel(repository: MockMarketplaceRepository()) XCTAssertTrue(vm.items.isEmpty) @@ -12,6 +18,17 @@ final class MarketplaceViewModelTests: XCTestCase { XCTAssertNil(vm.errorMessage) } + func testViewModelAndMockRepositoryReleaseSynchronouslyWithoutCrash() { + for _ in 0..<20 { + var vm: MarketplaceViewModel? = MarketplaceViewModel( + repository: MockMarketplaceRepository() + ) + weak var weakVM = vm + vm = nil + XCTAssertNil(weakVM) + } + } + func testLoadIfNeededPopulatesItems() async { let vm = MarketplaceViewModel(repository: MockMarketplaceRepository())