You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xcode代码报错求助:多个SwiftUI编译错误排查修复

SwiftUI项目编译错误修复方案

逐个错误排查与修复

1. Invalid redeclaration of ContentView

  • 问题:项目中存在重复的ContentView定义,或Xcode缓存导致识别错误。
  • 修复:
    • 检查所有.swift文件,删除其他定义ContentView的代码;
    • 执行Xcode清理操作:Cmd + Shift + K清除缓存后重新编译。

2. Instance will be immediately deallocated because property navigationDelegate is weak

  • 问题:WKWebView的navigationDelegate是弱引用,直接创建的WebViewCoordinator没有强引用持有,会被立即释放。
  • 修复:在BrowserView中用@State持有WebViewCoordinator实例,确保生命周期匹配:
    struct BrowserView: View {
        @State private var webView = WKWebView()
        @State private var coordinator: WebViewCoordinator?
    
        var body: some View {
            WebView(webView: $webView)
                .onAppear {
                    if let url = URL(string: "https://youtube.com") {
                        webView.load(URLRequest(url: url))
                    }
                    coordinator = WebViewCoordinator()
                    webView.navigationDelegate = coordinator
                }
        }
    }
    

3. weak may only be applied to class and class-bound protocol types, not FilesView

  • 问题:FilesView是SwiftUI值类型(struct),weak仅适用于类类型。
  • 修复:将FilesView的状态抽离到ObservableObject类中,让Coordinator持有该类的弱引用:
    class FilesViewModel: ObservableObject {
        @Published var downloadedFiles = [String]()
        @Published var storageTaken: String = ""
    
        func loadFiles() {
            // 原loadFiles逻辑
        }
    }
    
    struct FilesView: View {
        @StateObject private var viewModel = FilesViewModel()
    
        var body: some View {
            VStack {
                List(viewModel.downloadedFiles, id: \.self) { Text($0) }
                Text("Storage Taken: \(viewModel.storageTaken)")
            }
            .onAppear(perform: viewModel.loadFiles)
        }
    }
    
    class WebViewCoordinator: NSObject, WKNavigationDelegate {
        weak var filesViewModel: FilesViewModel?
    
        init(filesViewModel: FilesViewModel? = nil) {
            self.filesViewModel = filesViewModel
        }
    }
    

4. Value of type WebViewCoordinator has no member getDocumentsDirectory

  • 问题:getDocumentsDirectory是全局函数,类中调用需确保作用域。
  • 修复:将全局函数转为FileManager扩展,调用更规范:
    extension FileManager {
        static func getDocumentsDirectory() -> URL {
            let paths = self.default.urls(for: .documentDirectory, in: .userDomainMask)
            return paths[0]
        }
    }
    
    // Coordinator中调用方式
    let destinationURL = FileManager.getDocumentsDirectory().appendingPathComponent("\(sanitizeFileName(fileName: title)).mp4")
    

5. Value of type WebViewCoordinator has no member sanitizeFileName

  • 问题:同全局函数调用问题,直接调用全局函数即可,无需修改函数本身。

6. Value of type [URLResourceKey: Any] has no member totalFileAllocatedSize

  • 问题:原NSURL的fileSize()方法未完成,获取文件大小的方式错误。
  • 修复:完善URL扩展的fileSize()方法:
    extension URL {
        func fileSize() -> Int {
            guard let attributes = try? FileManager.default.attributesOfItem(atPath: path) else { return 0 }
            return attributes[.size] as? Int ?? 0
        }
    }
    
    // 计算存储占用的调用方式
    let size = files.reduce(0, { $0 + $1.fileSize() })
    

完整修复后的代码

import SwiftUI
import WebKit

// 统一通知名称定义
extension Notification.Name {
    static let downloadCompleted = Notification.Name("DownloadCompleted")
}

@main
struct UDOWNLOADERZApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

struct ContentView: View {
    var body: some View {
        TabView {
            BrowserView()
                .tabItem { Label("Browser", systemImage: "globe") }
            FilesView()
                .tabItem { Label("Files", systemImage: "folder") }
            SettingsView()
                .tabItem { Label("Settings", systemImage: "gearshape") }
        }
    }
}

struct BrowserView: View {
    @State private var webView = WKWebView()
    @State private var coordinator: WebViewCoordinator?

    var body: some View {
        WebView(webView: $webView)
            .onAppear {
                if let url = URL(string: "https://youtube.com") {
                    webView.load(URLRequest(url: url))
                }
                coordinator = WebViewCoordinator()
                webView.navigationDelegate = coordinator
            }
    }
}

struct WebView: UIViewRepresentable {
    @Binding var webView: WKWebView

    func makeUIView(context: Context) -> WKWebView { webView }
    func updateUIView(_ uiView: WKWebView, context: Context) {}
}

class WebViewCoordinator: NSObject, WKNavigationDelegate {
    weak var filesViewModel: FilesViewModel?

    init(filesViewModel: FilesViewModel? = nil) {
        self.filesViewModel = filesViewModel
    }

    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        let jsCode = """
        (function() {
            var video = document.querySelector('video');
            var audio = document.querySelector('audio');
            var mediaElement = video || audio;
            if (mediaElement) {
                return {
                    src: mediaElement.src,
                    title: document.title || mediaElement.getAttribute('title') || 'Unknown'
                };
            }
            return null;
        })();
        """
        
        webView.evaluateJavaScript(jsCode) { [weak self] result, error in
            guard let resultDict = result as? [String: String],
                  let urlString = resultDict["src"],
                  let title = resultDict["title"],
                  let url = URL(string: urlString) else { return }
            
            self?.downloadContent(url: url, title: title)
        }
    }

    func downloadContent(url: URL, title: String) {
        DispatchQueue.global().async {
            let destinationURL = FileManager.getDocumentsDirectory().appendingPathComponent("\(sanitizeFileName(fileName: title)).mp4")

            URLSession.shared.downloadTask(with: url) { tempURL, _, error in
                guard let tempURL = tempURL, error == nil else { return }
                do {
                    try FileManager.default.moveItem(at: tempURL, to: destinationURL)
                    DispatchQueue.main.async {
                        NotificationCenter.default.post(name: .downloadCompleted, object: nil)
                        self.filesViewModel?.reloadFilesList()
                    }
                } catch {
                    print("Download failed: \(error.localizedDescription)")
                }
            }.resume()
        }
    }
}

class FilesViewModel: ObservableObject {
    @Published var downloadedFiles = [String]()
    @Published var storageTaken: String = ""

    func loadFiles() {
        DispatchQueue.global().async {
            do {
                let files = try FileManager.default.contentsOfDirectory(at: FileManager.getDocumentsDirectory(), includingPropertiesForKeys: nil, options: [])
                DispatchQueue.main.async {
                    self.downloadedFiles = files.map { $0.lastPathComponent }
                    self.calculateStorageTaken(files: files)
                }
            } catch {
                print("Failed to load files: \(error.localizedDescription)")
            }
        }
    }

    func reloadFilesList() { loadFiles() }

    private func calculateStorageTaken(files: [URL]) {
        let size = files.reduce(0, { $0 + $1.fileSize() })
        storageTaken = ByteCountFormatter.string(fromByteCount: Int64(size), countStyle: .file)
    }
}

struct FilesView: View {
    @StateObject private var viewModel = FilesViewModel()

    var body: some View {
        VStack {
            List(viewModel.downloadedFiles, id: \.self) { Text($0) }
            Text("Storage Taken: \(viewModel.storageTaken)")
        }
        .onAppear(perform: viewModel.loadFiles)
        .onReceive(NotificationCenter.default.publisher(for: .downloadCompleted)) { _ in
            viewModel.reloadFilesList()
        }
    }
}

struct SettingsView: View {
    var body: some View {
        VStack {
            Text("Contact: Udownloaderz2@proton.me")
            Text("Twitter: N/A")
        }
    }
}

extension FileManager {
    static func getDocumentsDirectory() -> URL {
        let paths = self.default.urls(for: .documentDirectory, in: .userDomainMask)
        return paths[0]
    }
}

func sanitizeFileName(fileName: String) -> String {
    let invalidCharacters = CharacterSet(charactersIn: "\\/:*?\"<>|")
    return fileName.components(separatedBy: invalidCharacters).joined(separator: "_")
}

extension URL {
    func fileSize() -> Int {
        guard let attributes = try? FileManager.default.attributesOfItem(atPath: path) else { return 0 }
        return attributes[.size] as? Int ?? 0
    }
}

内容的提问来源于stack exchange,提问作者rights4a

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 12:14:51