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

SwiftUI硬编码500+歌词加载慢、Xcode卡Indexing问题求助

问题根因

Xcode索引卡死

500余首歌词全部硬编码为Swift静态数组字面量,单个文件内存在数百个结构体初始化调用,Xcode的语法解析、类型检查、索引引擎需要逐行校验所有参数类型与语法规则,超大字面量数组会直接耗尽索引进程资源,导致卡在Indexing | Processing files状态。

应用加载卡顿/启动慢

  • 静态数组LyricList.hymnLa的初始化逻辑在首次访问时执行,你在ViewModel的主线程初始化方法中直接调用加载,相当于主线程同步完成500+结构体、数千个字符串属性的创建,直接阻塞主线程渲染。
  • 列表页一次性渲染所有歌词的全量文本内容,哪怕使用LazyVStack,每个列表单元也需要同时渲染最多10个Text控件,滑动时的视图计算开销会随数据量指数级上升。
解决方案

1. 替换硬编码数据为静态资源文件

将所有歌词数据导出为JSON/Plist格式,放入项目的Resources文件夹下作为静态资源,Xcode不会对资源文件做语法索引,从根源解决索引卡死问题。
你只需让Lyric遵守Codable协议,即可直接通过JSONDecoder反序列化得到数据,无需修改原有业务逻辑。
JSON示例:

[
  {
    "id": 1,
    "zoTitle": "Pasian Phatna",
    "engTitle": "Praise God, from whom All Blessings Flow",
    "key": "Key: G",
    "musicStyle": "",
    "translation": "S.S. 9",
    "verse1": "Thupha kheempeuh hong pia Pasian \nPhat un, leitung mi khempeuh aw, \nPhat un vantung mi honpi'n zong; \nPhat un Pa, Ta le Kha Siangtho.",
    "isFavoriteSong": false,
    "isNotedSong": false,
    "isHighlightedSong": false,
    "isBookMarkedSong": false,
    "isSharedSong": false,
    "isCopySong": false
  }
]

2. 异步加载数据

将本地资源读取、反序列化逻辑放到后台线程执行,完成后再切回主线程更新UI,避免阻塞启动流程:
优化后ViewModel代码:

import SwiftUI
import Foundation

class SongViewModel: ObservableObject {
    @Published var songs: [Lyric] = []
    
    init() {
        loadSongsAsync()
    }
    
    private func loadSongsAsync() {
        // 后台线程加载数据
        DispatchQueue.global(qos: .userInitiated).async { [weak self] in
            guard let url = Bundle.main.url(forResource: "hymnLa", withExtension: "json"),
                  let data = try? Data(contentsOf: url),
                  let songs = try? JSONDecoder().decode([Lyric].self, from: data) else {
                DispatchQueue.main.async {
                    self?.songs = []
                }
                return
            }
            // 切回主线程更新UI
            DispatchQueue.main.async {
                self?.songs = songs
            }
        }
    }
}

3. 视图层优化

  • 列表页仅渲染必要的缩略信息(id、歌名),点击跳转到详情页后再加载完整歌词内容,避免列表一次性渲染全量文本。
  • 原有代码存在拼写错误:forgroundColor应为foregroundColor,预览处ContenView应为ContentView。
    优化后列表代码示例:
struct ContentView: View {
    @StateObject private var vm = SongViewModel()
    
    var body: some View {
        NavigationStack {
            ScrollView(showsIndicators: false) {
                LazyVStack(spacing: 12) {
                    ForEach(vm.songs) { song in
                        NavigationLink {
                            SongDetailView(song: song)
                        } label: {
                            HStack(spacing: 16) {
                                Text("\(song.id)")
                                    .frame(width: 30)
                                VStack(alignment: .leading, spacing: 4) {
                                    Text(song.zoTitle)
                                        .font(.headline)
                                    Text(song.engTitle)
                                        .font(.subheadline)
                                        .foregroundColor(.secondary)
                                }
                                Spacer()
                            }
                            .padding(.horizontal)
                            .padding(.vertical, 8)
                        }
                    }
                }
            }
            .navigationTitle("歌词列表")
        }
        .environmentObject(vm)
    }
}

// 单独的歌词详情页
struct SongDetailView: View {
    let song: Lyric
    
    var body: some View {
        ScrollView {
            VStack(spacing: 20) {
                Text(song.zoTitle)
                    .font(.title)
                Text(song.engTitle)
                    .font(.title2)
                    .foregroundColor(.secondary)
                Text(song.verse1)
                if let cho = song.chorus {
                    Text(cho)
                }
                if let v2 = song.verse2 {
                    Text(v2)
                }
                if let v3 = song.verse3 {
                    Text(v3)
                }
                if let v4 = song.verse4 {
                    Text(v4)
                }
                if let v5 = song.verse5 {
                    Text(v5)
                }
            }
            .padding()
        }
    }
}

4. 数据结构优化

将零散的可选歌词段落属性替换为数组存储,减少冗余代码,提升渲染效率:

struct Lyric: Identifiable, Codable {
    let id: Int
    let zoTitle: String
    let engTitle: String
    let key: String
    let musicStyle: String
    let translation: String
    let verses: [String] // 所有主歌按顺序存入数组
    let preChorus: String?
    let chorus: String?
    let bridge: String?
    // 以下用户状态建议单独存储到本地数据库/ UserDefaults,不要和静态歌词绑定
    var isFavoriteSong: Bool = false
    var isNotedSong: Bool = false
    var isHighlightedSong: Bool = false
    var isBookMarkedSong: Bool = false
    var isSharedSong: Bool = false
    var isCopySong: Bool = false
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 20:24:04