Files
suixinkan_ios_uikit/Scripts/convert_observable.py
2026-06-26 14:33:31 +08:00

65 lines
1.9 KiB
Python

#!/usr/bin/env python3
"""Convert ObservableObject/@Published Swift files to imperative onChange pattern."""
import re
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1] / "suixinkan_ios"
def convert_file(path: Path) -> bool:
text = path.read_text(encoding="utf-8")
original = text
if "ObservableObject" not in text and "@Published" not in text and "import Combine" not in text:
return False
text = re.sub(r"^import Combine\n", "", text, flags=re.MULTILINE)
if ": ObservableObject" in text:
text = text.replace(": ObservableObject", "")
# Insert onChange after class opening brace
def add_onchange(match):
decl = match.group(0)
if "onChange:" in text[match.end():match.end()+200]:
return decl
return decl + "\n var onChange: (() -> Void)?\n"
text = re.sub(
r"(final class \w+[^{]*\{)\n",
add_onchange,
text,
count=1,
)
def repl_published(m):
access = m.group(1) or ""
name = m.group(2)
rest = m.group(3)
return f"{access}var {name}{rest} {{ didSet {{ onChange?() }} }}\n"
text = re.sub(
r"@Published\s+(private\(set\)\s+)?var\s+(\w+)([^=\n]*=\s*[^\n]+)\n",
repl_published,
text,
)
text = re.sub(
r"@Published\s+(private\(set\)\s+)?var\s+(\w+)([^=\n]*)\n",
lambda m: f"{m.group(1) or ''}var {m.group(2)}{m.group(3)} {{ didSet {{ onChange?() }} }}\n",
text,
)
if text != original:
path.write_text(text, encoding="utf-8")
return True
return False
def main():
changed = 0
for path in ROOT.rglob("*.swift"):
if convert_file(path):
changed += 1
print(f"converted: {path.relative_to(ROOT.parent)}")
print(f"Done. {changed} files converted.")
if __name__ == "__main__":
main()