fix(icon): TestFlight-Validation grün — Icon + CFBundleIconName

Apple Validator hatte drei Fehler geworfen:
  - Missing 120x120 (iPhone) und 152x152 (iPad)
  - Missing Info.plist key CFBundleIconName

Root-Cause: AppIcon.appiconset hatte keinen filename gesetzt → keine
PNG-Variants im Bundle. Plus: bei GENERATE_INFOPLIST_FILE=NO injiziert
Xcode CFBundleIconName nicht automatisch, das muss explizit in die
plist.

Fixes:
- scripts/make-appicon.swift erzeugt 1024×1024-PNG-Platzhalter in
  paper-Theme-Farben (Sienna-Background, dunkles Z, zwei
  Anführungszeichen-Akzente) analog cards-native
- Sources/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json
  verlinkt AppIcon-1024.png für light / dark / tinted (3 Appearances)
- project.yml setzt CFBundleIconName: AppIcon im Info.plist-Root

Archive-Verifikation:
  $ /usr/libexec/PlistBuddy -c "Print :CFBundleIconName" Info.plist
  → AppIcon
  $ ls ZitareNative.app | grep AppIcon
  → AppIcon, AppIcon60x60@2x.png (=120×120), AppIcon76x76@2x~ipad.png
    (=152×152)

Platzhalter — vor produktivem App-Store-Launch durch designtes Icon
ersetzen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till 2026-05-14 21:00:24 +02:00
parent c0260fa55f
commit 7b4cb13afe
5 changed files with 194 additions and 24 deletions

View file

@ -0,0 +1,89 @@
#!/usr/bin/env swift
// Generiert ein 1024×1024-AppIcon-PNG als Platzhalter für Zitare.
//
// Design (paper-Variant aus @mana/themes):
// - Background: warm Sienna (paper-primary, HSL 18 50% 38%)
// - Zwei stilisierte typografische Anführungszeichen oben in
// gebrochenem Weiß (paper-background)
// - Großes serifenbetontes Z" zentriert in dunklem Sepia
//
// Aufruf:
// swift scripts/make-appicon.swift
//
// Schreibt nach: Sources/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon-1024.png
//
// Dies ist ein Platzhalter vor App-Store-Submission durch ein
// designter Icon ersetzen.
import AppKit
import CoreGraphics
let size = 1024
let cs = CGColorSpaceCreateDeviceRGB()
guard let ctx = CGContext(
data: nil,
width: size, height: size,
bitsPerComponent: 8, bytesPerRow: 0,
space: cs,
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue
) else {
print("CGContext creation failed")
exit(1)
}
// HSL(18, 50%, 38%) paper-primary, warm Sienna
let background = CGColor(red: 0.57, green: 0.30, blue: 0.19, alpha: 1)
ctx.setFillColor(background)
ctx.fill(CGRect(x: 0, y: 0, width: size, height: size))
// Zwei Anführungs"-Akzente oben gebrochenes Weiß (paper-bg-light)
let accent = CGColor(red: 0.95, green: 0.93, blue: 0.88, alpha: 0.92)
let quoteFont = NSFont(name: "Georgia-Bold", size: 360)
?? NSFont.boldSystemFont(ofSize: 360)
let quoteAttrs: [NSAttributedString.Key: Any] = [
.font: quoteFont,
.foregroundColor: NSColor(cgColor: accent) ?? .white,
]
// Anführungszeichen links ()
let leftQuote = NSAttributedString(string: "\u{201E}", attributes: quoteAttrs)
let leftLine = CTLineCreateWithAttributedString(leftQuote)
ctx.textPosition = CGPoint(x: 160, y: 520)
CTLineDraw(leftLine, ctx)
// Anführungszeichen rechts (")
let rightQuote = NSAttributedString(string: "\u{201C}", attributes: quoteAttrs)
let rightLine = CTLineCreateWithAttributedString(rightQuote)
ctx.textPosition = CGPoint(x: 620, y: 520)
CTLineDraw(rightLine, ctx)
// Großes Z" mittig dunkles Sepia, serif
let zFont = NSFont(name: "Georgia-Bold", size: 640)
?? NSFont.boldSystemFont(ofSize: 640)
let darkSepia = CGColor(red: 0.22, green: 0.16, blue: 0.12, alpha: 1)
let zAttrs: [NSAttributedString.Key: Any] = [
.font: zFont,
.foregroundColor: NSColor(cgColor: darkSepia) ?? .black,
]
let zStr = NSAttributedString(string: "Z", attributes: zAttrs)
let zLine = CTLineCreateWithAttributedString(zStr)
let zBounds = CTLineGetImageBounds(zLine, ctx)
let tx = (CGFloat(size) - zBounds.width) / 2 - zBounds.origin.x
let ty = (CGFloat(size) - zBounds.height) / 2 - zBounds.origin.y - 40
ctx.textPosition = CGPoint(x: tx, y: ty)
CTLineDraw(zLine, ctx)
// PNG schreiben
guard let cgImage = ctx.makeImage() else {
print("makeImage failed")
exit(1)
}
let bitmap = NSBitmapImageRep(cgImage: cgImage)
guard let data = bitmap.representation(using: .png, properties: [:]) else {
print("PNG encoding failed")
exit(1)
}
let target = URL(fileURLWithPath: "Sources/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon-1024.png")
try data.write(to: target)
print("Wrote \(target.path) (\(data.count) bytes)")