├── .all-contributorsrc
├── .editorconfig
├── .gitattributes
├── .github
├── .commitlint.rules.js
├── FUNDING.yml
├── ISSUE_TEMPLATE.md
├── PULL_REQUEST_TEMPLATE.md
├── invite-contributors.yml
└── workflows
│ ├── conventional-pr.yml
│ ├── release.yml
│ └── xcodeproj.yml
├── .gitignore
├── .mise.toml
├── .mise
└── tasks
│ ├── build
│ ├── build-linux
│ ├── lint
│ ├── lint-fix
│ ├── test
│ └── test-linux
├── .spi.yml
├── .swift-version
├── .swiftformat
├── .swiftlint.yml
├── Assets
└── diagram.png
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── Documentation
├── README.md
├── getting-started.md
└── migration-guides.md
├── Fixtures
├── FileSharedAcrossTargets
│ ├── FileSharedAcrossTargets.xcodeproj
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace
│ │ │ ├── contents.xcworkspacedata
│ │ │ └── xcshareddata
│ │ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcuserdata
│ │ │ └── pepicrft.xcuserdatad
│ │ │ └── xcschemes
│ │ │ └── xcschememanagement.plist
│ ├── FileSharedAcrossTargets
│ │ ├── FileSharedAcrossTargets.h
│ │ └── SharedHeader.h
│ └── FileSharedAcrossTargetsTests
│ │ └── FileSharedAcrossTargetsTests.swift
├── Schemes
│ ├── AppClip.xcscheme
│ ├── BuildArchitectures.xcscheme
│ ├── MinimalInformation.xcscheme
│ ├── NoBlueprintID.xcscheme
│ ├── RunPostActionsOnFailure.xcscheme
│ ├── RunnableWithoutBuildableReference.xcscheme
│ └── xcschememanagement.plist
├── SynchronizedRootGroups
│ ├── .gitignore
│ ├── SynchronizedRootGroups.xcodeproj
│ │ ├── project.pbxproj
│ │ └── xcuserdata
│ │ │ └── pepicrft.xcuserdatad
│ │ │ └── xcschemes
│ │ │ └── xcschememanagement.plist
│ └── SynchronizedRootGroups
│ │ ├── Exception
│ │ └── Exception.swift
│ │ └── SynchronizedRootGroups.swift
├── TargetWithCustomBuildRules
│ ├── TargetWithCustomBuildRules.xcodeproj
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace
│ │ │ ├── contents.xcworkspacedata
│ │ │ ├── xcshareddata
│ │ │ │ └── IDEWorkspaceChecks.plist
│ │ │ └── xcuserdata
│ │ │ │ └── pepicrft.xcuserdatad
│ │ │ │ └── UserInterfaceState.xcuserstate
│ │ └── xcuserdata
│ │ │ └── pepicrft.xcuserdatad
│ │ │ └── xcschemes
│ │ │ └── xcschememanagement.plist
│ └── TargetWithCustomBuildRules
│ │ ├── TargetWithCustomBuildRules.h
│ │ └── TargetWithCustomBuildRules.m
├── WithoutWorkspace
│ ├── .gitignore
│ ├── Package.swift
│ ├── Sources
│ │ └── WithoutWorkspace
│ │ │ └── WithoutWorkspace.swift
│ ├── Tests
│ │ └── WithoutWorkspaceTests
│ │ │ └── WithoutWorkspaceTests.swift
│ └── WithoutWorkspace.xcodeproj
│ │ ├── WithoutWorkspaceTests_Info.plist
│ │ ├── WithoutWorkspace_Info.plist
│ │ ├── project.pbxproj
│ │ └── xcshareddata
│ │ └── xcschemes
│ │ ├── WithoutWorkspace-Package.xcscheme
│ │ └── xcschememanagement.plist
├── Workspace.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── WorkspaceSettings
│ ├── Default.xcsettings
│ ├── OriginalAbsoluteDerivedData.xcsettings
│ ├── OriginalBuildSystem.xcsettings
│ └── OriginalRelativeDerivedData.xcsettings
├── XCConfigs
│ ├── Children.xcconfig
│ └── Parent.xcconfig
├── Xcode16
│ ├── README.md
│ ├── Test.xcodeproj
│ │ └── project.pbxproj
│ ├── Test
│ │ ├── Assets.xcassets
│ │ │ ├── AccentColor.colorset
│ │ │ │ └── Contents.json
│ │ │ ├── AppIcon.appiconset
│ │ │ │ └── Contents.json
│ │ │ └── Contents.json
│ │ ├── ContentView.swift
│ │ ├── Preview Content
│ │ │ └── Preview Assets.xcassets
│ │ │ │ └── Contents.json
│ │ ├── Test.entitlements
│ │ └── TestApp.swift
│ └── copy.xcodeproj
│ │ └── project.pbxproj
├── Xcode16ProjectReferenceOrder
│ ├── Sources
│ │ └── AppDelegate.swift
│ ├── Test.xcodeproj
│ │ ├── project.pbxproj
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── App.xcscheme
│ └── Wrong.xcodeproj
│ │ ├── project.pbxproj
│ │ └── xcshareddata
│ │ └── xcschemes
│ │ └── App.xcscheme
├── dummy.framework
│ └── .gitkeep
└── iOS
│ ├── AppWithExtensions
│ ├── AppWithExtensions.xcodeproj
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace
│ │ │ ├── contents.xcworkspacedata
│ │ │ └── xcshareddata
│ │ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ ├── AppWithExtensions.xcscheme
│ │ │ ├── MessagesExtension.xcscheme
│ │ │ ├── TodayViewExtension.xcscheme
│ │ │ ├── WatchApp (Notification).xcscheme
│ │ │ └── WatchApp.xcscheme
│ ├── AppWithExtensions
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets
│ │ │ ├── AppIcon.appiconset
│ │ │ │ └── Contents.json
│ │ │ └── Contents.json
│ │ ├── Base.lproj
│ │ │ └── LaunchScreen.storyboard
│ │ ├── ContentView.swift
│ │ ├── Info.plist
│ │ ├── Preview Content
│ │ │ └── Preview Assets.xcassets
│ │ │ │ └── Contents.json
│ │ └── SceneDelegate.swift
│ ├── MessagesExtension
│ │ ├── Assets.xcassets
│ │ │ ├── Contents.json
│ │ │ └── iMessage App Icon.stickersiconset
│ │ │ │ └── Contents.json
│ │ ├── Base.lproj
│ │ │ └── MainInterface.storyboard
│ │ ├── Info.plist
│ │ └── MessagesViewController.swift
│ ├── TodayViewExtension
│ │ ├── Base.lproj
│ │ │ └── MainInterface.storyboard
│ │ ├── Info.plist
│ │ └── TodayViewController.swift
│ ├── WatchApp Extension
│ │ ├── Assets.xcassets
│ │ │ ├── Complication.complicationset
│ │ │ │ ├── Circular.imageset
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── Contents.json
│ │ │ │ ├── Extra Large.imageset
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── Graphic Bezel.imageset
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── Graphic Circular.imageset
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── Graphic Corner.imageset
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── Graphic Large Rectangular.imageset
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── Modular.imageset
│ │ │ │ │ └── Contents.json
│ │ │ │ └── Utilitarian.imageset
│ │ │ │ │ └── Contents.json
│ │ │ └── Contents.json
│ │ ├── ContentView.swift
│ │ ├── ExtensionDelegate.swift
│ │ ├── HostingController.swift
│ │ ├── Info.plist
│ │ ├── NotificationController.swift
│ │ ├── NotificationView.swift
│ │ ├── Preview Content
│ │ │ └── Preview Assets.xcassets
│ │ │ │ └── Contents.json
│ │ └── PushNotificationPayload.apns
│ └── WatchApp
│ │ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ └── Contents.json
│ │ └── Contents.json
│ │ ├── Base.lproj
│ │ └── Interface.storyboard
│ │ └── Info.plist
│ ├── BuildSettings.xcodeproj
│ └── project.pbxproj
│ ├── MyLocalPackage
│ ├── .gitignore
│ ├── Package.swift
│ ├── README.md
│ ├── Sources
│ │ └── MyLocalPackage
│ │ │ └── MyLocalPackage.swift
│ └── Tests
│ │ ├── LinuxMain.swift
│ │ └── MyLocalPackageTests
│ │ ├── MyLocalPackageTests.swift
│ │ └── XCTestManifests.swift
│ ├── MyOtherLocalPackage
│ └── MyOtherLocalPackage
│ │ ├── .gitignore
│ │ ├── Package.swift
│ │ ├── README.md
│ │ ├── Sources
│ │ └── MyOtherLocalPackage
│ │ │ └── MyOtherLocalPackage.swift
│ │ └── Tests
│ │ ├── LinuxMain.swift
│ │ └── MyOtherLocalPackageTests
│ │ ├── MyLocalPackageTests.swift
│ │ └── XCTestManifests.swift
│ ├── Project.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ ├── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── swiftpm
│ │ │ │ └── Package.resolved
│ │ └── xcuserdata
│ │ │ └── pepicrft.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ ├── xcshareddata
│ │ ├── xcdebugger
│ │ │ └── Breakpoints_v2.xcbkptlist
│ │ └── xcschemes
│ │ │ └── iOS.xcscheme
│ └── xcuserdata
│ │ ├── username1.xcuserdatad
│ │ ├── xcdebugger
│ │ │ └── Breakpoints_v2.xcbkptlist
│ │ └── xcschemes
│ │ │ ├── iOS-debug.xcscheme
│ │ │ ├── iOS-other.xcscheme
│ │ │ ├── iOS-release.xcscheme
│ │ │ └── xcschememanagement.plist
│ │ ├── username2.xcuserdatad
│ │ └── xcschemes
│ │ │ └── iOSTests.xcscheme
│ │ └── username3.xcuserdatad
│ │ └── xcschemes
│ │ └── custom-scheme.xcscheme
│ ├── ProjectWithRelativeXCLocalSwiftPackageReference
│ └── ProjectWithRelativeXCLocalSwiftPackageReference.xcodeproj
│ │ └── project.pbxproj
│ ├── ProjectWithXCLocalSwiftPackageReference.xcodeproj
│ └── project.pbxproj
│ ├── ProjectWithXCLocalSwiftPackageReferences.xcodeproj
│ └── project.pbxproj
│ ├── ProjectWithoutProductsGroup.xcodeproj
│ ├── project.pbxproj
│ └── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
│ ├── SameName
│ └── SameName.h
│ ├── iOS.xctestplan
│ ├── iOS
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ ├── Info.plist
│ ├── Model.xcdatamodeld
│ │ └── Model.xcdatamodel
│ │ │ └── contents
│ ├── Private.h
│ ├── Protected.h
│ ├── Public.h
│ └── ViewController.swift
│ └── iOSTests
│ ├── Info.plist
│ └── iOSTests.swift
├── LICENSE.md
├── Package.resolved
├── Package.swift
├── README.md
├── Scripts
└── dump-known-file-extensions.py
├── Sources
└── XcodeProj
│ ├── Errors
│ └── Errors.swift
│ ├── Extensions
│ ├── AEXML+XcodeFormat.swift
│ ├── Array+Extras.swift
│ ├── Bool+Extras.swift
│ ├── KeyedDecodingContainer+Additions.swift
│ ├── NSRecursiveLock+Sync.swift
│ ├── Path+Extras.swift
│ ├── String+Utils.swift
│ └── String+md5.swift
│ ├── Objects
│ ├── BuildPhase
│ │ ├── BuildFileSetting.swift
│ │ ├── BuildPhase.swift
│ │ ├── PBXBuildFile.swift
│ │ ├── PBXBuildPhase.swift
│ │ ├── PBXBuildRule.swift
│ │ ├── PBXCopyFilesBuildPhase.swift
│ │ ├── PBXFrameworksBuildPhase.swift
│ │ ├── PBXHeadersBuildPhase.swift
│ │ ├── PBXResourcesBuildPhase.swift
│ │ ├── PBXRezBuildPhase.swift
│ │ ├── PBXShellScriptBuildPhase.swift
│ │ └── PBXSourcesBuildPhase.swift
│ ├── Configuration
│ │ ├── BuildSettings.swift
│ │ ├── XCBuildConfiguration.swift
│ │ └── XCConfigurationList.swift
│ ├── Files
│ │ ├── PBXContainerItem.swift
│ │ ├── PBXContainerItemProxy.swift
│ │ ├── PBXFileElement.swift
│ │ ├── PBXFileReference.swift
│ │ ├── PBXFileSystemSynchronizedBuildFileExceptionSet.swift
│ │ ├── PBXFileSystemSynchronizedExceptionSet.swift
│ │ ├── PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet.swift
│ │ ├── PBXFileSystemSynchronizedRootGroup.swift
│ │ ├── PBXGroup.swift
│ │ ├── PBXSourceTree.swift
│ │ ├── PBXVariantGroup.swift
│ │ └── XCVersionGroup.swift
│ ├── Project
│ │ ├── PBXObject.swift
│ │ ├── PBXObjectDictionaryEntry.swift
│ │ ├── PBXObjectReference.swift
│ │ ├── PBXObjects.swift
│ │ ├── PBXOutputSettings.swift
│ │ ├── PBXProj.swift
│ │ ├── PBXProjEncoder.swift
│ │ ├── PBXProject.swift
│ │ └── ProjectAttribute.swift
│ ├── Sourcery
│ │ ├── Equality.generated.swift
│ │ └── Sourcery.swift
│ ├── SwiftPackage
│ │ ├── XCLocalSwiftPackageReference.swift
│ │ ├── XCRemoteSwiftPackageReference.swift
│ │ └── XCSwiftPackageProductDependency.swift
│ └── Targets
│ │ ├── PBXAggregateTarget.swift
│ │ ├── PBXLegacyTarget.swift
│ │ ├── PBXNativeTarget.swift
│ │ ├── PBXProductType.swift
│ │ ├── PBXReferenceProxy.swift
│ │ ├── PBXTarget.swift
│ │ └── PBXTargetDependency.swift
│ ├── Project
│ ├── WorkspaceSettings.swift
│ ├── XCBreakpointList.swift
│ ├── XCDebugger.swift
│ ├── XCSharedData.swift
│ ├── XCUserData.swift
│ ├── Xcode.swift
│ └── XcodeProj.swift
│ ├── Protocols
│ └── Writable.swift
│ ├── Scheme
│ ├── XCScheme+AditionalOption.swift
│ ├── XCScheme+AnalyzeAction.swift
│ ├── XCScheme+ArchiveAction.swift
│ ├── XCScheme+BuildAction.swift
│ ├── XCScheme+BuildableProductRunnable.swift
│ ├── XCScheme+BuildableReference.swift
│ ├── XCScheme+CommandLineArguments.swift
│ ├── XCScheme+EnvironmentVariable.swift
│ ├── XCScheme+ExecutionAction.swift
│ ├── XCScheme+LaunchAction.swift
│ ├── XCScheme+LocationScenarioReference.swift
│ ├── XCScheme+PathRunnable.swift
│ ├── XCScheme+ProfileAction.swift
│ ├── XCScheme+RemoteRunnable.swift
│ ├── XCScheme+Runnable.swift
│ ├── XCScheme+SerialAction.swift
│ ├── XCScheme+StoreKitConfigurationFileReference.swift
│ ├── XCScheme+TestAction.swift
│ ├── XCScheme+TestItem.swift
│ ├── XCScheme+TestParallelization.swift
│ ├── XCScheme+TestPlanReference.swift
│ ├── XCScheme+TestableReference.swift
│ ├── XCScheme.swift
│ └── XCSchemeManagement.swift
│ ├── Utils
│ ├── BuildSettingsProvider.swift
│ ├── CommentedString.swift
│ ├── Decoders.swift
│ ├── JSONDecoding.swift
│ ├── PBXBatchUpdater.swift
│ ├── PlistDecoding.swift
│ ├── PlistValue.swift
│ ├── ReferenceGenerator.swift
│ └── XCConfig.swift
│ └── Workspace
│ ├── XCWorkspace.swift
│ ├── XCWorkspaceData.swift
│ ├── XCWorkspaceDataElement.swift
│ ├── XCWorkspaceDataElementLocationType.swift
│ ├── XCWorkspaceDataFileRef.swift
│ └── XCWorkspaceDataGroup.swift
├── Templates
└── Equality.stencil
├── Tests
└── XcodeProjTests
│ ├── Extensions
│ ├── AEXML+XcodeFormatTests.swift
│ ├── PathExtrasTests.swift
│ ├── XCTestCase+Assertions.swift
│ ├── XCTestCase+Shell.swift
│ └── XCTestCase+Temporary.swift
│ ├── Objects
│ ├── BuildPhase
│ │ ├── BuildPhaseTests.swift
│ │ ├── PBXBuildFileTests.swift
│ │ ├── PBXBuildPhaseTests.swift
│ │ ├── PBXBuildRuleTests.swift
│ │ ├── PBXCopyFilesBuildPhaseTests.swift
│ │ ├── PBXFrameworksBuildPhaseTests.swift
│ │ ├── PBXHeadersBuildPhaseTests.swift
│ │ ├── PBXResourcesBuildPhaseTests.swift
│ │ ├── PBXRezBuildPhaseTests.swift
│ │ ├── PBXShellScriptBuildPhaseTests.swift
│ │ ├── PBXSourcesBuildPhase+Fixtures.swift
│ │ └── PBXSourcesBuildPhaseTests.swift
│ ├── Configuration
│ │ ├── BuildFileSettingTests.swift
│ │ ├── BuildSettingTests.swift
│ │ ├── XCBuildConfiguration+Fixtures.swift
│ │ ├── XCBuildConfigurationTests.swift
│ │ ├── XCConfigurationList+Fixtures.swift
│ │ └── XCConfigurationListTests.swift
│ ├── Files
│ │ ├── PBXContainerItemProxyTests.swift
│ │ ├── PBXFileElementTests.swift
│ │ ├── PBXFileReference+Fixtures.swift
│ │ ├── PBXFileReferenceTests.swift
│ │ ├── PBXFileSystemSynchronizedBuildFileExceptionSet+Fixtures.swift
│ │ ├── PBXFileSystemSynchronizedBuildFileExceptionSetTests.swift
│ │ ├── PBXFileSystemSynchronizedRootGroup+Fixtures.swift
│ │ ├── PBXFileSystemSynchronizedRootGroupTests.swift
│ │ ├── PBXGroup+Fixtures.swift
│ │ ├── PBXGroupTests.swift
│ │ ├── PBXSourceTreeTests.swift
│ │ ├── PBXVariantGroupTests.swift
│ │ ├── XCVersionGroup+Fixtures.swift
│ │ └── XCVersionGroupTests.swift
│ ├── Project
│ │ ├── PBXOutputSettingsTests.swift
│ │ ├── PBXProj+Fixtures.swift
│ │ ├── PBXProj+XCTest.swift
│ │ ├── PBXProjEncoderTests.swift
│ │ ├── PBXProjIntegrationTests.swift
│ │ ├── PBXProjObjectsHelpersTests.swift
│ │ ├── PBXProject+Fixtures.swift
│ │ └── PBXProjectTests.swift
│ ├── SwiftPackage
│ │ ├── XCLocalSwiftPackageReferenceTests.swift
│ │ ├── XCRemoteSwiftPackageReferenceTests.swift
│ │ └── XCSwiftPackageProductDependencyTests.swift
│ └── Targets
│ │ ├── PBXAggregateTargetTests.swift
│ │ ├── PBXLegacyTargetTests.swift
│ │ ├── PBXNativeTargetTests.swift
│ │ ├── PBXProductTypeTests.swift
│ │ ├── PBXReferenceProxyTests.swift
│ │ ├── PBXTarget+Fixtures.swift
│ │ ├── PBXTargetDependencyTests.swift
│ │ └── PBXTargetTests.swift
│ ├── Project
│ ├── WorkspaceSettingsTests.swift
│ ├── XCBreakpointListTests.swift
│ ├── XCUserDataTests.swift
│ └── XcodeProjIntegrationTests.swift
│ ├── Scheme
│ ├── XCScheme+BuildableReferenceTests.swift
│ ├── XCSchemeManagementTests.swift
│ └── XCSchemeTests.swift
│ ├── Tests
│ ├── Fixtures.swift
│ └── testWrite.swift
│ ├── Utils
│ ├── BuildSettingsProviderTests.swift
│ ├── CommentedStringTests.swift
│ ├── ObjectReferenceTests.swift
│ ├── PBXBatchUpdaterTests.swift
│ ├── PlistValueTests.swift
│ ├── ReferenceGeneratorTests.swift
│ └── XCConfigTests.swift
│ └── Workspace
│ ├── XCWorkspaceDataElementTests.swift
│ ├── XCWorkspaceDataTests.swift
│ └── XCWorkspaceTests.swift
├── Tuist.swift
├── cliff.toml
└── renovate.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [*.{json,js,jsx,html,css}]
12 | indent_style = space
13 | indent_size = 2
14 |
15 | [*.md]
16 | trim_trailing_whitespace = false
17 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | CHANGELOG merge=union
2 |
--------------------------------------------------------------------------------
/.github/.commitlint.rules.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | rules: {
3 | 'header-max-length': [2, 'always', 200],
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | open_collective: tuistapp
2 | github: tuist
3 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## Context 🕵️♀️
2 | > Provide information that helps with understanding your issue. For example your use case that the project doesn't cover, what you were doing when you found the bug... You can also provide the version of the library that you were using, how you integrated it with your project, the platform version...
3 |
4 | ## What 🌱
5 | > Describe here your issue, a bug you found, an idea that you had, something that you think can be improved...
6 |
7 | ## Proposal 🎉
8 | > Attach your own proposal *(if you have it)*. We'll discuss in on the issue to find the best one that fits into the library.
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | Resolves https://github.com/tuist/xcodeproj/issues/YYY
2 |
3 | ### Short description 📝
4 | > Describe here the purpose of your PR.
5 |
6 | ### Solution 📦
7 | > Describe the solution you came up with and the reasons that led you to that solution. If you thought about other solutions don't forget about mentioning them.
8 |
9 | ### Implementation 👩💻👨💻
10 | > Detail in a checklist the steps that you took to implement the PR.
11 |
12 | - [ ] Step 1
13 | - [ ] Step 2
14 |
--------------------------------------------------------------------------------
/.github/invite-contributors.yml:
--------------------------------------------------------------------------------
1 | team: Contributors
2 |
--------------------------------------------------------------------------------
/.github/workflows/conventional-pr.yml:
--------------------------------------------------------------------------------
1 | name: conventional-pr
2 | on:
3 | pull_request:
4 | branches:
5 | - main
6 | - master
7 | types:
8 | - opened
9 | - edited
10 | - synchronize
11 | jobs:
12 | lint-pr:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v2
16 | - uses: CondeNast/conventional-pull-request-action@v0.2.0
17 | with:
18 | commitTitleMatch: false
19 | commitlintRulesPath: ".github/.commitlint.rules.js"
20 | env:
21 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
22 |
--------------------------------------------------------------------------------
/.github/workflows/xcodeproj.yml:
--------------------------------------------------------------------------------
1 | # https://help.github.com/en/github/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#jobsjob_idname
2 | name: XcodeProj
3 |
4 | on:
5 | push:
6 | branches:
7 | - main
8 | pull_request: {}
9 |
10 | concurrency:
11 | group: xcodeproj-${{ github.head_ref }}
12 | cancel-in-progress: true
13 |
14 | env:
15 | MISE_EXPERIMENTAL: 1
16 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
17 |
18 | jobs:
19 | build:
20 | name: Build (macOS)
21 | runs-on: macos-latest
22 | steps:
23 | - uses: actions/checkout@v3
24 | - uses: jdx/mise-action@v2
25 | - name: Build
26 | run: mise run build
27 | build-linux:
28 | name: Build (Linux)
29 | runs-on: ubuntu-latest
30 | steps:
31 | - uses: actions/checkout@v3
32 | - uses: jdx/mise-action@v2
33 | - run: mise use swift@6.0.2
34 | - name: Build
35 | run: swift build --configuration release
36 | test:
37 | name: Test (macOS / Xcode)
38 | runs-on: macos-latest
39 | steps:
40 | - uses: actions/checkout@v3
41 | - uses: jdx/mise-action@v2
42 | - name: Run tests
43 | run: mise run test
44 |
45 | test-linux:
46 | name: Test (Linux)
47 | runs-on: ubuntu-latest
48 | steps:
49 | - uses: actions/checkout@v3
50 | - uses: jdx/mise-action@v2
51 | - run: mise use swift@6.0.2
52 | - run: |
53 | git config --global user.email 'xcodeproj@tuist.dev'
54 | git config --global user.name 'xcodeproj'
55 | git config --global init.defaultBranch main
56 | - name: Test
57 | run: swift test
58 |
59 | lint:
60 | name: Lint
61 | runs-on: macos-latest
62 | steps:
63 | - uses: actions/checkout@v3
64 | - uses: jdx/mise-action@v2
65 | - run: mise run lint
66 |
--------------------------------------------------------------------------------
/.mise.toml:
--------------------------------------------------------------------------------
1 | [tools]
2 | swiftformat = "0.54.3"
3 | tuist = "4.50.2"
4 | swiftlint = "0.55.1"
5 | "git-cliff" = "2.4.0"
6 |
--------------------------------------------------------------------------------
/.mise/tasks/build:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # mise description="Build the package in the host"
3 |
4 | swift build --package-path $MISE_PROJECT_ROOT --configuration debug
5 |
--------------------------------------------------------------------------------
/.mise/tasks/build-linux:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # mise description="Build on Linux"
3 |
4 | CONTAINER_RUNTIME=$(command -v podman || command -v docker)
5 |
6 | if [ -z "$CONTAINER_RUNTIME" ]; then
7 | echo "Neither podman nor docker is available. Please install one to proceed."
8 | exit 1
9 | fi
10 |
11 | $CONTAINER_RUNTIME run --rm --interactive --tty --volume "$(pwd):/package" --workdir "/package" swift:5.10.1 bash -c "
12 | git config --global user.email 'xcodeproj@tuist.io' &&
13 | git config --global user.name 'xcodeproj' &&
14 | git config --global init.defaultBranch main &&
15 | swift build --configuration release
16 | "
17 |
--------------------------------------------------------------------------------
/.mise/tasks/lint:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # mise description="Lint the workspace"
3 | set -euo pipefail
4 |
5 | swiftformat $MISE_PROJECT_ROOT --lint
6 | swiftlint lint --quiet --config $MISE_PROJECT_ROOT/.swiftlint.yml $MISE_PROJECT_ROOT/Sources
7 |
--------------------------------------------------------------------------------
/.mise/tasks/lint-fix:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # mise description="Lint the workspace fixing issues"
3 | set -euo pipefail
4 |
5 | swiftformat $MISE_PROJECT_ROOT
6 | swiftlint lint --fix --quiet --config $MISE_PROJECT_ROOT/.swiftlint.yml $MISE_PROJECT_ROOT/Sources
7 |
--------------------------------------------------------------------------------
/.mise/tasks/test:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # mise description="Run tests in the host"
3 |
4 | swift test --package-path $MISE_PROJECT_ROOT
5 |
--------------------------------------------------------------------------------
/.mise/tasks/test-linux:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # mise description="Run tests on Linux"
3 |
4 | CONTAINER_RUNTIME=docker
5 |
6 | if [ -z "$CONTAINER_RUNTIME" ]; then
7 | echo "Neither podman nor docker is available. Please install one to proceed."
8 | exit 1
9 | fi
10 |
11 | $CONTAINER_RUNTIME run --rm --interactive --tty --volume "$(pwd):/package" --workdir "/package" swift:5.10.1 bash -c "
12 | git config --global user.email 'xcodeproj@tuist.io' &&
13 | git config --global user.name 'xcodeproj' &&
14 | git config --global init.defaultBranch main &&
15 | swift test
16 | "
17 |
--------------------------------------------------------------------------------
/.spi.yml:
--------------------------------------------------------------------------------
1 | version: 1
2 | builder:
3 | configs:
4 | - documentation_targets: [XcodeProj]
5 |
--------------------------------------------------------------------------------
/.swift-version:
--------------------------------------------------------------------------------
1 | 6.0
2 |
--------------------------------------------------------------------------------
/.swiftformat:
--------------------------------------------------------------------------------
1 | # file options
2 |
3 | --symlinks ignore
4 | --exclude Tests/XCTestManifests.swift
5 |
6 | # format options
7 |
8 | --disable wrapMultilineStatementBraces
9 | --disable braces
10 | --allman false
11 | --binarygrouping 4,8
12 | --commas always
13 | --comments indent
14 | --decimalgrouping 3,6
15 | --elseposition same-line
16 | --empty void
17 | --exponentcase lowercase
18 | --exponentgrouping disabled
19 | --fractiongrouping disabled
20 | --header ignore
21 | --hexgrouping 4,8
22 | --hexliteralcase uppercase
23 | --ifdef indent
24 | --indent 4
25 | --indentcase false
26 | --importgrouping testable-bottom
27 | --linebreaks lf
28 | --octalgrouping 4,8
29 | --operatorfunc spaced
30 | --patternlet hoist
31 | --ranges spaced
32 | --self remove
33 | --semicolons inline
34 | --stripunusedargs always
35 | --trimwhitespace always
36 | --wraparguments preserve
37 | --wrapcollections preserve
38 |
--------------------------------------------------------------------------------
/.swiftlint.yml:
--------------------------------------------------------------------------------
1 | included:
2 | - Sources
3 | excluded:
4 | - Assets
5 | - Fixtures
6 | - Tests
7 | - .build
8 | - Sources/XcodeProj/String+md5.swift
9 | disabled_rules:
10 | - trailing_whitespace
11 | - trailing_comma
12 | - nesting
13 | - cyclomatic_complexity
14 | - file_length
15 | - todo
16 | line_length:
17 | warning: 150
18 | error: 170
19 | identifier_name:
20 | min_length:
21 | error: 1
22 | warning: 1
23 | function_parameter_count:
24 | warning: 7
25 | error: 8
26 |
--------------------------------------------------------------------------------
/Assets/diagram.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tuist/XcodeProj/4488984883071307a9136251e7ccf06a41b6258d/Assets/diagram.png
--------------------------------------------------------------------------------
/Documentation/README.md:
--------------------------------------------------------------------------------
1 | # Documentation
2 |
3 | In the following pages you'll find documentation that will help you understand Xcode projects and how to interact with them using xcodeproj.
4 |
5 | - [Getting started](getting-started.md): If you are the type of person that likes to get the hands a bit dirty before reading documentation, this is a good place to start from to get a sense of what the interface of the library looks like.
6 | - [Migration guides](migration-guides.md): If you are already using a version of xcodeproj and would like to migrate to the newest version, this document describes the necessary steps that you need to take to migrate between versions.
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Fixtures/FileSharedAcrossTargets/FileSharedAcrossTargets.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Fixtures/FileSharedAcrossTargets/FileSharedAcrossTargets.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Fixtures/FileSharedAcrossTargets/FileSharedAcrossTargets.xcodeproj/xcuserdata/pepicrft.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | FileSharedAcrossTargets.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Fixtures/FileSharedAcrossTargets/FileSharedAcrossTargets/FileSharedAcrossTargets.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | //! Project version number for FileSharedAcrossTargets.
4 | FOUNDATION_EXPORT double FileSharedAcrossTargetsVersionNumber;
5 |
6 | //! Project version string for FileSharedAcrossTargets.
7 | FOUNDATION_EXPORT const unsigned char FileSharedAcrossTargetsVersionString[];
8 |
--------------------------------------------------------------------------------
/Fixtures/FileSharedAcrossTargets/FileSharedAcrossTargets/SharedHeader.h:
--------------------------------------------------------------------------------
1 | #ifndef SharedHeader_h
2 | #define SharedHeader_h
3 |
4 |
5 | #endif /* SharedHeader_h */
6 |
--------------------------------------------------------------------------------
/Fixtures/FileSharedAcrossTargets/FileSharedAcrossTargetsTests/FileSharedAcrossTargetsTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import FileSharedAcrossTargets
3 |
4 | final class FileSharedAcrossTargetsTests: XCTestCase {
5 | override func setUpWithError() throws {
6 | // Put setup code here. This method is called before the invocation of each test method in the class.
7 | }
8 |
9 | override func tearDownWithError() throws {
10 | // Put teardown code here. This method is called after the invocation of each test method in the class.
11 | }
12 |
13 | func testExample() throws {
14 | // This is an example of a functional test case.
15 | // Use XCTAssert and related functions to verify your tests produce the correct results.
16 | // Any test you write for XCTest can be annotated as throws and async.
17 | // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
18 | // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
19 | }
20 |
21 | func testPerformanceExample() throws {
22 | // This is an example of a performance test case.
23 | measure {
24 | // Put the code you want to measure the time of here.
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Fixtures/Schemes/MinimalInformation.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
9 |
15 |
16 |
17 |
18 |
19 |
21 |
22 |
23 |
29 |
30 |
31 |
32 |
33 |
35 |
36 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/Fixtures/Schemes/NoBlueprintID.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
11 |
12 |
13 |
14 |
15 |
21 |
22 |
24 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/Fixtures/Schemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | App.xcscheme
8 |
9 | isShown
10 |
11 | orderHint
12 | 0
13 |
14 | Test 0.xcscheme
15 |
16 | orderHint
17 | 3
18 |
19 | Test 1.xcscheme
20 |
21 | isShown
22 |
23 | orderHint
24 | 4
25 |
26 | Tuist.xcscheme_^#shared#^_
27 |
28 | isShown
29 |
30 | orderHint
31 | 1
32 |
33 | XcodeProj.xcscheme
34 |
35 | orderHint
36 | 2
37 |
38 |
39 | SuppressBuildableAutocreation
40 |
41 | E525238B16245A900012E2BA
42 |
43 | primary
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/Fixtures/SynchronizedRootGroups/.gitignore:
--------------------------------------------------------------------------------
1 | *.xcodeproj/xcuserdata
2 |
--------------------------------------------------------------------------------
/Fixtures/SynchronizedRootGroups/SynchronizedRootGroups.xcodeproj/xcuserdata/pepicrft.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | SynchronizedRootGroups.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Fixtures/SynchronizedRootGroups/SynchronizedRootGroups/Exception/Exception.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Exception.swift
3 | // SynchronizedRootGroups
4 | //
5 | // Created by Pedro Piñera Buendía on 26.07.24.
6 | //
7 |
--------------------------------------------------------------------------------
/Fixtures/SynchronizedRootGroups/SynchronizedRootGroups/SynchronizedRootGroups.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SynchronizedRootGroups.swift
3 | // SynchronizedRootGroups
4 | //
5 | // Created by Pedro Piñera Buendía on 26.07.24.
6 | //
7 |
--------------------------------------------------------------------------------
/Fixtures/TargetWithCustomBuildRules/TargetWithCustomBuildRules.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Fixtures/TargetWithCustomBuildRules/TargetWithCustomBuildRules.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Fixtures/TargetWithCustomBuildRules/TargetWithCustomBuildRules.xcodeproj/project.xcworkspace/xcuserdata/pepicrft.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tuist/XcodeProj/4488984883071307a9136251e7ccf06a41b6258d/Fixtures/TargetWithCustomBuildRules/TargetWithCustomBuildRules.xcodeproj/project.xcworkspace/xcuserdata/pepicrft.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/Fixtures/TargetWithCustomBuildRules/TargetWithCustomBuildRules.xcodeproj/xcuserdata/pepicrft.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | TargetWithCustomBuildRules.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Fixtures/TargetWithCustomBuildRules/TargetWithCustomBuildRules/TargetWithCustomBuildRules.h:
--------------------------------------------------------------------------------
1 | //
2 | // TargetWithCustomBuildRules.h
3 | // TargetWithCustomBuildRules
4 | //
5 | // Created by Pedro Piñera Buendía on 06.07.23.
6 | //
7 |
8 | #import
9 |
10 | @interface TargetWithCustomBuildRules : NSObject
11 |
12 | @end
13 |
--------------------------------------------------------------------------------
/Fixtures/TargetWithCustomBuildRules/TargetWithCustomBuildRules/TargetWithCustomBuildRules.m:
--------------------------------------------------------------------------------
1 | //
2 | // TargetWithCustomBuildRules.m
3 | // TargetWithCustomBuildRules
4 | //
5 | // Created by Pedro Piñera Buendía on 06.07.23.
6 | //
7 |
8 | #import "TargetWithCustomBuildRules.h"
9 |
10 | @implementation TargetWithCustomBuildRules
11 |
12 | @end
13 |
--------------------------------------------------------------------------------
/Fixtures/WithoutWorkspace/.gitignore:
--------------------------------------------------------------------------------
1 | .build/
2 |
--------------------------------------------------------------------------------
/Fixtures/WithoutWorkspace/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:4.0
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "WithoutWorkspace",
8 | products: [
9 | // Products define the executables and libraries produced by a package, and make them visible to other packages.
10 | .library(
11 | name: "WithoutWorkspace",
12 | targets: ["WithoutWorkspace"]
13 | ),
14 | ],
15 | dependencies: [
16 | // Dependencies declare other packages that this package depends on.
17 | // .package(url: /* package url */, from: "1.0.0"),
18 | ],
19 | targets: [
20 | // Targets are the basic building blocks of a package. A target can define a module or a test suite.
21 | // Targets can depend on other targets in this package, and on products in packages which this package depends on.
22 | .target(
23 | name: "WithoutWorkspace",
24 | dependencies: []
25 | ),
26 | .testTarget(
27 | name: "WithoutWorkspaceTests",
28 | dependencies: ["WithoutWorkspace"]
29 | ),
30 | ]
31 | )
32 |
--------------------------------------------------------------------------------
/Fixtures/WithoutWorkspace/Sources/WithoutWorkspace/WithoutWorkspace.swift:
--------------------------------------------------------------------------------
1 | struct WithoutWorkspace {
2 | var text = "Hello, World!"
3 | }
4 |
--------------------------------------------------------------------------------
/Fixtures/WithoutWorkspace/Tests/WithoutWorkspaceTests/WithoutWorkspaceTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import WithoutWorkspace
3 |
4 | class WithoutWorkspaceTests: XCTestCase {
5 | func testExample() {
6 | // This is an example of a functional test case.
7 | // Use XCTAssert and related functions to verify your tests produce the correct
8 | // results.
9 | XCTAssertEqual(WithoutWorkspace().text, "Hello, World!")
10 | }
11 |
12 | static var allTests = [
13 | ("testExample", testExample),
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/Fixtures/WithoutWorkspace/WithoutWorkspace.xcodeproj/WithoutWorkspaceTests_Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | CFBundleDevelopmentRegion
5 | en
6 | CFBundleExecutable
7 | $(EXECUTABLE_NAME)
8 | CFBundleIdentifier
9 | $(PRODUCT_BUNDLE_IDENTIFIER)
10 | CFBundleInfoDictionaryVersion
11 | 6.0
12 | CFBundleName
13 | $(PRODUCT_NAME)
14 | CFBundlePackageType
15 | BNDL
16 | CFBundleShortVersionString
17 | 1.0
18 | CFBundleSignature
19 | ????
20 | CFBundleVersion
21 | $(CURRENT_PROJECT_VERSION)
22 | NSPrincipalClass
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Fixtures/WithoutWorkspace/WithoutWorkspace.xcodeproj/WithoutWorkspace_Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | CFBundleDevelopmentRegion
5 | en
6 | CFBundleExecutable
7 | $(EXECUTABLE_NAME)
8 | CFBundleIdentifier
9 | $(PRODUCT_BUNDLE_IDENTIFIER)
10 | CFBundleInfoDictionaryVersion
11 | 6.0
12 | CFBundleName
13 | $(PRODUCT_NAME)
14 | CFBundlePackageType
15 | FMWK
16 | CFBundleShortVersionString
17 | 1.0
18 | CFBundleSignature
19 | ????
20 | CFBundleVersion
21 | $(CURRENT_PROJECT_VERSION)
22 | NSPrincipalClass
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Fixtures/WithoutWorkspace/WithoutWorkspace.xcodeproj/xcshareddata/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | SchemeUserState
5 |
6 | WithoutWorkspace-Package.xcscheme
7 |
8 |
9 | SuppressBuildableAutocreation
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/Fixtures/Workspace.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
9 |
10 |
12 |
13 |
14 |
17 |
18 |
20 |
21 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Fixtures/Workspace.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Fixtures/WorkspaceSettings/Default.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Fixtures/WorkspaceSettings/OriginalAbsoluteDerivedData.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BuildSystemType
6 | Original
7 | DerivedDataCustomLocation
8 | /User/xcodeproj/DerivedData
9 | DerivedDataLocationStyle
10 | AbsolutePath
11 |
12 |
13 |
--------------------------------------------------------------------------------
/Fixtures/WorkspaceSettings/OriginalBuildSystem.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BuildSystemType
6 | Original
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Fixtures/WorkspaceSettings/OriginalRelativeDerivedData.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BuildSystemType
6 | Original
7 | DerivedDataCustomLocation
8 | CustomizedDerivedData
9 | DerivedDataLocationStyle
10 | WorkspaceRelativePath
11 |
12 |
13 |
--------------------------------------------------------------------------------
/Fixtures/XCConfigs/Children.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Parent.xcconfig"
2 |
3 | CONFIGURATION_BUILD_DIR = Test/ // NOTE: Test comment line to check several slashes
4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited)
5 | WARNING_CFLAGS = -Wall -Wno-direct-ivar-access -Wno-objc-missing-property-synthesis -Wno-readonly-iboutlet-property -Wno-switch-enum -Wno-padded
6 |
--------------------------------------------------------------------------------
/Fixtures/XCConfigs/Parent.xcconfig:
--------------------------------------------------------------------------------
1 | // NOTE: Top level comment
2 | OTHER_SWIFT_FLAGS_XCODE_0821 = $(inherited)
3 | OTHER_SWIFT_FLAGS_XCODE_0830 = $(inherited) -enable-bridging-pch
4 | PRODUCT_NAME = $(TARGET_NAME) // NOTE: Test Comment
5 | SWIFT_OPTIMIZATION_LEVEL = -Onone// Edge-case when a comment has no space
6 |
--------------------------------------------------------------------------------
/Fixtures/Xcode16/README.md:
--------------------------------------------------------------------------------
1 | # Xcode 16 project
2 |
3 | Xcode 16 introduced some changes in Xcode projects, like [this one](https://github.com/tuist/XcodeProj/issues/861), so this fixture tries to capture those changes to run tests against them.
4 |
--------------------------------------------------------------------------------
/Fixtures/Xcode16/Test/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Fixtures/Xcode16/Test/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "platform" : "ios",
6 | "size" : "1024x1024"
7 | },
8 | {
9 | "appearances" : [
10 | {
11 | "appearance" : "luminosity",
12 | "value" : "dark"
13 | }
14 | ],
15 | "idiom" : "universal",
16 | "platform" : "ios",
17 | "size" : "1024x1024"
18 | },
19 | {
20 | "appearances" : [
21 | {
22 | "appearance" : "luminosity",
23 | "value" : "tinted"
24 | }
25 | ],
26 | "idiom" : "universal",
27 | "platform" : "ios",
28 | "size" : "1024x1024"
29 | },
30 | {
31 | "idiom" : "mac",
32 | "scale" : "1x",
33 | "size" : "16x16"
34 | },
35 | {
36 | "idiom" : "mac",
37 | "scale" : "2x",
38 | "size" : "16x16"
39 | },
40 | {
41 | "idiom" : "mac",
42 | "scale" : "1x",
43 | "size" : "32x32"
44 | },
45 | {
46 | "idiom" : "mac",
47 | "scale" : "2x",
48 | "size" : "32x32"
49 | },
50 | {
51 | "idiom" : "mac",
52 | "scale" : "1x",
53 | "size" : "128x128"
54 | },
55 | {
56 | "idiom" : "mac",
57 | "scale" : "2x",
58 | "size" : "128x128"
59 | },
60 | {
61 | "idiom" : "mac",
62 | "scale" : "1x",
63 | "size" : "256x256"
64 | },
65 | {
66 | "idiom" : "mac",
67 | "scale" : "2x",
68 | "size" : "256x256"
69 | },
70 | {
71 | "idiom" : "mac",
72 | "scale" : "1x",
73 | "size" : "512x512"
74 | },
75 | {
76 | "idiom" : "mac",
77 | "scale" : "2x",
78 | "size" : "512x512"
79 | }
80 | ],
81 | "info" : {
82 | "author" : "xcode",
83 | "version" : 1
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/Fixtures/Xcode16/Test/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Fixtures/Xcode16/Test/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // Test
4 | //
5 | // Created by F1248 on 30.09.24.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct ContentView: View {
11 | var body: some View {
12 | VStack {
13 | Image(systemName: "globe")
14 | .imageScale(.large)
15 | .foregroundStyle(.tint)
16 | Text("Hello, world!")
17 | }
18 | .padding()
19 | }
20 | }
21 |
22 | #Preview {
23 | ContentView()
24 | }
25 |
--------------------------------------------------------------------------------
/Fixtures/Xcode16/Test/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Fixtures/Xcode16/Test/Test.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.files.user-selected.read-only
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Fixtures/Xcode16/Test/TestApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TestApp.swift
3 | // Test
4 | //
5 | // Created by F1248 on 30.09.24.
6 | //
7 |
8 | import SwiftUI
9 |
10 | @main
11 | struct TestApp: App {
12 | var body: some Scene {
13 | WindowGroup {
14 | ContentView()
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Fixtures/Xcode16ProjectReferenceOrder/Sources/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import Framework1
2 | import Framework2
3 | import UIKit
4 |
5 | @main
6 | class AppDelegate: UIResponder, UIApplicationDelegate {
7 | var window: UIWindow?
8 |
9 | func applicationDidFinishLaunching(_: UIApplication) {
10 | print(hello())
11 | }
12 |
13 | func hello() -> String {
14 | "AppDelegate.hello()"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Fixtures/dummy.framework/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tuist/XcodeProj/4488984883071307a9136251e7ccf06a41b6258d/Fixtures/dummy.framework/.gitkeep
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/AppWithExtensions.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/AppWithExtensions.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/AppWithExtensions/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | @main
4 | class AppDelegate: UIResponder, UIApplicationDelegate {
5 | func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
6 | // Override point for customization after application launch.
7 | true
8 | }
9 |
10 | // MARK: UISceneSession Lifecycle
11 |
12 | func application(_: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options _: UIScene.ConnectionOptions) -> UISceneConfiguration {
13 | // Called when a new scene session is being created.
14 | // Use this method to select a configuration to create the new scene with.
15 | UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
16 | }
17 |
18 | func application(_: UIApplication, didDiscardSceneSessions _: Set) {
19 | // Called when the user discards a scene session.
20 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
21 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/AppWithExtensions/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/AppWithExtensions/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/AppWithExtensions/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/AppWithExtensions/ContentView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | struct ContentView: View {
4 | var body: some View {
5 | Text("Hello, World!")
6 | }
7 | }
8 |
9 | struct ContentView_Previews: PreviewProvider {
10 | static var previews: some View {
11 | ContentView()
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/AppWithExtensions/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UIApplicationSceneManifest
24 |
25 | UIApplicationSupportsMultipleScenes
26 |
27 | UISceneConfigurations
28 |
29 | UIWindowSceneSessionRoleApplication
30 |
31 |
32 | UISceneConfigurationName
33 | Default Configuration
34 | UISceneDelegateClassName
35 | $(PRODUCT_MODULE_NAME).SceneDelegate
36 |
37 |
38 |
39 |
40 | UILaunchStoryboardName
41 | LaunchScreen
42 | UIRequiredDeviceCapabilities
43 |
44 | armv7
45 |
46 | UISupportedInterfaceOrientations
47 |
48 | UIInterfaceOrientationPortrait
49 | UIInterfaceOrientationLandscapeLeft
50 | UIInterfaceOrientationLandscapeRight
51 |
52 | UISupportedInterfaceOrientations~ipad
53 |
54 | UIInterfaceOrientationPortrait
55 | UIInterfaceOrientationPortraitUpsideDown
56 | UIInterfaceOrientationLandscapeLeft
57 | UIInterfaceOrientationLandscapeRight
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/AppWithExtensions/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/MessagesExtension/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/MessagesExtension/Assets.xcassets/iMessage App Icon.stickersiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "29x29"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "29x29"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "60x45"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "60x45"
22 | },
23 | {
24 | "idiom" : "ipad",
25 | "scale" : "2x",
26 | "size" : "29x29"
27 | },
28 | {
29 | "idiom" : "ipad",
30 | "scale" : "2x",
31 | "size" : "67x50"
32 | },
33 | {
34 | "idiom" : "ipad",
35 | "scale" : "2x",
36 | "size" : "74x55"
37 | },
38 | {
39 | "idiom" : "ios-marketing",
40 | "scale" : "1x",
41 | "size" : "1024x1024"
42 | },
43 | {
44 | "idiom" : "universal",
45 | "platform" : "ios",
46 | "scale" : "2x",
47 | "size" : "27x20"
48 | },
49 | {
50 | "idiom" : "universal",
51 | "platform" : "ios",
52 | "scale" : "3x",
53 | "size" : "27x20"
54 | },
55 | {
56 | "idiom" : "universal",
57 | "platform" : "ios",
58 | "scale" : "2x",
59 | "size" : "32x24"
60 | },
61 | {
62 | "idiom" : "universal",
63 | "platform" : "ios",
64 | "scale" : "3x",
65 | "size" : "32x24"
66 | },
67 | {
68 | "idiom" : "ios-marketing",
69 | "platform" : "ios",
70 | "scale" : "1x",
71 | "size" : "1024x768"
72 | }
73 | ],
74 | "info" : {
75 | "author" : "xcode",
76 | "version" : 1
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/MessagesExtension/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleDisplayName
8 | MessagesExtension
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | NSExtension
24 |
25 | NSExtensionMainStoryboard
26 | MainInterface
27 | NSExtensionPointIdentifier
28 | com.apple.message-payload-provider
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/TodayViewExtension/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleDisplayName
8 | TodayViewExtension
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | NSExtension
24 |
25 | NSExtensionMainStoryboard
26 | MainInterface
27 | NSExtensionPointIdentifier
28 | com.apple.widget-extension
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/TodayViewExtension/TodayViewController.swift:
--------------------------------------------------------------------------------
1 | import NotificationCenter
2 | import UIKit
3 |
4 | class TodayViewController: UIViewController, NCWidgetProviding {
5 | override func viewDidLoad() {
6 | super.viewDidLoad()
7 | // Do any additional setup after loading the view.
8 | }
9 |
10 | func widgetPerformUpdate(completionHandler: @escaping (NCUpdateResult) -> Void) {
11 | // Perform any setup necessary in order to update the view.
12 |
13 | // If an error is encountered, use NCUpdateResult.Failed
14 | // If there's no update required, use NCUpdateResult.NoData
15 | // If there's an update, use NCUpdateResult.NewData
16 |
17 | completionHandler(NCUpdateResult.newData)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "watch",
5 | "scale" : "2x",
6 | "screen-width" : "<=145"
7 | },
8 | {
9 | "idiom" : "watch",
10 | "scale" : "2x",
11 | "screen-width" : ">161"
12 | },
13 | {
14 | "idiom" : "watch",
15 | "scale" : "2x",
16 | "screen-width" : ">145"
17 | },
18 | {
19 | "idiom" : "watch",
20 | "scale" : "2x",
21 | "screen-width" : ">183"
22 | }
23 | ],
24 | "info" : {
25 | "author" : "xcode",
26 | "version" : 1
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "assets" : [
3 | {
4 | "filename" : "Circular.imageset",
5 | "idiom" : "watch",
6 | "role" : "circular"
7 | },
8 | {
9 | "filename" : "Extra Large.imageset",
10 | "idiom" : "watch",
11 | "role" : "extra-large"
12 | },
13 | {
14 | "filename" : "Graphic Bezel.imageset",
15 | "idiom" : "watch",
16 | "role" : "graphic-bezel"
17 | },
18 | {
19 | "filename" : "Graphic Circular.imageset",
20 | "idiom" : "watch",
21 | "role" : "graphic-circular"
22 | },
23 | {
24 | "filename" : "Graphic Corner.imageset",
25 | "idiom" : "watch",
26 | "role" : "graphic-corner"
27 | },
28 | {
29 | "filename" : "Graphic Large Rectangular.imageset",
30 | "idiom" : "watch",
31 | "role" : "graphic-large-rectangular"
32 | },
33 | {
34 | "filename" : "Modular.imageset",
35 | "idiom" : "watch",
36 | "role" : "modular"
37 | },
38 | {
39 | "filename" : "Utilitarian.imageset",
40 | "idiom" : "watch",
41 | "role" : "utilitarian"
42 | }
43 | ],
44 | "info" : {
45 | "author" : "xcode",
46 | "version" : 1
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Extra Large.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "watch",
5 | "scale" : "2x",
6 | "screen-width" : "<=145"
7 | },
8 | {
9 | "idiom" : "watch",
10 | "scale" : "2x",
11 | "screen-width" : ">161"
12 | },
13 | {
14 | "idiom" : "watch",
15 | "scale" : "2x",
16 | "screen-width" : ">145"
17 | },
18 | {
19 | "idiom" : "watch",
20 | "scale" : "2x",
21 | "screen-width" : ">183"
22 | }
23 | ],
24 | "info" : {
25 | "author" : "xcode",
26 | "version" : 1
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Graphic Bezel.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "watch",
5 | "scale" : "2x",
6 | "screen-width" : "<=145"
7 | },
8 | {
9 | "idiom" : "watch",
10 | "scale" : "2x",
11 | "screen-width" : ">161"
12 | },
13 | {
14 | "idiom" : "watch",
15 | "scale" : "2x",
16 | "screen-width" : ">145"
17 | },
18 | {
19 | "idiom" : "watch",
20 | "scale" : "2x",
21 | "screen-width" : ">183"
22 | }
23 | ],
24 | "info" : {
25 | "author" : "xcode",
26 | "version" : 1
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Graphic Circular.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "watch",
5 | "scale" : "2x",
6 | "screen-width" : "<=145"
7 | },
8 | {
9 | "idiom" : "watch",
10 | "scale" : "2x",
11 | "screen-width" : ">161"
12 | },
13 | {
14 | "idiom" : "watch",
15 | "scale" : "2x",
16 | "screen-width" : ">145"
17 | },
18 | {
19 | "idiom" : "watch",
20 | "scale" : "2x",
21 | "screen-width" : ">183"
22 | }
23 | ],
24 | "info" : {
25 | "author" : "xcode",
26 | "version" : 1
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Graphic Corner.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "watch",
5 | "scale" : "2x",
6 | "screen-width" : "<=145"
7 | },
8 | {
9 | "idiom" : "watch",
10 | "scale" : "2x",
11 | "screen-width" : ">161"
12 | },
13 | {
14 | "idiom" : "watch",
15 | "scale" : "2x",
16 | "screen-width" : ">145"
17 | },
18 | {
19 | "idiom" : "watch",
20 | "scale" : "2x",
21 | "screen-width" : ">183"
22 | }
23 | ],
24 | "info" : {
25 | "author" : "xcode",
26 | "version" : 1
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Graphic Large Rectangular.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "watch",
5 | "scale" : "2x",
6 | "screen-width" : "<=145"
7 | },
8 | {
9 | "idiom" : "watch",
10 | "scale" : "2x",
11 | "screen-width" : ">161"
12 | },
13 | {
14 | "idiom" : "watch",
15 | "scale" : "2x",
16 | "screen-width" : ">145"
17 | },
18 | {
19 | "idiom" : "watch",
20 | "scale" : "2x",
21 | "screen-width" : ">183"
22 | }
23 | ],
24 | "info" : {
25 | "author" : "xcode",
26 | "version" : 1
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "watch",
5 | "scale" : "2x",
6 | "screen-width" : "<=145"
7 | },
8 | {
9 | "idiom" : "watch",
10 | "scale" : "2x",
11 | "screen-width" : ">161"
12 | },
13 | {
14 | "idiom" : "watch",
15 | "scale" : "2x",
16 | "screen-width" : ">145"
17 | },
18 | {
19 | "idiom" : "watch",
20 | "scale" : "2x",
21 | "screen-width" : ">183"
22 | }
23 | ],
24 | "info" : {
25 | "author" : "xcode",
26 | "version" : 1
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "watch",
5 | "scale" : "2x",
6 | "screen-width" : "<=145"
7 | },
8 | {
9 | "idiom" : "watch",
10 | "scale" : "2x",
11 | "screen-width" : ">161"
12 | },
13 | {
14 | "idiom" : "watch",
15 | "scale" : "2x",
16 | "screen-width" : ">145"
17 | },
18 | {
19 | "idiom" : "watch",
20 | "scale" : "2x",
21 | "screen-width" : ">183"
22 | }
23 | ],
24 | "info" : {
25 | "author" : "xcode",
26 | "version" : 1
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/WatchApp Extension/ContentView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | struct ContentView: View {
4 | var body: some View {
5 | Text("Hello, World!")
6 | }
7 | }
8 |
9 | struct ContentView_Previews: PreviewProvider {
10 | static var previews: some View {
11 | ContentView()
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/WatchApp Extension/HostingController.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import SwiftUI
3 | import WatchKit
4 |
5 | class HostingController: WKHostingController {
6 | override var body: ContentView {
7 | ContentView()
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleDisplayName
8 | WatchApp Extension
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | NSExtension
24 |
25 | NSExtensionAttributes
26 |
27 | WKAppBundleIdentifier
28 | io.tuist.xcodeproj.fixtures.AppWithExtensions.watchkitapp
29 |
30 | NSExtensionPointIdentifier
31 | com.apple.watchkit
32 |
33 | WKExtensionDelegateClassName
34 | $(PRODUCT_MODULE_NAME).ExtensionDelegate
35 |
36 |
37 |
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/WatchApp Extension/NotificationController.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import UserNotifications
3 | import WatchKit
4 |
5 | class NotificationController: WKUserNotificationHostingController {
6 | override var body: NotificationView {
7 | NotificationView()
8 | }
9 |
10 | override func willActivate() {
11 | // This method is called when watch view controller is about to be visible to user
12 | super.willActivate()
13 | }
14 |
15 | override func didDeactivate() {
16 | // This method is called when watch view controller is no longer visible
17 | super.didDeactivate()
18 | }
19 |
20 | override func didReceive(_: UNNotification) {
21 | // This method is called when a notification needs to be presented.
22 | // Implement it if you use a dynamic notification interface.
23 | // Populate your dynamic notification interface as quickly as possible.
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/WatchApp Extension/NotificationView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | struct NotificationView: View {
4 | var body: some View {
5 | Text("Hello, World!")
6 | }
7 | }
8 |
9 | struct NotificationView_Previews: PreviewProvider {
10 | static var previews: some View {
11 | NotificationView()
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/WatchApp Extension/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/WatchApp Extension/PushNotificationPayload.apns:
--------------------------------------------------------------------------------
1 | {
2 | "aps": {
3 | "alert": {
4 | "body": "Test message",
5 | "title": "Optional title",
6 | "subtitle": "Optional subtitle"
7 | },
8 | "category": "myCategory",
9 | "thread-id": "5280"
10 | },
11 |
12 | "WatchKit Simulator Actions": [
13 | {
14 | "title": "First Button",
15 | "identifier": "firstButtonAction"
16 | }
17 | ],
18 |
19 | "customKey": "Use this file to define a testing payload for your notifications. The aps dictionary specifies the category, alert text and title. The WatchKit Simulator Actions array can provide info for one or more action buttons in addition to the standard Dismiss button. Any other top level keys are custom payload. If you have multiple such JSON files in your project, you'll be able to select them when choosing to debug the notification interface of your Watch App."
20 | }
21 |
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/WatchApp/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "watch",
5 | "role" : "notificationCenter",
6 | "scale" : "2x",
7 | "size" : "24x24",
8 | "subtype" : "38mm"
9 | },
10 | {
11 | "idiom" : "watch",
12 | "role" : "notificationCenter",
13 | "scale" : "2x",
14 | "size" : "27.5x27.5",
15 | "subtype" : "42mm"
16 | },
17 | {
18 | "idiom" : "watch",
19 | "role" : "companionSettings",
20 | "scale" : "2x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "watch",
25 | "role" : "companionSettings",
26 | "scale" : "3x",
27 | "size" : "29x29"
28 | },
29 | {
30 | "idiom" : "watch",
31 | "role" : "appLauncher",
32 | "scale" : "2x",
33 | "size" : "40x40",
34 | "subtype" : "38mm"
35 | },
36 | {
37 | "idiom" : "watch",
38 | "role" : "appLauncher",
39 | "scale" : "2x",
40 | "size" : "44x44",
41 | "subtype" : "40mm"
42 | },
43 | {
44 | "idiom" : "watch",
45 | "role" : "appLauncher",
46 | "scale" : "2x",
47 | "size" : "50x50",
48 | "subtype" : "44mm"
49 | },
50 | {
51 | "idiom" : "watch",
52 | "role" : "quickLook",
53 | "scale" : "2x",
54 | "size" : "86x86",
55 | "subtype" : "38mm"
56 | },
57 | {
58 | "idiom" : "watch",
59 | "role" : "quickLook",
60 | "scale" : "2x",
61 | "size" : "98x98",
62 | "subtype" : "42mm"
63 | },
64 | {
65 | "idiom" : "watch",
66 | "role" : "quickLook",
67 | "scale" : "2x",
68 | "size" : "108x108",
69 | "subtype" : "44mm"
70 | },
71 | {
72 | "idiom" : "watch-marketing",
73 | "scale" : "1x",
74 | "size" : "1024x1024"
75 | }
76 | ],
77 | "info" : {
78 | "author" : "xcode",
79 | "version" : 1
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/WatchApp/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/WatchApp/Base.lproj/Interface.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/Fixtures/iOS/AppWithExtensions/WatchApp/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleDisplayName
8 | AppWithExtensions
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | UISupportedInterfaceOrientations
24 |
25 | UIInterfaceOrientationPortrait
26 | UIInterfaceOrientationPortraitUpsideDown
27 |
28 | WKCompanionAppBundleIdentifier
29 | io.tuist.xcodeproj.fixtures.AppWithExtensions
30 | WKWatchKitApp
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/Fixtures/iOS/MyLocalPackage/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /.build
3 | /Packages
4 | /*.xcodeproj
5 |
--------------------------------------------------------------------------------
/Fixtures/iOS/MyLocalPackage/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.1
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "MyLocalPackage",
8 | products: [
9 | // Products define the executables and libraries produced by a package, and make them visible to other packages.
10 | .library(
11 | name: "MyLocalPackage",
12 | targets: ["MyLocalPackage"]
13 | ),
14 | ],
15 | dependencies: [
16 | // Dependencies declare other packages that this package depends on.
17 | // .package(url: /* package url */, from: "1.0.0"),
18 | ],
19 | targets: [
20 | // Targets are the basic building blocks of a package. A target can define a module or a test suite.
21 | // Targets can depend on other targets in this package, and on products in packages which this package depends on.
22 | .target(
23 | name: "MyLocalPackage",
24 | dependencies: []
25 | ),
26 | .testTarget(
27 | name: "MyLocalPackageTests",
28 | dependencies: ["MyLocalPackage"]
29 | ),
30 | ]
31 | )
32 |
--------------------------------------------------------------------------------
/Fixtures/iOS/MyLocalPackage/README.md:
--------------------------------------------------------------------------------
1 | # MyLocalPackage
2 |
3 | A description of this package.
4 |
--------------------------------------------------------------------------------
/Fixtures/iOS/MyLocalPackage/Sources/MyLocalPackage/MyLocalPackage.swift:
--------------------------------------------------------------------------------
1 | struct MyLocalPackage {
2 | var text = "Hello, World!"
3 | }
4 |
--------------------------------------------------------------------------------
/Fixtures/iOS/MyLocalPackage/Tests/LinuxMain.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 |
3 | import MyLocalPackageTests
4 |
5 | var tests = [XCTestCaseEntry]()
6 | tests += MyLocalPackageTests.allTests()
7 | XCTMain(tests)
8 |
--------------------------------------------------------------------------------
/Fixtures/iOS/MyLocalPackage/Tests/MyLocalPackageTests/MyLocalPackageTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import MyLocalPackage
3 |
4 | final class MyLocalPackageTests: XCTestCase {
5 | func testExample() {
6 | // This is an example of a functional test case.
7 | // Use XCTAssert and related functions to verify your tests produce the correct
8 | // results.
9 | XCTAssertEqual(MyLocalPackage().text, "Hello, World!")
10 | }
11 |
12 | static var allTests = [
13 | ("testExample", testExample),
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/Fixtures/iOS/MyLocalPackage/Tests/MyLocalPackageTests/XCTestManifests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 |
3 | #if !canImport(ObjectiveC)
4 | public func allTests() -> [XCTestCaseEntry] {
5 | [
6 | testCase(MyLocalPackageTests.allTests),
7 | ]
8 | }
9 | #endif
10 |
--------------------------------------------------------------------------------
/Fixtures/iOS/MyOtherLocalPackage/MyOtherLocalPackage/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /.build
3 | /Packages
4 | /*.xcodeproj
5 |
--------------------------------------------------------------------------------
/Fixtures/iOS/MyOtherLocalPackage/MyOtherLocalPackage/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.1
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "MyOtherLocalPackage",
8 | products: [
9 | // Products define the executables and libraries produced by a package, and make them visible to other packages.
10 | .library(
11 | name: "MyOtherLocalPackage",
12 | targets: ["MyOtherLocalPackage"]
13 | ),
14 | ],
15 | dependencies: [
16 | // Dependencies declare other packages that this package depends on.
17 | // .package(url: /* package url */, from: "1.0.0"),
18 | ],
19 | targets: [
20 | // Targets are the basic building blocks of a package. A target can define a module or a test suite.
21 | // Targets can depend on other targets in this package, and on products in packages which this package depends on.
22 | .target(
23 | name: "MyOtherLocalPackage",
24 | dependencies: []
25 | ),
26 | .testTarget(
27 | name: "MyOtherLocalPackageTests",
28 | dependencies: ["MyOtherLocalPackage"]
29 | ),
30 | ]
31 | )
32 |
--------------------------------------------------------------------------------
/Fixtures/iOS/MyOtherLocalPackage/MyOtherLocalPackage/README.md:
--------------------------------------------------------------------------------
1 | # MyLocalPackage
2 |
3 | A description of this package.
4 |
--------------------------------------------------------------------------------
/Fixtures/iOS/MyOtherLocalPackage/MyOtherLocalPackage/Sources/MyOtherLocalPackage/MyOtherLocalPackage.swift:
--------------------------------------------------------------------------------
1 | struct MyOtherLocalPackage {
2 | var text = "Hello, World!"
3 | }
4 |
--------------------------------------------------------------------------------
/Fixtures/iOS/MyOtherLocalPackage/MyOtherLocalPackage/Tests/LinuxMain.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 |
3 | import MyLocalPackageTests
4 |
5 | var tests = [XCTestCaseEntry]()
6 | tests += MyLocalPackageTests.allTests()
7 | XCTMain(tests)
8 |
--------------------------------------------------------------------------------
/Fixtures/iOS/MyOtherLocalPackage/MyOtherLocalPackage/Tests/MyOtherLocalPackageTests/MyLocalPackageTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import MyOtherLocalPackage
3 |
4 | final class MyLocalPackageTests: XCTestCase {
5 | func testExample() {
6 | // This is an example of a functional test case.
7 | // Use XCTAssert and related functions to verify your tests produce the correct
8 | // results.
9 | XCTAssertEqual(MyOtherLocalPackage().text, "Hello, World!")
10 | }
11 |
12 | static var allTests = [
13 | ("testExample", testExample),
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/Fixtures/iOS/MyOtherLocalPackage/MyOtherLocalPackage/Tests/MyOtherLocalPackageTests/XCTestManifests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 |
3 | #if !canImport(ObjectiveC)
4 | public func allTests() -> [XCTestCaseEntry] {
5 | [
6 | testCase(MyLocalPackageTests.allTests),
7 | ]
8 | }
9 | #endif
10 |
--------------------------------------------------------------------------------
/Fixtures/iOS/Project.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Fixtures/iOS/Project.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Fixtures/iOS/Project.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "object": {
3 | "pins": [
4 | {
5 | "package": "RxSwift",
6 | "repositoryURL": "https://github.com/ReactiveX/RxSwift",
7 | "state": {
8 | "branch": null,
9 | "revision": "b3e888b4972d9bc76495dd74d30a8c7fad4b9395",
10 | "version": "5.0.1"
11 | }
12 | }
13 | ]
14 | },
15 | "version": 1
16 | }
17 |
--------------------------------------------------------------------------------
/Fixtures/iOS/Project.xcodeproj/project.xcworkspace/xcuserdata/pepicrft.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tuist/XcodeProj/4488984883071307a9136251e7ccf06a41b6258d/Fixtures/iOS/Project.xcodeproj/project.xcworkspace/xcuserdata/pepicrft.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/Fixtures/iOS/Project.xcodeproj/xcuserdata/username1.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
8 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
25 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/Fixtures/iOS/Project.xcodeproj/xcuserdata/username1.xcuserdatad/xcschemes/iOS-debug.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
50 |
51 |
53 |
54 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/Fixtures/iOS/Project.xcodeproj/xcuserdata/username1.xcuserdatad/xcschemes/iOS-other.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
50 |
51 |
53 |
54 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/Fixtures/iOS/Project.xcodeproj/xcuserdata/username1.xcuserdatad/xcschemes/iOS-release.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
50 |
51 |
53 |
54 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/Fixtures/iOS/Project.xcodeproj/xcuserdata/username1.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Rx (Playground) 1.xcscheme
8 |
9 | isShown
10 |
11 | orderHint
12 | 5
13 |
14 | Rx (Playground) 2.xcscheme
15 |
16 | isShown
17 |
18 | orderHint
19 | 6
20 |
21 | Rx (Playground).xcscheme
22 |
23 | isShown
24 |
25 | orderHint
26 | 4
27 |
28 | iOS-debug.xcscheme
29 |
30 | orderHint
31 | 0
32 |
33 | iOS-release.xcscheme
34 |
35 | orderHint
36 | 1
37 |
38 | iOS.xcscheme_^#shared#^_
39 |
40 | orderHint
41 | 3
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/Fixtures/iOS/Project.xcodeproj/xcuserdata/username2.xcuserdatad/xcschemes/iOSTests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
10 |
11 |
16 |
17 |
20 |
26 |
27 |
28 |
29 |
30 |
40 |
41 |
47 |
48 |
50 |
51 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/Fixtures/iOS/Project.xcodeproj/xcuserdata/username3.xcuserdatad/xcschemes/custom-scheme.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
50 |
51 |
53 |
54 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/Fixtures/iOS/ProjectWithoutProductsGroup.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 48;
7 | objects = {
8 |
9 | /* Begin PBXGroup section */
10 | E4C06A7B1FC1CA8500A9AB51 = {
11 | isa = PBXGroup;
12 | children = (
13 | );
14 | sourceTree = "";
15 | };
16 | /* End PBXGroup section */
17 |
18 | /* Begin PBXProject section */
19 | E4C06A7C1FC1CA8500A9AB51 /* Project object */ = {
20 | isa = PBXProject;
21 | attributes = {
22 | LastSwiftUpdateCheck = 0920;
23 | LastUpgradeCheck = 0920;
24 | ORGANIZATIONNAME = tuist;
25 | };
26 | buildConfigurationList = E4C06A7F1FC1CA8500A9AB51 /* Build configuration list for PBXProject "ProjectWithoutProductsGroup" */;
27 | compatibilityVersion = "Xcode 8.0";
28 | developmentRegion = en;
29 | hasScannedForEncodings = 0;
30 | mainGroup = E4C06A7B1FC1CA8500A9AB51;
31 | productRefGroup = E4C06A7B1FC1CA8500A9AB51;
32 | projectDirPath = "";
33 | projectRoot = "";
34 | targets = (
35 | );
36 | };
37 | /* End PBXProject section */
38 |
39 | /* Begin XCBuildConfiguration section */
40 | E4C06A941FC1CA8500A9AB51 /* Debug */ = {
41 | isa = XCBuildConfiguration;
42 | buildSettings = {
43 | };
44 | name = Debug;
45 | };
46 | /* End XCBuildConfiguration section */
47 |
48 | /* Begin XCConfigurationList section */
49 | E4C06A7F1FC1CA8500A9AB51 /* Build configuration list for PBXProject "ProjectWithoutProductsGroup" */ = {
50 | isa = XCConfigurationList;
51 | buildConfigurations = (
52 | E4C06A941FC1CA8500A9AB51 /* Debug */,
53 | );
54 | defaultConfigurationIsVisible = 0;
55 | defaultConfigurationName = Debug;
56 | };
57 | /* End XCConfigurationList section */
58 | };
59 | rootObject = E4C06A7C1FC1CA8500A9AB51 /* Project object */;
60 | }
61 |
--------------------------------------------------------------------------------
/Fixtures/iOS/ProjectWithoutProductsGroup.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Fixtures/iOS/ProjectWithoutProductsGroup.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Fixtures/iOS/SameName/SameName.h:
--------------------------------------------------------------------------------
1 | //
2 | // SameName.h
3 | // SameName
4 | //
5 | // Created by Timothy Costa on 2022/06/10.
6 | // Copyright © 2022 es.ppinera. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for SameName.
12 | FOUNDATION_EXPORT double SameNameVersionNumber;
13 |
14 | //! Project version string for SameName.
15 | FOUNDATION_EXPORT const unsigned char SameNameVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Fixtures/iOS/iOS.xctestplan:
--------------------------------------------------------------------------------
1 | {
2 | "configurations" : [
3 | {
4 | "id" : "B98CEFD8-D0A3-4BDD-BA5B-8DC671AA5291",
5 | "name" : "Configuration 1",
6 | "options" : {
7 |
8 | }
9 | }
10 | ],
11 | "defaultOptions" : {
12 | "codeCoverage" : {
13 | "targets" : [
14 |
15 | ]
16 | },
17 | "commandLineArgumentEntries" : [
18 | {
19 | "argument" : "MyLaunchArgument"
20 | }
21 | ],
22 | "environmentVariableEntries" : [
23 | {
24 | "key" : "ENV_VAR",
25 | "value" : "RUN"
26 | }
27 | ],
28 | "targetForVariableExpansion" : {
29 | "containerPath" : "container:Project.xcodeproj",
30 | "identifier" : "23766C111EAA3484007A9026",
31 | "name" : "iOS"
32 | }
33 | },
34 | "testTargets" : [
35 | {
36 | "target" : {
37 | "containerPath" : "container:Project.xcodeproj",
38 | "identifier" : "23766C251EAA3484007A9026",
39 | "name" : "iOSTests"
40 | }
41 | }
42 | ],
43 | "version" : 1
44 | }
45 |
--------------------------------------------------------------------------------
/Fixtures/iOS/iOS/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // iOS
4 | //
5 | // Created by Pedro Pinera Buendia on 21.04.17.
6 | // Copyright © 2017 es.ppinera. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @main
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 | var window: UIWindow?
14 |
15 | func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
16 | // Override point for customization after application launch.
17 | true
18 | }
19 |
20 | func applicationWillResignActive(_: UIApplication) {
21 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
22 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
23 | }
24 |
25 | func applicationDidEnterBackground(_: UIApplication) {
26 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
27 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
28 | }
29 |
30 | func applicationWillEnterForeground(_: UIApplication) {
31 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
32 | }
33 |
34 | func applicationDidBecomeActive(_: UIApplication) {
35 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
36 | }
37 |
38 | func applicationWillTerminate(_: UIApplication) {
39 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Fixtures/iOS/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | }
33 | ],
34 | "info" : {
35 | "version" : 1,
36 | "author" : "xcode"
37 | }
38 | }
--------------------------------------------------------------------------------
/Fixtures/iOS/iOS/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/Fixtures/iOS/iOS/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Fixtures/iOS/iOS/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/Fixtures/iOS/iOS/Model.xcdatamodeld/Model.xcdatamodel/contents:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/Fixtures/iOS/iOS/Private.h:
--------------------------------------------------------------------------------
1 | //
2 | // Private.h
3 | // Project
4 | //
5 | // Created by Pedro Piñera Buendía on 11.07.17.
6 | // Copyright © 2017 es.ppinera. All rights reserved.
7 | //
8 |
9 | #ifndef Private_h
10 | #define Private_h
11 |
12 |
13 | #endif /* Private_h */
14 |
--------------------------------------------------------------------------------
/Fixtures/iOS/iOS/Protected.h:
--------------------------------------------------------------------------------
1 | //
2 | // Protected.h
3 | // Project
4 | //
5 | // Created by Pedro Piñera Buendía on 11.07.17.
6 | // Copyright © 2017 es.ppinera. All rights reserved.
7 | //
8 |
9 | #ifndef Protected_h
10 | #define Protected_h
11 |
12 |
13 | #endif /* Protected_h */
14 |
--------------------------------------------------------------------------------
/Fixtures/iOS/iOS/Public.h:
--------------------------------------------------------------------------------
1 | //
2 | // Public.h
3 | // Project
4 | //
5 | // Created by Pedro Piñera Buendía on 11.07.17.
6 | // Copyright © 2017 es.ppinera. All rights reserved.
7 | //
8 |
9 | #ifndef Public_h
10 | #define Public_h
11 |
12 |
13 | #endif /* Public_h */
14 |
--------------------------------------------------------------------------------
/Fixtures/iOS/iOS/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // iOS
4 | //
5 | // Created by Pedro Pinera Buendia on 21.04.17.
6 | // Copyright © 2017 es.ppinera. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ViewController: UIViewController {
12 | override func viewDidLoad() {
13 | super.viewDidLoad()
14 | // Do any additional setup after loading the view, typically from a nib.
15 | }
16 |
17 | override func didReceiveMemoryWarning() {
18 | super.didReceiveMemoryWarning()
19 | // Dispose of any resources that can be recreated.
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Fixtures/iOS/iOSTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Fixtures/iOS/iOSTests/iOSTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // iOSTests.swift
3 | // iOSTests
4 | //
5 | // Created by Pedro Pinera Buendia on 21.04.17.
6 | // Copyright © 2017 es.ppinera. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import iOS
11 |
12 | class iOSTests: XCTestCase {
13 | override func setUp() {
14 | super.setUp()
15 | // Put setup code here. This method is called before the invocation of each test method in the class.
16 | }
17 |
18 | override func tearDown() {
19 | // Put teardown code here. This method is called after the invocation of each test method in the class.
20 | super.tearDown()
21 | }
22 |
23 | func testExample() {
24 | // This is an example of a functional test case.
25 | // Use XCTAssert and related functions to verify your tests produce the correct results.
26 | }
27 |
28 | func testPerformanceExample() {
29 | // This is an example of a performance test case.
30 | measure {
31 | // Put the code you want to measure the time of here.
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) from 2018 Pedro Piñera Buendía
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "originHash" : "8a13a61314cb0e10f1d22dddaabfebd1ab407a7b9ab06aa42fc1e38041a202a5",
3 | "pins" : [
4 | {
5 | "identity" : "aexml",
6 | "kind" : "remoteSourceControl",
7 | "location" : "https://github.com/tadija/AEXML.git",
8 | "state" : {
9 | "revision" : "db806756c989760b35108146381535aec231092b",
10 | "version" : "4.7.0"
11 | }
12 | },
13 | {
14 | "identity" : "pathkit",
15 | "kind" : "remoteSourceControl",
16 | "location" : "https://github.com/kylef/PathKit.git",
17 | "state" : {
18 | "revision" : "3bfd2737b700b9a36565a8c94f4ad2b050a5e574",
19 | "version" : "1.0.1"
20 | }
21 | },
22 | {
23 | "identity" : "spectre",
24 | "kind" : "remoteSourceControl",
25 | "location" : "https://github.com/kylef/Spectre.git",
26 | "state" : {
27 | "revision" : "26cc5e9ae0947092c7139ef7ba612e34646086c7",
28 | "version" : "0.10.1"
29 | }
30 | }
31 | ],
32 | "version" : 3
33 | }
34 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:6.0.0
2 |
3 | import PackageDescription
4 |
5 | let package = Package(
6 | name: "XcodeProj",
7 | products: [
8 | .library(name: "XcodeProj", targets: ["XcodeProj"]),
9 | ],
10 | dependencies: [
11 | .package(url: "https://github.com/tadija/AEXML.git", .upToNextMinor(from: "4.7.0")),
12 | .package(url: "https://github.com/kylef/PathKit.git", .upToNextMinor(from: "1.0.1")),
13 | ],
14 | targets: [
15 | .target(name: "XcodeProj",
16 | dependencies: [
17 | .product(name: "PathKit", package: "PathKit"),
18 | .product(name: "AEXML", package: "AEXML"),
19 | ],
20 | swiftSettings: [
21 | .enableExperimentalFeature("StrictConcurrency"),
22 | ]),
23 | .testTarget(name: "XcodeProjTests", dependencies: ["XcodeProj"]),
24 | ]
25 | )
26 |
--------------------------------------------------------------------------------
/Scripts/dump-known-file-extensions.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import json
4 | import os
5 | import plistlib
6 | import re
7 | import subprocess
8 | import sys
9 |
10 |
11 | def parse_xcspec(path):
12 | # convert ASCII plist to XML format and read as string
13 | xml_string = subprocess.check_output([
14 | 'plutil', '-convert', 'xml1', '-o', '-', '--', path])
15 | return plistlib.readPlistFromString(xml_string)
16 |
17 |
18 | def extract_extensions(path, into={}):
19 | data = parse_xcspec(path)
20 | extracted = 0
21 |
22 | if isinstance(data, list):
23 | for item in data:
24 | if 'Identifier' in item and 'Extensions' in item:
25 | ident = item['Identifier']
26 | for ext in item['Extensions']:
27 | into[ext] = ident
28 | extracted += 1
29 |
30 | if extracted > 0:
31 | sys.stderr.write('** Extracted {} extensions from {}\n'.format(
32 | extracted, path))
33 |
34 | return into
35 |
36 |
37 | def run(xcode_path):
38 | plugins_path = os.path.join(xcode_path, 'Contents', 'PlugIns')
39 | matcher = re.compile(r'\.(xcspec|pbfilespec)$')
40 | all_extensions = {}
41 | for root, dirs, files in os.walk(plugins_path):
42 | for file in files:
43 | if matcher.search(file):
44 | path = os.path.join(root, file)
45 | extract_extensions(path, all_extensions)
46 |
47 | json.dump(
48 | all_extensions,
49 | sys.stdout,
50 | sort_keys=True,
51 | indent=4,
52 | separators=(',', ': ')
53 | )
54 |
55 |
56 | if __name__ == '__main__':
57 | if len(sys.argv) == 2:
58 | run(sys.argv[1])
59 | else:
60 | sys.stderr.write('usage: {} /path/to/Xcode.app\n'.format(
61 | os.path.basename(sys.argv[0])))
62 | exit(1)
63 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Extensions/Array+Extras.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | public extension Array where Element: Hashable {
4 | /// Return the array with all duplicates removed.
5 | ///
6 | /// i.e. `[ 1, 2, 3, 1, 2 ].uniqued() == [ 1, 2, 3 ]`
7 | ///
8 | /// - note: Taken from stackoverflow.com/a/46354989/3141234, as
9 | /// per @Alexander's comment.
10 | func uniqued() -> [Element] {
11 | var seen = Set()
12 | return filter { seen.insert($0).inserted }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Extensions/Bool+Extras.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | public extension Bool {
4 | /// Returns a XML string value that represents the boolean.
5 | var xmlString: String {
6 | self ? "YES" : "NO"
7 | }
8 |
9 | /// Returns a 1 for true and 0 for false
10 | var int: UInt {
11 | self ? 1 : 0
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Extensions/KeyedDecodingContainer+Additions.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | extension KeyedDecodingContainer {
4 | func decode(_ key: KeyedDecodingContainer.Key) throws -> T where T: Decodable {
5 | try decode(T.self, forKey: key)
6 | }
7 |
8 | func decodeIfPresent(_ key: KeyedDecodingContainer.Key) throws -> T? where T: Decodable {
9 | try decodeIfPresent(T.self, forKey: key)
10 | }
11 |
12 | func decodeIntIfPresent(_ key: KeyedDecodingContainer.Key) throws -> UInt? {
13 | if let string: String = try? decodeIfPresent(key) {
14 | UInt(string)
15 | } else if let bool: Bool = try? decodeIfPresent(key) {
16 | bool ? 0 : 1
17 | } else if let int: UInt = try decodeIfPresent(key) {
18 | // don't `try?` here in case key _does_ exist but isn't an expected type
19 | // ie. not a string/bool/int
20 | int
21 | } else {
22 | nil
23 | }
24 | }
25 |
26 | func decodeIntBool(_ key: KeyedDecodingContainer.Key) throws -> Bool {
27 | guard let bool = try decodeIntBoolIfPresent(key) else {
28 | return false
29 | }
30 | return bool
31 | }
32 |
33 | func decodeIntBoolIfPresent(_ key: KeyedDecodingContainer.Key) throws -> Bool? {
34 | guard let int = try decodeIntIfPresent(key) else {
35 | return nil
36 | }
37 |
38 | guard int <= 2 else {
39 | throw DecodingError.dataCorruptedError(forKey: key, in: self, debugDescription: "Expected to decode Bool but found a number that is not 0 or 1")
40 | }
41 |
42 | return int == 1
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Extensions/NSRecursiveLock+Sync.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | extension NSRecursiveLock {
4 | func whileLocked(closure: () -> T) -> T {
5 | lock()
6 | defer {
7 | unlock()
8 | }
9 | let value = closure()
10 | return value
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Extensions/String+Utils.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | public extension String {
4 | var quoted: String {
5 | "\"\(self)\""
6 | }
7 |
8 | var isQuoted: Bool {
9 | hasPrefix("\"") && hasSuffix("\"")
10 | }
11 |
12 | static func random(length: Int = 20) -> String {
13 | let base = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
14 | var randomString = ""
15 |
16 | for _ in 0 ..< length {
17 | let randomValue = Int.random(in: 0 ..< base.count)
18 | randomString += "\(base[base.index(base.startIndex, offsetBy: randomValue)])"
19 | }
20 | return randomString
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Objects/BuildPhase/BuildFileSetting.swift:
--------------------------------------------------------------------------------
1 | public enum BuildFileSetting: Sendable, Equatable, Hashable {
2 | case string(String)
3 | case array([String])
4 |
5 | public var stringValue: String? {
6 | if case let .string(value) = self {
7 | value
8 | } else {
9 | nil
10 | }
11 | }
12 |
13 | public var arrayValue: [String]? {
14 | if case let .array(value) = self {
15 | value
16 | } else {
17 | nil
18 | }
19 | }
20 | }
21 |
22 | extension BuildFileSetting: Codable {
23 | public init(from decoder: Decoder) throws {
24 | let container = try decoder.singleValueContainer()
25 | do {
26 | let string = try container.decode(String.self)
27 | self = .string(string)
28 | } catch {
29 | let array = try container.decode([String].self)
30 | self = .array(array)
31 | }
32 | }
33 |
34 | public func encode(to encoder: Encoder) throws {
35 | var container = encoder.singleValueContainer()
36 | switch self {
37 | case let .string(string):
38 | try container.encode(string)
39 | case let .array(array):
40 | try container.encode(array)
41 | }
42 | }
43 | }
44 |
45 | extension BuildFileSetting: ExpressibleByArrayLiteral {
46 | public init(arrayLiteral elements: String...) {
47 | self = .array(elements)
48 | }
49 | }
50 |
51 | extension BuildFileSetting: ExpressibleByStringInterpolation {
52 | public init(stringLiteral value: StringLiteralType) {
53 | self = .string(value)
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Objects/BuildPhase/BuildPhase.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// Enum that encapsulates all kind of build phases available in Xcode.
4 | ///
5 | /// - sources: sources.
6 | /// - frameworks: frameworks.
7 | /// - resources: resources.
8 | /// - copyFiles: files.
9 | /// - runScript: scripts.
10 | /// - headers: headers.
11 | /// - carbonResources: build legacy Carbon resources.
12 | public enum BuildPhase: String {
13 | case sources = "Sources"
14 | case frameworks = "Frameworks"
15 | case resources = "Resources"
16 | case copyFiles = "CopyFiles"
17 | case runScript = "Run Script"
18 | case headers = "Headers"
19 | case carbonResources = "Rez"
20 | }
21 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Objects/BuildPhase/PBXFrameworksBuildPhase.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// This is the element for the framework link build phase.
4 | public final class PBXFrameworksBuildPhase: PBXBuildPhase {
5 | override public var buildPhase: BuildPhase {
6 | .frameworks
7 | }
8 |
9 | override func isEqual(to object: Any?) -> Bool {
10 | guard let rhs = object as? PBXFrameworksBuildPhase else { return false }
11 | return isEqual(to: rhs)
12 | }
13 | }
14 |
15 | // MARK: - PBXFrameworksBuildPhase Extension (PlistSerializable)
16 |
17 | extension PBXFrameworksBuildPhase: PlistSerializable {
18 | func plistKeyAndValue(proj: PBXProj, reference: String) throws -> (key: CommentedString, value: PlistValue) {
19 | var dictionary: [CommentedString: PlistValue] = try plistValues(proj: proj, reference: reference)
20 | dictionary["isa"] = .string(CommentedString(PBXFrameworksBuildPhase.isa))
21 | return (key: CommentedString(reference, comment: "Frameworks"), value: .dictionary(dictionary))
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Objects/BuildPhase/PBXHeadersBuildPhase.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import PathKit
3 |
4 | /// This is the element for the framework headers build phase.
5 | public final class PBXHeadersBuildPhase: PBXBuildPhase {
6 | override public var buildPhase: BuildPhase {
7 | .headers
8 | }
9 |
10 | override func isEqual(to object: Any?) -> Bool {
11 | guard let rhs = object as? PBXHeadersBuildPhase else { return false }
12 | return isEqual(to: rhs)
13 | }
14 | }
15 |
16 | // MARK: - PBXHeadersBuildPhase Extension (PlistSerializable)
17 |
18 | extension PBXHeadersBuildPhase: PlistSerializable {
19 | func plistKeyAndValue(proj: PBXProj, reference: String) throws -> (key: CommentedString, value: PlistValue) {
20 | var dictionary: [CommentedString: PlistValue] = try plistValues(proj: proj, reference: reference)
21 | dictionary["isa"] = .string(CommentedString(PBXHeadersBuildPhase.isa))
22 | return (key: CommentedString(reference, comment: "Headers"), value: .dictionary(dictionary))
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Objects/BuildPhase/PBXResourcesBuildPhase.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// This is the element for the resources copy build phase.
4 | public final class PBXResourcesBuildPhase: PBXBuildPhase {
5 | override public var buildPhase: BuildPhase {
6 | .resources
7 | }
8 |
9 | override func isEqual(to object: Any?) -> Bool {
10 | guard let rhs = object as? PBXResourcesBuildPhase else { return false }
11 | return isEqual(to: rhs)
12 | }
13 | }
14 |
15 | // MARK: - PBXResourcesBuildPhase Extension (PlistSerializable)
16 |
17 | extension PBXResourcesBuildPhase: PlistSerializable {
18 | func plistKeyAndValue(proj: PBXProj, reference: String) throws -> (key: CommentedString, value: PlistValue) {
19 | var dictionary: [CommentedString: PlistValue] = try plistValues(proj: proj, reference: reference)
20 | dictionary["isa"] = .string(CommentedString(PBXResourcesBuildPhase.isa))
21 | return (key: CommentedString(reference, comment: "Resources"), value: .dictionary(dictionary))
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Objects/BuildPhase/PBXRezBuildPhase.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// This is the element for the Build Carbon Resources build phase.
4 | /// These are legacy .r files from the Classic Mac OS era.
5 | public final class PBXRezBuildPhase: PBXBuildPhase {
6 | override public var buildPhase: BuildPhase {
7 | .carbonResources
8 | }
9 |
10 | override func isEqual(to object: Any?) -> Bool {
11 | guard let rhs = object as? PBXRezBuildPhase else { return false }
12 | return isEqual(to: rhs)
13 | }
14 | }
15 |
16 | // MARK: - PBXRezBuildPhase Extension (PlistSerializable)
17 |
18 | extension PBXRezBuildPhase: PlistSerializable {
19 | func plistKeyAndValue(proj: PBXProj, reference: String) throws -> (key: CommentedString, value: PlistValue) {
20 | var dictionary: [CommentedString: PlistValue] = try plistValues(proj: proj, reference: reference)
21 | dictionary["isa"] = .string(CommentedString(PBXRezBuildPhase.isa))
22 | return (key: CommentedString(reference, comment: "Rez"), value: .dictionary(dictionary))
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Objects/BuildPhase/PBXSourcesBuildPhase.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// This is the element for the sources compilation build phase.
4 | public final class PBXSourcesBuildPhase: PBXBuildPhase {
5 | override public var buildPhase: BuildPhase {
6 | .sources
7 | }
8 |
9 | override func isEqual(to object: Any?) -> Bool {
10 | guard let rhs = object as? PBXSourcesBuildPhase else { return false }
11 | return isEqual(to: rhs)
12 | }
13 | }
14 |
15 | extension PBXSourcesBuildPhase: PlistSerializable {
16 | // MARK: - PlistSerializable
17 |
18 | func plistKeyAndValue(proj: PBXProj, reference: String) throws -> (key: CommentedString, value: PlistValue) {
19 | var dictionary: [CommentedString: PlistValue] = try plistValues(proj: proj, reference: reference)
20 | dictionary["isa"] = .string(CommentedString(PBXSourcesBuildPhase.isa))
21 | return (key: CommentedString(reference, comment: "Sources"), value: .dictionary(dictionary))
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Objects/Configuration/BuildSettings.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// Build settings.
4 | public typealias BuildSettings = [String: BuildSetting]
5 |
6 | private let yes = "YES"
7 | private let no = "NO"
8 |
9 | public enum BuildSetting: Sendable, Equatable {
10 | case string(String)
11 | case array([String])
12 |
13 | public var stringValue: String? {
14 | if case let .string(value) = self {
15 | value
16 | } else {
17 | nil
18 | }
19 | }
20 |
21 | public var boolValue: Bool? {
22 | if case let .string(value) = self {
23 | switch value {
24 | case yes: true
25 | case no: false
26 | default: nil
27 | }
28 | } else {
29 | nil
30 | }
31 | }
32 |
33 | public var arrayValue: [String]? {
34 | if case let .array(value) = self {
35 | value
36 | } else {
37 | nil
38 | }
39 | }
40 | }
41 |
42 | extension BuildSetting: CustomStringConvertible {
43 | public var description: String {
44 | switch self {
45 | case let .string(string):
46 | string
47 | case let .array(array):
48 | array.joined(separator: " ")
49 | }
50 | }
51 | }
52 |
53 | extension BuildSetting: Decodable {
54 | public init(from decoder: Decoder) throws {
55 | let container = try decoder.singleValueContainer()
56 | do {
57 | let string = try container.decode(String.self)
58 | self = .string(string)
59 | } catch {
60 | let array = try container.decode([String].self)
61 | self = .array(array)
62 | }
63 | }
64 | }
65 |
66 | extension BuildSetting: ExpressibleByArrayLiteral {
67 | public init(arrayLiteral elements: String...) {
68 | self = .array(elements)
69 | }
70 | }
71 |
72 | extension BuildSetting: ExpressibleByStringInterpolation {
73 | public init(stringLiteral value: StringLiteralType) {
74 | self = .string(value)
75 | }
76 | }
77 |
78 | extension BuildSetting: ExpressibleByBooleanLiteral {
79 | public init(booleanLiteral value: Bool) {
80 | self = .string(value ? yes : no)
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Objects/Files/PBXContainerItem.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// Class representing an element that may contain other elements.
4 | public class PBXContainerItem: PBXObject {
5 | /// User comments for the object.
6 | var comments: String?
7 |
8 | // MARK: - Init
9 |
10 | init(comments: String? = nil) {
11 | self.comments = comments
12 | super.init()
13 | }
14 |
15 | // MARK: - Decodable
16 |
17 | fileprivate enum CodingKeys: String, CodingKey {
18 | case comments
19 | }
20 |
21 | public required init(from decoder: Decoder) throws {
22 | let container = try decoder.container(keyedBy: CodingKeys.self)
23 | comments = try container.decodeIfPresent(.comments)
24 | try super.init(from: decoder)
25 | }
26 |
27 | func plistValues(proj _: PBXProj, reference _: String) throws -> [CommentedString: PlistValue] {
28 | var dictionary = [CommentedString: PlistValue]()
29 | if let comments {
30 | dictionary["comments"] = .string(CommentedString(comments))
31 | }
32 | return dictionary
33 | }
34 |
35 | override func isEqual(to object: Any?) -> Bool {
36 | guard let rhs = object as? PBXContainerItem else { return false }
37 | return isEqual(to: rhs)
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedExceptionSet.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// Common class for exception sets, such as `PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet` and `PBXFileSystemSynchronizedBuildFileExceptionSet`
4 | public class PBXFileSystemSynchronizedExceptionSet: PBXObject {}
5 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Objects/Files/PBXVariantGroup.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | // This is the element for referencing localized resources.
4 | public final class PBXVariantGroup: PBXGroup {
5 | override func isEqual(to object: Any?) -> Bool {
6 | guard let rhs = object as? PBXVariantGroup else { return false }
7 | return isEqual(to: rhs)
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Objects/Project/ProjectAttribute.swift:
--------------------------------------------------------------------------------
1 | public enum ProjectAttribute: Equatable {
2 | case string(String)
3 | case array([String])
4 | case targetReference(PBXObject)
5 | case attributeDictionary([String: [String: ProjectAttribute]])
6 |
7 | public var stringValue: String? {
8 | if case let .string(value) = self {
9 | value
10 | } else {
11 | nil
12 | }
13 | }
14 |
15 | public var arrayValue: [String]? {
16 | if case let .array(value) = self {
17 | value
18 | } else {
19 | nil
20 | }
21 | }
22 | }
23 |
24 | extension ProjectAttribute: Decodable {
25 | public init(from decoder: Decoder) throws {
26 | let container = try decoder.singleValueContainer()
27 |
28 | if let string = try? container.decode(String.self) {
29 | self = .string(string)
30 | } else if let array = try? container.decode([String].self) {
31 | self = .array(array)
32 | } else {
33 | let targetAttributes = try container.decode([String: [String: ProjectAttribute]].self)
34 | self = .attributeDictionary(targetAttributes)
35 | }
36 | }
37 | }
38 |
39 | extension ProjectAttribute: ExpressibleByArrayLiteral {
40 | public init(arrayLiteral elements: String...) {
41 | self = .array(elements)
42 | }
43 | }
44 |
45 | extension ProjectAttribute: ExpressibleByStringInterpolation {
46 | public init(stringLiteral value: StringLiteralType) {
47 | self = .string(value)
48 | }
49 | }
50 |
51 | extension ProjectAttribute: ExpressibleByDictionaryLiteral {
52 | public init(dictionaryLiteral elements: (String, [String: ProjectAttribute])...) {
53 | self = .attributeDictionary(Dictionary(uniqueKeysWithValues: elements))
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Objects/Sourcery/Sourcery.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | protocol AutoEquatable {}
4 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Objects/SwiftPackage/XCLocalSwiftPackageReference.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// This element is an abstract parent for specialized targets.
4 | public class XCLocalSwiftPackageReference: PBXContainerItem, PlistSerializable {
5 | /// Repository url.
6 | public var relativePath: String
7 |
8 | /// Initializes the local swift package reference with its attributes.
9 | ///
10 | /// - Parameters:
11 | /// - repositoryPath: Package repository path.
12 | public init(relativePath: String) {
13 | self.relativePath = relativePath
14 | super.init()
15 | }
16 |
17 | enum CodingKeys: String, CodingKey {
18 | case relativePath
19 | }
20 |
21 | public required init(from decoder: Decoder) throws {
22 | let container = try decoder.container(keyedBy: CodingKeys.self)
23 |
24 | relativePath = try container.decode(String.self, forKey: .relativePath)
25 |
26 | try super.init(from: decoder)
27 | }
28 |
29 | /// It returns the name of the package reference.
30 | public var name: String? {
31 | relativePath
32 | }
33 |
34 | func plistKeyAndValue(proj: PBXProj, reference: String) throws -> (key: CommentedString, value: PlistValue) {
35 | var dictionary = try super.plistValues(proj: proj, reference: reference)
36 | dictionary["isa"] = .string(CommentedString(XCLocalSwiftPackageReference.isa))
37 | dictionary["relativePath"] = .string(.init(relativePath))
38 | return (key: CommentedString(reference, comment: "XCLocalSwiftPackageReference \"\(name ?? "")\""),
39 | value: .dictionary(dictionary))
40 | }
41 |
42 | override func isEqual(to object: Any?) -> Bool {
43 | guard let rhs = object as? XCLocalSwiftPackageReference else { return false }
44 | return isEqual(to: rhs)
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Objects/Targets/PBXAggregateTarget.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// This is the element for a build target that aggregates several others.
4 | public final class PBXAggregateTarget: PBXTarget {
5 | override func isEqual(to object: Any?) -> Bool {
6 | guard let rhs = object as? PBXAggregateTarget else { return false }
7 | return isEqual(to: rhs)
8 | }
9 | }
10 |
11 | // MARK: - PBXAggregateTarget Extension (PlistSerializable)
12 |
13 | extension PBXAggregateTarget: PlistSerializable {
14 | func plistKeyAndValue(proj: PBXProj, reference: String) throws -> (key: CommentedString, value: PlistValue) {
15 | try plistValues(proj: proj, isa: PBXAggregateTarget.isa, reference: reference)
16 | }
17 | }
18 |
19 | // MARK: - Helpers
20 |
21 | public extension PBXAggregateTarget {
22 | /// Adds a local target dependency to the target.
23 | ///
24 | /// - Parameter target: dependency target.
25 | /// - Returns: target dependency reference.
26 | /// - Throws: an error if the dependency cannot be created.
27 | func addDependency(target: PBXTarget) throws -> PBXTargetDependency? {
28 | let objects = try target.objects()
29 | guard let project = objects.projects.first?.value else {
30 | return nil
31 | }
32 | let proxy = PBXContainerItemProxy(containerPortal: .project(project),
33 | remoteGlobalID: .object(target),
34 | proxyType: .nativeTarget,
35 | remoteInfo: target.name)
36 | objects.add(object: proxy)
37 | let targetDependency = PBXTargetDependency(name: target.name,
38 | target: target,
39 | targetProxy: proxy)
40 | objects.add(object: targetDependency)
41 | dependencies.append(targetDependency)
42 | return targetDependency
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Project/XCDebugger.swift:
--------------------------------------------------------------------------------
1 | import AEXML
2 | import Foundation
3 | import PathKit
4 |
5 | enum XCDebugger {
6 | /// Returns debugger folder path relative to the given path.
7 | ///
8 | /// - Parameter path: parent folder of debugger folder (xcshareddata or xcuserdata)
9 | /// - Returns: debugger folder path relative to the given path.
10 | public static func path(_ path: Path) -> Path {
11 | path + "xcdebugger"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Protocols/Writable.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import PathKit
3 |
4 | /// Protocol that defines how an entity can be written to disk
5 | public protocol Writable {
6 | /// Writes the object that conforms the protocol.
7 | ///
8 | /// - Parameter path: The path to write to
9 | /// - Parameter override: True if the content should be overridden if it already exists.
10 | /// - Throws: writing error if something goes wrong.
11 | func write(path: Path, override: Bool) throws
12 |
13 | /// Writes the object that conforms the protocol.
14 | ///
15 | /// - Parameter pathString: The path string to write to
16 | /// - Parameter override: True if the content should be overridden if it already exists.
17 | /// - Throws: writing error if something goes wrong.
18 | func write(pathString: String, override: Bool) throws
19 |
20 | /// Gets the data representation of the object that conforms to the protocol.
21 | ///
22 | /// - Throws: error if encoding to Data fails.
23 | func dataRepresentation() throws -> Data?
24 | }
25 |
26 | public extension Writable {
27 | func write(pathString: String, override: Bool) throws {
28 | let path = Path(pathString)
29 | try write(path: path, override: override)
30 | }
31 |
32 | func dataRepresentation() throws -> Data? { nil }
33 | }
34 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Scheme/XCScheme+AditionalOption.swift:
--------------------------------------------------------------------------------
1 | import AEXML
2 | import Foundation
3 | import PathKit
4 |
5 | public extension XCScheme {
6 | final class AdditionalOption: Equatable {
7 | // MARK: - Attributes
8 |
9 | public var key: String
10 | public var value: String
11 | public var isEnabled: Bool
12 |
13 | // MARK: - Init
14 |
15 | public init(key: String, value: String, isEnabled: Bool) {
16 | self.key = key
17 | self.value = value
18 | self.isEnabled = isEnabled
19 | }
20 |
21 | init(element: AEXMLElement) throws {
22 | key = element.attributes["key"]!
23 | value = element.attributes["value"]!
24 | isEnabled = element.attributes["isEnabled"] == "YES"
25 | }
26 |
27 | // MARK: - XML
28 |
29 | func xmlElement() -> AEXMLElement {
30 | AEXMLElement(name: "AdditionalOption",
31 | value: nil,
32 | attributes: [
33 | "key": key,
34 | "value": value,
35 | "isEnabled": isEnabled.xmlString,
36 | ])
37 | }
38 |
39 | // MARK: - Equatable
40 |
41 | public static func == (lhs: AdditionalOption, rhs: AdditionalOption) -> Bool {
42 | lhs.key == rhs.key &&
43 | lhs.value == rhs.value &&
44 | lhs.isEnabled == rhs.isEnabled
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Scheme/XCScheme+AnalyzeAction.swift:
--------------------------------------------------------------------------------
1 | import AEXML
2 | import Foundation
3 | import PathKit
4 |
5 | public extension XCScheme {
6 | final class AnalyzeAction: Equatable {
7 | // MARK: - Static
8 |
9 | // Xcode disables PreActions and PostActions for Analyze actions, so this Action
10 | // does not exetend SerialAction.
11 | private static let defaultBuildConfiguration = "Debug"
12 |
13 | // MARK: - Attributes
14 |
15 | public var buildConfiguration: String
16 |
17 | // MARK: - Init
18 |
19 | public init(buildConfiguration: String) {
20 | self.buildConfiguration = buildConfiguration
21 | }
22 |
23 | init(element: AEXMLElement) throws {
24 | buildConfiguration = element.attributes["buildConfiguration"] ?? AnalyzeAction.defaultBuildConfiguration
25 | }
26 |
27 | // MARK: - XML
28 |
29 | func xmlElement() -> AEXMLElement {
30 | var attributes: [String: String] = [:]
31 | attributes["buildConfiguration"] = buildConfiguration
32 | return AEXMLElement(name: "AnalyzeAction", value: nil, attributes: attributes)
33 | }
34 |
35 | // MARK: - Equatable
36 |
37 | public static func == (lhs: AnalyzeAction, rhs: AnalyzeAction) -> Bool {
38 | lhs.buildConfiguration == rhs.buildConfiguration
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Scheme/XCScheme+BuildableProductRunnable.swift:
--------------------------------------------------------------------------------
1 | import AEXML
2 | import Foundation
3 |
4 | public extension XCScheme {
5 | final class BuildableProductRunnable: Runnable {
6 | // MARK: - XML
7 |
8 | override func xmlElement() -> AEXMLElement {
9 | let element = super.xmlElement()
10 | element.name = "BuildableProductRunnable"
11 | return element
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Scheme/XCScheme+EnvironmentVariable.swift:
--------------------------------------------------------------------------------
1 | import AEXML
2 | import Foundation
3 |
4 | public extension XCScheme {
5 | struct EnvironmentVariable: Equatable {
6 | // MARK: - Attributes
7 |
8 | public let variable: String
9 | public let value: String
10 | public let enabled: Bool
11 |
12 | // MARK: - Init
13 |
14 | public init(variable: String, value: String, enabled: Bool) {
15 | self.variable = variable
16 | self.value = value
17 | self.enabled = enabled
18 | }
19 |
20 | // MARK: - XML
21 |
22 | func xmlElement() -> AEXMLElement {
23 | AEXMLElement(name: "EnvironmentVariable",
24 | value: nil,
25 | attributes: ["key": variable, "value": value, "isEnabled": enabled ? "YES" : "NO"])
26 | }
27 |
28 | static func parseVariables(from element: AEXMLElement) throws -> [EnvironmentVariable] {
29 | try element.children.map { elt in
30 | guard let variableKey = elt.attributes["key"] else {
31 | throw XCSchemeError.missing(property: "key")
32 | }
33 | guard let variableValue = elt.attributes["value"] else {
34 | throw XCSchemeError.missing(property: "value")
35 | }
36 | guard let variableEnabledRaw = elt.attributes["isEnabled"] else {
37 | throw XCSchemeError.missing(property: "isEnabled")
38 | }
39 |
40 | return EnvironmentVariable(variable: variableKey, value: variableValue, enabled: variableEnabledRaw == "YES")
41 | }
42 | }
43 |
44 | static func xmlElement(from variables: [EnvironmentVariable]) -> AEXMLElement {
45 | let element = AEXMLElement(name: "EnvironmentVariables",
46 | value: nil)
47 | for arg in variables {
48 | element.addChild(arg.xmlElement())
49 | }
50 |
51 | return element
52 | }
53 |
54 | // MARK: - Equatable
55 |
56 | public static func == (lhs: EnvironmentVariable, rhs: EnvironmentVariable) -> Bool {
57 | lhs.variable == rhs.variable &&
58 | lhs.value == rhs.value &&
59 | lhs.enabled == rhs.enabled
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Scheme/XCScheme+LocationScenarioReference.swift:
--------------------------------------------------------------------------------
1 | import AEXML
2 | import Foundation
3 |
4 | public extension XCScheme {
5 | final class LocationScenarioReference: Equatable {
6 | // MARK: - Attributes
7 |
8 | public var identifier: String
9 | public var referenceType: String
10 |
11 | // MARK: - Init
12 |
13 | public init(identifier: String, referenceType: String) {
14 | self.identifier = identifier
15 | self.referenceType = referenceType
16 | }
17 |
18 | init(element: AEXMLElement) throws {
19 | identifier = element.attributes["identifier"]!
20 | referenceType = element.attributes["referenceType"]!
21 | }
22 |
23 | // MARK: - XML
24 |
25 | func xmlElement() -> AEXMLElement {
26 | AEXMLElement(name: "LocationScenarioReference",
27 | value: nil,
28 | attributes: [
29 | "identifier": identifier,
30 | "referenceType": referenceType,
31 | ])
32 | }
33 |
34 | // MARK: - Equatable
35 |
36 | public static func == (lhs: LocationScenarioReference, rhs: LocationScenarioReference) -> Bool {
37 | lhs.identifier == rhs.identifier &&
38 | lhs.referenceType == rhs.referenceType
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Scheme/XCScheme+PathRunnable.swift:
--------------------------------------------------------------------------------
1 | import AEXML
2 | import Foundation
3 | import PathKit
4 |
5 | public extension XCScheme {
6 | class PathRunnable: Runnable {
7 | // MARK: - Attributes
8 |
9 | public var filePath: String
10 |
11 | // MARK: - Init
12 |
13 | public init(filePath: String,
14 | runnableDebuggingMode: String = "0") {
15 | self.filePath = filePath
16 | super.init(buildableReference: nil,
17 | runnableDebuggingMode: runnableDebuggingMode)
18 | }
19 |
20 | override init(element: AEXMLElement) throws {
21 | filePath = element.attributes["FilePath"] ?? ""
22 | try super.init(element: element)
23 | }
24 |
25 | // MARK: - XML
26 |
27 | override func xmlElement() -> AEXMLElement {
28 | let element = super.xmlElement()
29 | element.name = "PathRunnable"
30 | element.attributes["FilePath"] = filePath
31 | return element
32 | }
33 |
34 | // MARK: - Equatable
35 |
36 | override func isEqual(other: XCScheme.Runnable) -> Bool {
37 | guard let other = other as? PathRunnable else {
38 | return false
39 | }
40 |
41 | return super.isEqual(other: other) &&
42 | filePath == other.filePath
43 | }
44 |
45 | public static func == (lhs: PathRunnable, rhs: PathRunnable) -> Bool {
46 | lhs.isEqual(other: rhs)
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Scheme/XCScheme+RemoteRunnable.swift:
--------------------------------------------------------------------------------
1 | import AEXML
2 | import Foundation
3 |
4 | public extension XCScheme {
5 | final class RemoteRunnable: Runnable {
6 | // MARK: - Attributes
7 |
8 | public var bundleIdentifier: String
9 | public var remotePath: String?
10 |
11 | // MARK: - Init
12 |
13 | public init(buildableReference: BuildableReference,
14 | bundleIdentifier: String,
15 | runnableDebuggingMode: String = "0",
16 | remotePath: String? = nil) {
17 | self.bundleIdentifier = bundleIdentifier
18 | self.remotePath = remotePath
19 | super.init(buildableReference: buildableReference,
20 | runnableDebuggingMode: runnableDebuggingMode)
21 | }
22 |
23 | override init(element: AEXMLElement) throws {
24 | bundleIdentifier = element.attributes["BundleIdentifier"] ?? ""
25 | remotePath = element.attributes["RemotePath"]
26 | try super.init(element: element)
27 | }
28 |
29 | // MARK: - XML
30 |
31 | override func xmlElement() -> AEXMLElement {
32 | let element = super.xmlElement()
33 | element.name = "RemoteRunnable"
34 | element.attributes["BundleIdentifier"] = bundleIdentifier
35 | element.attributes["RemotePath"] = remotePath
36 | return element
37 | }
38 |
39 | // MARK: - Equatable
40 |
41 | override func isEqual(other: XCScheme.Runnable) -> Bool {
42 | guard let other = other as? RemoteRunnable else {
43 | return false
44 | }
45 |
46 | return super.isEqual(other: other) &&
47 | bundleIdentifier == other.bundleIdentifier &&
48 | remotePath == other.remotePath
49 | }
50 |
51 | public static func == (lhs: RemoteRunnable, rhs: RemoteRunnable) -> Bool {
52 | lhs.isEqual(other: rhs) && rhs.isEqual(other: lhs)
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Scheme/XCScheme+Runnable.swift:
--------------------------------------------------------------------------------
1 | import AEXML
2 | import Foundation
3 |
4 | public extension XCScheme {
5 | class Runnable: Equatable {
6 | // MARK: - Attributes
7 |
8 | public var runnableDebuggingMode: String
9 | public var buildableReference: BuildableReference?
10 |
11 | // MARK: - Init
12 |
13 | public init(buildableReference: BuildableReference?,
14 | runnableDebuggingMode: String = "0") {
15 | self.buildableReference = buildableReference
16 | self.runnableDebuggingMode = runnableDebuggingMode
17 | }
18 |
19 | init(element: AEXMLElement) throws {
20 | runnableDebuggingMode = element.attributes["runnableDebuggingMode"] ?? "0"
21 | buildableReference = try? BuildableReference(element: element["BuildableReference"])
22 | }
23 |
24 | // MARK: - XML
25 |
26 | func xmlElement() -> AEXMLElement {
27 | let element = AEXMLElement(name: "Runnable",
28 | value: nil,
29 | attributes: ["runnableDebuggingMode": runnableDebuggingMode])
30 | if let buildableReference {
31 | element.addChild(buildableReference.xmlElement())
32 | }
33 | return element
34 | }
35 |
36 | // MARK: - Equatable
37 |
38 | func isEqual(other: Runnable) -> Bool {
39 | runnableDebuggingMode == other.runnableDebuggingMode &&
40 | buildableReference == other.buildableReference
41 | }
42 |
43 | public static func == (lhs: Runnable, rhs: Runnable) -> Bool {
44 | lhs.isEqual(other: rhs) && rhs.isEqual(other: lhs)
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Scheme/XCScheme+SerialAction.swift:
--------------------------------------------------------------------------------
1 | import AEXML
2 | import Foundation
3 |
4 | public extension XCScheme {
5 | class SerialAction: Equatable {
6 | // MARK: - Attributes
7 |
8 | public var preActions: [ExecutionAction]
9 | public var postActions: [ExecutionAction]
10 |
11 | // MARK: - Init
12 |
13 | init(_ preActions: [ExecutionAction], _ postActions: [ExecutionAction]) {
14 | self.preActions = preActions
15 | self.postActions = postActions
16 | }
17 |
18 | init(element: AEXMLElement) throws {
19 | preActions = try element["PreActions"]["ExecutionAction"].all?.map(ExecutionAction.init) ?? []
20 | postActions = try element["PostActions"]["ExecutionAction"].all?.map(ExecutionAction.init) ?? []
21 | }
22 |
23 | // MARK: - XML
24 |
25 | func writeXML(parent element: AEXMLElement) {
26 | if !preActions.isEmpty {
27 | let preActions = element.addChild(name: "PreActions")
28 | for preAction in self.preActions {
29 | preActions.addChild(preAction.xmlElement())
30 | }
31 | }
32 | if !postActions.isEmpty {
33 | let postActions = element.addChild(name: "PostActions")
34 | for postAction in self.postActions {
35 | postActions.addChild(postAction.xmlElement())
36 | }
37 | }
38 | }
39 |
40 | // MARK: - Equatable
41 |
42 | func isEqual(to: Any?) -> Bool {
43 | guard let rhs = to as? SerialAction else { return false }
44 | return preActions == rhs.preActions &&
45 | postActions == rhs.postActions
46 | }
47 |
48 | public static func == (lhs: SerialAction, rhs: SerialAction) -> Bool {
49 | lhs.isEqual(to: rhs)
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Scheme/XCScheme+StoreKitConfigurationFileReference.swift:
--------------------------------------------------------------------------------
1 | import AEXML
2 | import Foundation
3 |
4 | public extension XCScheme {
5 | final class StoreKitConfigurationFileReference: Equatable {
6 | // MARK: - Attributes
7 |
8 | public var identifier: String
9 |
10 | // MARK: - Init
11 |
12 | public init(identifier: String) {
13 | self.identifier = identifier
14 | }
15 |
16 | init(element: AEXMLElement) throws {
17 | identifier = element.attributes["identifier"]!
18 | }
19 |
20 | // MARK: - XML
21 |
22 | func xmlElement() -> AEXMLElement {
23 | AEXMLElement(name: "StoreKitConfigurationFileReference",
24 | value: nil,
25 | attributes: [
26 | "identifier": identifier,
27 | ])
28 | }
29 |
30 | // MARK: - Equatable
31 |
32 | public static func == (lhs: StoreKitConfigurationFileReference, rhs: StoreKitConfigurationFileReference) -> Bool {
33 | lhs.identifier == rhs.identifier
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Scheme/XCScheme+TestItem.swift:
--------------------------------------------------------------------------------
1 | import AEXML
2 | import Foundation
3 |
4 | public extension XCScheme {
5 | final class TestItem: Equatable {
6 | // MARK: - Attributes
7 |
8 | public var identifier: String
9 |
10 | // MARK: - Init
11 |
12 | public init(identifier: String) {
13 | self.identifier = identifier
14 | }
15 |
16 | init(element: AEXMLElement) throws {
17 | identifier = element.attributes["Identifier"]!
18 | }
19 |
20 | // MARK: - XML
21 |
22 | func xmlElement() -> AEXMLElement {
23 | AEXMLElement(name: "Test",
24 | value: nil,
25 | attributes: ["Identifier": identifier])
26 | }
27 |
28 | // MARK: - Equatable
29 |
30 | public static func == (lhs: TestItem, rhs: TestItem) -> Bool {
31 | lhs.identifier == rhs.identifier
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Scheme/XCScheme+TestParallelization.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | public extension XCScheme {
4 | /// With the introduction of Swift Testing and Xcode 16, you can now choose to run your tests
5 | // in parallel across either the full suite of tests in a target with `.all`, just those created
6 | // under Swift Testing with `.swiftTestingOnly`, or run them serially with the `.none` option.
7 | enum TestParallelization: String {
8 | case all
9 | case swiftTestingOnly
10 | case none
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Scheme/XCScheme+TestPlanReference.swift:
--------------------------------------------------------------------------------
1 | import AEXML
2 | import Foundation
3 |
4 | public extension XCScheme {
5 | final class TestPlanReference: Equatable {
6 | // MARK: - Attributes
7 |
8 | public var reference: String
9 | public var `default`: Bool
10 |
11 | // MARK: - Init
12 |
13 | public init(reference: String,
14 | default: Bool = false) {
15 | self.reference = reference
16 | self.default = `default`
17 | }
18 |
19 | init(element: AEXMLElement) throws {
20 | reference = element.attributes["reference"]!
21 | `default` = element.attributes["default"] == "YES"
22 | }
23 |
24 | // MARK: - XML
25 |
26 | func xmlElement() -> AEXMLElement {
27 | var attributes: [String: String] = ["reference": reference]
28 | if `default` {
29 | attributes["default"] = `default`.xmlString
30 | }
31 |
32 | let element = AEXMLElement(name: "TestPlanReference",
33 | value: nil,
34 | attributes: attributes)
35 |
36 | return element
37 | }
38 |
39 | // MARK: - Equatable
40 |
41 | public static func == (lhs: TestPlanReference, rhs: TestPlanReference) -> Bool {
42 | lhs.reference == rhs.reference &&
43 | lhs.default == rhs.default
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Utils/Decoders.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// Project object reference repository.
4 | class PBXObjectReferenceRepository {
5 | /// References.
6 | var references: [String: PBXObjectReference] = [:]
7 | let lock = NSRecursiveLock()
8 |
9 | /// Returns and creates if it doesn't exist an object reference.
10 | ///
11 | /// - Parameters:
12 | /// - reference: reference value.
13 | /// - objects: objects.
14 | /// - Returns: object reference.
15 | func getOrCreate(reference: String, objects: PBXObjects) -> PBXObjectReference {
16 | lock.whileLocked {
17 | if let objectReference = references[reference] {
18 | return objectReference
19 | }
20 | let objectReference = PBXObjectReference(reference, objects: objects)
21 | references[reference] = objectReference
22 | return objectReference
23 | }
24 | }
25 | }
26 |
27 | /// Context used when the project is being decoded.
28 | class ProjectDecodingContext {
29 | /// Object reference repository.
30 | let objectReferenceRepository: PBXObjectReferenceRepository
31 |
32 | /// Objects.
33 | let objects: PBXObjects
34 |
35 | init() {
36 | objectReferenceRepository = PBXObjectReferenceRepository()
37 | objects = PBXObjects(objects: [])
38 | }
39 | }
40 |
41 | // MARK: - CodingUserInfoKey (Context)
42 |
43 | extension CodingUserInfoKey {
44 | /// Context user info key.
45 | static let context: CodingUserInfoKey = .init(rawValue: "context")!
46 | }
47 |
48 | /// Xcodeproj JSON decoder.
49 | final class XcodeprojJSONDecoder: JSONDecoder, @unchecked Sendable {
50 | /// Default init.
51 | init(context: ProjectDecodingContext = ProjectDecodingContext()) {
52 | super.init()
53 | userInfo = [.context: context]
54 | }
55 | }
56 |
57 | /// Xcodeproj property list decoder.
58 | final class XcodeprojPropertyListDecoder: PropertyListDecoder, @unchecked Sendable {
59 | /// Default init.
60 | init(context: ProjectDecodingContext = ProjectDecodingContext()) {
61 | super.init()
62 | userInfo = [.context: context]
63 | }
64 | }
65 |
66 | // MARK: - Decoder (Context)
67 |
68 | extension Decoder {
69 | /// Returns the decoding context.
70 | var context: ProjectDecodingContext {
71 | // swiftlint:disable:next force_cast
72 | userInfo[.context] as! ProjectDecodingContext
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Utils/PlistDecoding.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | indirect enum PlistObject: Sendable, Equatable {
4 | case string(String)
5 | case array([String])
6 | case dictionary([String: PlistObject])
7 | }
8 |
9 | extension PlistObject: Codable {
10 | public init(from decoder: Decoder) throws {
11 | let container = try decoder.singleValueContainer()
12 | do {
13 | let string = try container.decode(String.self)
14 | self = .string(string)
15 | } catch {
16 | do {
17 | let array = try container.decode([String].self)
18 | self = .array(array)
19 | } catch {
20 | let dictionary = try container.decode([String: PlistObject].self)
21 | self = .dictionary(dictionary)
22 | }
23 | }
24 | }
25 |
26 | public func encode(to encoder: Encoder) throws {
27 | var container = encoder.singleValueContainer()
28 | switch self {
29 | case let .string(string):
30 | try container.encode(string)
31 | case let .array(array):
32 | try container.encode(array)
33 | case let .dictionary(dictionary):
34 | try container.encode(dictionary)
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Workspace/XCWorkspaceDataElement.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | public enum XCWorkspaceDataElement: Equatable {
4 | public enum Error: Swift.Error {
5 | case unknownName(String)
6 | }
7 |
8 | case file(XCWorkspaceDataFileRef)
9 | case group(XCWorkspaceDataGroup)
10 |
11 | /// Returns the location to the workspace data element.
12 | public var location: XCWorkspaceDataElementLocationType {
13 | switch self {
14 | case let .file(ref):
15 | ref.location
16 | case let .group(ref):
17 | ref.location
18 | }
19 | }
20 |
21 | // MARK: - Equatable
22 |
23 | public static func == (lhs: XCWorkspaceDataElement, rhs: XCWorkspaceDataElement) -> Bool {
24 | switch (lhs, rhs) {
25 | case let (.file(lhs), .file(rhs)):
26 | lhs == rhs
27 | case let (.group(lhs), .group(rhs)):
28 | lhs == rhs
29 | default:
30 | false
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Workspace/XCWorkspaceDataFileRef.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | public final class XCWorkspaceDataFileRef {
4 | public var location: XCWorkspaceDataElementLocationType
5 |
6 | public init(location: XCWorkspaceDataElementLocationType) {
7 | self.location = location
8 | }
9 | }
10 |
11 | extension XCWorkspaceDataFileRef: Equatable {
12 | public static func == (lhs: XCWorkspaceDataFileRef, rhs: XCWorkspaceDataFileRef) -> Bool {
13 | lhs.location == rhs.location
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Sources/XcodeProj/Workspace/XCWorkspaceDataGroup.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | public final class XCWorkspaceDataGroup {
4 | public var location: XCWorkspaceDataElementLocationType
5 | public var name: String?
6 | public var children: [XCWorkspaceDataElement]
7 |
8 | public init(location: XCWorkspaceDataElementLocationType, name: String?, children: [XCWorkspaceDataElement]) {
9 | self.location = location
10 | self.name = name
11 | self.children = children
12 | }
13 | }
14 |
15 | extension XCWorkspaceDataGroup: Equatable {
16 | public static func == (lhs: XCWorkspaceDataGroup, rhs: XCWorkspaceDataGroup) -> Bool {
17 | lhs.location == rhs.location &&
18 | lhs.name == rhs.name &&
19 | lhs.children == rhs.children
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Templates/Equality.stencil:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | {% for type in types.implementing.AutoEquatable|class|!annotated:"skipEquality" %}
4 | extension {{ type.name }} {
5 | /// :nodoc:
6 | func isEqual(to rhs: {{ type.name }}) -> Bool {
7 | {% for variable in type.storedVariables %}
8 | {% if variable.typeName.dictionary %}
9 | if !NSDictionary(dictionary: {{ variable.name}}{% if variable.typeName.isOptional %} ?? [:]{% endif %}).isEqual(NSDictionary(dictionary: rhs.{{ variable.name }}{% if variable.typeName.isOptional %} ?? [:]{% endif %})) { return false }
10 | {% elif variable|!annotated:"skipEquality" %}
11 | if {{ variable.name }} != rhs.{{ variable.name }} { return false }
12 | {% endif %}
13 | {% endfor %}
14 | {% for variable in type.computedVariables|annotated:"forceEquality" %}if self.{{ variable.name }} != rhs.{{ variable.name }} { return false }
15 | {% endfor %}
16 | {% if type.inheritedTypes.first == "NSObject" %}return true{% else %}return super.isEqual(to: rhs){% endif %}
17 | }
18 | }
19 |
20 | {% endfor %}
21 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Extensions/PathExtrasTests.swift:
--------------------------------------------------------------------------------
1 | import PathKit
2 | import XCTest
3 | @testable import XcodeProj
4 |
5 | final class PathExtrasTests: XCTestCase {
6 | func testThat_GivenAbsoluteSubPath_WhenRelativeToAbsoluteSuperPath_ThenResultIsTheRemainder() {
7 | XCTAssertEqual(Path("/absolute/dir/file.txt").relative(to: Path("/absolute")), Path("dir/file.txt"))
8 | }
9 |
10 | func testThat_GivenAbsolutePath_WhenRelativeToNotSuperseedingAbsolutePath_ThenResultHasDoubleDot() {
11 | XCTAssertEqual(Path("/absolute/dir/file.txt").relative(to: Path("/absolute/anotherDir")), Path("../dir/file.txt"))
12 | }
13 |
14 | func testThat_GivenAbsoluteSubPath_WhenRelativeToIntersectingAbsolutePath_ThenResultIsTheFullPathToTheRootAndThenFullAbsolutePath() {
15 | XCTAssertEqual(Path("/absolute/dir/file.txt").relative(to: Path("/var")), Path("../absolute/dir/file.txt"))
16 | }
17 |
18 | func testThat_GivenSubPath_WhenRelativeToSuperPath_ThenResultIsTheRemainder() {
19 | XCTAssertEqual(Path("some/dir/file.txt").relative(to: Path("some")), Path("dir/file.txt"))
20 | }
21 |
22 | func testThat_GivenPath_WhenRelativeToNotSuperseedingPath_ThenResultHasDoubleDot() {
23 | XCTAssertEqual(Path("some/dir/file.txt").relative(to: Path("anotherDir")), Path("../some/dir/file.txt"))
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Extensions/XCTestCase+Assertions.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import XCTest
3 |
4 | extension XCTestCase {
5 | typealias EquatableError = Equatable & Error
6 |
7 | func XCTAssertNotNilAndUnwrap(_ obj: T?, message: String = "") -> T {
8 | guard let unwrappedObj = obj else {
9 | XCTAssertNotNil(obj, message)
10 | fatalError() // Unreachable since the above assertion will fail
11 | }
12 | return unwrappedObj
13 | }
14 |
15 | func XCTAssertThrowsSpecificError(_ expression: @autoclosure () throws -> some Any, _ error: E, _ message: @autoclosure () -> String = "", file: StaticString = #filePath, line: UInt = #line) {
16 | XCTAssertThrowsError(try expression(), message(), file: file, line: line) { actualError in
17 | let message = "Expected \(error) got \(actualError)"
18 |
19 | guard let actualCastedError = actualError as? E else {
20 | XCTFail(message, file: file, line: line)
21 | return
22 | }
23 | XCTAssertEqual(actualCastedError, error, message, file: file, line: line)
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Extensions/XCTestCase+Shell.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// Returns the output of running `executable` with `args`. Throws an error if the process exits indicating failure.
4 | @discardableResult
5 | func checkedOutput(_ executable: String, _ args: [String]) throws -> String? {
6 | let process = Process()
7 | let output = Pipe()
8 |
9 | if executable.contains("/") {
10 | process.launchPath = executable
11 | } else {
12 | process.launchPath = try checkedOutput("/usr/bin/which", [executable])?.trimmingCharacters(in: .newlines)
13 | }
14 |
15 | process.arguments = args
16 | process.standardOutput = output
17 | process.launch()
18 | process.waitUntilExit()
19 |
20 | guard process.terminationStatus == 0 else {
21 | throw NSError(domain: NSPOSIXErrorDomain, code: Int(process.terminationStatus))
22 | }
23 |
24 | return String(data: output.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8)
25 | }
26 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Extensions/XCTestCase+Temporary.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import PathKit
3 | import XCTest
4 |
5 | extension XCTestCase {
6 | func withTemporaryDirectory(_ closure: (Path) throws -> Void) throws {
7 | let directory = try Path.uniqueTemporary()
8 | try closure(directory)
9 | try directory.delete()
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Objects/BuildPhase/BuildPhaseTests.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import XcodeProj
3 | import XCTest
4 |
5 | class BuildPhaseTests: XCTestCase {
6 | func test_sources_hasTheCorrectRawValue() {
7 | XCTAssertEqual(BuildPhase.sources.rawValue, "Sources")
8 | }
9 |
10 | func test_frameworks_hasTheCorrectRawValue() {
11 | XCTAssertEqual(BuildPhase.frameworks.rawValue, "Frameworks")
12 | }
13 |
14 | func test_resources_hasTheCorrectRawValue() {
15 | XCTAssertEqual(BuildPhase.resources.rawValue, "Resources")
16 | }
17 |
18 | func test_copyFiles_hasTheCorrectRawValue() {
19 | XCTAssertEqual(BuildPhase.copyFiles.rawValue, "CopyFiles")
20 | }
21 |
22 | func test_runStript_hasTheCorrectRawValue() {
23 | XCTAssertEqual(BuildPhase.runScript.rawValue, "Run Script")
24 | }
25 |
26 | func test_headers_hasTheCorrectRawValue() {
27 | XCTAssertEqual(BuildPhase.headers.rawValue, "Headers")
28 | }
29 |
30 | func test_carbonResources_hasTheCorrectRawValue() {
31 | XCTAssertEqual(BuildPhase.carbonResources.rawValue, "Rez")
32 | }
33 |
34 | func test_sources_hasTheCorrectBuildPhase() {
35 | XCTAssertEqual(BuildPhase.sources, PBXSourcesBuildPhase().buildPhase)
36 | }
37 |
38 | func test_frameworks_hasTheCorrectBuildPhase() {
39 | XCTAssertEqual(BuildPhase.frameworks, PBXFrameworksBuildPhase().buildPhase)
40 | }
41 |
42 | func test_resources_hasTheCorrectBuildPhase() {
43 | XCTAssertEqual(BuildPhase.resources, PBXResourcesBuildPhase().buildPhase)
44 | }
45 |
46 | func test_copyFiles_hasTheCorrectBuildPhase() {
47 | XCTAssertEqual(BuildPhase.copyFiles, PBXCopyFilesBuildPhase().buildPhase)
48 | }
49 |
50 | func test_runStript_hasTheCorrectBuildPhase() {
51 | XCTAssertEqual(BuildPhase.runScript, PBXShellScriptBuildPhase().buildPhase)
52 | }
53 |
54 | func test_headers_hasTheCorrectBuildPhase() {
55 | XCTAssertEqual(BuildPhase.headers, PBXHeadersBuildPhase().buildPhase)
56 | }
57 |
58 | func test_carbonResources_hasTheCorrectBuildPhase() {
59 | XCTAssertEqual(BuildPhase.carbonResources, PBXRezBuildPhase().buildPhase)
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Objects/BuildPhase/PBXBuildFileTests.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import XcodeProj
3 | import XCTest
4 |
5 | final class PBXBuildFileTests: XCTestCase {
6 | func test_isa_returnsTheCorrectValue() {
7 | XCTAssertEqual(PBXBuildFile.isa, "PBXBuildFile")
8 | }
9 |
10 | func test_platformFilterIsSet() {
11 | let pbxBuildFile = PBXBuildFile(
12 | platformFilter: "platformFilter"
13 | )
14 | XCTAssertEqual(pbxBuildFile.platformFilter, "platformFilter")
15 | }
16 |
17 | func test_platformCompilerFlagsIsSet() {
18 | let expected = "flagValue"
19 | let pbxBuildFile = PBXBuildFile(
20 | settings: ["COMPILER_FLAGS": .string(expected)]
21 | )
22 | XCTAssertEqual(pbxBuildFile.compilerFlags, expected)
23 | }
24 |
25 | func test_platformAttributesIsSet() {
26 | let expected = ["Public"]
27 | let pbxBuildFile = PBXBuildFile(
28 | settings: ["ATTRIBUTES": .array(expected)]
29 | )
30 | XCTAssertEqual(pbxBuildFile.attributes, expected)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Objects/BuildPhase/PBXBuildPhaseTests.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import XCTest
3 | @testable import XcodeProj
4 |
5 | final class PBXBuildPhaseTests: XCTestCase {
6 | var subject: PBXBuildPhase!
7 | var proj: PBXProj!
8 |
9 | override func setUp() {
10 | super.setUp()
11 | subject = PBXSourcesBuildPhase()
12 | proj = PBXProj.fixture()
13 | proj.add(object: subject)
14 | }
15 |
16 | func test_add_files() throws {
17 | let file = PBXFileElement(sourceTree: .absolute,
18 | path: "path",
19 | name: "name",
20 | includeInIndex: false,
21 | wrapsLines: true)
22 |
23 | let buildFile = try subject.add(file: file)
24 | XCTAssertEqual(subject.files?.contains(buildFile), true)
25 | }
26 |
27 | func test_add_files_only_once() throws {
28 | let file = PBXFileElement(sourceTree: .absolute,
29 | path: "path",
30 | name: "name",
31 | includeInIndex: false,
32 | wrapsLines: true)
33 |
34 | let buildFile = try subject.add(file: file)
35 | let sameBuildFile = try subject.add(file: file)
36 | XCTAssertEqual(buildFile, sameBuildFile, "Expected adding a file only once but it didn't")
37 |
38 | let fileOccurrencesCount = subject.files?.filter { $0 == buildFile }.count
39 | XCTAssertTrue(fileOccurrencesCount == 1, "Expected adding a file only once but it didn't")
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Objects/BuildPhase/PBXBuildRuleTests.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import XcodeProj
3 | import XCTest
4 |
5 | final class PBXBuildRuleTests: XCTestCase {
6 | var subject: PBXBuildRule!
7 |
8 | override func setUp() {
9 | super.setUp()
10 | subject = PBXBuildRule(compilerSpec: "spec",
11 | fileType: "type",
12 | isEditable: true,
13 | filePatterns: "pattern",
14 | name: "rule",
15 | outputFiles: ["a", "b"],
16 | outputFilesCompilerFlags: ["-1", "-2"],
17 | script: "script",
18 | runOncePerArchitecture: false)
19 | }
20 |
21 | func test_init_initializesTheBuildRuleWithTheRightAttributes() {
22 | XCTAssertEqual(subject.compilerSpec, "spec")
23 | XCTAssertEqual(subject.filePatterns, "pattern")
24 | XCTAssertEqual(subject.fileType, "type")
25 | XCTAssertEqual(subject.isEditable, true)
26 | XCTAssertEqual(subject.name, "rule")
27 | XCTAssertEqual(subject.outputFiles, ["a", "b"])
28 | XCTAssertEqual(subject.outputFilesCompilerFlags ?? [], ["-1", "-2"])
29 | XCTAssertEqual(subject.script, "script")
30 | XCTAssertEqual(subject.runOncePerArchitecture, false)
31 | }
32 |
33 | func test_isa_returnsTheCorrectValue() {
34 | XCTAssertEqual(PBXBuildRule.isa, "PBXBuildRule")
35 | }
36 |
37 | func test_equal_shouldReturnTheCorrectValue() {
38 | let another = PBXBuildRule(compilerSpec: "spec",
39 | fileType: "type",
40 | isEditable: true,
41 | filePatterns: "pattern",
42 | name: "rule",
43 | outputFiles: ["a", "b"],
44 | outputFilesCompilerFlags: ["-1", "-2"],
45 | script: "script",
46 | runOncePerArchitecture: false)
47 | XCTAssertEqual(subject, another)
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Objects/BuildPhase/PBXFrameworksBuildPhaseTests.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import XCTest
3 | @testable import XcodeProj
4 |
5 | final class PBXFrameworksBuildPhaseTests: XCTestCase {
6 | func test_isa_returnsTheRightValue() {
7 | XCTAssertEqual(PBXFrameworksBuildPhase.isa, "PBXFrameworksBuildPhase")
8 | }
9 |
10 | func test_init_fails_whenTheFilesAreMissing() {
11 | var dictionary = testDictionary()
12 | dictionary.removeValue(forKey: "files")
13 | let data = try! JSONSerialization.data(withJSONObject: dictionary, options: [])
14 | let decoder = XcodeprojJSONDecoder()
15 | do {
16 | let phase = try decoder.decode(PBXCopyFilesBuildPhase.self, from: data)
17 | XCTAssertNil(phase.files, "Expected files to be nil but it's present")
18 | } catch {}
19 | }
20 |
21 | private func testDictionary() -> [String: Any] {
22 | [
23 | "files": ["file1"],
24 | "runOnlyForDeploymentPostprocessing": 0,
25 | "reference": "reference",
26 | ]
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Objects/BuildPhase/PBXHeadersBuildPhaseTests.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import XCTest
3 | @testable import XcodeProj
4 |
5 | final class PBXHeadersBuildPhaseTests: XCTestCase {
6 | func test_isa_returnsTheCorrectValue() {
7 | XCTAssertEqual(PBXHeadersBuildPhase.isa, "PBXHeadersBuildPhase")
8 | }
9 |
10 | func test_init_failsWhenTheBuildActionMaskIsMissing() {
11 | var dictionary = testDictionary()
12 | dictionary.removeValue(forKey: "buildActionMask")
13 | let data = try! JSONSerialization.data(withJSONObject: dictionary, options: [])
14 | let decoder = XcodeprojJSONDecoder()
15 | do {
16 | let phase = try decoder.decode(PBXCopyFilesBuildPhase.self, from: data)
17 | XCTAssertEqual(phase.buildActionMask, PBXBuildPhase.defaultBuildActionMask, "Expected buildActionMask to be equal to \(PBXBuildPhase.defaultBuildActionMask) but it's \(phase.buildActionMask)")
18 | } catch {}
19 | }
20 |
21 | func test_init_failWhenFilesIsMissing() {
22 | var dictionary = testDictionary()
23 | dictionary.removeValue(forKey: "files")
24 | let data = try! JSONSerialization.data(withJSONObject: dictionary, options: [])
25 | let decoder = XcodeprojJSONDecoder()
26 | do {
27 | let phase = try decoder.decode(PBXCopyFilesBuildPhase.self, from: data)
28 | XCTAssertNil(phase.files, "Expected files to be nil but it's present")
29 | } catch {}
30 | }
31 |
32 | func test_init_failsWhenRunOnlyForDeploymentPostProcessingIsMissing() {
33 | var dictionary = testDictionary()
34 | dictionary.removeValue(forKey: "runOnlyForDeploymentPostprocessing")
35 | let data = try! JSONSerialization.data(withJSONObject: dictionary, options: [])
36 | let decoder = XcodeprojJSONDecoder()
37 | do {
38 | let phase = try decoder.decode(PBXCopyFilesBuildPhase.self, from: data)
39 | XCTAssertFalse(phase.runOnlyForDeploymentPostprocessing, "Expected runOnlyForDeploymentPostprocessing to default to false")
40 | } catch {}
41 | }
42 |
43 | private func testDictionary() -> [String: Any] {
44 | [
45 | "buildActionMask": 3,
46 | "files": ["file"],
47 | "runOnlyForDeploymentPostprocessing": 2,
48 | "reference": "reference",
49 | ]
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Objects/BuildPhase/PBXResourcesBuildPhaseTests.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import XcodeProj
3 | import XCTest
4 |
5 | final class PBXResourcesBuildPhaseTests: XCTestCase {
6 | func test_isa_returnsTheCorrectValue() {
7 | XCTAssertEqual(PBXResourcesBuildPhase.isa, "PBXResourcesBuildPhase")
8 | }
9 |
10 | private func testDictionary() -> [String: Any] {
11 | [
12 | "files": ["file1"],
13 | "buildActionMask": "333",
14 | "runOnlyForDeploymentPostprocessing": "3",
15 | ]
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Objects/BuildPhase/PBXRezBuildPhaseTests.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import XcodeProj
3 | import XCTest
4 |
5 | final class PBXRezBuildPhaseTests: XCTestCase {
6 | func test_isa_returnsTheCorrectValue() {
7 | XCTAssertEqual(PBXRezBuildPhase.isa, "PBXRezBuildPhase")
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Objects/BuildPhase/PBXSourcesBuildPhase+Fixtures.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | @testable import XcodeProj
4 |
5 | extension PBXSourcesBuildPhase {
6 | static func fixture(files: [PBXBuildFile] = []) -> PBXSourcesBuildPhase {
7 | PBXSourcesBuildPhase(files: files,
8 | inputFileListPaths: nil,
9 | outputFileListPaths: nil,
10 | buildActionMask: PBXBuildPhase.defaultBuildActionMask,
11 | runOnlyForDeploymentPostprocessing: false)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Objects/BuildPhase/PBXSourcesBuildPhaseTests.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import XcodeProj
3 | import XCTest
4 |
5 | class PBXSourcesBuildPhaseTests: XCTestCase {
6 | func test_itHasTheCorrectIsa() {
7 | XCTAssertEqual(PBXSourcesBuildPhase.isa, "PBXSourcesBuildPhase")
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Objects/Configuration/BuildFileSettingTests.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import Testing
3 | @testable import XcodeProj
4 |
5 | struct BuildFileSettingTests {
6 | @Test func test_BuildSettings_encodes_to_JSON() async throws {
7 | let expectedJSON = #"{"one":"one","two":["two","two"]}"#
8 |
9 | let settings: [String: BuildFileSetting] = [
10 | "one": .string("one"),
11 | "two": .array(["two", "two"]),
12 | ]
13 |
14 | let encoder = JSONEncoder()
15 | encoder.outputFormatting = .sortedKeys
16 |
17 | let result = try encoder.encode(settings)
18 |
19 | #expect(result == expectedJSON.data(using: .utf8))
20 | }
21 |
22 | @Test func test_buildSettings_decodes_from_JSON() async throws {
23 | let json = #"{"one":"one","two":["two","two"]}"#
24 |
25 | let expectedSettings: [String: BuildFileSetting] = [
26 | "one": .string("one"),
27 | "two": .array(["two", "two"]),
28 | ]
29 |
30 | let result = try JSONDecoder().decode([String: BuildFileSetting].self, from: json.data(using: .utf8)!)
31 |
32 | #expect(result == expectedSettings)
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Objects/Configuration/BuildSettingTests.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import Testing
3 | @testable import XcodeProj
4 |
5 | struct BuildSettingTests {
6 | @Test func test_BuildSettings_encode_to_string() async throws {
7 | #expect(BuildSetting.string("one").description == "one")
8 | #expect(BuildSetting.array(["one", "two"]).description == "one two")
9 | }
10 |
11 | @Test func test_buildSettings_decodes_from_JSON() async throws {
12 | let json = #"{"one":"one","two":["two","two"]}"#
13 |
14 | let expectedSettings: BuildSettings = [
15 | "one": .string("one"),
16 | "two": .array(["two", "two"]),
17 | ]
18 |
19 | let result = try JSONDecoder().decode(BuildSettings.self, from: json.data(using: .utf8)!)
20 |
21 | #expect(result == expectedSettings)
22 | }
23 |
24 | @Test func test_buildSettings_bool_conversion() async throws {
25 | let settings: [BuildSetting] = [
26 | BuildSetting.string("YES"),
27 | BuildSetting.string("NO"),
28 | BuildSetting.string("tuist"),
29 | BuildSetting.string("No"),
30 | BuildSetting.string("yES"),
31 | BuildSetting.array(["YES", "yES"]),
32 | true,
33 | false,
34 | ]
35 |
36 | let expected: [Bool?] = [
37 | true,
38 | false,
39 | nil,
40 | nil,
41 | nil,
42 | nil,
43 | true,
44 | false,
45 | ]
46 |
47 | #expect(settings.map(\.boolValue) == expected)
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Objects/Configuration/XCBuildConfiguration+Fixtures.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | @testable import XcodeProj
3 |
4 | extension XCBuildConfiguration {
5 | static func fixture(name: String = "Debug") -> XCBuildConfiguration {
6 | XCBuildConfiguration(name: name)
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Objects/Configuration/XCConfigurationList+Fixtures.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | @testable import XcodeProj
3 |
4 | extension XCConfigurationList {
5 | static func fixture(buildConfigurations: [XCBuildConfiguration] = [XCBuildConfiguration.fixture(name: "Debug"),
6 | XCBuildConfiguration.fixture(name: "Release")],
7 | defaultConfigurationName: String? = "Debug",
8 | defaultConfigurationIsVisible _: Bool = true) -> XCConfigurationList
9 | {
10 | XCConfigurationList(buildConfigurations: buildConfigurations,
11 | defaultConfigurationName: defaultConfigurationName)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Objects/Configuration/XCConfigurationListTests.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import XCTest
3 | @testable import XcodeProj
4 |
5 | final class XCConfigurationListTests: XCTestCase {
6 | func test_isa_returnsTheCorrectValue() {
7 | XCTAssertEqual(XCConfigurationList.isa, "XCConfigurationList")
8 | }
9 |
10 | func test_addDefaultConfigurations() throws {
11 | let objects = PBXObjects()
12 | let configurationList = XCConfigurationList(buildConfigurations: [])
13 | objects.add(object: configurationList)
14 | let configurations = try configurationList.addDefaultConfigurations()
15 | let names = configurations.map(\.name)
16 |
17 | XCTAssertEqual(configurations.count, 2)
18 | XCTAssertTrue(names.contains("Debug"))
19 | XCTAssertTrue(names.contains("Release"))
20 | }
21 |
22 | func test_configuration_with_name() throws {
23 | let objects = PBXObjects()
24 | let configurationList = XCConfigurationList(buildConfigurations: [])
25 | objects.add(object: configurationList)
26 | let configurations = try configurationList.addDefaultConfigurations()
27 |
28 | XCTAssertEqual(
29 | configurationList.configuration(name: "Debug"),
30 | configurations.first(where: { $0.name == "Debug" })
31 | )
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Objects/Files/PBXContainerItemProxyTests.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import XCTest
3 | @testable import XcodeProj
4 |
5 | final class PBXContainerItemProxyTests: XCTestCase {
6 | func test_itHasTheCorrectIsa() {
7 | XCTAssertEqual(PBXContainerItemProxy.isa, "PBXContainerItemProxy")
8 | }
9 |
10 | func test_init_shouldFail_ifContainerPortalIsMissing() {
11 | var dictionary = testDictionary()
12 | dictionary.removeValue(forKey: "containerPortal")
13 | let data = try! JSONSerialization.data(withJSONObject: dictionary, options: [])
14 | let decoder = XcodeprojJSONDecoder()
15 | do {
16 | _ = try decoder.decode(PBXContainerItemProxy.self, from: data)
17 | XCTAssertTrue(false, "Expected to throw an error but it didn't")
18 | } catch {}
19 | }
20 |
21 | func test_maintains_remoteID() {
22 | let target = PBXNativeTarget(name: "")
23 | let project = PBXProject(name: "", buildConfigurationList: XCConfigurationList(), compatibilityVersion: "", preferredProjectObjectVersion: nil, minimizedProjectReferenceProxies: nil, mainGroup: PBXGroup())
24 | let containerProxy = PBXContainerItemProxy(containerPortal: .project(project), remoteGlobalID: .object(target))
25 |
26 | XCTAssertEqual(target.uuid, containerProxy.remoteGlobalID?.uuid)
27 | }
28 |
29 | private func testDictionary() -> [String: Any] {
30 | [
31 | "containerPortal": "containerPortal",
32 | "remoteGlobalIDString": "remoteGlobalIDString",
33 | "remoteInfo": "remoteInfo",
34 | "reference": "reference",
35 | ]
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Objects/Files/PBXFileReference+Fixtures.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | @testable import XcodeProj
4 |
5 | extension PBXFileReference {
6 | static func fixture(sourceTree _: PBXSourceTree = .group,
7 | name: String? = "Test") -> PBXFileReference
8 | {
9 | PBXFileReference(sourceTree: .group, name: name)
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Objects/Files/PBXFileReferenceTests.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import XcodeProj
3 | import XCTest
4 |
5 | final class PBXFileReferenceTests: XCTestCase {
6 | var subject: PBXFileReference!
7 |
8 | override func setUp() {
9 | super.setUp()
10 | subject = PBXFileReference(sourceTree: .absolute,
11 | name: "name",
12 | fileEncoding: 1,
13 | explicitFileType: "type",
14 | lastKnownFileType: "last",
15 | path: "path")
16 | }
17 |
18 | func test_init_initializesTheReferenceWithTheRightAttributes() {
19 | XCTAssertEqual(subject.name, "name")
20 | XCTAssertEqual(subject.sourceTree, .absolute)
21 | XCTAssertEqual(subject.fileEncoding, 1)
22 | XCTAssertEqual(subject.explicitFileType, "type")
23 | XCTAssertEqual(subject.lastKnownFileType, "last")
24 | XCTAssertEqual(subject.path, "path")
25 | }
26 |
27 | func test_isa_hashTheCorrectValue() {
28 | XCTAssertEqual(PBXFileReference.isa, "PBXFileReference")
29 | }
30 |
31 | func test_equal_returnsTheCorrectValue() {
32 | let another = PBXFileReference(sourceTree: .absolute,
33 | name: "name",
34 | fileEncoding: 1,
35 | explicitFileType: "type",
36 | lastKnownFileType: "last",
37 | path: "path")
38 | XCTAssertEqual(subject, another)
39 | }
40 |
41 | private func testDictionary() -> [String: Any] {
42 | [
43 | "name": "name",
44 | "sourceTree": "group",
45 | ]
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet+Fixtures.swift:
--------------------------------------------------------------------------------
1 | import XcodeProj
2 |
3 | extension PBXFileSystemSynchronizedBuildFileExceptionSet {
4 | static func fixture(target: PBXTarget = .fixture(),
5 | membershipExceptions: [String]? = [],
6 | publicHeaders: [String]? = [],
7 | privateHeaders: [String]? = [],
8 | additionalCompilerFlagsByRelativePath: [String: String]? = nil,
9 | attributesByRelativePath: [String: [String]]? = nil) -> PBXFileSystemSynchronizedBuildFileExceptionSet {
10 | PBXFileSystemSynchronizedBuildFileExceptionSet(target: target,
11 | membershipExceptions: membershipExceptions,
12 | publicHeaders: publicHeaders,
13 | privateHeaders: privateHeaders,
14 | additionalCompilerFlagsByRelativePath: additionalCompilerFlagsByRelativePath,
15 | attributesByRelativePath: attributesByRelativePath)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSetTests.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import XCTest
3 | @testable import XcodeProj
4 |
5 | final class PBXFileSystemSynchronizedBuildFileExceptionSetTests: XCTestCase {
6 | var target: PBXTarget!
7 | var subject: PBXFileSystemSynchronizedBuildFileExceptionSet!
8 |
9 | override func setUp() {
10 | super.setUp()
11 | target = PBXTarget.fixture()
12 | subject = PBXFileSystemSynchronizedBuildFileExceptionSet.fixture(target: target)
13 | }
14 |
15 | override func tearDown() {
16 | target = nil
17 | subject = nil
18 | super.tearDown()
19 | }
20 |
21 | func test_itHasTheCorrectIsa() {
22 | XCTAssertEqual(PBXFileSystemSynchronizedBuildFileExceptionSet.isa, "PBXFileSystemSynchronizedBuildFileExceptionSet")
23 | }
24 |
25 | func test_equal_returnsTheCorrectValue() {
26 | let another = PBXFileSystemSynchronizedBuildFileExceptionSet.fixture(target: target)
27 | XCTAssertEqual(subject, another)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedRootGroup+Fixtures.swift:
--------------------------------------------------------------------------------
1 | import XcodeProj
2 |
3 | extension PBXFileSystemSynchronizedRootGroup {
4 | static func fixture(sourceTree: PBXSourceTree? = nil,
5 | path: String? = nil,
6 | name: String? = nil,
7 | includeInIndex: Bool? = nil,
8 | usesTabs: Bool? = nil,
9 | indentWidth: UInt? = nil,
10 | tabWidth: UInt? = nil,
11 | wrapsLines: Bool? = nil,
12 | explicitFileTypes: [String: String] = [:],
13 | exceptions: [PBXFileSystemSynchronizedBuildFileExceptionSet] = [],
14 | explicitFolders: [String] = []) -> PBXFileSystemSynchronizedRootGroup {
15 | PBXFileSystemSynchronizedRootGroup(sourceTree: sourceTree,
16 | path: path,
17 | name: name,
18 | includeInIndex: includeInIndex,
19 | usesTabs: usesTabs,
20 | indentWidth: indentWidth,
21 | tabWidth: tabWidth,
22 | wrapsLines: wrapsLines,
23 | explicitFileTypes: explicitFileTypes,
24 | exceptions: exceptions,
25 | explicitFolders: explicitFolders)
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Objects/Files/PBXGroup+Fixtures.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | @testable import XcodeProj
3 |
4 | extension PBXGroup {
5 | static func fixture(children _: [PBXFileElement] = [],
6 | sourceTree: PBXSourceTree = .group,
7 | name: String = "test") -> PBXGroup
8 | {
9 | PBXGroup(children: [],
10 | sourceTree: sourceTree,
11 | name: name)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Objects/Files/PBXVariantGroupTests.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import XcodeProj
3 | import XCTest
4 |
5 | final class PBXVariantGroupTests: XCTestCase {
6 | func test_itHasTheCorrectIsa() {
7 | XCTAssertEqual(PBXVariantGroup.isa, "PBXVariantGroup")
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Objects/Files/XCVersionGroup+Fixtures.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | @testable import XcodeProj
3 |
4 | extension XCVersionGroup {
5 | static func fixture(objects: PBXObjects,
6 | currentVersion: PBXFileReference? = PBXFileReference(name: "currentVersion"),
7 | path: String = "path",
8 | name: String? = "name",
9 | sourceTree: PBXSourceTree = .group,
10 | versionGroupType: String = "versionGroupType",
11 | children: [PBXFileReference] = [PBXFileReference(name: "currentVersion")]) -> XCVersionGroup
12 | {
13 | let group = XCVersionGroup(currentVersion: currentVersion,
14 | path: path,
15 | name: name,
16 | sourceTree: sourceTree,
17 | versionGroupType: versionGroupType,
18 | children: children)
19 | if let currentVersion {
20 | objects.add(object: currentVersion)
21 | }
22 | children.forEach { objects.add(object: $0) }
23 | objects.add(object: group)
24 | return group
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Objects/Files/XCVersionGroupTests.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import XCTest
3 | @testable import XcodeProj
4 |
5 | final class XCVersionGroupTests: XCTestCase {}
6 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Objects/Project/PBXProj+Fixtures.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | @testable import XcodeProj
3 |
4 | extension PBXProj {
5 | static func fixture(rootObject: PBXProject? = PBXProject.fixture(),
6 | objectVersion: UInt = Xcode.LastKnown.objectVersion,
7 | archiveVersion: UInt = Xcode.LastKnown.archiveVersion,
8 | classes: [String: [String]] = [:],
9 | objects: [PBXObject] = []) -> PBXProj
10 | {
11 | PBXProj(rootObject: rootObject,
12 | objectVersion: objectVersion,
13 | archiveVersion: archiveVersion,
14 | classes: classes,
15 | objects: objects)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Objects/Project/PBXProj+XCTest.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | @testable import XcodeProj
3 |
4 | extension PBXProj {
5 | func encode() throws -> String {
6 | let outputSettings = PBXOutputSettings()
7 | let encoder = PBXProjEncoder(outputSettings: outputSettings)
8 | return try encoder.encode(proj: self)
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Objects/Project/PBXProjObjectsHelpersTests.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import XCTest
3 | @testable import XcodeProj
4 |
5 | final class PBXProjObjectsHelpersTests: XCTestCase {
6 | var subject: PBXObjects!
7 |
8 | override func setUp() {
9 | super.setUp()
10 | subject = PBXObjects(objects: [])
11 | }
12 |
13 | func test_targetsNamed_returnsTheCorrectValue() {
14 | let nativeTarget = PBXNativeTarget(name: "test", buildConfigurationList: nil)
15 | let legacyTarget = PBXLegacyTarget(name: "test", buildConfigurationList: nil)
16 | let aggregateTarget = PBXAggregateTarget(name: "test", buildConfigurationList: nil)
17 | subject.add(object: nativeTarget)
18 | subject.add(object: legacyTarget)
19 | subject.add(object: aggregateTarget)
20 | let got = subject.targets(named: "test").map { $0 }
21 | XCTAssertTrue(got.contains(nativeTarget))
22 | XCTAssertTrue(got.contains(legacyTarget))
23 | XCTAssertTrue(got.contains(aggregateTarget))
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Objects/Project/PBXProject+Fixtures.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | @testable import XcodeProj
3 |
4 | extension PBXProject {
5 | static func fixture(name: String = "test",
6 | buildConfigurationList: XCConfigurationList = XCConfigurationList.fixture(),
7 | compatibilityVersion: String = Xcode.Default.compatibilityVersion,
8 | mainGroup: PBXGroup = PBXGroup.fixture()) -> PBXProject
9 | {
10 | PBXProject(name: name,
11 | buildConfigurationList: buildConfigurationList,
12 | compatibilityVersion: compatibilityVersion,
13 | preferredProjectObjectVersion: nil,
14 | minimizedProjectReferenceProxies: nil,
15 | mainGroup: mainGroup)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Objects/SwiftPackage/XCLocalSwiftPackageReferenceTests.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import XCTest
3 |
4 | @testable import XcodeProj
5 |
6 | final class XCLocalSwiftPackageReferenceTests: XCTestCase {
7 | func test_init() throws {
8 | // Given
9 | let decoder = XcodeprojPropertyListDecoder()
10 | let plist: [String: [String: Any]] = ["ref": ["relativePath": "path"]]
11 |
12 | let data = try PropertyListSerialization.data(fromPropertyList: plist, format: .xml, options: 0)
13 |
14 | // When
15 | let decoded = try decoder.decode([String: XCLocalSwiftPackageReference].self, from: data)
16 | let got = try XCTUnwrap(decoded["ref"])
17 |
18 | // Then
19 | XCTAssertEqual(got.reference.value, "ref")
20 | XCTAssertEqual(got.relativePath, "path")
21 | }
22 |
23 | func test_plistValues() throws {
24 | // When
25 | let proj = PBXProj()
26 | let subject = XCLocalSwiftPackageReference(relativePath: "repository")
27 |
28 | // Given
29 | let got = try subject.plistKeyAndValue(proj: proj, reference: "ref")
30 |
31 | // Then
32 | XCTAssertEqual(got.value, .dictionary([
33 | "isa": "XCLocalSwiftPackageReference",
34 | "relativePath": "repository",
35 | ]))
36 | }
37 |
38 | func test_equal() {
39 | // When
40 | let first = XCLocalSwiftPackageReference(relativePath: "repository")
41 | let second = XCLocalSwiftPackageReference(relativePath: "repository")
42 |
43 | // Then
44 | XCTAssertEqual(first, second)
45 | }
46 |
47 | func test_name() {
48 | // When
49 | let subject = XCLocalSwiftPackageReference(relativePath: "tuist/xcodeproj")
50 |
51 | // Then
52 | XCTAssertEqual(subject.name, "tuist/xcodeproj")
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Objects/Targets/PBXLegacyTargetTests.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import XCTest
3 | @testable import XcodeProj
4 |
5 | final class PBXLegacyTargetTests: XCTestCase {
6 | var subject: PBXLegacyTarget!
7 |
8 | override func setUp() {
9 | super.setUp()
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Objects/Targets/PBXReferenceProxyTests.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import XCTest
3 | @testable import XcodeProj
4 |
5 | final class PBXReferenceProxyTests: XCTestCase {}
6 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Objects/Targets/PBXTarget+Fixtures.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | @testable import XcodeProj
4 |
5 | extension PBXTarget {
6 | static func fixture(name: String = "Test",
7 | buildConfigurationList: XCConfigurationList = XCConfigurationList.fixture(),
8 | buildPhases: [PBXBuildPhase] = [],
9 | buildRules: [PBXBuildRule] = [],
10 | dependencies: [PBXTargetDependency] = [],
11 | productName: String? = "Test",
12 | product: PBXFileReference = PBXFileReference.fixture(name: "Test.app"),
13 | productType: PBXProductType = PBXProductType.application) -> PBXTarget
14 | {
15 | PBXTarget(name: name,
16 | buildConfigurationList: buildConfigurationList,
17 | buildPhases: buildPhases,
18 | buildRules: buildRules,
19 | dependencies: dependencies,
20 | productName: productName,
21 | product: product,
22 | productType: productType)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Objects/Targets/PBXTargetDependencyTests.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import XCTest
3 | @testable import XcodeProj
4 |
5 | final class PBXTargetDependencyTests: XCTestCase {
6 | func test_hasTheCorrectIsa() {
7 | XCTAssertEqual(PBXTargetDependency.isa, "PBXTargetDependency")
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Project/XCUserDataTests.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import PathKit
3 | import XCTest
4 | @testable import XcodeProj
5 |
6 | final class XCUserDataTests: XCTestCase {
7 | func test_read_userData() throws {
8 | let subject = try XCUserData(path: userDataPath)
9 | assert(userData: subject, userName: "username1")
10 | }
11 |
12 | func test_write_userData() throws {
13 | try testWrite(from: userDataPath,
14 | initModel: { try? XCUserData(path: $0) },
15 | modify: { userData in
16 | // XCScheme's that are already in place (the removed element) should not be removed by a write
17 | userData.schemes = userData.schemes.filter { $0.name != "iOS-other" }
18 | return userData
19 | },
20 | assertion: {
21 | assert(userData: $1, userName: "copy")
22 | })
23 | }
24 |
25 | func test_read_write_produces_no_diff() throws {
26 | try testReadWriteProducesNoDiff(from: userDataPath, initModel: XCUserData.init(path:))
27 | }
28 |
29 | // MARK: - Private
30 |
31 | private func assert(userData: XCUserData, userName: String) {
32 | XCTAssertEqual(userData.userName, userName)
33 | XCTAssertEqual(userData.schemes.count, 3)
34 | XCTAssertEqual(userData.breakpoints?.breakpoints.count, 2)
35 | XCTAssertEqual(userData.schemeManagement?.schemeUserState?.count, 6)
36 | }
37 |
38 | private var userDataPath: Path {
39 | fixturesPath() + "iOS/Project.xcodeproj/xcuserdata/username1.xcuserdatad"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Scheme/XCScheme+BuildableReferenceTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import XcodeProj
3 |
4 | final class XCSchemeBuildableReferenceTests: XCTestCase {
5 | func test_hash() throws {
6 | // Values that are equal must generate the same hash value
7 | let aBuildRef = XCScheme.BuildableReference(
8 | referencedContainer: "container ref",
9 | blueprint: nil,
10 | buildableName: "buildable name",
11 | blueprintName: "blueprint name"
12 | )
13 | let bBuildRef = XCScheme.BuildableReference(
14 | referencedContainer: "container ref",
15 | blueprint: nil,
16 | buildableName: "buildable name",
17 | blueprintName: "blueprint name"
18 | )
19 | XCTAssertEqual(aBuildRef, bBuildRef)
20 |
21 | let buildRefs: Set = [aBuildRef]
22 | XCTAssertTrue(buildRefs.contains(bBuildRef))
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Tests/Fixtures.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import PathKit
3 | @testable import XcodeProj
4 |
5 | func fixturesPath() -> Path {
6 | Path(#filePath).parent().parent().parent().parent() + "Fixtures"
7 | }
8 |
9 | func synchronizedRootGroupsFixture() throws -> Data {
10 | let synchronizedRootGroups = fixturesPath() + "SynchronizedRootGroups/SynchronizedRootGroups.xcodeproj/project.pbxproj"
11 | return try Data(contentsOf: synchronizedRootGroups.url)
12 | }
13 |
14 | func iosProjectData() throws -> Data {
15 | let iosProject = fixturesPath() + "iOS/Project.xcodeproj/project.pbxproj"
16 | return try Data(contentsOf: iosProject.url)
17 | }
18 |
19 | func fileSharedAcrossTargetsData() throws -> Data {
20 | let fileSharedAcrossTargetsProject = fixturesPath() + "FileSharedAcrossTargets/FileSharedAcrossTargets.xcodeproj/project.pbxproj"
21 | return try Data(contentsOf: fileSharedAcrossTargetsProject.url)
22 | }
23 |
24 | func targetWithCustomBuildRulesData() throws -> Data {
25 | let targetWithCustomBuildRulesProject = fixturesPath() + "TargetWithCustomBuildRules/TargetWithCustomBuildRules.xcodeproj/project.pbxproj"
26 | return try Data(contentsOf: targetWithCustomBuildRulesProject.url)
27 | }
28 |
29 | func iosProjectWithXCLocalSwiftPackageReference() throws -> Data {
30 | let iosProjectWithXCLocalSwiftPackageReference = fixturesPath() + "iOS/ProjectWithXCLocalSwiftPackageReference.xcodeproj/project.pbxproj"
31 | return try Data(contentsOf: iosProjectWithXCLocalSwiftPackageReference.url)
32 | }
33 |
34 | func iosProjectWithRelativeXCLocalSwiftPackageReferences() throws -> Data {
35 | let iosProjectWithXCLocalSwiftPackageReference = fixturesPath() + "iOS/ProjectWithRelativeXCLocalSwiftPackageReference/ProjectWithRelativeXCLocalSwiftPackageReference.xcodeproj/project.pbxproj"
36 | return try Data(contentsOf: iosProjectWithXCLocalSwiftPackageReference.url)
37 | }
38 |
39 | func iosProjectWithXCLocalSwiftPackageReferences() throws -> Data {
40 | let iosProjectWithXCLocalSwiftPackageReference = fixturesPath() + "iOS/ProjectWithXCLocalSwiftPackageReferences.xcodeproj/project.pbxproj"
41 | return try Data(contentsOf: iosProjectWithXCLocalSwiftPackageReference.url)
42 | }
43 |
44 | func projectWithWrongProjectReferencesOrder() throws -> Data {
45 | let iosProjectWithProjectReferences = fixturesPath() + "Xcode16ProjectReferenceOrder/Wrong.xcodeproj/project.pbxproj"
46 | return try Data(contentsOf: iosProjectWithProjectReferences.url)
47 | }
48 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Utils/PlistValueTests.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import PathKit
3 | import XCTest
4 | @testable import XcodeProj
5 |
6 | class Dictionary_PlistValueTests: XCTestCase {
7 | func test_dictionaryPlistValue_returnsTheCorrectValue() {
8 | let dictionary: [String: Any] = [
9 | "a": "b",
10 | "c": ["d", "e"],
11 | "f": [
12 | "g": "h",
13 | ],
14 | ]
15 | let plist = dictionary.plist()
16 | XCTAssertEqual(plist.dictionary?[CommentedString("a")], .string(CommentedString("b")))
17 | XCTAssertEqual(plist.dictionary?[CommentedString("c")], .array([.string(CommentedString("d")), .string(CommentedString("e"))]))
18 | XCTAssertEqual(plist.dictionary?[CommentedString("f")], .dictionary([CommentedString("g"): .string(CommentedString("h"))]))
19 | }
20 | }
21 |
22 | class Array_PlistValueTests: XCTestCase {
23 | func test_arrayPlistValue_returnsTheCorrectValue() {
24 | let array: [Any] = [
25 | "a",
26 | ["b", "c"],
27 | ["d": "e"],
28 | ]
29 | let plist = array.plist()
30 | XCTAssertEqual(plist.array?[0], .string(CommentedString("a")))
31 | XCTAssertEqual(plist.array?[1], .array([.string(CommentedString("b")), .string(CommentedString("c"))]))
32 | XCTAssertEqual(plist.array?[2], .dictionary([CommentedString("d"): .string(CommentedString("e"))]))
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Tests/XcodeProjTests/Workspace/XCWorkspaceTests.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import PathKit
3 | import XcodeProj
4 | import XCTest
5 |
6 | final class XCWorkspaceIntegrationTests: XCTestCase {
7 | func test_initTheWorkspaceWithTheRightPropeties() {
8 | let path = fixturesPath() + "iOS/Project.xcodeproj/project.xcworkspace"
9 | let got = try? XCWorkspace(path: path)
10 | XCTAssertNotNil(got)
11 | }
12 |
13 | func test_initFailsIfThePathIsWrong() {
14 | do {
15 | _ = try XCWorkspace(path: Path("/test"))
16 | XCTAssertTrue(false, "Expected to throw an error but it didn't")
17 | } catch {}
18 | }
19 |
20 | func test_init_returnsAWorkspaceWithTheCorrectReference() {
21 | XCTAssertEqual(XCWorkspace().data.children.count, 1)
22 | XCTAssertEqual(XCWorkspace().data.children.first, .file(.init(location: .current(""))))
23 | }
24 |
25 | func test_equatable_emptyWorkspacesAreEqual() {
26 | // When
27 | let firstWorkspace = XCWorkspace(data: .init(children: []))
28 | let secondWorkspace = XCWorkspace(data: .init(children: []))
29 |
30 | // Then
31 | XCTAssertEqual(firstWorkspace, secondWorkspace)
32 | }
33 |
34 | func test_equatable_unEqualWorkspacesAreNotEqual() {
35 | // Given
36 | let pathOne = fixturesPath() + "iOS/Project.xcodeproj/project.xcworkspace"
37 | let pathTwo = fixturesPath() + "iOS/Workspace.xcworkspace"
38 |
39 | // When
40 | let firstWorkspace = try? XCWorkspace(path: pathOne)
41 | let secondWorkspace = try? XCWorkspace(path: pathTwo)
42 |
43 | // Then
44 | XCTAssertNotEqual(firstWorkspace, secondWorkspace)
45 | }
46 |
47 | func test_equatable_equalWorkspacesAreEqual() {
48 | // Given
49 | let pathOne = fixturesPath() + "iOS/Workspace.xcworkspace"
50 | let pathTwo = fixturesPath() + "iOS/Workspace.xcworkspace"
51 |
52 | // When
53 | let firstWorkspace = try? XCWorkspace(path: pathOne)
54 | let secondWorkspace = try? XCWorkspace(path: pathTwo)
55 |
56 | // Then
57 | XCTAssertEqual(firstWorkspace, secondWorkspace)
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Tuist.swift:
--------------------------------------------------------------------------------
1 | import ProjectDescription
2 |
3 | let tuist = Tuist(generationOptions: .options())
4 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": [
4 | ":semanticCommits",
5 | ":disableDependencyDashboard",
6 | "config:recommended"
7 | ],
8 | "packageRules": [
9 | {
10 | "matchUpdateTypes": ["minor", "patch", "pin", "digest"],
11 | "automerge": true,
12 | "automergeType": "pr",
13 | "automergeStrategy": "auto",
14 | "semanticCommitScope": "deps"
15 | },
16 | {
17 | "matchManagers": ["swift"],
18 | "semanticCommitScope": "spm"
19 | }
20 | ],
21 | "lockFileMaintenance": {
22 | "enabled": true,
23 | "automerge": true
24 | }
25 | }
26 |
--------------------------------------------------------------------------------