commit 86820a2aa18c3039a80113013f43a638e7c2758d
parent 8062849feb3bcefef09c9dcf3122da39e22b1755
Author: Michael Camilleri <[email protected]>
Date: Thu, 7 May 2026 18:49:13 +0900
Move debug puzzles to new top-level directory
Diffstat:
10 files changed, 85 insertions(+), 47 deletions(-)
diff --git a/Crossmate.xcodeproj/project.pbxproj b/Crossmate.xcodeproj/project.pbxproj
@@ -51,19 +51,17 @@
82918A74836E5076CBFA1592 /* SyncEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73DDDED719CFFDD6035C3B48 /* SyncEngine.swift */; };
8478F0BC0CA624C78DC0A3B5 /* ImportedBrowseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87B1BB8AB6309AF111671CB5 /* ImportedBrowseView.swift */; };
849970A21D62C34EC382A27E /* GameShareItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5ABB557BA10CBE9909056882 /* GameShareItem.swift */; };
+ 8B356C953DA0FAF149C3391A /* Puzzles in Resources */ = {isa = PBXBuildFile; fileRef = BA67C509B467132D1B7510A4 /* Puzzles */; };
8F5CB2F94E083D06D7E04280 /* PlayerSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20B331CC55827FEF3420ABCE /* PlayerSession.swift */; };
9789150602A3321D2E1E7E81 /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0BF60C84D92A9024AC1A53FC /* Media.xcassets */; };
978F91DBAE94BC5DA1D94705 /* DriveMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70AD1A006E6D03E4429E3BF0 /* DriveMonitor.swift */; };
- 97D77230A98330DCB757FA81 /* sample.xd in Resources */ = {isa = PBXBuildFile; fileRef = 5C63A148D98E2D37EABF2CF5 /* sample.xd */; };
98F8FBF324ED00D53FEBB1DB /* Game.swift in Sources */ = {isa = PBXBuildFile; fileRef = 465F2BB469EFE84CF3733398 /* Game.swift */; };
9CB8808193A4A106D721D767 /* XDFileType.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC61E2582D94B1E6EC67136 /* XDFileType.swift */; };
A98382E7659991FAF0F4ED0A /* AuthorIdentityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 457B06DBFDC358D213A7CE54 /* AuthorIdentityTests.swift */; };
AA28425BD26F72A9E2B58742 /* BundledBrowseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A4B7C6A8A23C6E4CCEC759F /* BundledBrowseView.swift */; };
AA38A51862FC0AB8F7D34899 /* NYTToXDConverterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C54223FED97577A593B7964E /* NYTToXDConverterTests.swift */; };
- AA992F67F509EC8EFDDFC7CB /* morning.xd in Resources */ = {isa = PBXBuildFile; fileRef = 0B73A791FD061430AE286E11 /* morning.xd */; };
AB05765D2C3F4841026344E5 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AF633D73818BD59F759FAC4 /* AboutView.swift */; };
AF4F1AE2A1F94E92C785C524 /* Square.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB851649DE78AAAC5A928C52 /* Square.swift */; };
- B42454D72FAA219D60DEA334 /* garden.xd in Resources */ = {isa = PBXBuildFile; fileRef = 50992CDA4082429EBB17F65C /* garden.xd */; };
B762200F54C52E8377A80D15 /* NYTToXDConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6F111BE8750697C4BC7A17 /* NYTToXDConverter.swift */; };
B94919176DEC6EC31637B037 /* ClueList.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9BD3F7EAFD344D8E10E8C3B /* ClueList.swift */; };
BCB9A4D5E06EE5006186465D /* ShareController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C74683332956B0D1CA37589 /* ShareController.swift */; };
@@ -108,7 +106,6 @@
/* Begin PBXFileReference section */
07C57DEE9E0EFA684D8BD00B /* NYTLoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NYTLoginView.swift; sourceTree = "<group>"; };
- 0B73A791FD061430AE286E11 /* morning.xd */ = {isa = PBXFileReference; path = morning.xd; sourceTree = "<group>"; };
0BF60C84D92A9024AC1A53FC /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Media.xcassets; sourceTree = "<group>"; };
0C0A7348E1283E7CD2486E2A /* RecordSerializer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordSerializer.swift; sourceTree = "<group>"; };
14F2AC5C3B50F4178859E9AC /* CrossmateApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CrossmateApp.swift; sourceTree = "<group>"; };
@@ -135,12 +132,10 @@
4AF633D73818BD59F759FAC4 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = "<group>"; };
4DC7784917397BCD6B8D679D /* PuzzleCatalog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PuzzleCatalog.swift; sourceTree = "<group>"; };
4F4EBC0F07FF815274C028CA /* XDAcceptTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XDAcceptTests.swift; sourceTree = "<group>"; };
- 50992CDA4082429EBB17F65C /* garden.xd */ = {isa = PBXFileReference; path = garden.xd; sourceTree = "<group>"; };
52B8E26067849A63758DDEA4 /* MoveBuffer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoveBuffer.swift; sourceTree = "<group>"; };
543481AA9FA32BF14076EB1C /* MoveLogTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoveLogTests.swift; sourceTree = "<group>"; };
56BC76178319D0D669CD50FF /* CloudService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudService.swift; sourceTree = "<group>"; };
5ABB557BA10CBE9909056882 /* GameShareItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameShareItem.swift; sourceTree = "<group>"; };
- 5C63A148D98E2D37EABF2CF5 /* sample.xd */ = {isa = PBXFileReference; path = sample.xd; sourceTree = "<group>"; };
5C74683332956B0D1CA37589 /* ShareController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareController.swift; sourceTree = "<group>"; };
64C8064F04FC6177D987ACA2 /* Puzzle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Puzzle.swift; sourceTree = "<group>"; };
666155B0C17A8CED11C45A80 /* GamePlayerColorStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GamePlayerColorStore.swift; sourceTree = "<group>"; };
@@ -174,6 +169,7 @@
B369788E0FEA0DCE1B125816 /* PUZToXDConverter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PUZToXDConverter.swift; sourceTree = "<group>"; };
B689A7138429641E61E9E558 /* Crossmate.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = Crossmate.app; sourceTree = BUILT_PRODUCTS_DIR; };
B9031A1574C21866940F6A2C /* XD.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XD.swift; sourceTree = "<group>"; };
+ BA67C509B467132D1B7510A4 /* Puzzles */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Puzzles; sourceTree = SOURCE_ROOT; };
BAC1B64755AE15CF45350DBB /* MoveBufferTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoveBufferTests.swift; sourceTree = "<group>"; };
BF6F111BE8750697C4BC7A17 /* NYTToXDConverter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NYTToXDConverter.swift; sourceTree = "<group>"; };
BFC1C59A30FB2571598273E4 /* GameMutatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameMutatorTests.swift; sourceTree = "<group>"; };
@@ -303,7 +299,6 @@
0BF60C84D92A9024AC1A53FC /* Media.xcassets */,
41DB2417FF67A47FE6890256 /* Models */,
565DBAFC8DB2589B3F0AF90E /* Persistence */,
- F53443E4827221C62DB7AA36 /* Resources */,
D8F0E3376B2616B4E917129C /* Services */,
074C2962E79CAE6C0EA6431A /* Sync */,
84445EA9CACB6AAAEDE6965F /* Views */,
@@ -368,6 +363,7 @@
C5342A31D253372339517EEE = {
isa = PBXGroup;
children = (
+ BA67C509B467132D1B7510A4 /* Puzzles */,
5770CE69DB2B0B7462FACE53 /* Crossmate */,
6F470E54D9E6E99FCEA893D1 /* Generated */,
9BF7383FE2AB07F12434C013 /* Shared */,
@@ -396,16 +392,6 @@
path = Services;
sourceTree = "<group>";
};
- F53443E4827221C62DB7AA36 /* Resources */ = {
- isa = PBXGroup;
- children = (
- 50992CDA4082429EBB17F65C /* garden.xd */,
- 0B73A791FD061430AE286E11 /* morning.xd */,
- 5C63A148D98E2D37EABF2CF5 /* sample.xd */,
- );
- path = Resources;
- sourceTree = "<group>";
- };
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -490,9 +476,7 @@
buildActionMask = 2147483647;
files = (
9789150602A3321D2E1E7E81 /* Media.xcassets in Resources */,
- B42454D72FAA219D60DEA334 /* garden.xd in Resources */,
- AA992F67F509EC8EFDDFC7CB /* morning.xd in Resources */,
- 97D77230A98330DCB757FA81 /* sample.xd in Resources */,
+ 8B356C953DA0FAF149C3391A /* Puzzles in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
diff --git a/Crossmate/Models/PuzzleCatalog.swift b/Crossmate/Models/PuzzleCatalog.swift
@@ -1,7 +1,7 @@
import Foundation
-/// Knows about all bundled `.xd` puzzle resources. Used by the new-game
-/// picker to list available puzzles.
+/// Knows about packaged `.xd` puzzle resources. Used by the new-game picker
+/// to list available puzzles.
struct PuzzleCatalog {
struct Entry: Identifiable {
let id: String // resource name (e.g. "sample")
@@ -10,7 +10,15 @@ struct PuzzleCatalog {
}
static func bundledPuzzles() -> [Entry] {
- guard let urls = Bundle.main.urls(forResourcesWithExtension: "xd", subdirectory: nil) else {
+ puzzles(in: "Puzzles/Bundled")
+ }
+
+ static func debugPuzzles() -> [Entry] {
+ puzzles(in: "Puzzles/Debug")
+ }
+
+ private static func puzzles(in subdirectory: String) -> [Entry] {
+ guard let urls = Bundle.main.urls(forResourcesWithExtension: "xd", subdirectory: subdirectory) else {
return []
}
return urls.compactMap { url in
diff --git a/Crossmate/Models/PuzzleSource.swift b/Crossmate/Models/PuzzleSource.swift
@@ -2,6 +2,7 @@ import Foundation
enum PuzzleSource: String, CaseIterable, Identifiable {
case bundled
+ case debug
case imported
case nyt
@@ -10,6 +11,7 @@ enum PuzzleSource: String, CaseIterable, Identifiable {
var title: String {
switch self {
case .bundled: "Bundled"
+ case .debug: "Debug"
case .imported: "Imported"
case .nyt: "NYT"
}
diff --git a/Crossmate/Views/BundledBrowseView.swift b/Crossmate/Views/BundledBrowseView.swift
@@ -18,3 +18,22 @@ struct BundledBrowseView: View {
}
}
}
+
+struct DebugBrowseView: View {
+ let onSelected: (String) -> Void
+
+ private var puzzles: [PuzzleCatalog.Entry] {
+ PuzzleCatalog.debugPuzzles()
+ }
+
+ var body: some View {
+ List(puzzles) { entry in
+ Button {
+ onSelected(entry.source)
+ } label: {
+ Text(entry.title)
+ .foregroundStyle(.primary)
+ }
+ }
+ }
+}
diff --git a/Crossmate/Views/NewGameSheet.swift b/Crossmate/Views/NewGameSheet.swift
@@ -6,12 +6,21 @@ struct NewGameSheet: View {
@Environment(\.dismiss) private var dismiss
@Environment(NYTAuthService.self) private var nytAuth
@AppStorage("lastPuzzleSource") private var storedSource: PuzzleSource = .bundled
+ @AppStorage("debugMode") private var debugMode = false
@State private var selection: PuzzleSource = .bundled
@State private var duplicateSource: String?
@State private var createError: String?
private var availableSources: [PuzzleSource] {
- nytAuth.isSignedIn ? [.bundled, .imported, .nyt] : [.bundled, .imported]
+ var sources: [PuzzleSource] = [.bundled]
+ if debugMode {
+ sources.append(.debug)
+ }
+ sources.append(.imported)
+ if nytAuth.isSignedIn {
+ sources.append(.nyt)
+ }
+ return sources
}
var body: some View {
@@ -29,6 +38,8 @@ struct NewGameSheet: View {
switch selection {
case .bundled:
BundledBrowseView(onSelected: handleSelected)
+ case .debug:
+ DebugBrowseView(onSelected: handleSelected)
case .imported:
ImportedBrowseView(onSelected: handleSelected)
case .nyt:
diff --git a/Crossmate/Views/SettingsView.swift b/Crossmate/Views/SettingsView.swift
@@ -6,9 +6,11 @@ struct SettingsView: View {
@Environment(\.dismiss) private var dismiss
@Environment(\.resetDatabase) private var resetDatabase
+ @AppStorage("debugMode") private var debugMode = false
@State private var showingNYTLogin = false
@State private var showResetConfirmation = false
@State private var externalSource: ExternalSource?
+ @State private var easterEggTaps = 0
var body: some View {
@Bindable var preferences = preferences
@@ -35,32 +37,34 @@ struct SettingsView: View {
}
}
- Section("Debugging") {
- Toggle("Enable iCloud Sync", isOn: $preferences.isICloudSyncEnabled)
+ if debugMode {
+ Section("Debugging") {
+ Toggle("Enable iCloud Sync", isOn: $preferences.isICloudSyncEnabled)
- NavigationLink("iCloud Diagnostics") {
- DiagnosticsView()
- }
- NavigationLink("Share Diagnostics") {
- ShareDiagnosticsView()
- }
- NavigationLink("Record Editor") {
- RecordEditorView()
- }
+ NavigationLink("iCloud Diagnostics") {
+ DiagnosticsView()
+ }
+ NavigationLink("Share Diagnostics") {
+ ShareDiagnosticsView()
+ }
+ NavigationLink("Record Editor") {
+ RecordEditorView()
+ }
- Button("Reset Database", role: .destructive) {
- showResetConfirmation = true
- }
- .alert(
- "Delete all games?",
- isPresented: $showResetConfirmation
- ) {
- Button("Delete All Games", role: .destructive) {
- Task { await resetDatabase?() }
+ Button("Reset Database", role: .destructive) {
+ showResetConfirmation = true
+ }
+ .alert(
+ "Delete all games?",
+ isPresented: $showResetConfirmation
+ ) {
+ Button("Delete All Games", role: .destructive) {
+ Task { await resetDatabase?() }
+ }
+ Button("Cancel", role: .cancel) {}
+ } message: {
+ Text("This removes every game and clears the sync state on this device. Games on other devices and in iCloud are not affected.")
}
- Button("Cancel", role: .cancel) {}
- } message: {
- Text("This removes every game and clears the sync state on this device. Games on other devices and in iCloud are not affected.")
}
}
@@ -73,6 +77,13 @@ struct SettingsView: View {
.frame(maxWidth: .infinity)
.multilineTextAlignment(.center)
.padding(.top, 24)
+ .onTapGesture {
+ easterEggTaps += 1
+ if easterEggTaps >= 4 {
+ debugMode.toggle()
+ easterEggTaps = 0
+ }
+ }
}
}
.navigationTitle("Settings")
diff --git a/Crossmate/Resources/garden.xd b/Puzzles/Debug/garden.xd
diff --git a/Crossmate/Resources/morning.xd b/Puzzles/Debug/morning.xd
diff --git a/Crossmate/Resources/sample.xd b/Puzzles/Debug/sample.xd
diff --git a/project.yml b/project.yml
@@ -26,6 +26,9 @@ targets:
sources:
- Crossmate
- Shared
+ - path: Puzzles
+ type: folder
+ buildPhase: resources
info:
path: Crossmate/Info.plist
properties: