Compare commits

...

5 Commits

Author SHA1 Message Date
Roman Aliyev
64c8a5802a
Merge f0e1db79e0 into f51719ce29 2024-05-26 11:42:05 +02:00
Yonas Kolb
f51719ce29
Update cache hook docs 2024-05-21 11:28:45 +10:00
Yonas Kolb
1b0720d139 Update to 2.41.0 2024-05-20 21:37:00 +10:00
Yonas Kolb
576739bcb5
Add cache command (#1476)
* add cache command

* docs: update git hook info
2024-05-20 21:32:24 +10:00
Roman Aliyev
f0e1db79e0 added examples of production-ready manifests for common tasks. 2023-09-24 17:22:56 +02:00
15 changed files with 419 additions and 29 deletions

View File

@ -2,6 +2,16 @@
## Next Version
## 2.41.0
### Added
- Added `xcodegen cache` command that writes the cache. Useful for `post-commit` git hook integration #1476 @yonaskolb
### Changed
- Include folders in file sorting #1466 @jflan-dd
### Fixed
- Fixed `supportedDestinations` validation when it contains watchOS for multiplatform apps. #1470 @tatsuky

View File

@ -9,10 +9,14 @@ Absolutely. You will get the most out of XcodeGen by adding your project to your
>Note that you can run `xcodegen` as a step in your build process on CI.
## What happens when I switch branches
If files were added or removed in the new checkout you will most likely need to run `xcodegen` again so that your project will reference all your files. Unfortunately this is a manual step at the moment, but in the future this could be automated.
If files were added or removed in the new checkout you will most likely need to run `xcodegen` again so that your project will reference all your files.
For now you can always add xcodegen as a git `post-checkout` hook.
It's recommended to use `--use-cache` so that the project is not needlessly generated.
It's recommended to set up some [git hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) to automate the process:
- run `xcodegen generate --use-cache` on the following hooks. This will make sure the project is up to date when checking out, merging and rebasing
- `post-checkout`
- `post-rewrite`
- `post-merge`
- run `xcodegen cache` on `pre-commit`. This will make sure that when switching branches the cache will be updated in case you made local changes, or are ammending a commit that added a new file.
## Can I use CocoaPods
Yes, you will just need to run `pod install` after the project is generated to integrate Cocoapods changes.

View File

@ -0,0 +1,53 @@
# (iOS) Alternate app icons
## Description
Adds alternate app icons to include in the built product.
## File structure
```diff
.
├── MyApp
│   ├── AppDelegate.swift
│   ├── Assets.xcassets
│   │   ├── AppIcon.appiconset
│   │   │   ├── AppIcon.png
| | | └── Contents.json
+│   │   ├── AppIcon2.appiconset
+│   │   │   ├── AppIcon.png
+| | | └── Contents.json
+│   │   ├── AppIcon3.appiconset
+│   │   │   ├── AppIcon.png
+| | | └── Contents.json
│   │   └── Contents.json
│   ├── LaunchScreen.storyboard
│   └── RootViewController.swift
└── project.yml
```
## project.yml
```diff
name: MyApp
targets:
MyApp:
type: application
platform: iOS
deploymentTarget: 12.0
settings:
TARGETED_DEVICE_FAMILY: 1
MARKETING_VERSION: 1.0
CURRENT_PROJECT_VERSION: 1
DEVELOPMENT_TEAM: MYTEAMID
PRODUCT_BUNDLE_IDENTIFIER: com.mycompany.myapp
GENERATE_INFOPLIST_FILE: YES
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents: YES
INFOPLIST_KEY_UILaunchStoryboardName: LaunchScreen.storyboard
INFOPLIST_KEY_UISupportedInterfaceOrientations: UIInterfaceOrientationPortrait
+ ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS: YES
+ ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES: AppIcon2 AppIcon3
sources:
- MyApp
```

View File

@ -0,0 +1,72 @@
# (iOS) Environments
## Description
Best way to setup Development, Testing and Production environments.
## File structure
```diff
.
├── MyApp
│   ├── AppDelegate.swift
│   ├── Assets.xcassets
│   │   ├── AppIcon.appiconset
│   │   │   ├── AppIcon.png
| | | └── Contents.json
│   │   └── Contents.json
│   ├── LaunchScreen.storyboard
│   └── RootViewController.swift
└── project.yml
```
## project.yml
```diff
name: MyApp
+configs:
+ Dev Debug: debug
+ Test Debug: debug
+ Prod Debug: debug
+ Dev Release: release
+ Test Release: release
+ Prod Release: release
+settings:
+ configs:
+ Dev Debug:
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS: DEBUG DEV
+ Test Debug:
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS: DEBUG TEST
+ Prod Debug:
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS: DEBUG PROD
+ Dev Release:
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS: DEV
+ Test Release:
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS: TEST
+ Prod Release:
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS: PROD
targets:
MyApp:
type: application
platform: iOS
deploymentTarget: 12.0
settings:
TARGETED_DEVICE_FAMILY: 1
MARKETING_VERSION: 1.0
CURRENT_PROJECT_VERSION: 1
DEVELOPMENT_TEAM: MYTEAMID
PRODUCT_BUNDLE_IDENTIFIER: com.mycompany.myapp
GENERATE_INFOPLIST_FILE: YES
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents: YES
INFOPLIST_KEY_UILaunchStoryboardName: LaunchScreen.storyboard
INFOPLIST_KEY_UISupportedInterfaceOrientations: UIInterfaceOrientationPortrait
sources:
- MyApp
+ scheme:
+ configVariants:
+ - Dev
+ - Test
+ - Prod
```

View File

@ -0,0 +1,90 @@
# (iOS) Playground
## Description
An alternative to XCode Playground.
Example of Source.swift:
```swift
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
var greeting = "Hello, playground"
print(greeting)
return true
}
}
```
Another example with UI:
```swift
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let window = UIWindow()
self.window = window
let rootViewController = RootViewController(nibName: nil, bundle: nil)
window.rootViewController = rootViewController
window.makeKeyAndVisible()
return true
}
}
class RootViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
```
## File structure
```diff
.
├── Source.swift
└── project.yml
```
## project.yml
```diff
name: MyApp
targets:
MyApp:
type: application
platform: iOS
deploymentTarget: 12.0
settings:
TARGETED_DEVICE_FAMILY: 1
MARKETING_VERSION: 1.0
CURRENT_PROJECT_VERSION: 1
DEVELOPMENT_TEAM: MYTEAMID
PRODUCT_BUNDLE_IDENTIFIER: com.mycompany.myapp
GENERATE_INFOPLIST_FILE: YES
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents: YES
- INFOPLIST_KEY_UILaunchStoryboardName: LaunchScreen.storyboard
+ INFOPLIST_KEY_UILaunchScreen_Generation: YES
INFOPLIST_KEY_UISupportedInterfaceOrientations: UIInterfaceOrientationPortrait
sources:
- Source.swift
```

View File

@ -0,0 +1,58 @@
# (iOS) UI Testing Bundle
## Description
Adds a user interface testing bundle that uses the XCTest framework.
## File structure
```diff
.
├── MyApp
│   ├── AppDelegate.swift
│   ├── Assets.xcassets
│   │   ├── AppIcon.appiconset
│   │   │   ├── AppIcon.png
| | | └── Contents.json
│   │   └── Contents.json
│   ├── LaunchScreen.storyboard
│   └── RootViewController.swift
+├── MyAppUITests
+│   └── SomeUITests.swift
└── project.yml
```
## project.yml
```diff
name: MyApp
targets:
MyApp:
type: application
platform: iOS
deploymentTarget: 12.0
settings:
TARGETED_DEVICE_FAMILY: 1
MARKETING_VERSION: 1.0
CURRENT_PROJECT_VERSION: 1
DEVELOPMENT_TEAM: MYTEAMID
PRODUCT_BUNDLE_IDENTIFIER: com.mycompany.myapp
GENERATE_INFOPLIST_FILE: YES
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents: YES
INFOPLIST_KEY_UILaunchStoryboardName: LaunchScreen.storyboard
INFOPLIST_KEY_UISupportedInterfaceOrientations: UIInterfaceOrientationPortrait
sources:
- MyApp
+ MyAppUITests:
+ type: bundle.ui-testing
+ platform: iOS
+ settings:
+ DEVELOPMENT_TEAM: MYTEAMID
+ PRODUCT_BUNDLE_IDENTIFIER: com.company.myappuitests
+ GENERATE_INFOPLIST_FILE: YES
+ sources:
+ - MyAppUITests
+ dependencies:
+ - target: MyApp
```

View File

@ -0,0 +1,58 @@
# (iOS) Unit Testing Bundle
## Description
Adds a unit test bundle that uses the XCTest framework.
## File structure
```diff
.
├── MyApp
│   ├── AppDelegate.swift
│   ├── Assets.xcassets
│   │   ├── AppIcon.appiconset
│   │   │   ├── AppIcon.png
| | | └── Contents.json
│   │   └── Contents.json
│   ├── LaunchScreen.storyboard
│   └── RootViewController.swift
+├── MyAppTests
+│   └── SomeTests.swift
└── project.yml
```
## project.yml
```diff
name: MyApp
targets:
MyApp:
type: application
platform: iOS
deploymentTarget: 12.0
settings:
TARGETED_DEVICE_FAMILY: 1
MARKETING_VERSION: 1.0
CURRENT_PROJECT_VERSION: 1
DEVELOPMENT_TEAM: MYTEAMID
PRODUCT_BUNDLE_IDENTIFIER: com.mycompany.myapp
GENERATE_INFOPLIST_FILE: YES
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents: YES
INFOPLIST_KEY_UILaunchStoryboardName: LaunchScreen.storyboard
INFOPLIST_KEY_UISupportedInterfaceOrientations: UIInterfaceOrientationPortrait
sources:
- MyApp
+ MyAppTests:
+ type: bundle.unit-test
+ platform: iOS
+ settings:
+ DEVELOPMENT_TEAM: MYTEAMID
+ PRODUCT_BUNDLE_IDENTIFIER: com.company.myapptests
+ GENERATE_INFOPLIST_FILE: YES
+ sources:
+ - MyAppTests
+ dependencies:
+ - target: MyApp
```

View File

@ -1,6 +1,6 @@
TOOL_NAME = XcodeGen
export EXECUTABLE_NAME = xcodegen
VERSION = 2.40.1
VERSION = 2.41.0
PREFIX = /usr/local
INSTALL_PATH = $(PREFIX)/bin/$(EXECUTABLE_NAME)

View File

@ -113,7 +113,7 @@ swift run xcodegen
Add the following to your Package.swift file's dependencies:
```swift
.package(url: "https://github.com/yonaskolb/XcodeGen.git", from: "2.40.1"),
.package(url: "https://github.com/yonaskolb/XcodeGen.git", from: "2.41.0"),
```
And then import wherever needed: `import XcodeGenKit`

View File

@ -3,6 +3,6 @@ import ProjectSpec
import XcodeGenCLI
import Version
let version = Version("2.40.1")
let version = Version("2.41.0")
let cli = XcodeGenCLI(version: version)
cli.execute()

View File

@ -0,0 +1,44 @@
import Foundation
import PathKit
import ProjectSpec
import SwiftCLI
import XcodeGenKit
import XcodeProj
import Version
class CacheCommand: ProjectCommand {
@Key("--cache-path", description: "Where the cache file will be loaded from and save to. Defaults to ~/.xcodegen/cache/{SPEC_PATH_HASH}")
var cacheFilePath: Path?
init(version: Version) {
super.init(version: version,
name: "cache",
shortDescription: "Write the project cache")
}
override func execute(specLoader: SpecLoader, projectSpecPath: Path, project: Project) throws {
let cacheFilePath = self.cacheFilePath ?? Path("~/.xcodegen/cache/\(projectSpecPath.absolute().string.md5)").absolute()
var cacheFile: CacheFile?
// generate cache
do {
cacheFile = try specLoader.generateCacheFile()
} catch {
throw GenerationError.projectSpecParsingError(error)
}
// write cache
if let cacheFile = cacheFile {
do {
try cacheFilePath.parent().mkpath()
try cacheFilePath.write(cacheFile.string)
success("Wrote cache to \(cacheFilePath)")
} catch {
info("Failed to write cache: \(error.localizedDescription)")
}
}
}
}

View File

@ -49,7 +49,7 @@ class DumpCommand: ProjectCommand {
try file.parent().mkpath()
try file.write(output)
} else {
stdout.print(output)
success(output)
}
}
}

View File

@ -8,9 +8,6 @@ import Version
class GenerateCommand: ProjectCommand {
@Flag("-q", "--quiet", description: "Suppress all informational and success output")
var quiet: Bool
@Flag("-c", "--use-cache", description: "Use a cache for the xcodegen spec. This will prevent unnecessarily generating the project if nothing has changed")
var useCache: Bool
@ -46,7 +43,7 @@ class GenerateCommand: ProjectCommand {
Path("~/.xcodegen/cache/\(projectSpecPath.absolute().string.md5)").absolute()
var cacheFile: CacheFile?
// read cache
// generate cache
if useCache || self.cacheFilePath != nil {
do {
cacheFile = try specLoader.generateCacheFile()
@ -138,22 +135,4 @@ class GenerateCommand: ProjectCommand {
try Task.run(bash: command, directory: projectDirectory.absolute().string)
}
}
func info(_ string: String) {
if !quiet {
stdout.print(string)
}
}
func warning(_ string: String) {
if !quiet {
stdout.print(string.yellow)
}
}
func success(_ string: String) {
if !quiet {
stdout.print(string.green)
}
}
}

View File

@ -12,6 +12,9 @@ class ProjectCommand: Command {
let name: String
let shortDescription: String
@Flag("-q", "--quiet", description: "Suppress all informational and success output")
var quiet: Bool
@Key("-s", "--spec", description: "The path to the project spec file. Defaults to project.yml. (It is also possible to link to multiple spec files by comma separating them. Note that all other flags will be the same.)")
var spec: String?
@ -58,4 +61,22 @@ class ProjectCommand: Command {
}
func execute(specLoader: SpecLoader, projectSpecPath: Path, project: Project) throws {}
func info(_ string: String) {
if !quiet {
stdout.print(string)
}
}
func warning(_ string: String) {
if !quiet {
stdout.print(string.yellow)
}
}
func success(_ string: String) {
if !quiet {
stdout.print(string.green)
}
}
}

View File

@ -15,6 +15,7 @@ public class XcodeGenCLI {
description: "Generates Xcode projects",
commands: [
generateCommand,
CacheCommand(version: version),
DumpCommand(version: version),
]
)