diff --git a/Sources/XCLinting/Rules/GroupsAreSortedRule.swift b/Sources/XCLinting/Rules/GroupsAreSortedRule.swift
index 109d753..a38ee50 100644
--- a/Sources/XCLinting/Rules/GroupsAreSortedRule.swift
+++ b/Sources/XCLinting/Rules/GroupsAreSortedRule.swift
@@ -1,38 +1,46 @@
 import Foundation
 import XcodeProj
 
-func groupsAreSortedRule(_ environment: XCLinter.Environment) -> [Violation] {
-	var violations = [Violation]()
-	for group in environment.project.pbxproj.groups {
-		violations.append(contentsOf: validateGroupIsSorted(group))
+struct GroupsAreSortedRule {
+	func run(_ environment: XCLinter.Environment) throws -> [Violation] {
+		var violations = [Violation]()
+		for group in environment.project.pbxproj.groups {
+			violations.append(contentsOf: validateGroupIsSorted(group))
+		}
+		return violations
 	}
-	return violations
-}
 
-private func validateGroupIsSorted(_ group: PBXGroup) -> [Violation] {
-	var violations = [Violation]()
-	let children = group.children.compactMap(\.path)
-	let sortedChildren = children.sorted { lhs, rhs in
-		lhs.compare(
-			rhs,
-			options: [
-				.numeric,
-				.caseInsensitive,
-				.widthInsensitive,
-				.forcedOrdering
-			],
-			locale: .current
-		) == .orderedAscending
-	}
-	
-	// some groups have no path, like the auto-generated "Products". Let's skip those, as they appear to not even always show up in the UI.
-	if children != sortedChildren, let path = group.path {
-		violations.append(.init("Group \"\(path)\" contains unsorted children"))
-	}
+	private func validateGroupIsSorted(_ group: PBXGroup) -> [Violation] {
+		var violations = [Violation]()
 
-	for childGroup in group.children.compactMap({ $0 as? PBXGroup }) {
-		violations.append(contentsOf: validateGroupIsSorted(childGroup))
-	}
+		// a path can contain components, but only the last matters from the UI's perspective
+		let children = group.children
+			.compactMap(\.path)
+			.map { $0.split(separator: "/").last }
+			.compactMap { $0 }
 
-	return violations
+		let sortedChildren = children.sorted { lhs, rhs in
+			lhs.compare(
+				rhs,
+				options: [
+					.numeric,
+					.caseInsensitive,
+					.widthInsensitive,
+					.forcedOrdering
+				],
+				locale: .current
+			) == .orderedAscending
+		}
+
+		// some groups have no path, like the auto-generated "Products". Let's skip those, as they appear to not even always show up in the UI.
+		if children != sortedChildren, let path = group.path {
+			violations.append(.init("Group \"\(path)\" contains unsorted children"))
+		}
+
+		for childGroup in group.children.compactMap({ $0 as? PBXGroup }) {
+			violations.append(contentsOf: validateGroupIsSorted(childGroup))
+		}
+
+		return violations
+	}
 }
diff --git a/Sources/XCLinting/XCLinter.swift b/Sources/XCLinting/XCLinter.swift
index a537e92..25598d0 100644
--- a/Sources/XCLinting/XCLinter.swift
+++ b/Sources/XCLinting/XCLinter.swift
@@ -76,7 +76,7 @@ extension XCLinter {
 	public static let ruleMap: [String: Rule] = [
 		"embedded_build_setting": { try EmbeddedBuildSettingsRule().run($0) },
 		"build_files_ordered": { try BuildFilesAreOrderedRule().run($0) },
-		"groups_sorted": { groupsAreSortedRule($0) },
+		"groups_sorted": { try GroupsAreSortedRule().run($0) },
 		"validate_build_settings": { try ValidateBuildSettingsRule().run($0) },
 		"implicit_dependencies": { try ImplicitDependenciesRule().run($0) },
 		"targets_use_xcconfig": { try TargetsUseXCConfigRule().run($0) },
diff --git a/Tests/XCLintTests/GroupsAreSortedRuleTests.swift b/Tests/XCLintTests/GroupsAreSortedRuleTests.swift
index 545a3ff..f82d611 100644
--- a/Tests/XCLintTests/GroupsAreSortedRuleTests.swift
+++ b/Tests/XCLintTests/GroupsAreSortedRuleTests.swift
@@ -15,7 +15,7 @@ final class GroupsAreSortedRuleTests: XCTestCase {
             configuration: Configuration()
         )
         
-        let violations = groupsAreSortedRule(env)
+		let violations = try GroupsAreSortedRule().run(env)
         XCTAssertTrue(violations.isEmpty)
     }
     
@@ -30,8 +30,23 @@ final class GroupsAreSortedRuleTests: XCTestCase {
             configuration: Configuration()
         )
         
-        let violations = groupsAreSortedRule(env)
+        let violations = try GroupsAreSortedRule().run(env)
         XCTAssertFalse(violations.isEmpty)
     }
+
+	func testProjectWithoutGroupsSortedByReference() throws {
+		let url = try Bundle.module.testDataURL(named: "SortedGroupsByReference.xcodeproj")
+
+		let project = try XcodeProj(pathString: url.path)
+
+		let env = XCLinter.Environment(
+			project: project,
+			projectRootURL: url,
+			configuration: Configuration()
+		)
+
+		let violations = try GroupsAreSortedRule().run(env)
+		XCTAssertTrue(violations.isEmpty)
+	}
 }
 
diff --git a/Tests/XCLintTests/TestData/SortedGroupsByReference.xcodeproj/project.pbxproj b/Tests/XCLintTests/TestData/SortedGroupsByReference.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..256e457
--- /dev/null
+++ b/Tests/XCLintTests/TestData/SortedGroupsByReference.xcodeproj/project.pbxproj
@@ -0,0 +1,198 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 56;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		C965BD2C2AE6E5D700E5836A /* StockMacOSAppApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C965BD2B2AE6E5D700E5836A /* StockMacOSAppApp.swift */; };
+		C965BD2E2AE6E5D700E5836A /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C965BD2D2AE6E5D700E5836A /* ContentView.swift */; };
+		C965BD302AE6E5D800E5836A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C965BD2F2AE6E5D800E5836A /* Assets.xcassets */; };
+		C965BD332AE6E5D800E5836A /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C965BD322AE6E5D800E5836A /* Preview Assets.xcassets */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+		C965BD282AE6E5D700E5836A /* .app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = .app; sourceTree = BUILT_PRODUCTS_DIR; };
+		C965BD2B2AE6E5D700E5836A /* StockMacOSAppApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = StockMacOSAppApp.swift; path = A/StockMacOSAppApp.swift; sourceTree = SOURCE_ROOT; };
+		C965BD2D2AE6E5D700E5836A /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = SOURCE_ROOT; };
+		C965BD2F2AE6E5D800E5836A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+		C965BD322AE6E5D800E5836A /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
+		C965BD342AE6E5D800E5836A /* StockMacOSApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = StockMacOSApp.entitlements; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		C965BD252AE6E5D700E5836A /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		C965BD1F2AE6E5D700E5836A = {
+			isa = PBXGroup;
+			children = (
+				C965BD2A2AE6E5D700E5836A /* StockMacOSApp */,
+				C965BD292AE6E5D700E5836A /* Products */,
+			);
+			sourceTree = "<group>";
+		};
+		C965BD292AE6E5D700E5836A /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				C965BD282AE6E5D700E5836A /* .app */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		C965BD2A2AE6E5D700E5836A /* StockMacOSApp */ = {
+			isa = PBXGroup;
+			children = (
+				C965BD2F2AE6E5D800E5836A /* Assets.xcassets */,
+				C965BD2D2AE6E5D700E5836A /* ContentView.swift */,
+				C965BD312AE6E5D800E5836A /* Preview Content */,
+				C965BD342AE6E5D800E5836A /* StockMacOSApp.entitlements */,
+				C965BD2B2AE6E5D700E5836A /* StockMacOSAppApp.swift */,
+			);
+			path = StockMacOSApp;
+			sourceTree = "<group>";
+		};
+		C965BD312AE6E5D800E5836A /* Preview Content */ = {
+			isa = PBXGroup;
+			children = (
+				C965BD322AE6E5D800E5836A /* Preview Assets.xcassets */,
+			);
+			path = "Preview Content";
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		C965BD272AE6E5D700E5836A /* StockMacOSApp */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = C965BD372AE6E5D800E5836A /* Build configuration list for PBXNativeTarget "StockMacOSApp" */;
+			buildPhases = (
+				C965BD242AE6E5D700E5836A /* Sources */,
+				C965BD252AE6E5D700E5836A /* Frameworks */,
+				C965BD262AE6E5D700E5836A /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = StockMacOSApp;
+			productName = StockMacOSApp;
+			productReference = C965BD282AE6E5D700E5836A /* .app */;
+			productType = "com.apple.product-type.application";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		C965BD202AE6E5D700E5836A /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				BuildIndependentTargetsInParallel = 1;
+				LastSwiftUpdateCheck = 1510;
+				LastUpgradeCheck = 1510;
+				TargetAttributes = {
+					C965BD272AE6E5D700E5836A = {
+						CreatedOnToolsVersion = 15.1;
+					};
+				};
+			};
+			buildConfigurationList = C965BD232AE6E5D700E5836A /* Build configuration list for PBXProject "SortedGroupsByReference" */;
+			compatibilityVersion = "Xcode 14.0";
+			developmentRegion = en;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+				Base,
+			);
+			mainGroup = C965BD1F2AE6E5D700E5836A;
+			productRefGroup = C965BD292AE6E5D700E5836A /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				C965BD272AE6E5D700E5836A /* StockMacOSApp */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		C965BD262AE6E5D700E5836A /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				C965BD332AE6E5D800E5836A /* Preview Assets.xcassets in Resources */,
+				C965BD302AE6E5D800E5836A /* Assets.xcassets in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		C965BD242AE6E5D700E5836A /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				C965BD2E2AE6E5D700E5836A /* ContentView.swift in Sources */,
+				C965BD2C2AE6E5D700E5836A /* StockMacOSAppApp.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+		C965BD352AE6E5D800E5836A /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+			};
+			name = Debug;
+		};
+		C965BD362AE6E5D800E5836A /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+			};
+			name = Release;
+		};
+		C965BD382AE6E5D800E5836A /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+			};
+			name = Debug;
+		};
+		C965BD392AE6E5D800E5836A /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		C965BD232AE6E5D700E5836A /* Build configuration list for PBXProject "SortedGroupsByReference" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				C965BD352AE6E5D800E5836A /* Debug */,
+				C965BD362AE6E5D800E5836A /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		C965BD372AE6E5D800E5836A /* Build configuration list for PBXNativeTarget "StockMacOSApp" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				C965BD382AE6E5D800E5836A /* Debug */,
+				C965BD392AE6E5D800E5836A /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = C965BD202AE6E5D700E5836A /* Project object */;
+}
diff --git a/Tests/XCLintTests/TestData/SortedGroupsByReference.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Tests/XCLintTests/TestData/SortedGroupsByReference.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..919434a
--- /dev/null
+++ b/Tests/XCLintTests/TestData/SortedGroupsByReference.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "self:">
+   </FileRef>
+</Workspace>
diff --git a/Tests/XCLintTests/TestData/SortedGroupsByReference.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Tests/XCLintTests/TestData/SortedGroupsByReference.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/Tests/XCLintTests/TestData/SortedGroupsByReference.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>IDEDidComputeMac32BitWarning</key>
+	<true/>
+</dict>
+</plist>