Fix compile errors: guard unwrap + ViewBuilder var mutation
Build iOS App / Build .ipa and publish source (push) Successful in 25s

- PRDetailView: guard let current = selectedTab ?? .overview fails because
  ?? already unwraps to non-optional. Use let current = selectedTab ?? .overview.
- MarkdownText: var inCodeBlock inside ViewBuilder is not allowed. Precompute
  lines with their code-block state as a computed property on a private struct,
  then iterate via ForEach in the builder. Bump to 3.3.
This commit is contained in:
Hermes committed 2026-08-14 09:45:44 +00:00
1 parent dd0fc84238
commit a9d408ee85
2 files changed
+58 -32

No files matched your search

+2 -2
View File
@@ -195,8 +195,8 @@ struct PRDetailView: View {
DragGesture(minimumDistance: 20)
.onEnded { value in
let tabs = DetailTab.allCases
guard let current = selectedTab ?? .overview,
let idx = tabs.firstIndex(of: current) else { return }
let current = selectedTab ?? .overview
guard let idx = tabs.firstIndex(of: current) else { return }
if value.translation.width < -40, idx < tabs.count - 1 {
withAnimation { selectedTab = tabs[idx + 1] }
} else if value.translation.width > 40, idx > 0 {
+56 -30
View File
@@ -179,49 +179,75 @@ struct MarkdownText: View {
self.markdown = markdown
}
/// One markdown source line with its render type resolved upfront, so the
/// code-block state doesn't have to be mutated inside the ViewBuilder.
private struct Line: Identifiable {
enum Kind { case code, heading, bullet, text, blank }
let id = UUID()
let kind: Kind
let text: String
let level: Int // heading level
}
private var lines: [Line] {
let raw = markdown.components(separatedBy: "\n")
var out: [Line] = []
var inCodeBlock = false
for line in raw {
if line.hasPrefix("```") {
inCodeBlock.toggle()
continue
}
if inCodeBlock {
out.append(Line(kind: .code, text: line, level: 0))
} else if line.hasPrefix("# ") || line.hasPrefix("## ") || line.hasPrefix("### ") {
let level = line.prefix(while: { $0 == "#" }).count
out.append(Line(kind: .heading,
text: String(line.dropFirst(level)).trimmingCharacters(in: .whitespaces),
level: level))
} else if line.hasPrefix("- ") || line.hasPrefix("* ") {
out.append(Line(kind: .bullet,
text: String(line.dropFirst(2)),
level: 0))
} else if line.trimmingCharacters(in: .whitespaces).isEmpty {
out.append(Line(kind: .blank, text: "", level: 0))
} else {
out.append(Line(kind: .text, text: line, level: 0))
}
}
return out
}
var body: some View {
VStack(alignment: .leading, spacing: 6) {
let lines = markdown.components(separatedBy: "\n")
var inCodeBlock = false
ForEach(Array(lines.enumerated()), id: \.offset) { _, line in
if line.hasPrefix("```") {
inCodeBlock.toggle()
} else if inCodeBlock {
Text(line)
ForEach(lines) { line in
switch line.kind {
case .code:
Text(line.text)
.font(.caption.monospaced())
.frame(maxWidth: .infinity, alignment: .leading)
.padding(4)
.background(Color(.secondarySystemBackground))
} else if line.hasPrefix("# ") || line.hasPrefix("## ") || line.hasPrefix("### ") {
heading(line)
} else if line.hasPrefix("- ") || line.hasPrefix("* ") {
bullet(line)
} else if line.trimmingCharacters(in: .whitespaces).isEmpty {
case .heading:
Text(line.text)
.font(line.level == 1 ? .title2.bold() : line.level == 2 ? .title3.bold() : .headline)
.frame(maxWidth: .infinity, alignment: .leading)
case .bullet:
HStack(alignment: .top, spacing: 6) {
Text("•")
inline(line.text)
}
.frame(maxWidth: .infinity, alignment: .leading)
case .blank:
Spacer(minLength: 4)
} else {
inline(line)
case .text:
inline(line.text)
}
}
}
.frame(maxWidth: .infinity, alignment: .leading)
}
private func heading(_ line: String) -> some View {
let level = line.prefix(while: { $0 == "#" }).count
let text = line.dropFirst(level).trimmingCharacters(in: .whitespaces)
return Text(text)
.font(level == 1 ? .title2.bold() : level == 2 ? .title3.bold() : .headline)
.frame(maxWidth: .infinity, alignment: .leading)
}
private func bullet(_ line: String) -> some View {
HStack(alignment: .top, spacing: 6) {
Text("•")
inline(String(line.dropFirst(2)))
}
.frame(maxWidth: .infinity, alignment: .leading)
}
private func inline(_ text: String) -> some View {
if let attributed = try? AttributedString(markdown: text) {
Text(attributed)