ReadKeep/readeck/Data/Repository/OfflineSyncManager.swift
Ilyas Hallak e5334d456d refactor: Remove NWPathMonitor auto-sync, keep only on-demand server checks
- Delete NetworkConnectivity.swift with problematic NWPathMonitor
- Remove serverDidBecomeAvailable notification
- Remove unused startAutoSync from OfflineSyncManager
- Server check now only on app start via AppViewModel
2025-10-19 10:47:19 +02:00

128 lines
4.1 KiB
Swift

import Foundation
import CoreData
import SwiftUI
class OfflineSyncManager: ObservableObject, @unchecked Sendable {
static let shared = OfflineSyncManager()
@Published var isSyncing = false
@Published var syncStatus: String?
private let coreDataManager = CoreDataManager.shared
private let api: PAPI
private let checkServerReachabilityUseCase: PCheckServerReachabilityUseCase
init(api: PAPI = API(),
checkServerReachabilityUseCase: PCheckServerReachabilityUseCase = DefaultUseCaseFactory.shared.makeCheckServerReachabilityUseCase()) {
self.api = api
self.checkServerReachabilityUseCase = checkServerReachabilityUseCase
}
// MARK: - Sync Methods
func syncOfflineBookmarks() async {
// First check if server is reachable
guard await checkServerReachabilityUseCase.execute() else {
await MainActor.run {
isSyncing = false
syncStatus = "Server not reachable. Cannot sync."
}
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self.syncStatus = nil
}
return
}
await MainActor.run {
isSyncing = true
syncStatus = "Syncing bookmarks with server..."
}
let offlineBookmarks = getOfflineBookmarks()
guard !offlineBookmarks.isEmpty else {
await MainActor.run {
isSyncing = false
syncStatus = "No bookmarks to sync"
}
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
self.syncStatus = nil
}
return
}
var successCount = 0
var failedCount = 0
for bookmark in offlineBookmarks {
guard let url = bookmark.url else {
failedCount += 1
continue
}
let tags = bookmark.tags?.components(separatedBy: ",").filter { !$0.isEmpty } ?? []
let title = bookmark.title ?? ""
do {
// Try to upload via API
let dto = CreateBookmarkRequestDto(url: url, title: title, labels: tags.isEmpty ? nil : tags)
_ = try await api.createBookmark(createRequest: dto)
// If successful, delete from offline storage
deleteOfflineBookmark(bookmark)
successCount += 1
await MainActor.run {
syncStatus = "Synced \(successCount) bookmarks..."
}
} catch {
print("Failed to sync bookmark: \(url) - \(error)")
failedCount += 1
}
}
await MainActor.run {
isSyncing = false
if failedCount == 0 {
syncStatus = "✅ Successfully synced \(successCount) bookmarks"
} else {
syncStatus = "⚠️ Synced \(successCount), failed \(failedCount) bookmarks"
}
}
// Clear status after a few seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self.syncStatus = nil
}
}
func getOfflineBookmarksCount() -> Int {
return getOfflineBookmarks().count
}
private func getOfflineBookmarks() -> [ArticleURLEntity] {
do {
let fetchRequest: NSFetchRequest<ArticleURLEntity> = ArticleURLEntity.fetchRequest()
return try coreDataManager.context.safeFetch(fetchRequest)
} catch {
print("Failed to fetch offline bookmarks: \(error)")
return []
}
}
private func deleteOfflineBookmark(_ entity: ArticleURLEntity) {
do {
try coreDataManager.context.safePerform { [weak self] in
guard let self = self else { return }
self.coreDataManager.context.delete(entity)
self.coreDataManager.save()
}
} catch {
print("Failed to delete offline bookmark: \(error)")
}
}
}