Skip to content

Commit 7ad0c07

Browse files
committed
Fix UI freeze when viewing userscripts during onboarding
Moved synchronous file I/O in syncFromDataManager() off the main thread using Task.detached and a nonisolated helper function.
1 parent cc20dbb commit 7ad0c07

1 file changed

Lines changed: 39 additions & 11 deletions

File tree

wBlockCoreService/UserScriptManager.swift

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -144,27 +144,19 @@ public class UserScriptManager: ObservableObject {
144144
.map { $0.userScripts }
145145
.removeDuplicates()
146146
.sink { [weak self] _ in
147-
Task { @MainActor [weak self] in
148-
self?.syncFromDataManager()
147+
Task { [weak self] in
148+
await self?.syncFromDataManager()
149149
}
150150
}
151151
.store(in: &cancellables)
152152
logger.info("✅ UserScriptManager data sync observer setup complete")
153153
}
154154
}
155155

156-
private func syncFromDataManager() {
156+
private func syncFromDataManager() async {
157157
let newUserScripts = dataManager.getUserScripts()
158158
logger.info("🔄 Syncing userscripts from data manager: \(newUserScripts.count) scripts")
159159

160-
// Update content from stored files
161-
var updatedScripts = newUserScripts
162-
for i in 0..<updatedScripts.count {
163-
if let content = readUserScriptContent(updatedScripts[i]) {
164-
updatedScripts[i].content = content
165-
}
166-
}
167-
168160
// If data manager has no scripts but we have defaults, don't sync from empty data manager
169161
if newUserScripts.isEmpty && !userScripts.isEmpty {
170162
logger.info(
@@ -173,13 +165,49 @@ public class UserScriptManager: ObservableObject {
173165
return
174166
}
175167

168+
// Update content from stored files (do file I/O off main thread)
169+
let scriptsToLoad = newUserScripts
170+
let updatedScripts = await Task.detached { [weak self] () -> [UserScript] in
171+
guard let self = self else { return scriptsToLoad }
172+
var scripts = scriptsToLoad
173+
for i in 0..<scripts.count {
174+
if let content = await self.readUserScriptContentOffMain(scripts[i]) {
175+
scripts[i].content = content
176+
}
177+
}
178+
return scripts
179+
}.value
180+
176181
// Only update if the scripts have actually changed to avoid unnecessary UI updates
177182
if !areUserScriptsEqual(userScripts, updatedScripts) {
178183
userScripts = updatedScripts
179184
logger.info("✅ Updated userscripts from data manager")
180185
}
181186
}
182187

188+
/// Read userscript content off the main thread
189+
nonisolated private func readUserScriptContentOffMain(_ userScript: UserScript) -> String? {
190+
// Try fallback directory first
191+
if let fallbackURL = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first?
192+
.appendingPathComponent("wBlock").appendingPathComponent("userscripts") {
193+
let fileURL = fallbackURL.appendingPathComponent("\(userScript.id.uuidString).user.js")
194+
if FileManager.default.fileExists(atPath: fileURL.path),
195+
let content = try? String(contentsOf: fileURL, encoding: .utf8) {
196+
return content
197+
}
198+
}
199+
// Then try group directory
200+
if let groupURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.skula.wBlock")?
201+
.appendingPathComponent("userscripts") {
202+
let fileURL = groupURL.appendingPathComponent("\(userScript.id.uuidString).user.js")
203+
if FileManager.default.fileExists(atPath: fileURL.path),
204+
let content = try? String(contentsOf: fileURL, encoding: .utf8) {
205+
return content
206+
}
207+
}
208+
return nil
209+
}
210+
183211
private func areUserScriptsEqual(_ scripts1: [UserScript], _ scripts2: [UserScript]) -> Bool {
184212
guard scripts1.count == scripts2.count else { return false }
185213

0 commit comments

Comments
 (0)