summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorivar <i@oiee.no>2026-05-06 21:21:04 +0200
committerivar <i@oiee.no>2026-05-06 21:21:04 +0200
commitefae4d08083f454975f08a2c0c6871c6a3d41e95 (patch)
treed7b47d02d446b13094d787e961a8737ba08a0b85
parent01eee1c4fe8252bffc9334e4bb2dbbc15f002835 (diff)
downloadsolverv-efae4d08083f454975f08a2c0c6871c6a3d41e95.tar.xz
solverv-efae4d08083f454975f08a2c0c6871c6a3d41e95.zip
feat: wire Shared/ files into both Xcode targets via pbxproj
-rw-r--r--Solverv.xcodeproj/project.pbxproj60
-rw-r--r--scripts/update_pbxproj.py177
2 files changed, 235 insertions, 2 deletions
diff --git a/Solverv.xcodeproj/project.pbxproj b/Solverv.xcodeproj/project.pbxproj
index acb1dd0..093609f 100644
--- a/Solverv.xcodeproj/project.pbxproj
+++ b/Solverv.xcodeproj/project.pbxproj
@@ -7,6 +7,17 @@
objects = {
/* Begin PBXBuildFile section */
+ A4D1E5F62BE4C16BF9C35920 /* Season.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAAD0F6249013EFDE6CF3BDB /* Season.swift */; };
+ BDE41CA8E12EF58194F0FB28 /* Season.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAAD0F6249013EFDE6CF3BDB /* Season.swift */; };
+ 0602B5968563FDCC6AAE214B /* SolsticeEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = A53B1F64353769F3F3D52DCC /* SolsticeEvent.swift */; };
+ C1829841EA43782BD52878E9 /* SolsticeEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = A53B1F64353769F3F3D52DCC /* SolsticeEvent.swift */; };
+ 58EC7B50BC5232D031299280 /* SolsticeData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56A3A45E5A25BDBCC47A23EC /* SolsticeData.swift */; };
+ DB32B658D259B49C522786C8 /* SolsticeData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56A3A45E5A25BDBCC47A23EC /* SolsticeData.swift */; };
+ 63374A5CD6AAFBBA4A5E87AC /* AppGroupManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B12784AD1DE5013F9E3B677 /* AppGroupManager.swift */; };
+ 6FFB4853DA8AF00282F20F96 /* AppGroupManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B12784AD1DE5013F9E3B677 /* AppGroupManager.swift */; };
+ 594A94559E2251F892BC158B /* SunTimes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C4C2D0816DA7D3E6E6A4358 /* SunTimes.swift */; };
+ 90AEA7599D196072837AE994 /* SunTimes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C4C2D0816DA7D3E6E6A4358 /* SunTimes.swift */; };
+
1B8629D62EF0C656005A1C75 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1B8629D52EF0C656005A1C75 /* WidgetKit.framework */; };
1B8629D82EF0C656005A1C75 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1B8629D72EF0C656005A1C75 /* SwiftUI.framework */; };
1B8629E52EF0C657005A1C75 /* Solsnu.WidgetExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 1B8629D32EF0C656005A1C75 /* Solsnu.WidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
@@ -37,6 +48,12 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
+ BAAD0F6249013EFDE6CF3BDB /* Season.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Season.swift; sourceTree = "<group>"; };
+ A53B1F64353769F3F3D52DCC /* SolsticeEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SolsticeEvent.swift; sourceTree = "<group>"; };
+ 56A3A45E5A25BDBCC47A23EC /* SolsticeData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SolsticeData.swift; sourceTree = "<group>"; };
+ 3B12784AD1DE5013F9E3B677 /* AppGroupManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppGroupManager.swift; sourceTree = "<group>"; };
+ 5C4C2D0816DA7D3E6E6A4358 /* SunTimes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SunTimes.swift; sourceTree = "<group>"; };
+
1B8629BF2EF0C636005A1C75 /* Solverv.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Solverv.app; sourceTree = BUILT_PRODUCTS_DIR; };
1B8629D32EF0C656005A1C75 /* Solsnu.WidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = Solsnu.WidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
1B8629D52EF0C656005A1C75 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; };
@@ -89,6 +106,35 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
+ 0BFB6DC6E4F437012CF2990E /* Models */ = {
+ isa = PBXGroup;
+ children = (
+ BAAD0F6249013EFDE6CF3BDB /* Season.swift */,
+ A53B1F64353769F3F3D52DCC /* SolsticeEvent.swift */,
+ 56A3A45E5A25BDBCC47A23EC /* SolsticeData.swift */,
+ );
+ path = Models;
+ sourceTree = "<group>";
+ };
+ 5F48ADA595F6B3DE3DFE2A32 /* Utilities */ = {
+ isa = PBXGroup;
+ children = (
+ 3B12784AD1DE5013F9E3B677 /* AppGroupManager.swift */,
+ 5C4C2D0816DA7D3E6E6A4358 /* SunTimes.swift */,
+ );
+ path = Utilities;
+ sourceTree = "<group>";
+ };
+ 0EEEC5869B3AB7A2A2154C10 /* Shared */ = {
+ isa = PBXGroup;
+ children = (
+ 0BFB6DC6E4F437012CF2990E /* Models */,
+ 5F48ADA595F6B3DE3DFE2A32 /* Utilities */,
+ );
+ path = Shared;
+ sourceTree = "<group>";
+ };
+
1B8629B62EF0C636005A1C75 = {
isa = PBXGroup;
children = (
@@ -226,14 +272,24 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- );
+ A4D1E5F62BE4C16BF9C35920 /* Season.swift in Sources */,
+ 0602B5968563FDCC6AAE214B /* SolsticeEvent.swift in Sources */,
+ 58EC7B50BC5232D031299280 /* SolsticeData.swift in Sources */,
+ 63374A5CD6AAFBBA4A5E87AC /* AppGroupManager.swift in Sources */,
+ 594A94559E2251F892BC158B /* SunTimes.swift in Sources */,
+);
runOnlyForDeploymentPostprocessing = 0;
};
1B8629CF2EF0C656005A1C75 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- );
+ BDE41CA8E12EF58194F0FB28 /* Season.swift in Sources */,
+ C1829841EA43782BD52878E9 /* SolsticeEvent.swift in Sources */,
+ DB32B658D259B49C522786C8 /* SolsticeData.swift in Sources */,
+ 6FFB4853DA8AF00282F20F96 /* AppGroupManager.swift in Sources */,
+ 90AEA7599D196072837AE994 /* SunTimes.swift in Sources */,
+);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
diff --git a/scripts/update_pbxproj.py b/scripts/update_pbxproj.py
new file mode 100644
index 0000000..b213f4e
--- /dev/null
+++ b/scripts/update_pbxproj.py
@@ -0,0 +1,177 @@
+#!/usr/bin/env python3
+"""
+Updates Solverv.xcodeproj/project.pbxproj to:
+ - Add Shared/{Models,Utilities} file references for 5 shared files
+ - Add those files to both targets' PBXSourcesBuildPhase
+ - Remove old duplicate file references and build entries from both targets
+ - Add a Shared group in the project navigator
+
+Run from the project root: python3 scripts/update_pbxproj.py
+"""
+import re
+import secrets
+
+PROJ = "Solverv.xcodeproj/project.pbxproj"
+
+MODELS_FILES = ["Season.swift", "SolsticeEvent.swift", "SolsticeData.swift"]
+UTIL_FILES = ["AppGroupManager.swift", "SunTimes.swift"]
+ALL_FILES = MODELS_FILES + UTIL_FILES
+
+def uid():
+ return secrets.token_hex(12).upper()
+
+with open(PROJ) as f:
+ src = f.read()
+
+# ── 1. Find existing file-reference UUIDs (expect 2 per name: one per target) ──
+old_ref_uids = {}
+for name in ALL_FILES:
+ pat = r'([0-9A-Fa-f]{24})\s*/\*\s*' + re.escape(name) + r'\s*\*/'
+ found = re.findall(pat, src)
+ old_ref_uids[name] = [u.upper() for u in found]
+ if len(found) != 2:
+ print(f"WARNING: expected 2 PBXFileReference entries for {name}, found {len(found)}: {found}")
+
+# ── 2. Find existing build-file UUIDs (expect 2 per name: one per target) ──
+old_build_uids = {}
+for name in ALL_FILES:
+ pat = r'([0-9A-Fa-f]{24})\s*/\*\s*' + re.escape(name) + r'\s+in\s+Sources\s*\*/'
+ found = re.findall(pat, src)
+ old_build_uids[name] = [u.upper() for u in found]
+ if len(found) != 2:
+ print(f"WARNING: expected 2 PBXBuildFile entries for {name}, found {len(found)}: {found}")
+
+# ── 3. Generate new UUIDs ──
+new_ref_uid = {name: uid() for name in ALL_FILES}
+new_build_uid = {name: [uid(), uid()] for name in ALL_FILES} # [target0, target1]
+
+# ── 4. Insert new PBXFileReference entries ──
+new_refs = "\n".join(
+ f'\t\t{new_ref_uid[name]} /* {name} */ = '
+ f'{{isa = PBXFileReference; lastKnownFileType = sourcecode.swift; '
+ f'path = {name}; sourceTree = "<group>"; }};'
+ for name in ALL_FILES
+) + "\n"
+
+src = src.replace(
+ "/* Begin PBXFileReference section */",
+ "/* Begin PBXFileReference section */\n" + new_refs,
+ 1
+)
+
+# ── 5. Insert new PBXBuildFile entries ──
+new_builds = "\n".join(
+ f'\t\t{new_build_uid[name][i]} /* {name} in Sources */ = '
+ f'{{isa = PBXBuildFile; fileRef = {new_ref_uid[name]} /* {name} */; }};'
+ for name in ALL_FILES
+ for i in range(2)
+) + "\n"
+
+src = src.replace(
+ "/* Begin PBXBuildFile section */",
+ "/* Begin PBXBuildFile section */\n" + new_builds,
+ 1
+)
+
+# ── 6. Add new build files to both PBXSourcesBuildPhase sections ──
+phases = list(re.finditer(
+ r'(isa = PBXSourcesBuildPhase;.*?files = \()(.*?)(\);)',
+ src, re.DOTALL
+))
+if len(phases) != 2:
+ print(f"ERROR: expected 2 PBXSourcesBuildPhase, found {len(phases)}. Aborting.")
+ exit(1)
+
+# Replace in reverse order so offsets stay valid
+for i, phase in reversed(list(enumerate(phases))):
+ additions = "".join(
+ f'\t\t\t\t{new_build_uid[name][i]} /* {name} in Sources */,\n'
+ for name in ALL_FILES
+ )
+ new_phase = phase.group(1) + phase.group(2) + additions + phase.group(3)
+ src = src[:phase.start()] + new_phase + src[phase.end():]
+
+# ── 7. Remove old build-file entries from PBXSourcesBuildPhase ──
+for name in ALL_FILES:
+ for bu in old_build_uids[name]:
+ src = re.sub(r'\t{3,4}' + bu + r'\s*/\*[^*]*\*/,\n', '', src)
+
+# ── 8. Remove old PBXBuildFile entries ──
+for name in ALL_FILES:
+ for bu in old_build_uids[name]:
+ src = re.sub(r'\t\t' + bu + r'\s*/\*[^*]*\*/\s*=\s*\{[^}]*\};\n', '', src)
+
+# ── 9. Remove old PBXFileReference entries ──
+for name in ALL_FILES:
+ for ru in old_ref_uids[name]:
+ src = re.sub(r'\t\t' + ru + r'\s*/\*[^*]*\*/\s*=\s*\{[^}]*\};\n', '', src)
+
+# ── 10. Remove old file refs from PBXGroup children ──
+for name in ALL_FILES:
+ for ru in old_ref_uids[name]:
+ src = re.sub(r'\t{3,4}' + ru + r'\s*/\*[^*]*\*/,\n', '', src)
+
+# ── 11. Add Shared group hierarchy ──
+models_grp_uid = uid()
+utils_grp_uid = uid()
+shared_grp_uid = uid()
+
+models_children = "".join(
+ f'\t\t\t\t{new_ref_uid[n]} /* {n} */,\n' for n in MODELS_FILES
+)
+utils_children = "".join(
+ f'\t\t\t\t{new_ref_uid[n]} /* {n} */,\n' for n in UTIL_FILES
+)
+
+new_groups = (
+ f'\t\t{models_grp_uid} /* Models */ = {{\n'
+ f'\t\t\tisa = PBXGroup;\n'
+ f'\t\t\tchildren = (\n'
+ f'{models_children}'
+ f'\t\t\t);\n'
+ f'\t\t\tpath = Models;\n'
+ f'\t\t\tsourceTree = "<group>";\n'
+ f'\t\t}};\n'
+ f'\t\t{utils_grp_uid} /* Utilities */ = {{\n'
+ f'\t\t\tisa = PBXGroup;\n'
+ f'\t\t\tchildren = (\n'
+ f'{utils_children}'
+ f'\t\t\t);\n'
+ f'\t\t\tpath = Utilities;\n'
+ f'\t\t\tsourceTree = "<group>";\n'
+ f'\t\t}};\n'
+ f'\t\t{shared_grp_uid} /* Shared */ = {{\n'
+ f'\t\t\tisa = PBXGroup;\n'
+ f'\t\t\tchildren = (\n'
+ f'\t\t\t\t{models_grp_uid} /* Models */,\n'
+ f'\t\t\t\t{utils_grp_uid} /* Utilities */,\n'
+ f'\t\t\t);\n'
+ f'\t\t\tpath = Shared;\n'
+ f'\t\t\tsourceTree = "<group>";\n'
+ f'\t\t}};\n'
+)
+
+src = src.replace(
+ "/* Begin PBXGroup section */",
+ "/* Begin PBXGroup section */\n" + new_groups,
+ 1
+)
+
+# Add Shared group as child of the main project group
+main_grp_match = re.search(r'mainGroup = ([0-9A-Fa-f]{24})', src)
+if main_grp_match:
+ main_grp_uid = main_grp_match.group(1).upper()
+ # Find that group's children list and prepend the Shared group
+ src = re.sub(
+ r'(' + main_grp_uid + r'\s*/\*[^*]*\*/\s*=\s*\{[^{]*isa = PBXGroup;[^{]*children = \()',
+ r'\1\n\t\t\t\t' + shared_grp_uid + r' /* Shared */,',
+ src
+ )
+else:
+ print("WARNING: could not find mainGroup — add Shared group manually in Xcode")
+
+with open(PROJ, "w") as f:
+ f.write(src)
+
+print("project.pbxproj updated.")
+print("Open Xcode and verify the Shared group appears with all 5 files under both targets.")