diff --git a/.gitignore b/.gitignore
index 05ffc06..bdba1cf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
.DS_Store
Gemfile.lock
+/coverage/
*UserInterfaceState.xcuserstate
pkg
**/Example.app.dSYM.zip
diff --git a/.rubocop_general.yml b/.rubocop_general.yml
index 4283c8c..fee75a1 100644
--- a/.rubocop_general.yml
+++ b/.rubocop_general.yml
@@ -51,6 +51,11 @@ Metrics/MethodLength:
Exclude:
- 'lib/*/options.rb'
+Metrics/AbcSize:
+ Max: 60
+ Exclude:
+ - 'lib/*/options.rb'
+
# Both string notations are okay
Style/StringLiterals:
Enabled: false
@@ -83,10 +88,6 @@ Style/AlignHash:
Style/AndOr:
Enabled: false
-# Offense count: 20
-Metrics/AbcSize:
- Max: 60
-
# Configuration parameters: CountComments.
Metrics/ClassLength:
Max: 320
diff --git a/.travis.yml b/.travis.yml
index 9167d41..5e6f6f4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,12 @@
-language: objective-c
-osx_image: xcode6.4
-before_install: gem update --system
+language: ruby
+osx_image: xcode7.1
+os: osx
+env:
+ - USE_SIMPLE_POPEN=true
+before_install:
+ - gem update --system
+ - gem install bundler
rvm:
- - 2.0.0
+ - 2.1.6
+ - 2.2.2
script: bundle exec fastlane test
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 68a38a4..99f5ca2 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -6,7 +6,7 @@ To clone the [fastlane](https://fastlane.tools) repos, use the [countdown](https
Before submitting a new issue, do the following:
-- Verify you're runing the latest version by running `gym -v` and compare it with the [project page on GitHub](https://github.com/fastlane/gym).
+- Verify you're running the latest version by running `gym -v` and compare it with the [project page on GitHub](https://github.com/fastlane/gym).
- Verify you have Xcode tools installed by running `xcode-select --install`.
- Make sure to read through the [README](https://github.com/fastlane/gym) of the project.
diff --git a/Gemfile b/Gemfile
index 63b9871..232697e 100644
--- a/Gemfile
+++ b/Gemfile
@@ -2,3 +2,7 @@ source "https://rubygems.org"
# Specify your gem's dependencies in .gemspec
gemspec
+
+if `cd ..; git remote -v`.include?('countdown')
+ gem 'fastlane_core', path: '../fastlane_core'
+end
diff --git a/README.md b/README.md
index 03d2877..316271f 100644
--- a/README.md
+++ b/README.md
@@ -1,41 +1,42 @@
- deliver •
- snapshot •
- frameit •
- PEM •
+ deliver •
+ snapshot •
+ frameit •
+ pem •
sigh •
- produce •
- cert •
- codes •
+ produce •
+ cert •
spaceship •
pilot •
boarding •
- gym
+ gym •
+ scan •
+ match
-------
-
+
gym
============
-[](https://twitter.com/KrauseFx)
-[](https://github.com/fastlane/gym/blob/master/LICENSE)
+[](https://twitter.com/FastlaneTools)
+[](https://github.com/fastlane/gym/blob/master/LICENSE)
[](http://rubygems.org/gems/gym)
[](https://travis-ci.org/fastlane/gym)
###### Building your app has never been easier
-Get in contact with the developer on Twitter: [@KrauseFx](https://twitter.com/KrauseFx)
+Get in contact with the developer on Twitter: [@FastlaneTools](https://twitter.com/FastlaneTools)
-------
@@ -100,8 +101,6 @@ gym
# Installation
-This tool is still work in progress. You can already try it by cloning the repo and running
-
sudo gem install gym
Make sure, you have the latest version of the Xcode command line tools installed:
@@ -116,15 +115,32 @@ That's all you need to build your application. If you want more control, here ar
gym --workspace "Example.xcworkspace" --scheme "AppName" --clean
+If you need to use a different xcode install, use xcode-select or define DEVELOPER_DIR:
+
+ DEVELOPER_DIR="/Applications/Xcode6.2.app" gym
+
For a list of all available parameters use
gym --help
If you run into any issues, use the `verbose` mode to get more information
-
gym --verbose
+In general, if you run into issues while exporting the archive, try using:
+
+ gym --use_legacy_build_api
+
+Set the right export method if you're not uploading to App Store or TestFlight:
+
+ gym --export_method ad-hoc
+
+To pass boolean parameters make sure to use `gym` like this:
+
+ gym --include_bitcode true --include_symbols false
+
+To access the raw `xcodebuild` output open `~/Library/Logs/gym`
+
# Gymfile
Since you might want to manually trigger a new build but don't want to specify all the parameters every time, you can store your defaults in a so called `Gymfile`.
@@ -134,7 +150,7 @@ Run `gym init` to create a new configuration file. Example:
```ruby
scheme "Example"
-sdk "9.0"
+sdk "iphoneos9.0"
clean true
@@ -142,6 +158,28 @@ output_directory "./build" # store the ipa in this folder
output_name "MyApp" # the name of the ipa file
```
+## Export options
+
+Since Xcode 7, `gym` is using new Xcode API which allows us to specify export options using `plist` file. By default `gym` creates this file for you and you are able to modify some parameters by using `export_method`, `export_team_id`, `include_symbols` or `include_bitcode`. If you want to have more options, like creating manifest file or app thinning, you can provide your own `plist` file:
+
+```ruby
+export_options "./ExportOptions.plist"
+```
+
+or you can provide hash of values directly in the `Gymfile`:
+
+```ruby
+export_options(
+ method: "ad-hoc",
+ manifest: {
+ appURL: "https://example.com/My App.ipa",
+ },
+ thinning: ""
+)
+```
+
+For the list of available options run `xcodebuild -help`.
+
# Automating the whole process
`gym` works great together with [fastlane](https://fastlane.tools), which connects all deployment tools into one streamlined workflow.
@@ -158,7 +196,7 @@ end
You can then easily switch between the beta provider (e.g. `testflight`, `hockey`, `s3` and more).
-For more information visit the [fastlane GitHub page](https://github.com/KrauseFx/fastlane).
+For more information visit the [fastlane GitHub page](https://github.com/fastlane/fastlane).
# How does it work?
@@ -181,9 +219,27 @@ xcodebuild -scheme 'Example' \
archive | xcpretty
```
-
After building the archive it is being checked by `gym`. If it's valid, it gets packaged up and signed into an `ipa` file.
+`gym` automatically chooses a different packaging method depending on the version of Xcode you're using.
+
+### Xcode 7 and above
+
+```
+/usr/bin/xcrun path/to/xcbuild-safe.sh -exportArchive \
+-exportOptionsPlist '/tmp/gym_config_1442852529.plist' \
+-archivePath '/Users/fkrause/Library/Developer/Xcode/Archives/2015-09-21/App 2015-09-21 09.21.56.xcarchive' \
+-exportPath '/tmp/1442852529'
+```
+
+`gym` makes use of the new Xcode 7 API which allows us to specify the export options using a `plist` file. You can find more information about the available options by running `xcodebuild --help`.
+
+Using this method there are no workarounds for WatchKit or Swift required, as it uses the same technique Xcode uses when exporting your binary.
+
+Note: the [xcbuild-safe.sh script](https://github.com/fastlane/gym/tree/master/lib/assets/wrap_xcodebuild/xcbuild-safe.sh) wraps around xcodebuild to workaround some incompatibilities.
+
+### Xcode 6 and below
+
```
/usr/bin/xcrun /path/to/PackageApplication4Gym -v \
'/Users/felixkrause/Library/Developer/Xcode/Archives/2015-08-11/ExampleProductName 2015-08-11 18.15.30.xcarchive/Products/Applications/name.app' -o \
@@ -199,16 +255,17 @@ Afterwards the `ipa` file is moved to the output folder. The `dSYM` file is comp
## [`fastlane`](https://fastlane.tools) Toolchain
- [`fastlane`](https://fastlane.tools): Connect all deployment tools into one streamlined workflow
-- [`deliver`](https://github.com/KrauseFx/deliver): Upload screenshots, metadata and your app to the App Store
-- [`snapshot`](https://github.com/KrauseFx/snapshot): Automate taking localized screenshots of your iOS app on every device
-- [`frameit`](https://github.com/KrauseFx/frameit): Quickly put your screenshots into the right device frames
-- [`PEM`](https://github.com/KrauseFx/pem): Automatically generate and renew your push notification profiles
-- [`produce`](https://github.com/KrauseFx/produce): Create new iOS apps on iTunes Connect and Dev Portal using the command line
-- [`cert`](https://github.com/KrauseFx/cert): Automatically create and maintain iOS code signing certificates
-- [`codes`](https://github.com/KrauseFx/codes): Create promo codes for iOS Apps using the command line
+- [`deliver`](https://github.com/fastlane/deliver): Upload screenshots, metadata and your app to the App Store
+- [`snapshot`](https://github.com/fastlane/snapshot): Automate taking localized screenshots of your iOS app on every device
+- [`frameit`](https://github.com/fastlane/frameit): Quickly put your screenshots into the right device frames
+- [`pem`](https://github.com/fastlane/pem): Automatically generate and renew your push notification profiles
+- [`produce`](https://github.com/fastlane/produce): Create new iOS apps on iTunes Connect and Dev Portal using the command line
+- [`cert`](https://github.com/fastlane/cert): Automatically create and maintain iOS code signing certificates
- [`spaceship`](https://github.com/fastlane/spaceship): Ruby library to access the Apple Dev Center and iTunes Connect
- [`pilot`](https://github.com/fastlane/pilot): The best way to manage your TestFlight testers and builds from your terminal
- [`boarding`](https://github.com/fastlane/boarding): The easiest way to invite your TestFlight beta testers
+- [`scan`](https://github.com/fastlane/scan): The easiest way to run tests of your iOS and Mac app
+- [`match`](https://github.com/fastlane/match): Easily sync your certificates and profiles across your team using git
##### [Like this tool? Be the first to know about updates and new fastlane tools](https://tinyletter.com/krausefx)
@@ -218,6 +275,9 @@ Download and install the [Provisioning Plugin](https://github.com/chockenberry/P
# Need help?
Please submit an issue on GitHub and provide information about your setup
+# Code of Conduct
+Help us keep `gym` open and inclusive. Please read and follow our [Code of Conduct](https://github.com/fastlane/code-of-conduct).
+
# License
This project is licensed under the terms of the MIT license. See the LICENSE file.
diff --git a/Rakefile b/Rakefile
index ed2a05e..195f9ce 100644
--- a/Rakefile
+++ b/Rakefile
@@ -7,3 +7,7 @@ task default: :spec
task :test do
sh "../fastlane/bin/fastlane test"
end
+
+task :push do
+ sh "../fastlane/bin/fastlane release"
+end
diff --git a/assets/gym.png b/assets/gym.png
index 97d7532..8a3d16e 100644
Binary files a/assets/gym.png and b/assets/gym.png differ
diff --git a/assets/gym.pxm b/assets/gym.pxm
deleted file mode 100644
index 3bf7158..0000000
Binary files a/assets/gym.pxm and /dev/null differ
diff --git a/assets/gymFullSize.png b/assets/gymFullSize.png
deleted file mode 100644
index 73a7c58..0000000
Binary files a/assets/gymFullSize.png and /dev/null differ
diff --git a/examples/WatchKit/WatchKit.xcodeproj/project.pbxproj b/examples/WatchKit/WatchKit.xcodeproj/project.pbxproj
index 6db1529..b593aec 100644
--- a/examples/WatchKit/WatchKit.xcodeproj/project.pbxproj
+++ b/examples/WatchKit/WatchKit.xcodeproj/project.pbxproj
@@ -445,9 +445,10 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CODE_SIGN_IDENTITY = "iPhone Distribution";
INFOPLIST_FILE = WatchKit/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- PRODUCT_BUNDLE_IDENTIFIER = net.sunapps.WatchKit;
+ PRODUCT_BUNDLE_IDENTIFIER = tools.fastlane.app;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
@@ -456,9 +457,10 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CODE_SIGN_IDENTITY = "iPhone Distribution";
INFOPLIST_FILE = WatchKit/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- PRODUCT_BUNDLE_IDENTIFIER = net.sunapps.WatchKit;
+ PRODUCT_BUNDLE_IDENTIFIER = tools.fastlane.app;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
@@ -534,6 +536,7 @@
CA461FED1B84738C0037A37B /* Release */,
);
defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
};
CA46200F1B8473930037A37B /* Build configuration list for PBXNativeTarget "WatchKit WatchKit 1 App" */ = {
isa = XCConfigurationList;
@@ -542,6 +545,7 @@
CA4620111B8473930037A37B /* Release */,
);
defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
};
CA4620121B8473930037A37B /* Build configuration list for PBXNativeTarget "WatchKit WatchKit 1 Extension" */ = {
isa = XCConfigurationList;
@@ -550,6 +554,7 @@
CA4620141B8473930037A37B /* Release */,
);
defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
diff --git a/examples/WatchKit/WatchKit.xcodeproj/project.xcworkspace/xcshareddata/WatchKit.xcscmblueprint b/examples/WatchKit/WatchKit.xcodeproj/project.xcworkspace/xcshareddata/WatchKit.xcscmblueprint
new file mode 100644
index 0000000..229e29b
--- /dev/null
+++ b/examples/WatchKit/WatchKit.xcodeproj/project.xcworkspace/xcshareddata/WatchKit.xcscmblueprint
@@ -0,0 +1,30 @@
+{
+ "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "92E86429DF370BECD7A1A0FC00BA6FF984F9710D",
+ "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
+
+ },
+ "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
+ "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" : 0,
+ "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" : 0
+ },
+ "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "384272E4-7D16-4230-B482-A546F8BD54A7",
+ "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
+ "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" : "gym\/",
+ "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" : ""
+ },
+ "DVTSourceControlWorkspaceBlueprintNameKey" : "WatchKit",
+ "DVTSourceControlWorkspaceBlueprintVersion" : 204,
+ "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "examples\/WatchKit\/WatchKit.xcodeproj",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
+ {
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/fastlane\/countdown",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF"
+ },
+ {
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/krausefx\/gym",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "92E86429DF370BECD7A1A0FC00BA6FF984F9710D"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/examples/WatchKit/WatchKit.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/Notification - WatchKit WatchKit 1 App.xcscheme b/examples/WatchKit/WatchKit.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/Notification - WatchKit WatchKit 1 App.xcscheme
new file mode 100644
index 0000000..9a91d5e
--- /dev/null
+++ b/examples/WatchKit/WatchKit.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/Notification - WatchKit WatchKit 1 App.xcscheme
@@ -0,0 +1,145 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/WatchKit/WatchKit.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/WatchKit WatchKit 1 App.xcscheme b/examples/WatchKit/WatchKit.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/WatchKit WatchKit 1 App.xcscheme
new file mode 100644
index 0000000..91825a7
--- /dev/null
+++ b/examples/WatchKit/WatchKit.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/WatchKit WatchKit 1 App.xcscheme
@@ -0,0 +1,141 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/WatchKit/WatchKit.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/WatchKit.xcscheme b/examples/WatchKit/WatchKit.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/WatchKit.xcscheme
new file mode 100644
index 0000000..257cf89
--- /dev/null
+++ b/examples/WatchKit/WatchKit.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/WatchKit.xcscheme
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/WatchKit/WatchKit.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist b/examples/WatchKit/WatchKit.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist
new file mode 100644
index 0000000..708572f
--- /dev/null
+++ b/examples/WatchKit/WatchKit.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -0,0 +1,37 @@
+
+
+
+
+ SchemeUserState
+
+ Notification - WatchKit WatchKit 1 App.xcscheme
+
+ orderHint
+ 2
+
+ WatchKit WatchKit 1 App.xcscheme
+
+ orderHint
+ 1
+
+ WatchKit.xcscheme
+
+ orderHint
+ 0
+
+
+ SuppressBuildableAutocreation
+
+ CA461FD31B84738C0037A37B
+
+ primary
+
+
+ CA4620001B8473930037A37B
+
+ primary
+
+
+
+
+
diff --git a/examples/cocoapods/Example.xcodeproj/project.pbxproj b/examples/cocoapods/Example.xcodeproj/project.pbxproj
index 7431d27..b0026d0 100644
--- a/examples/cocoapods/Example.xcodeproj/project.pbxproj
+++ b/examples/cocoapods/Example.xcodeproj/project.pbxproj
@@ -471,10 +471,10 @@
baseConfigurationReference = ACCA4067A20AE82A13A55B4D /* Pods-Example.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
- CODE_SIGN_IDENTITY = "iPhone Distribution: SunApps GmbH (5A997XSHK2)";
+ CODE_SIGN_IDENTITY = "iPhone Distribution";
INFOPLIST_FILE = Example/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- PRODUCT_BUNDLE_IDENTIFIER = net.sunapps.9;
+ PRODUCT_BUNDLE_IDENTIFIER = tools.fastlane.app;
PRODUCT_NAME = "$(TARGET_NAME)ProductName";
PROVISIONING_PROFILE = "";
};
@@ -485,10 +485,10 @@
baseConfigurationReference = 7F17E2E4DB202336112485AA /* Pods-Example.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
- CODE_SIGN_IDENTITY = "iPhone Distribution: SunApps GmbH (5A997XSHK2)";
+ CODE_SIGN_IDENTITY = "iPhone Distribution";
INFOPLIST_FILE = Example/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- PRODUCT_BUNDLE_IDENTIFIER = net.sunapps.9;
+ PRODUCT_BUNDLE_IDENTIFIER = tools.fastlane.app;
PRODUCT_NAME = "$(TARGET_NAME)ProductName";
PROVISIONING_PROFILE = "";
};
diff --git a/examples/cocoapods/Example.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist b/examples/cocoapods/Example.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist
new file mode 100644
index 0000000..642a1dd
--- /dev/null
+++ b/examples/cocoapods/Example.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -0,0 +1,24 @@
+
+
+
+
+ SuppressBuildableAutocreation
+
+ CAC1E1971B6A326100A8B23A
+
+ primary
+
+
+ CAC1E1B01B6A326100A8B23A
+
+ primary
+
+
+ CAC1E1BB1B6A326200A8B23A
+
+ primary
+
+
+
+
+
diff --git a/examples/cocoapods/Example.xcworkspace/xcshareddata/Example.xcscmblueprint b/examples/cocoapods/Example.xcworkspace/xcshareddata/Example.xcscmblueprint
new file mode 100644
index 0000000..818935e
--- /dev/null
+++ b/examples/cocoapods/Example.xcworkspace/xcshareddata/Example.xcscmblueprint
@@ -0,0 +1,30 @@
+{
+ "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "92E86429DF370BECD7A1A0FC00BA6FF984F9710D",
+ "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
+
+ },
+ "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
+ "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" : 0,
+ "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" : 0
+ },
+ "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "B0B54A05-D112-479E-84BB-D9FA30D6062E",
+ "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
+ "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" : "gym\/",
+ "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" : ""
+ },
+ "DVTSourceControlWorkspaceBlueprintNameKey" : "Example",
+ "DVTSourceControlWorkspaceBlueprintVersion" : 204,
+ "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "examples\/cocoapods\/Example.xcworkspace",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
+ {
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/fastlane\/countdown",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF"
+ },
+ {
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/krausefx\/gym",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "92E86429DF370BECD7A1A0FC00BA6FF984F9710D"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/examples/cocoapods/Pods/Pods.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist b/examples/cocoapods/Pods/Pods.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist
new file mode 100644
index 0000000..c2d7cb5
--- /dev/null
+++ b/examples/cocoapods/Pods/Pods.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -0,0 +1,19 @@
+
+
+
+
+ SuppressBuildableAutocreation
+
+ 8E46265702A9FA999EF6AC4B2576B14F
+
+ primary
+
+
+ FB2714B8EF1C625F78C19E0D0EF6E5A8
+
+ primary
+
+
+
+
+
diff --git a/examples/multipleSchemes/Example.xcodeproj/project.pbxproj b/examples/multipleSchemes/Example.xcodeproj/project.pbxproj
index f4ca25b..ef00a57 100644
--- a/examples/multipleSchemes/Example.xcodeproj/project.pbxproj
+++ b/examples/multipleSchemes/Example.xcodeproj/project.pbxproj
@@ -411,10 +411,10 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
- CODE_SIGN_IDENTITY = "iPhone Distribution: SunApps GmbH (5A997XSHK2)";
+ CODE_SIGN_IDENTITY = "iPhone Distribution";
INFOPLIST_FILE = Example/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- PRODUCT_BUNDLE_IDENTIFIER = net.sunapps.9;
+ PRODUCT_BUNDLE_IDENTIFIER = tools.fastlane.app;
PRODUCT_NAME = "$(TARGET_NAME)ProductName";
PROVISIONING_PROFILE = "";
};
@@ -424,10 +424,10 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
- CODE_SIGN_IDENTITY = "iPhone Distribution: SunApps GmbH (5A997XSHK2)";
+ CODE_SIGN_IDENTITY = "iPhone Distribution";
INFOPLIST_FILE = Example/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- PRODUCT_BUNDLE_IDENTIFIER = net.sunapps.9;
+ PRODUCT_BUNDLE_IDENTIFIER = tools.fastlane.app;
PRODUCT_NAME = "$(TARGET_NAME)ProductName";
PROVISIONING_PROFILE = "";
};
diff --git a/examples/multipleSchemes/Example.xcodeproj/project.xcworkspace/xcshareddata/Example.xcscmblueprint b/examples/multipleSchemes/Example.xcodeproj/project.xcworkspace/xcshareddata/Example.xcscmblueprint
new file mode 100644
index 0000000..1255ee2
--- /dev/null
+++ b/examples/multipleSchemes/Example.xcodeproj/project.xcworkspace/xcshareddata/Example.xcscmblueprint
@@ -0,0 +1,30 @@
+{
+ "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "92E86429DF370BECD7A1A0FC00BA6FF984F9710D",
+ "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
+
+ },
+ "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
+ "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" : 0,
+ "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" : 0
+ },
+ "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "FF59B69C-C47E-4AB7-80E3-C9D623C3D37B",
+ "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
+ "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" : "gym\/",
+ "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" : ""
+ },
+ "DVTSourceControlWorkspaceBlueprintNameKey" : "Example",
+ "DVTSourceControlWorkspaceBlueprintVersion" : 204,
+ "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "examples\/multipleSchemes\/Example.xcodeproj",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
+ {
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/fastlane\/countdown",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF"
+ },
+ {
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/krausefx\/gym",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "92E86429DF370BECD7A1A0FC00BA6FF984F9710D"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/examples/multipleSchemes/Example.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist b/examples/multipleSchemes/Example.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist
new file mode 100644
index 0000000..642a1dd
--- /dev/null
+++ b/examples/multipleSchemes/Example.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -0,0 +1,24 @@
+
+
+
+
+ SuppressBuildableAutocreation
+
+ CAC1E1971B6A326100A8B23A
+
+ primary
+
+
+ CAC1E1B01B6A326100A8B23A
+
+ primary
+
+
+ CAC1E1BB1B6A326200A8B23A
+
+ primary
+
+
+
+
+
diff --git a/examples/standard/Example.xcodeproj/project.pbxproj b/examples/standard/Example.xcodeproj/project.pbxproj
index f4ca25b..ef00a57 100644
--- a/examples/standard/Example.xcodeproj/project.pbxproj
+++ b/examples/standard/Example.xcodeproj/project.pbxproj
@@ -411,10 +411,10 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
- CODE_SIGN_IDENTITY = "iPhone Distribution: SunApps GmbH (5A997XSHK2)";
+ CODE_SIGN_IDENTITY = "iPhone Distribution";
INFOPLIST_FILE = Example/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- PRODUCT_BUNDLE_IDENTIFIER = net.sunapps.9;
+ PRODUCT_BUNDLE_IDENTIFIER = tools.fastlane.app;
PRODUCT_NAME = "$(TARGET_NAME)ProductName";
PROVISIONING_PROFILE = "";
};
@@ -424,10 +424,10 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
- CODE_SIGN_IDENTITY = "iPhone Distribution: SunApps GmbH (5A997XSHK2)";
+ CODE_SIGN_IDENTITY = "iPhone Distribution";
INFOPLIST_FILE = Example/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- PRODUCT_BUNDLE_IDENTIFIER = net.sunapps.9;
+ PRODUCT_BUNDLE_IDENTIFIER = tools.fastlane.app;
PRODUCT_NAME = "$(TARGET_NAME)ProductName";
PROVISIONING_PROFILE = "";
};
diff --git a/examples/standard/Example.xcodeproj/project.xcworkspace/xcshareddata/Example.xcscmblueprint b/examples/standard/Example.xcodeproj/project.xcworkspace/xcshareddata/Example.xcscmblueprint
new file mode 100644
index 0000000..4f6a956
--- /dev/null
+++ b/examples/standard/Example.xcodeproj/project.xcworkspace/xcshareddata/Example.xcscmblueprint
@@ -0,0 +1,30 @@
+{
+ "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "92E86429DF370BECD7A1A0FC00BA6FF984F9710D",
+ "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
+
+ },
+ "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
+ "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" : 0,
+ "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" : 0
+ },
+ "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "F3078953-4758-46C3-AC3D-87FEF88956AF",
+ "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
+ "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" : "gym\/",
+ "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" : ""
+ },
+ "DVTSourceControlWorkspaceBlueprintNameKey" : "Example",
+ "DVTSourceControlWorkspaceBlueprintVersion" : 204,
+ "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "examples\/standard\/Example.xcodeproj",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
+ {
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/fastlane\/countdown",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF"
+ },
+ {
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/krausefx\/gym",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "92E86429DF370BECD7A1A0FC00BA6FF984F9710D"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/examples/standard/Example.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist b/examples/standard/Example.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist
new file mode 100644
index 0000000..642a1dd
--- /dev/null
+++ b/examples/standard/Example.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -0,0 +1,24 @@
+
+
+
+
+ SuppressBuildableAutocreation
+
+ CAC1E1971B6A326100A8B23A
+
+ primary
+
+
+ CAC1E1B01B6A326100A8B23A
+
+ primary
+
+
+ CAC1E1BB1B6A326200A8B23A
+
+ primary
+
+
+
+
+
diff --git a/examples/standard/ExampleExport.plist b/examples/standard/ExampleExport.plist
new file mode 100644
index 0000000..6c136c5
--- /dev/null
+++ b/examples/standard/ExampleExport.plist
@@ -0,0 +1,19 @@
+
+
+
+
+ embedOnDemandResourcesAssetPacksInBundle
+
+ manifest
+
+ appURL
+ https://www.example.com/Example.ipa
+ displayImageURL
+ https://www.example.com/display.png
+ fullSizeImageURL
+ https://www.example.com/fullSize.png
+
+ method
+ ad-hoc
+
+
diff --git a/examples/today/Example.xcodeproj/project.pbxproj b/examples/today/Example.xcodeproj/project.pbxproj
index 17ca18f..ef00a57 100644
--- a/examples/today/Example.xcodeproj/project.pbxproj
+++ b/examples/today/Example.xcodeproj/project.pbxproj
@@ -411,12 +411,12 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
- CODE_SIGN_IDENTITY = "iPhone Distribution: SunApps GmbH (5A997XSHK2)";
+ CODE_SIGN_IDENTITY = "iPhone Distribution";
INFOPLIST_FILE = Example/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- PRODUCT_BUNDLE_IDENTIFIER = net.sunapps.9;
+ PRODUCT_BUNDLE_IDENTIFIER = tools.fastlane.app;
PRODUCT_NAME = "$(TARGET_NAME)ProductName";
- PROVISIONING_PROFILE = "067a4fdf-f03d-4fb8-8dc9-f9dfb6dbd";
+ PROVISIONING_PROFILE = "";
};
name = Debug;
};
@@ -424,12 +424,12 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
- CODE_SIGN_IDENTITY = "iPhone Distribution: SunApps GmbH (5A997XSHK2)";
+ CODE_SIGN_IDENTITY = "iPhone Distribution";
INFOPLIST_FILE = Example/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- PRODUCT_BUNDLE_IDENTIFIER = net.sunapps.9;
+ PRODUCT_BUNDLE_IDENTIFIER = tools.fastlane.app;
PRODUCT_NAME = "$(TARGET_NAME)ProductName";
- PROVISIONING_PROFILE = "067a4fdf-f03d-4fb8-8dc9-f9dfb6dbd";
+ PROVISIONING_PROFILE = "";
};
name = Release;
};
diff --git a/examples/today/Example.xcodeproj/project.xcworkspace/xcshareddata/Example.xcscmblueprint b/examples/today/Example.xcodeproj/project.xcworkspace/xcshareddata/Example.xcscmblueprint
new file mode 100644
index 0000000..52370e6
--- /dev/null
+++ b/examples/today/Example.xcodeproj/project.xcworkspace/xcshareddata/Example.xcscmblueprint
@@ -0,0 +1,30 @@
+{
+ "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "92E86429DF370BECD7A1A0FC00BA6FF984F9710D",
+ "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
+
+ },
+ "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
+ "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" : 0,
+ "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" : 0
+ },
+ "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "40549047-3043-4E27-90B2-4497ECB8EE80",
+ "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
+ "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" : "gym\/",
+ "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" : ""
+ },
+ "DVTSourceControlWorkspaceBlueprintNameKey" : "Example",
+ "DVTSourceControlWorkspaceBlueprintVersion" : 204,
+ "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "examples\/today\/Example.xcodeproj",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
+ {
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/fastlane\/countdown",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF"
+ },
+ {
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/krausefx\/gym",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "92E86429DF370BECD7A1A0FC00BA6FF984F9710D"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/examples/today/Example.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist b/examples/today/Example.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist
new file mode 100644
index 0000000..642a1dd
--- /dev/null
+++ b/examples/today/Example.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -0,0 +1,24 @@
+
+
+
+
+ SuppressBuildableAutocreation
+
+ CAC1E1971B6A326100A8B23A
+
+ primary
+
+
+ CAC1E1B01B6A326100A8B23A
+
+ primary
+
+
+ CAC1E1BB1B6A326200A8B23A
+
+ primary
+
+
+
+
+
diff --git a/examples/tvapp/tvapp.xcodeproj/project.pbxproj b/examples/tvapp/tvapp.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..0581a01
--- /dev/null
+++ b/examples/tvapp/tvapp.xcodeproj/project.pbxproj
@@ -0,0 +1,301 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ CA7BF6BA1C33280500A161B3 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = CA7BF6B91C33280500A161B3 /* main.m */; };
+ CA7BF6BD1C33280500A161B3 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CA7BF6BC1C33280500A161B3 /* AppDelegate.m */; };
+ CA7BF6C01C33280500A161B3 /* FirstViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = CA7BF6BF1C33280500A161B3 /* FirstViewController.m */; };
+ CA7BF6C31C33280500A161B3 /* SecondViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = CA7BF6C21C33280500A161B3 /* SecondViewController.m */; };
+ CA7BF6C61C33280500A161B3 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CA7BF6C41C33280500A161B3 /* Main.storyboard */; };
+ CA7BF6C81C33280500A161B3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CA7BF6C71C33280500A161B3 /* Assets.xcassets */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ CA7BF6B51C33280500A161B3 /* tvapp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = tvapp.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ CA7BF6B91C33280500A161B3 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
+ CA7BF6BB1C33280500A161B3 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
+ CA7BF6BC1C33280500A161B3 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
+ CA7BF6BE1C33280500A161B3 /* FirstViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FirstViewController.h; sourceTree = ""; };
+ CA7BF6BF1C33280500A161B3 /* FirstViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FirstViewController.m; sourceTree = ""; };
+ CA7BF6C11C33280500A161B3 /* SecondViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SecondViewController.h; sourceTree = ""; };
+ CA7BF6C21C33280500A161B3 /* SecondViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SecondViewController.m; sourceTree = ""; };
+ CA7BF6C51C33280500A161B3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
+ CA7BF6C71C33280500A161B3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ CA7BF6C91C33280500A161B3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ CA7BF6B21C33280500A161B3 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ CA7BF6AC1C33280500A161B3 = {
+ isa = PBXGroup;
+ children = (
+ CA7BF6B71C33280500A161B3 /* tvapp */,
+ CA7BF6B61C33280500A161B3 /* Products */,
+ );
+ sourceTree = "";
+ };
+ CA7BF6B61C33280500A161B3 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ CA7BF6B51C33280500A161B3 /* tvapp.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ CA7BF6B71C33280500A161B3 /* tvapp */ = {
+ isa = PBXGroup;
+ children = (
+ CA7BF6BB1C33280500A161B3 /* AppDelegate.h */,
+ CA7BF6BC1C33280500A161B3 /* AppDelegate.m */,
+ CA7BF6BE1C33280500A161B3 /* FirstViewController.h */,
+ CA7BF6BF1C33280500A161B3 /* FirstViewController.m */,
+ CA7BF6C11C33280500A161B3 /* SecondViewController.h */,
+ CA7BF6C21C33280500A161B3 /* SecondViewController.m */,
+ CA7BF6C41C33280500A161B3 /* Main.storyboard */,
+ CA7BF6C71C33280500A161B3 /* Assets.xcassets */,
+ CA7BF6C91C33280500A161B3 /* Info.plist */,
+ CA7BF6B81C33280500A161B3 /* Supporting Files */,
+ );
+ path = tvapp;
+ sourceTree = "";
+ };
+ CA7BF6B81C33280500A161B3 /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ CA7BF6B91C33280500A161B3 /* main.m */,
+ );
+ name = "Supporting Files";
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ CA7BF6B41C33280500A161B3 /* tvapp */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = CA7BF6CC1C33280500A161B3 /* Build configuration list for PBXNativeTarget "tvapp" */;
+ buildPhases = (
+ CA7BF6B11C33280500A161B3 /* Sources */,
+ CA7BF6B21C33280500A161B3 /* Frameworks */,
+ CA7BF6B31C33280500A161B3 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = tvapp;
+ productName = tvapp;
+ productReference = CA7BF6B51C33280500A161B3 /* tvapp.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ CA7BF6AD1C33280500A161B3 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 0720;
+ ORGANIZATIONNAME = "Felix Krause";
+ TargetAttributes = {
+ CA7BF6B41C33280500A161B3 = {
+ CreatedOnToolsVersion = 7.2;
+ };
+ };
+ };
+ buildConfigurationList = CA7BF6B01C33280500A161B3 /* Build configuration list for PBXProject "tvapp" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = CA7BF6AC1C33280500A161B3;
+ productRefGroup = CA7BF6B61C33280500A161B3 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ CA7BF6B41C33280500A161B3 /* tvapp */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ CA7BF6B31C33280500A161B3 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ CA7BF6C81C33280500A161B3 /* Assets.xcassets in Resources */,
+ CA7BF6C61C33280500A161B3 /* Main.storyboard in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ CA7BF6B11C33280500A161B3 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ CA7BF6C31C33280500A161B3 /* SecondViewController.m in Sources */,
+ CA7BF6BD1C33280500A161B3 /* AppDelegate.m in Sources */,
+ CA7BF6C01C33280500A161B3 /* FirstViewController.m in Sources */,
+ CA7BF6BA1C33280500A161B3 /* main.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ CA7BF6C41C33280500A161B3 /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ CA7BF6C51C33280500A161B3 /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ CA7BF6CA1C33280500A161B3 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = appletvos;
+ TARGETED_DEVICE_FAMILY = 3;
+ TVOS_DEPLOYMENT_TARGET = 9.1;
+ };
+ name = Debug;
+ };
+ CA7BF6CB1C33280500A161B3 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = appletvos;
+ TARGETED_DEVICE_FAMILY = 3;
+ TVOS_DEPLOYMENT_TARGET = 9.1;
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ CA7BF6CD1C33280500A161B3 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
+ INFOPLIST_FILE = tvapp/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ PRODUCT_BUNDLE_IDENTIFIER = tools.fastlane.tvapp;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ CA7BF6CE1C33280500A161B3 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
+ INFOPLIST_FILE = tvapp/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ PRODUCT_BUNDLE_IDENTIFIER = tools.fastlane.tvapp;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ CA7BF6B01C33280500A161B3 /* Build configuration list for PBXProject "tvapp" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ CA7BF6CA1C33280500A161B3 /* Debug */,
+ CA7BF6CB1C33280500A161B3 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ CA7BF6CC1C33280500A161B3 /* Build configuration list for PBXNativeTarget "tvapp" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ CA7BF6CD1C33280500A161B3 /* Debug */,
+ CA7BF6CE1C33280500A161B3 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = CA7BF6AD1C33280500A161B3 /* Project object */;
+}
diff --git a/examples/tvapp/tvapp.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/examples/tvapp/tvapp.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..d1200ae
--- /dev/null
+++ b/examples/tvapp/tvapp.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/examples/tvapp/tvapp.xcodeproj/project.xcworkspace/xcshareddata/tvapp.xcscmblueprint b/examples/tvapp/tvapp.xcodeproj/project.xcworkspace/xcshareddata/tvapp.xcscmblueprint
new file mode 100644
index 0000000..f784964
--- /dev/null
+++ b/examples/tvapp/tvapp.xcodeproj/project.xcworkspace/xcshareddata/tvapp.xcscmblueprint
@@ -0,0 +1,30 @@
+{
+ "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "92E86429DF370BECD7A1A0FC00BA6FF984F9710D",
+ "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
+
+ },
+ "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
+ "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" : 0,
+ "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" : 0
+ },
+ "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "0C0BD7B7-BAC1-4D34-AD66-1835A6180719",
+ "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
+ "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" : "gym\/",
+ "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" : ""
+ },
+ "DVTSourceControlWorkspaceBlueprintNameKey" : "tvapp",
+ "DVTSourceControlWorkspaceBlueprintVersion" : 204,
+ "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "examples\/tvapp\/tvapp.xcodeproj",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
+ {
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/fastlane\/countdown",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF"
+ },
+ {
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/fastlane\/gym.git",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "92E86429DF370BECD7A1A0FC00BA6FF984F9710D"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/examples/tvapp/tvapp.xcodeproj/xcuserdata/felixkrause.xcuserdatad/xcschemes/tvapp.xcscheme b/examples/tvapp/tvapp.xcodeproj/xcuserdata/felixkrause.xcuserdatad/xcschemes/tvapp.xcscheme
new file mode 100644
index 0000000..374b390
--- /dev/null
+++ b/examples/tvapp/tvapp.xcodeproj/xcuserdata/felixkrause.xcuserdatad/xcschemes/tvapp.xcscheme
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/tvapp/tvapp.xcodeproj/xcuserdata/felixkrause.xcuserdatad/xcschemes/xcschememanagement.plist b/examples/tvapp/tvapp.xcodeproj/xcuserdata/felixkrause.xcuserdatad/xcschemes/xcschememanagement.plist
new file mode 100644
index 0000000..f8cc1b7
--- /dev/null
+++ b/examples/tvapp/tvapp.xcodeproj/xcuserdata/felixkrause.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -0,0 +1,22 @@
+
+
+
+
+ SchemeUserState
+
+ tvapp.xcscheme
+
+ orderHint
+ 0
+
+
+ SuppressBuildableAutocreation
+
+ CA7BF6B41C33280500A161B3
+
+ primary
+
+
+
+
+
diff --git a/examples/tvapp/tvapp/AppDelegate.h b/examples/tvapp/tvapp/AppDelegate.h
new file mode 100644
index 0000000..5a8961a
--- /dev/null
+++ b/examples/tvapp/tvapp/AppDelegate.h
@@ -0,0 +1,17 @@
+//
+// AppDelegate.h
+// tvapp
+//
+// Created by Felix Krause on 30/12/15.
+// Copyright © 2015 Felix Krause. All rights reserved.
+//
+
+#import
+
+@interface AppDelegate : UIResponder
+
+@property (strong, nonatomic) UIWindow *window;
+
+
+@end
+
diff --git a/examples/tvapp/tvapp/AppDelegate.m b/examples/tvapp/tvapp/AppDelegate.m
new file mode 100644
index 0000000..5736b75
--- /dev/null
+++ b/examples/tvapp/tvapp/AppDelegate.m
@@ -0,0 +1,45 @@
+//
+// AppDelegate.m
+// tvapp
+//
+// Created by Felix Krause on 30/12/15.
+// Copyright © 2015 Felix Krause. All rights reserved.
+//
+
+#import "AppDelegate.h"
+
+@interface AppDelegate ()
+
+@end
+
+@implementation AppDelegate
+
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+ // Override point for customization after application launch.
+ return YES;
+}
+
+- (void)applicationWillResignActive:(UIApplication *)application {
+ // 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.
+ // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
+}
+
+- (void)applicationDidEnterBackground:(UIApplication *)application {
+ // 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.
+ // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
+}
+
+- (void)applicationWillEnterForeground:(UIApplication *)application {
+ // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
+}
+
+- (void)applicationDidBecomeActive:(UIApplication *)application {
+ // 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.
+}
+
+- (void)applicationWillTerminate:(UIApplication *)application {
+ // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
+}
+
+@end
diff --git a/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Contents.json
new file mode 100644
index 0000000..0564959
--- /dev/null
+++ b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "idiom" : "tv",
+ "scale" : "1x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Contents.json
new file mode 100644
index 0000000..da4a164
--- /dev/null
+++ b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Contents.json
new file mode 100644
index 0000000..8bf75d9
--- /dev/null
+++ b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Contents.json
@@ -0,0 +1,17 @@
+{
+ "layers" : [
+ {
+ "filename" : "Front.imagestacklayer"
+ },
+ {
+ "filename" : "Middle.imagestacklayer"
+ },
+ {
+ "filename" : "Back.imagestacklayer"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Contents.json
new file mode 100644
index 0000000..0564959
--- /dev/null
+++ b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "idiom" : "tv",
+ "scale" : "1x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Contents.json
new file mode 100644
index 0000000..da4a164
--- /dev/null
+++ b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json
new file mode 100644
index 0000000..0564959
--- /dev/null
+++ b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "idiom" : "tv",
+ "scale" : "1x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Contents.json
new file mode 100644
index 0000000..da4a164
--- /dev/null
+++ b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Contents.json
new file mode 100644
index 0000000..0564959
--- /dev/null
+++ b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "idiom" : "tv",
+ "scale" : "1x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Contents.json
new file mode 100644
index 0000000..da4a164
--- /dev/null
+++ b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Contents.json
new file mode 100644
index 0000000..8bf75d9
--- /dev/null
+++ b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Contents.json
@@ -0,0 +1,17 @@
+{
+ "layers" : [
+ {
+ "filename" : "Front.imagestacklayer"
+ },
+ {
+ "filename" : "Middle.imagestacklayer"
+ },
+ {
+ "filename" : "Back.imagestacklayer"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Contents.json
new file mode 100644
index 0000000..0564959
--- /dev/null
+++ b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "idiom" : "tv",
+ "scale" : "1x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Contents.json
new file mode 100644
index 0000000..da4a164
--- /dev/null
+++ b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json
new file mode 100644
index 0000000..0564959
--- /dev/null
+++ b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "idiom" : "tv",
+ "scale" : "1x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Contents.json
new file mode 100644
index 0000000..da4a164
--- /dev/null
+++ b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json
new file mode 100644
index 0000000..6a3dcfa
--- /dev/null
+++ b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json
@@ -0,0 +1,26 @@
+{
+ "assets" : [
+ {
+ "size" : "1280x768",
+ "idiom" : "tv",
+ "filename" : "App Icon - Large.imagestack",
+ "role" : "primary-app-icon"
+ },
+ {
+ "size" : "400x240",
+ "idiom" : "tv",
+ "filename" : "App Icon - Small.imagestack",
+ "role" : "primary-app-icon"
+ },
+ {
+ "size" : "1920x720",
+ "idiom" : "tv",
+ "filename" : "Top Shelf Image.imageset",
+ "role" : "top-shelf-image"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json
new file mode 100644
index 0000000..0564959
--- /dev/null
+++ b/examples/tvapp/tvapp/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "idiom" : "tv",
+ "scale" : "1x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/examples/tvapp/tvapp/Assets.xcassets/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/Contents.json
new file mode 100644
index 0000000..da4a164
--- /dev/null
+++ b/examples/tvapp/tvapp/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/examples/tvapp/tvapp/Assets.xcassets/LaunchImage.launchimage/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/LaunchImage.launchimage/Contents.json
new file mode 100644
index 0000000..29d94c7
--- /dev/null
+++ b/examples/tvapp/tvapp/Assets.xcassets/LaunchImage.launchimage/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "orientation" : "landscape",
+ "idiom" : "tv",
+ "extent" : "full-screen",
+ "minimum-system-version" : "9.0",
+ "scale" : "1x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/examples/tvapp/tvapp/Assets.xcassets/first.imageset/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/first.imageset/Contents.json
new file mode 100644
index 0000000..33a7451
--- /dev/null
+++ b/examples/tvapp/tvapp/Assets.xcassets/first.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "first.pdf"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/examples/tvapp/tvapp/Assets.xcassets/first.imageset/first.pdf b/examples/tvapp/tvapp/Assets.xcassets/first.imageset/first.pdf
new file mode 100644
index 0000000..47d911d
Binary files /dev/null and b/examples/tvapp/tvapp/Assets.xcassets/first.imageset/first.pdf differ
diff --git a/examples/tvapp/tvapp/Assets.xcassets/second.imageset/Contents.json b/examples/tvapp/tvapp/Assets.xcassets/second.imageset/Contents.json
new file mode 100644
index 0000000..03bd9c9
--- /dev/null
+++ b/examples/tvapp/tvapp/Assets.xcassets/second.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "second.pdf"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/examples/tvapp/tvapp/Assets.xcassets/second.imageset/second.pdf b/examples/tvapp/tvapp/Assets.xcassets/second.imageset/second.pdf
new file mode 100644
index 0000000..401614e
Binary files /dev/null and b/examples/tvapp/tvapp/Assets.xcassets/second.imageset/second.pdf differ
diff --git a/examples/tvapp/tvapp/Base.lproj/Main.storyboard b/examples/tvapp/tvapp/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..1da21ff
--- /dev/null
+++ b/examples/tvapp/tvapp/Base.lproj/Main.storyboard
@@ -0,0 +1,111 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/tvapp/tvapp/FirstViewController.h b/examples/tvapp/tvapp/FirstViewController.h
new file mode 100644
index 0000000..eb65193
--- /dev/null
+++ b/examples/tvapp/tvapp/FirstViewController.h
@@ -0,0 +1,15 @@
+//
+// FirstViewController.h
+// tvapp
+//
+// Created by Felix Krause on 30/12/15.
+// Copyright © 2015 Felix Krause. All rights reserved.
+//
+
+#import
+
+@interface FirstViewController : UIViewController
+
+
+@end
+
diff --git a/examples/tvapp/tvapp/FirstViewController.m b/examples/tvapp/tvapp/FirstViewController.m
new file mode 100644
index 0000000..6179e1f
--- /dev/null
+++ b/examples/tvapp/tvapp/FirstViewController.m
@@ -0,0 +1,27 @@
+//
+// FirstViewController.m
+// tvapp
+//
+// Created by Felix Krause on 30/12/15.
+// Copyright © 2015 Felix Krause. All rights reserved.
+//
+
+#import "FirstViewController.h"
+
+@interface FirstViewController ()
+
+@end
+
+@implementation FirstViewController
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+ // Do any additional setup after loading the view, typically from a nib.
+}
+
+- (void)didReceiveMemoryWarning {
+ [super didReceiveMemoryWarning];
+ // Dispose of any resources that can be recreated.
+}
+
+@end
diff --git a/examples/tvapp/tvapp/Info.plist b/examples/tvapp/tvapp/Info.plist
new file mode 100644
index 0000000..4f33860
--- /dev/null
+++ b/examples/tvapp/tvapp/Info.plist
@@ -0,0 +1,32 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1
+ LSRequiresIPhoneOS
+
+ UIMainStoryboardFile
+ Main
+ UIRequiredDeviceCapabilities
+
+ arm64
+
+
+
diff --git a/examples/tvapp/tvapp/SecondViewController.h b/examples/tvapp/tvapp/SecondViewController.h
new file mode 100644
index 0000000..3d58a53
--- /dev/null
+++ b/examples/tvapp/tvapp/SecondViewController.h
@@ -0,0 +1,15 @@
+//
+// SecondViewController.h
+// tvapp
+//
+// Created by Felix Krause on 30/12/15.
+// Copyright © 2015 Felix Krause. All rights reserved.
+//
+
+#import
+
+@interface SecondViewController : UIViewController
+
+
+@end
+
diff --git a/examples/tvapp/tvapp/SecondViewController.m b/examples/tvapp/tvapp/SecondViewController.m
new file mode 100644
index 0000000..1d921ac
--- /dev/null
+++ b/examples/tvapp/tvapp/SecondViewController.m
@@ -0,0 +1,27 @@
+//
+// SecondViewController.m
+// tvapp
+//
+// Created by Felix Krause on 30/12/15.
+// Copyright © 2015 Felix Krause. All rights reserved.
+//
+
+#import "SecondViewController.h"
+
+@interface SecondViewController ()
+
+@end
+
+@implementation SecondViewController
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+ // Do any additional setup after loading the view, typically from a nib.
+}
+
+- (void)didReceiveMemoryWarning {
+ [super didReceiveMemoryWarning];
+ // Dispose of any resources that can be recreated.
+}
+
+@end
diff --git a/examples/tvapp/tvapp/main.m b/examples/tvapp/tvapp/main.m
new file mode 100644
index 0000000..37959e9
--- /dev/null
+++ b/examples/tvapp/tvapp/main.m
@@ -0,0 +1,16 @@
+//
+// main.m
+// tvapp
+//
+// Created by Felix Krause on 30/12/15.
+// Copyright © 2015 Felix Krause. All rights reserved.
+//
+
+#import
+#import "AppDelegate.h"
+
+int main(int argc, char * argv[]) {
+ @autoreleasepool {
+ return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
+ }
+}
diff --git a/examples/with spaces/With Spaces.xcodeproj/project.pbxproj b/examples/with spaces/With Spaces.xcodeproj/project.pbxproj
index 1276cd0..3c60f1b 100644
--- a/examples/with spaces/With Spaces.xcodeproj/project.pbxproj
+++ b/examples/with spaces/With Spaces.xcodeproj/project.pbxproj
@@ -362,9 +362,9 @@
CODE_SIGN_IDENTITY = "iPhone Distribution";
INFOPLIST_FILE = "With Spaces/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- PRODUCT_BUNDLE_IDENTIFIER = net.sunapps.9;
+ PRODUCT_BUNDLE_IDENTIFIER = tools.fastlane.app;
PRODUCT_NAME = "$(TARGET_NAME)";
- PROVISIONING_PROFILE = "675dcbd2-06a8-4a38-91a9-33ad0faf22b1";
+ PROVISIONING_PROFILE = "";
};
name = Debug;
};
@@ -375,9 +375,9 @@
CODE_SIGN_IDENTITY = "iPhone Distribution";
INFOPLIST_FILE = "With Spaces/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- PRODUCT_BUNDLE_IDENTIFIER = net.sunapps.9;
+ PRODUCT_BUNDLE_IDENTIFIER = tools.fastlane.app;
PRODUCT_NAME = "$(TARGET_NAME)";
- PROVISIONING_PROFILE = "675dcbd2-06a8-4a38-91a9-33ad0faf22b1";
+ PROVISIONING_PROFILE = "";
};
name = Release;
};
@@ -443,6 +443,7 @@
CAB109241B7A641500F53B5F /* Release */,
);
defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
diff --git a/examples/with spaces/With Spaces.xcodeproj/project.xcworkspace/xcshareddata/With Spaces.xcscmblueprint b/examples/with spaces/With Spaces.xcodeproj/project.xcworkspace/xcshareddata/With Spaces.xcscmblueprint
new file mode 100644
index 0000000..c515232
--- /dev/null
+++ b/examples/with spaces/With Spaces.xcodeproj/project.xcworkspace/xcshareddata/With Spaces.xcscmblueprint
@@ -0,0 +1,30 @@
+{
+ "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "92E86429DF370BECD7A1A0FC00BA6FF984F9710D",
+ "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
+
+ },
+ "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
+ "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" : 0,
+ "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" : 0
+ },
+ "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "DAED6B94-01EB-4907-BDCD-FC15F46055DF",
+ "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
+ "92E86429DF370BECD7A1A0FC00BA6FF984F9710D" : "gym\/",
+ "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF" : ""
+ },
+ "DVTSourceControlWorkspaceBlueprintNameKey" : "With Spaces",
+ "DVTSourceControlWorkspaceBlueprintVersion" : 204,
+ "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "examples\/with spaces\/With Spaces.xcodeproj",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
+ {
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/fastlane\/countdown",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "11BD64E841BB6AC3A48BA84DC911C1D4F58BDACF"
+ },
+ {
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/krausefx\/gym",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "92E86429DF370BECD7A1A0FC00BA6FF984F9710D"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/examples/with spaces/With Spaces.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/Notification - With Spaces WatchKit 1 App.xcscheme b/examples/with spaces/With Spaces.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/Notification - With Spaces WatchKit 1 App.xcscheme
new file mode 100644
index 0000000..543a4fe
--- /dev/null
+++ b/examples/with spaces/With Spaces.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/Notification - With Spaces WatchKit 1 App.xcscheme
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/with spaces/With Spaces.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/With Spaces WatchKit 1 App.xcscheme b/examples/with spaces/With Spaces.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/With Spaces WatchKit 1 App.xcscheme
new file mode 100644
index 0000000..a3abf36
--- /dev/null
+++ b/examples/with spaces/With Spaces.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/With Spaces WatchKit 1 App.xcscheme
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/with spaces/With Spaces.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/With Spaces.xcscheme b/examples/with spaces/With Spaces.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/With Spaces.xcscheme
new file mode 100644
index 0000000..e2a92f1
--- /dev/null
+++ b/examples/with spaces/With Spaces.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/With Spaces.xcscheme
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/with spaces/With Spaces.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist b/examples/with spaces/With Spaces.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist
new file mode 100644
index 0000000..12872a3
--- /dev/null
+++ b/examples/with spaces/With Spaces.xcodeproj/xcuserdata/fkrause.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -0,0 +1,37 @@
+
+
+
+
+ SchemeUserState
+
+ Notification - With Spaces WatchKit 1 App.xcscheme
+
+ orderHint
+ 2
+
+ With Spaces WatchKit 1 App.xcscheme
+
+ orderHint
+ 1
+
+ With Spaces.xcscheme
+
+ orderHint
+ 0
+
+
+ SuppressBuildableAutocreation
+
+ CAA5624F1B7A5A39007AECEF
+
+ primary
+
+
+ CAB109121B7A641500F53B5F
+
+ primary
+
+
+
+
+
diff --git a/fastlane/Fastfile b/fastlane/Fastfile
index efe9ca0..df97c81 100644
--- a/fastlane/Fastfile
+++ b/fastlane/Fastfile
@@ -1,2 +1,2 @@
# Fetch and use the latest Fastfile from the fastlane main repository
-import_from_git(url: "https://github.com/krausefx/fastlane/")
+import_from_git(url: "https://github.com/fastlane/fastlane/")
diff --git a/fastlane/README.md b/fastlane/README.md
index f1ac2a9..3a94720 100644
--- a/fastlane/README.md
+++ b/fastlane/README.md
@@ -20,4 +20,4 @@ Does everything that's needed for a release, this includes running tests and ver
Generate this documentation by running `fastlane docs`
More information about fastlane can be found on [https://fastlane.tools](https://fastlane.tools).
-The documentation of fastlane can be found on [GitHub](https://github.com/KrauseFx/fastlane)
\ No newline at end of file
+The documentation of fastlane can be found on [GitHub](https://github.com/fastlane/fastlane)
\ No newline at end of file
diff --git a/gym.gemspec b/gym.gemspec
index 77ccd0b..310eefc 100644
--- a/gym.gemspec
+++ b/gym.gemspec
@@ -21,16 +21,17 @@ Gem::Specification.new do |spec|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ["lib"]
- spec.add_dependency 'fastlane_core', '>= 0.16.1', '< 1.0.0' # all shared code and dependencies
- spec.add_dependency 'xcpretty' # pretty xcodebuild output
+ spec.add_dependency 'fastlane_core', '>= 0.36.1', '< 1.0.0' # all shared code and dependencies
+ spec.add_dependency 'xcpretty', '>= 0.2.1' # pretty xcodebuild output
spec.add_dependency 'terminal-table' # print out build information
+ spec.add_dependency 'plist' # Generate the Xcode config plist file
spec.add_dependency 'rubyzip', '>= 1.1.7' # fix swift/ipa
# Development only
spec.add_development_dependency "bundler"
- spec.add_development_dependency "fastlane", ">= 1.25.0" # yes, we use fastlane for testing
+ spec.add_development_dependency "fastlane", ">= 1.33.0" # yes, we use fastlane for testing
spec.add_development_dependency "rake"
- spec.add_development_dependency "rubocop"
+ spec.add_development_dependency "rubocop", '~> 0.35.1'
spec.add_development_dependency "rspec", "~> 3.1.0"
spec.add_development_dependency "pry"
spec.add_development_dependency "yard", "~> 0.8.7.4"
diff --git a/lib/assets/GymfileTemplate b/lib/assets/GymfileTemplate
index b0fa0fe..561d4e6 100644
--- a/lib/assets/GymfileTemplate
+++ b/lib/assets/GymfileTemplate
@@ -1,14 +1,13 @@
-# For more information about this configuation visit
+# For more information about this configuration visit
# https://github.com/fastlane/gym#gymfile
# In general, you can use the options available
# gym --help
-# Remove the # in front of the line to enable this
-# particular option
+# Remove the # in front of the line to enable the option
# scheme "Example"
-# sdk "9.0"
+# sdk "iphoneos9.0"
output_directory "./"
diff --git a/lib/assets/wrap_xcodebuild/xcbuild-safe.sh b/lib/assets/wrap_xcodebuild/xcbuild-safe.sh
new file mode 100755
index 0000000..8fc1a6c
--- /dev/null
+++ b/lib/assets/wrap_xcodebuild/xcbuild-safe.sh
@@ -0,0 +1,49 @@
+#!/bin/bash --login
+
+# Originally from, http://stackoverflow.com/questions/33041109
+# Modified to work in RVM and non RVM environments
+#
+# Xcode 7 (incl. 7.0.1) seems to have a dependency on the system ruby.
+# xcodebuild is screwed up by using rvm to map to another non-system
+# ruby†. This script is a fix that allows you call xcodebuild in a
+# "safe" rvm environment, but will not (AFAIK) affect the "external"
+# rvm setting.
+#
+# The script is a drop in replacement for your xcodebuild call.
+#
+# xcodebuild arg1 ... argn
+#
+# would become
+#
+# path/to/xcbuild-safe.sh arg1 ... argn
+#
+# -----
+# †Because, you know, that *never* happens when you are building
+# Xcode projects, say with abstruse tools like Rake or CocoaPods.
+
+which rvm > /dev/null
+
+if [[ $? -eq 0 ]]; then
+ echo "RVM detected, forcing to use system ruby"
+ # This allows you to use rvm in a script. Otherwise you get a BS
+ # error along the lines of "cannot use rvm as function". Jeez.
+ [[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm"
+
+ # Cause rvm to use system ruby. AFAIK, this is effective only for
+ # the scope of this script.
+ rvm use system
+
+ # rvm doesn't unset itself properly without doing this
+ unset RUBYLIB
+ unset RUBYOPT
+ unset BUNDLE_BIN_PATH
+ unset _ORIGINAL_GEM_PATH
+ unset BUNDLE_GEMFILE
+fi
+
+# to help troubleshooting
+# env | sort > /tmp/env.wrapper
+# rvm info >> /tmp/env.wrapper
+
+set -x # echoes commands
+xcodebuild "$@" # calls xcodebuild with all the arguments passed to this
\ No newline at end of file
diff --git a/lib/gym.rb b/lib/gym.rb
index 7c1167a..279780a 100644
--- a/lib/gym.rb
+++ b/lib/gym.rb
@@ -1,18 +1,13 @@
require 'json'
require 'gym/version'
require 'gym/manager'
-require 'gym/project'
-require 'gym/build_command_generator'
-require 'gym/package_command_generator'
+require 'gym/generators/build_command_generator'
+require 'gym/generators/package_command_generator'
require 'gym/runner'
require 'gym/error_handler'
require 'gym/options'
require 'gym/detect_values'
-
-# Import all the fixes
-require 'gym/xcodebuild_fixes/swift_fix'
-require 'gym/xcodebuild_fixes/watchkit_fix'
-require 'gym/xcodebuild_fixes/package_application_fix'
+require 'gym/xcode'
require 'fastlane_core'
require 'terminal-table'
@@ -23,19 +18,29 @@ class << self
attr_accessor :project
+ attr_accessor :cache
+
def config=(value)
@config = value
DetectValues.set_additional_default_values
+ @cache = {}
end
def gymfile_name
"Gymfile"
end
- def xcode_path
- @path ||= `xcode-select --print-path`.strip
+ def init_libs
+ # Import all the fixes
+ require 'gym/xcodebuild_fixes/swift_fix'
+ require 'gym/xcodebuild_fixes/watchkit_fix'
+ require 'gym/xcodebuild_fixes/watchkit2_fix'
+ require 'gym/xcodebuild_fixes/package_application_fix'
end
end
Helper = FastlaneCore::Helper # you gotta love Ruby: Helper.* should use the Helper class contained in FastlaneCore
+ UI = FastlaneCore::UI
+
+ Gym.init_libs
end
diff --git a/lib/gym/build_command_generator.rb b/lib/gym/build_command_generator.rb
deleted file mode 100644
index e1ef677..0000000
--- a/lib/gym/build_command_generator.rb
+++ /dev/null
@@ -1,88 +0,0 @@
-module Gym
- # Responsible for building the fully working xcodebuild command
- class BuildCommandGenerator
- class << self
- def generate
- parts = prefix
- parts << "xcodebuild"
- parts += options
- parts += actions
- parts += suffix
- parts += pipe
-
- parts
- end
-
- def prefix
- ["set -o pipefail &&"]
- end
-
- # Path to the project or workspace as parameter
- # This will also include the scheme (if given)
- # @return [Array] The array with all the components to join
- def project_path_array
- config = Gym.config
- proj = []
- proj << "-workspace '#{config[:workspace]}'" if config[:workspace]
- proj << "-scheme '#{config[:scheme]}'" if config[:scheme]
- proj << "-project '#{config[:project]}'" if config[:project]
-
- return proj if proj.count > 0
- raise "No project/workspace found"
- end
-
- def options
- config = Gym.config
-
- options = []
- options += project_path_array
- options << "-configuration '#{config[:configuration]}'" if config[:configuration]
- options << "-sdk '#{config[:sdk]}'" if config[:sdk]
- options << "-destination '#{config[:destination]}'" if config[:destination]
- options << "-xcconfig '#{config[:xcconfig]}'" if config[:xcconfig]
- options << "-archivePath '#{archive_path}'"
- options << config[:xcargs] if config[:xcargs]
-
- options
- end
-
- def actions
- config = Gym.config
-
- actions = []
- actions << :clean if config[:clean]
- actions << :archive
-
- actions
- end
-
- def suffix
- []
- end
-
- def pipe
- ["| xcpretty"]
- end
-
- # The path to set the Derived Data to
- def build_path
- unless @build_path
- day = Time.now.strftime("%F") # e.g. 2015-08-07
-
- @build_path = File.expand_path("~/Library/Developer/Xcode/Archives/#{day}/")
- FileUtils.mkdir_p @build_path
- end
- @build_path
- end
-
- def archive_path
- @archive_path ||= Gym.config[:archive_path]
- unless @archive_path
- file_name = [Gym.config[:output_name], Time.now.strftime("%F %H.%M.%S")] # e.g. 2015-08-07 14.49.12
- @archive_path = File.join(build_path, file_name.join(" ") + ".xcarchive")
- end
- return @archive_path
- end
- end
- end
-end
diff --git a/lib/gym/commands_generator.rb b/lib/gym/commands_generator.rb
index 425f49a..a92d5b7 100644
--- a/lib/gym/commands_generator.rb
+++ b/lib/gym/commands_generator.rb
@@ -6,6 +6,7 @@
module Gym
class CommandsGenerator
include Commander::Methods
+ UI = FastlaneCore::UI
FastlaneCore::CommanderGenerator.new.generate(Gym::Options.available_options)
@@ -46,10 +47,12 @@ def run
c.syntax = "gym init"
c.description = "Creates a new Gymfile for you"
c.action do |_args, options|
- raise "Gymfile already exists" if File.exist?(Gym.gymfile_name)
+ containing = (File.directory?("fastlane") ? 'fastlane' : '.')
+ path = File.join(containing, Gym.gymfile_name)
+ UI.user_error! "Gymfile already exists" if File.exist?(path)
template = File.read("#{Helper.gem_path('gym')}/lib/assets/GymfileTemplate")
- File.write(Gym.gymfile_name, template)
- Helper.log.info "Successfully created '#{Gym.gymfile_name}'. Open the file using a code editor.".green
+ File.write(path, template)
+ UI.success "Successfully created '#{path}'. Open the file using a code editor."
end
end
diff --git a/lib/gym/detect_values.rb b/lib/gym/detect_values.rb
index 6fe2671..71c5a96 100644
--- a/lib/gym/detect_values.rb
+++ b/lib/gym/detect_values.rb
@@ -6,24 +6,21 @@ class DetectValues
def self.set_additional_default_values
config = Gym.config
- detect_projects
+ # First, try loading the Gymfile from the current directory
+ config.load_configuration_file(Gym.gymfile_name)
- if config[:workspace].to_s.length > 0 and config[:project].to_s.length > 0
- raise "You can only pass either a workspace or a project path, not both".red
- end
-
- if config[:workspace].to_s.length == 0 and config[:project].to_s.length == 0
- raise "No project/workspace found in the current directory.".red
- end
-
- Gym.project = Project.new(config)
+ # Detect the project
+ FastlaneCore::Project.detect_projects(config)
+ Gym.project = FastlaneCore::Project.new(config)
detect_provisioning_profile
- # Go into the project's folder
+ # Go into the project's folder, as there might be a Gymfile there
Dir.chdir(File.expand_path("..", Gym.project.path)) do
config.load_configuration_file(Gym.gymfile_name)
end
+ config[:use_legacy_build_api] = true if Xcode.pre_7?
+
detect_scheme
detect_platform # we can only do that *after* we have the scheme
detect_configuration
@@ -42,7 +39,7 @@ def self.detect_provisioning_profile
if profiles.count == 1
profile = File.expand_path(profiles.last)
elsif profiles.count > 1
- puts "Found more than one provisioning profile in the project directory:"
+ UI.message "Found more than one provisioning profile in the project directory:"
profile = choose(*(profiles))
end
@@ -55,84 +52,20 @@ def self.detect_provisioning_profile
end
end
- def self.detect_projects
- if Gym.config[:workspace].to_s.length == 0
- workspace = Dir["./*.xcworkspace"]
- if workspace.count > 1
- puts "Select Workspace: "
- Gym.config[:workspace] = choose(*(workspace))
- else
- Gym.config[:workspace] = workspace.first # this will result in nil if no files were found
- end
- end
-
- if Gym.config[:workspace].to_s.length == 0 and Gym.config[:project].to_s.length == 0
- project = Dir["./*.xcodeproj"]
- if project.count > 1
- puts "Select Project: "
- Gym.config[:project] = choose(*(project))
- else
- Gym.config[:project] = project.first # this will result in nil if no files were found
- end
- end
- end
-
- def self.choose_project
- loop do
- path = ask("Couldn't automatically detect the project file, please provide a path: ".yellow).strip
- if File.directory? path
- if path.end_with? ".xcworkspace"
- config[:workspace] = path
- break
- elsif path.end_with? ".xcodeproj"
- config[:project] = path
- break
- else
- Helper.log.error "Path must end with either .xcworkspace or .xcodeproj"
- end
- else
- Helper.log.error "Couldn't find project at path '#{File.expand_path(path)}'".red
- end
- end
- end
-
def self.detect_scheme
- config = Gym.config
- proj_schemes = Gym.project.schemes
-
- if config[:scheme].to_s.length > 0
- # Verify the scheme is available
- unless proj_schemes.include?(config[:scheme].to_s)
- Helper.log.error "Couldn't find specified scheme '#{config[:scheme]}'.".red
- config[:scheme] = nil
- end
- end
-
- return if config[:scheme].to_s.length > 0
-
- if proj_schemes.count == 1
- config[:scheme] = proj_schemes.last
- elsif proj_schemes.count > 1
- if Helper.is_ci?
- Helper.log.error "Multiple schemes found but you haven't specified one.".red
- Helper.log.error "Since this is a CI, please pass one using the `scheme` option".red
- raise "Multiple schemes found".red
- else
- puts "Select Scheme: "
- config[:scheme] = choose(*(proj_schemes))
- end
- else
- Helper.log.error "Couldn't find any schemes in this project, make sure that the scheme is shared if you are using a workspace".red
- Helper.log.error "Open Xcode, click on `Manage Schemes` and check the `Shared` box for the schemes you want to use".red
-
- raise "No Schemes found".red
- end
+ Gym.project.select_scheme
end
# Is it an iOS device or a Mac?
def self.detect_platform
return if Gym.config[:destination]
- platform = Gym.project.build_settings(key: "PLATFORM_DISPLAY_NAME") || "iOS" # either `iOS` or `OS X`
+ platform = if Gym.project.mac?
+ "OS X"
+ elsif Gym.project.tvos?
+ "tvOS"
+ else
+ "iOS"
+ end
Gym.config[:destination] = "generic/platform=#{platform}"
end
@@ -146,29 +79,10 @@ def self.detect_configuration
if config[:configuration]
# Verify the configuration is available
unless configurations.include?(config[:configuration])
- Helper.log.error "Couldn't find specified configuration '#{config[:configuration]}'.".red
+ UI.error "Couldn't find specified configuration '#{config[:configuration]}'."
config[:configuration] = nil
end
end
-
- # Usually we want `Release`
- # We prefer `Release` to export the DSYM file as well
- config[:configuration] ||= "Release" if configurations.include?("Release")
-
- return if config[:configuration].to_s.length > 0
-
- if configurations.count == 1
- config[:configuration] = configurations.last
- else
- if Helper.is_ci?
- Helper.log.error "Multiple configurations found but you haven't specified one.".red
- Helper.log.error "Since this is a CI, please pass one using the `configuration` option".red
- raise "Multiple configurations found".red
- else
- puts "Select Configuration: "
- config[:configuration] = choose(*(configurations))
- end
- end
end
end
end
diff --git a/lib/gym/error_handler.rb b/lib/gym/error_handler.rb
index 767d977..83d31a6 100644
--- a/lib/gym/error_handler.rb
+++ b/lib/gym/error_handler.rb
@@ -2,7 +2,7 @@ module Gym
# This classes methods are called when something goes wrong in the building process
class ErrorHandler
class << self
- # @param [Array] The output of the errored build (line by line)
+ # @param [String] The output of the errored build
# This method should raise an exception in any case, as the return code indicated a failed build
def handle_build_error(output)
# The order of the handling below is import
@@ -10,31 +10,31 @@ def handle_build_error(output)
when /Your build settings specify a provisioning profile with the UUID/
print "Invalid code signing settings"
print "Your project defines a provisioning profile which doesn't exist on your local machine"
- print "You can use sigh (https://github.com/KrauseFx/sigh) to download and install the provisioning profile"
- print "Follow this guide: https://github.com/KrauseFx/fastlane/blob/master/docs/CodeSigning.md"
+ print "You can use sigh (https://github.com/fastlane/sigh) to download and install the provisioning profile"
+ print "Follow this guide: https://github.com/fastlane/fastlane/blob/master/docs/CodeSigning.md"
when /Provisioning profile does not match bundle identifier/
print "Invalid code signing settings"
print "Your project defines a provisioning profile that doesn't match the bundle identifier of your app"
print "Make sure you use the correct provisioning profile for this app"
print "Take a look at the ouptput above for more information"
- print "You can follow this guide: https://github.com/KrauseFx/fastlane/blob/master/docs/CodeSigning.md"
+ print "You can follow this guide: https://github.com/fastlane/fastlane/blob/master/docs/CodeSigning.md"
when /provisioning profiles matching the bundle identifier .(.*)./ # the . around the (.*) are for the strange "
print "You don't have the provisioning profile for '#{$1}' installed on the local machine"
print "Make sure you have the profile on this computer and it's properly installed"
- print "You can use sigh (https://github.com/KrauseFx/sigh) to download and install the provisioning profile"
- print "Follow this guide: https://github.com/KrauseFx/fastlane/blob/master/docs/CodeSigning.md"
+ print "You can use sigh (https://github.com/fastlane/sigh) to download and install the provisioning profile"
+ print "Follow this guide: https://github.com/fastlane/fastlane/blob/master/docs/CodeSigning.md"
when /matching the bundle identifier .(.*). were found/ # the . around the (.*) are for the strange "
print "You don't have a provisioning profile for the bundle identifier '#{$1}' installed on the local machine"
print "Make sure you have the profile on this computer and it's properly installed"
- print "You can use sigh (https://github.com/KrauseFx/sigh) to download and install the provisioning profile"
- print "Follow this guide: https://github.com/KrauseFx/fastlane/blob/master/docs/CodeSigning.md"
+ print "You can use sigh (https://github.com/fastlane/sigh) to download and install the provisioning profile"
+ print "Follow this guide: https://github.com/fastlane/fastlane/blob/master/docs/CodeSigning.md"
# Insert more code signing specific errors here
when /code signing is required/
print "Your project settings define invalid code signing settings"
print "To generate an ipa file you need to enable code signing for your project"
print "Additionally make sure you have a code signing identity set"
- print "Follow this guide: https://github.com/KrauseFx/fastlane/blob/master/docs/CodeSigning.md"
+ print "Follow this guide: https://github.com/fastlane/fastlane/blob/master/docs/CodeSigning.md"
when /US\-ASCII/
print "Your shell environment is not correctly configured"
print "Instead of UTF-8 your shell uses US-ASCII"
@@ -47,9 +47,9 @@ def handle_build_error(output)
print "You'll have to restart your shell session after updating the file."
print "If you are using zshell or another shell, make sure to edit the correct bash file."
print "For more information visit this stackoverflow answer:"
- print "http://stackoverflow.com/a/17031697/445598"
+ print "https://stackoverflow.com/a/17031697/445598"
end
- raise "Error building the application - see the log above".red
+ UI.user_error!("Error building the application - see the log above")
end
# @param [Array] The output of the errored build (line by line)
@@ -69,8 +69,6 @@ def handle_package_error(output)
print "Could not find provisioning profile with the name '#{$1}'"
print "Make sure the name of the provisioning profile is correct"
print "and it matches a locally installed profile"
- print "You can pass the name of the provisioning profile using the"
- print "`--provisioning_profile_path` option"
when /mismatch between specified provisioning profile and signing identity/
print "Mismatch between provisioning profile and code signing identity"
print "This means, the specified provisioning profile was not created using"
@@ -80,22 +78,45 @@ def handle_package_error(output)
when /Codesign check fails/
print "A general code signing error occurred. Make sure you passed a valid"
print "provisioning profile and code signing identity."
+ when /expected one of \{\}/
+ print "It seems like you ran into this radar"
+ print "https://openradar.appspot.com/radar?id=4952000420642816"
+ print "You can temporary use the :use_legacy_build_api option to get the build to work again"
+ when /IDEDistributionErrorDomain error 1/
+ when /Error Domain=IDEDistributionErrorDomain Code=/
+ standard_output = read_standard_output output
+ print standard_output if standard_output
+ print "There was an error exporting your application"
+ print "Unfortunately the new Xcode export API is unstable and causes problems on some project"
+ print "You can temporary use the :use_legacy_build_api option to get the build to work again"
end
- raise "Error packaging up the application".red
+ UI.user_error!("Error packaging up the application")
end
def handle_empty_archive
print "The generated archive is invalid, this can have various reasons:"
print "Usually it's caused by the `Skip Install` option in Xcode, set it to `NO`"
print "For more information visit https://developer.apple.com/library/ios/technotes/tn2215/_index.html"
- raise "Archive invalid"
+ print "Also, make sure to have a valid code signing identity and provisioning profile installed"
+ print "Follow this guide to setup code signing https://github.com/fastlane/fastlane/blob/master/docs/CodeSigning.md"
+ UI.user_error!("Archive invalid")
+ end
+
+ def find_standard_output_path(output)
+ m = /Created bundle at path '(.*)'/.match(output)
+ return File.join(m[1], 'IDEDistribution.standard.log') unless m.nil?
end
private
+ def read_standard_output(output)
+ path = find_standard_output_path output
+ return File.read(path) if File.exist?(path)
+ end
+
# Just to make things easier
def print(text)
- Helper.log.error text.red
+ UI.error text
end
end
end
diff --git a/lib/gym/generators/README.md b/lib/gym/generators/README.md
new file mode 100644
index 0000000..4d7905e
--- /dev/null
+++ b/lib/gym/generators/README.md
@@ -0,0 +1 @@
+All the classes that generate commands we want to execute
diff --git a/lib/gym/generators/build_command_generator.rb b/lib/gym/generators/build_command_generator.rb
new file mode 100644
index 0000000..54c43c8
--- /dev/null
+++ b/lib/gym/generators/build_command_generator.rb
@@ -0,0 +1,115 @@
+require 'shellwords'
+
+module Gym
+ # Responsible for building the fully working xcodebuild command
+ class BuildCommandGenerator
+ class << self
+ def generate
+ parts = prefix
+ parts << "xcodebuild"
+ parts += options
+ parts += actions
+ parts += suffix
+ parts += pipe
+
+ parts
+ end
+
+ def prefix
+ ["set -o pipefail &&"]
+ end
+
+ # Path to the project or workspace as parameter
+ # This will also include the scheme (if given)
+ # @return [Array] The array with all the components to join
+ def project_path_array
+ proj = Gym.project.xcodebuild_parameters
+ return proj if proj.count > 0
+ UI.user_error!("No project/workspace found")
+ end
+
+ def options
+ config = Gym.config
+
+ options = []
+ options += project_path_array
+ options << "-configuration '#{config[:configuration]}'" if config[:configuration]
+ options << "-sdk '#{config[:sdk]}'" if config[:sdk]
+ options << "-destination '#{config[:destination]}'" if config[:destination]
+ options << "-xcconfig '#{config[:xcconfig]}'" if config[:xcconfig]
+ options << "-archivePath '#{archive_path}'"
+ options << "-derivedDataPath '#{config[:derived_data_path]}'" if config[:derived_data_path]
+ options << "-resultBundlePath '#{result_bundle_path}'" if config[:result_bundle]
+ options << config[:xcargs] if config[:xcargs]
+
+ options
+ end
+
+ def actions
+ config = Gym.config
+
+ actions = []
+ actions << :clean if config[:clean]
+ actions << :archive
+
+ actions
+ end
+
+ def suffix
+ suffix = []
+ suffix << "CODE_SIGN_IDENTITY='#{Gym.config[:codesigning_identity]}'" if Gym.config[:codesigning_identity]
+ suffix
+ end
+
+ def pipe
+ pipe = []
+ pipe << "| tee #{xcodebuild_log_path.shellescape} | xcpretty"
+ pipe << "--no-color" if Helper.colors_disabled?
+ pipe << "> /dev/null" if Gym.config[:suppress_xcode_output]
+
+ pipe
+ end
+
+ def xcodebuild_log_path
+ file_name = "#{Gym.project.app_name}-#{Gym.config[:scheme]}.log"
+ containing = File.expand_path(Gym.config[:buildlog_path])
+ FileUtils.mkdir_p(containing)
+
+ return File.join(containing, file_name)
+ end
+
+ # The path where archive will be created
+ def build_path
+ unless Gym.cache[:build_path]
+ Gym.cache[:build_path] = Gym.config[:build_path]
+ unless Gym.cache[:build_path]
+ day = Time.now.strftime("%F") # e.g. 2015-08-07
+ Gym.cache[:build_path] = File.expand_path("~/Library/Developer/Xcode/Archives/#{day}/")
+ end
+ FileUtils.mkdir_p Gym.cache[:build_path]
+ end
+ Gym.cache[:build_path]
+ end
+
+ def archive_path
+ Gym.cache[:archive_path] ||= Gym.config[:archive_path]
+ unless Gym.cache[:archive_path]
+ file_name = [Gym.config[:output_name], Time.now.strftime("%F %H.%M.%S")] # e.g. 2015-08-07 14.49.12
+ Gym.cache[:archive_path] = File.join(build_path, file_name.join(" ") + ".xcarchive")
+ end
+
+ if File.extname(Gym.cache[:archive_path]) != ".xcarchive"
+ Gym.cache[:archive_path] += ".xcarchive"
+ end
+ return Gym.cache[:archive_path]
+ end
+
+ def result_bundle_path
+ unless Gym.cache[:result_bundle_path]
+ Gym.cache[:result_bundle_path] = File.join(Gym.config[:output_directory], Gym.config[:output_name]) + ".result"
+ end
+ return Gym.cache[:result_bundle_path]
+ end
+ end
+ end
+end
diff --git a/lib/gym/generators/package_command_generator.rb b/lib/gym/generators/package_command_generator.rb
new file mode 100644
index 0000000..20a636d
--- /dev/null
+++ b/lib/gym/generators/package_command_generator.rb
@@ -0,0 +1,58 @@
+# encoding: utf-8
+# from http://stackoverflow.com/a/9857493/445598
+# because of
+# `incompatible encoding regexp match (UTF-8 regexp with ASCII-8BIT string) (Encoding::CompatibilityError)`
+
+require 'shellwords'
+
+# The concrete implementations
+require 'gym/generators/package_command_generator_legacy'
+require 'gym/generators/package_command_generator_xcode7'
+
+module Gym
+ class PackageCommandGenerator
+ class << self
+ def generate
+ generator.generate
+ end
+
+ def appfile_path
+ generator.appfile_path
+ end
+
+ # The path in which the ipa file will be available after executing the command
+ def ipa_path
+ generator.ipa_path
+ end
+
+ def dsym_path
+ generator.dsym_path
+ end
+
+ def manifest_path
+ generator.manifest_path
+ end
+
+ def app_thinning_path
+ generator.app_thinning_path
+ end
+
+ def app_thinning_size_report_path
+ generator.app_thinning_size_report_path
+ end
+
+ def apps_path
+ generator.apps_path
+ end
+
+ # The generator we need to use for the currently used Xcode version
+ def generator
+ if Gym.config[:use_legacy_build_api]
+ PackageCommandGeneratorLegacy
+ else
+ PackageCommandGeneratorXcode7
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gym/package_command_generator.rb b/lib/gym/generators/package_command_generator_legacy.rb
similarity index 78%
rename from lib/gym/package_command_generator.rb
rename to lib/gym/generators/package_command_generator_legacy.rb
index ddcb9f4..624e3c1 100644
--- a/lib/gym/package_command_generator.rb
+++ b/lib/gym/generators/package_command_generator_legacy.rb
@@ -1,11 +1,14 @@
-require 'shellwords'
+# encoding: utf-8
+# from http://stackoverflow.com/a/9857493/445598
+# because of
+# `incompatible encoding regexp match (UTF-8 regexp with ASCII-8BIT string) (Encoding::CompatibilityError)`
module Gym
- # Responsible for building the fully working xcodebuild command
+ # Responsible for building the fully working xcodebuild command on Xcode < 7
#
# Because of a known bug in PackageApplication Perl script used by Xcode the packaging process is performed with
# a patched version of the script.
- class PackageCommandGenerator
+ class PackageCommandGeneratorLegacy
class << self
def generate
parts = ["/usr/bin/xcrun #{XcodebuildFixes.patch_package_application} -v"]
@@ -53,6 +56,22 @@ def ipa_path
def dsym_path
Dir[BuildCommandGenerator.archive_path + "/**/*.app.dSYM"].last
end
+
+ def manifest_path
+ ""
+ end
+
+ def app_thinning_path
+ ""
+ end
+
+ def app_thinning_size_report_path
+ ""
+ end
+
+ def apps_path
+ ""
+ end
end
end
end
diff --git a/lib/gym/generators/package_command_generator_xcode7.rb b/lib/gym/generators/package_command_generator_xcode7.rb
new file mode 100644
index 0000000..5c27725
--- /dev/null
+++ b/lib/gym/generators/package_command_generator_xcode7.rb
@@ -0,0 +1,170 @@
+# encoding: utf-8
+# from http://stackoverflow.com/a/9857493/445598
+# because of
+# `incompatible encoding regexp match (UTF-8 regexp with ASCII-8BIT string) (Encoding::CompatibilityError)`
+
+require 'tempfile'
+
+module Gym
+ # Responsible for building the fully working xcodebuild command
+ class PackageCommandGeneratorXcode7
+ class << self
+ def generate
+ print_legacy_information unless Helper.fastlane_enabled?
+
+ parts = ["/usr/bin/xcrun #{XcodebuildFixes.wrap_xcodebuild} -exportArchive"]
+ parts += options
+ parts += pipe
+
+ File.write(config_path, config_content) # overwrite everytime. Could be optimized
+
+ parts
+ end
+
+ def options
+ options = []
+
+ options << "-exportOptionsPlist '#{config_path}'"
+ options << "-archivePath '#{BuildCommandGenerator.archive_path}'"
+ options << "-exportPath '#{temporary_output_path}'"
+
+ options
+ end
+
+ def pipe
+ [""]
+ end
+
+ # We export the ipa into this directory, as we can't specify the ipa file directly
+ def temporary_output_path
+ Gym.cache[:temporary_output_path] ||= "#{Tempfile.new('gym').path}.gym_output"
+ end
+
+ def ipa_path
+ unless Gym.cache[:ipa_path]
+ path = Dir[File.join(temporary_output_path, "*.ipa")].last
+ if path
+ # Try to find IPA file in the output directory, used when app thinning was not set
+ Gym.cache[:ipa_path] = File.join(temporary_output_path, "#{Gym.config[:output_name]}.ipa")
+ FileUtils.mv(path, Gym.cache[:ipa_path]) if File.expand_path(path).downcase != File.expand_path(Gym.cache[:ipa_path]).downcase
+ elsif Dir.exist?(apps_path)
+ # Try to find "generic" IPA file inside "Apps" folder, used when app thinning was set
+ files = Dir[File.join(apps_path, "*.ipa")]
+ # Generic IPA file doesn't have suffix so its name is the shortest
+ path = files.min_by(&:length)
+ Gym.cache[:ipa_path] = File.join(temporary_output_path, "#{Gym.config[:output_name]}.ipa")
+ FileUtils.cp(path, Gym.cache[:ipa_path]) if File.expand_path(path).downcase != File.expand_path(Gym.cache[:ipa_path]).downcase
+ else
+ ErrorHandler.handle_empty_archive unless path
+ end
+ end
+ Gym.cache[:ipa_path]
+ end
+
+ # The path the the dsym file for this app. Might be nil
+ def dsym_path
+ Dir[BuildCommandGenerator.archive_path + "/**/*.app.dSYM"].last
+ end
+
+ # The path the config file we use to sign our app
+ def config_path
+ Gym.cache[:config_path] ||= "#{Tempfile.new('gym').path}_config.plist"
+ return Gym.cache[:config_path]
+ end
+
+ # The path to the manifest plist file
+ def manifest_path
+ Gym.cache[:manifest_path] ||= File.join(temporary_output_path, "manifest.plist")
+ end
+
+ # The path to the app-thinning plist file
+ def app_thinning_path
+ Gym.cache[:app_thinning] ||= File.join(temporary_output_path, "app-thinning.plist")
+ end
+
+ # The path to the App Thinning Size Report file
+ def app_thinning_size_report_path
+ Gym.cache[:app_thinning_size_report] ||= File.join(temporary_output_path, "App Thinning Size Report.txt")
+ end
+
+ # The path to the Apps folder
+ def apps_path
+ Gym.cache[:apps_path] ||= File.join(temporary_output_path, "Apps")
+ end
+
+ private
+
+ def normalize_export_options(hash)
+ # Normalize some values
+ hash[:onDemandResourcesAssetPacksBaseURL] = URI.escape(hash[:onDemandResourcesAssetPacksBaseURL]) if hash[:onDemandResourcesAssetPacksBaseURL]
+ if hash[:manifest]
+ hash[:manifest][:appURL] = URI.escape(hash[:manifest][:appURL]) if hash[:manifest][:appURL]
+ hash[:manifest][:displayImageURL] = URI.escape(hash[:manifest][:displayImageURL]) if hash[:manifest][:displayImageURL]
+ hash[:manifest][:fullSizeImageURL] = URI.escape(hash[:manifest][:fullSizeImageURL]) if hash[:manifest][:fullSizeImageURL]
+ hash[:manifest][:assetPackManifestURL] = URI.escape(hash[:manifest][:assetPackManifestURL]) if hash[:manifest][:assetPackManifestURL]
+ end
+ hash
+ end
+
+ def keys_to_symbols(hash)
+ # Convert keys to symbols
+ hash = hash.each_with_object({}) do |(k, v), memo|
+ memo[k.to_sym] = v
+ memo
+ end
+ hash
+ end
+
+ def read_export_options
+ # Reads export options
+ if Gym.config[:export_options]
+ if Gym.config[:export_options].kind_of?(Hash)
+ # Reads options from hash
+ hash = normalize_export_options(Gym.config[:export_options])
+ else
+ # Reads optoins from file
+ hash = Plist.parse_xml(Gym.config[:export_options])
+ # Convert keys to symbols
+ hash = keys_to_symbols(hash)
+ end
+
+ # Saves configuration for later use
+ Gym.config[:export_method] ||= hash[:method]
+ Gym.config[:include_symbols] = hash[:uploadSymbols] if Gym.config[:include_symbols].nil?
+ Gym.config[:include_bitcode] = hash[:uploadBitcode] if Gym.config[:include_bitcode].nil?
+ Gym.config[:export_team_id] ||= hash[:teamID]
+ else
+ hash = {}
+ # Sets default values
+ Gym.config[:export_method] ||= "app-store"
+ Gym.config[:include_symbols] = true if Gym.config[:include_symbols].nil?
+ Gym.config[:include_bitcode] = false if Gym.config[:include_bitcode].nil?
+ end
+ hash
+ end
+
+ def config_content
+ require 'plist'
+
+ hash = read_export_options
+
+ # Overrides export options if needed
+ hash[:method] = Gym.config[:export_method]
+ if Gym.config[:export_method] == 'app-store'
+ hash[:uploadSymbols] = (Gym.config[:include_symbols] ? true : false) unless Gym.config[:include_symbols].nil?
+ hash[:uploadBitcode] = (Gym.config[:include_bitcode] ? true : false) unless Gym.config[:include_bitcode].nil?
+ end
+ hash[:teamID] = Gym.config[:export_team_id] if Gym.config[:export_team_id]
+
+ hash.to_plist
+ end
+
+ def print_legacy_information
+ if Gym.config[:provisioning_profile_path]
+ UI.important "You're using Xcode 7, the `provisioning_profile_path` value will be ignored"
+ UI.important "Please follow the Code Signing Guide: https://github.com/fastlane/fastlane/blob/master/docs/CodeSigning.md"
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gym/manager.rb b/lib/gym/manager.rb
index 9f4d499..d0ed071 100644
--- a/lib/gym/manager.rb
+++ b/lib/gym/manager.rb
@@ -3,30 +3,11 @@ class Manager
def work(options)
Gym.config = options
- print_summary
+ FastlaneCore::PrintTable.print_values(config: Gym.config,
+ hide_keys: [],
+ title: "Summary for gym #{Gym::VERSION}")
return Runner.new.run
end
-
- private
-
- def print_summary
- config = Gym.config
- rows = []
- rows << ["Project", config[:project]] if config[:project]
- rows << ["Workspace", config[:workspace]] if config[:workspace]
- rows << ["Scheme", config[:scheme]] if config[:scheme]
- rows << ["Configuration", config[:configuration]] if config[:configuration]
- rows << ["Archive Path", config[:archive_path]] if config[:archive_path]
- rows << ["Platform", Gym.project.ios? ? "iOS" : "Mac"]
- rows << ["Xcode Path", Gym.xcode_path.gsub("/Contents/Developer", "")]
-
- puts ""
- puts Terminal::Table.new(
- title: "Building Application 🚀".green,
- rows: rows
- )
- puts ""
- end
end
end
diff --git a/lib/gym/options.rb b/lib/gym/options.rb
index 1d9b406..ceba663 100644
--- a/lib/gym/options.rb
+++ b/lib/gym/options.rb
@@ -18,9 +18,13 @@ def self.plain_options
description: "Path the workspace file",
verify_block: proc do |value|
v = File.expand_path(value.to_s)
- raise "Workspace file not found at path '#{v}'".red unless File.exist?(v)
- raise "Workspace file invalid".red unless File.directory?(v)
- raise "Workspace file is not a workspace, must end with .xcworkspace".red unless v.include?(".xcworkspace")
+ UI.user_error!("Workspace file not found at path '#{v}'") unless File.exist?(v)
+ UI.user_error!("Workspace file invalid") unless File.directory?(v)
+ UI.user_error!("Workspace file is not a workspace, must end with .xcworkspace") unless v.include?(".xcworkspace")
+ end,
+ conflicting_options: [:project],
+ conflict_block: proc do |value|
+ UI.user_error!("You can only pass either a 'workspace' or a '#{value.key}', not both")
end),
FastlaneCore::ConfigItem.new(key: :project,
short_option: "-p",
@@ -29,9 +33,13 @@ def self.plain_options
description: "Path the project file",
verify_block: proc do |value|
v = File.expand_path(value.to_s)
- raise "Project file not found at path '#{v}'".red unless File.exist?(v)
- raise "Project file invalid".red unless File.directory?(v)
- raise "Project file is not a project file, must end with .xcodeproj".red unless v.include?(".xcodeproj")
+ UI.user_error!("Project file not found at path '#{v}'") unless File.exist?(v)
+ UI.user_error!("Project file invalid") unless File.directory?(v)
+ UI.user_error!("Project file is not a project file, must end with .xcodeproj") unless v.include?(".xcodeproj")
+ end,
+ conflicting_options: [:workspace],
+ conflict_block: proc do |value|
+ UI.user_error!("You can only pass either a 'project' or a '#{value.key}', not both")
end),
FastlaneCore::ConfigItem.new(key: :scheme,
short_option: "-s",
@@ -49,11 +57,6 @@ def self.plain_options
env_name: "GYM_OUTPUT_DIRECTORY",
description: "The directory in which the ipa file should be stored in",
default_value: "."),
- FastlaneCore::ConfigItem.new(key: :archive_path,
- short_option: "-b",
- env_name: "GYM_ARCHIVE_PATH",
- description: "The directory in which the archive file should be stored in",
- optional: true),
FastlaneCore::ConfigItem.new(key: :output_name,
short_option: "-n",
env_name: "GYM_OUTPUT_NAME",
@@ -62,11 +65,6 @@ def self.plain_options
verify_block: proc do |value|
value.gsub!(".ipa", "")
end),
- FastlaneCore::ConfigItem.new(key: :sdk,
- short_option: "-k",
- env_name: "GYM_SDK",
- description: "The SDK that should be used for building the application",
- optional: true),
FastlaneCore::ConfigItem.new(key: :configuration,
short_option: "-q",
env_name: "GYM_CONFIGURATION",
@@ -83,11 +81,97 @@ def self.plain_options
env_name: "GYM_CODE_SIGNING_IDENTITY",
description: "The name of the code signing identity to use. It has to match the name exactly. e.g. 'iPhone Distribution: SunApps GmbH'",
optional: true),
+ FastlaneCore::ConfigItem.new(key: :include_symbols,
+ short_option: "-m",
+ env_name: "GYM_INCLUDE_SYMBOLS",
+ description: "Should the ipa file include symbols?",
+ is_string: false,
+ optional: true),
+ FastlaneCore::ConfigItem.new(key: :include_bitcode,
+ short_option: "-z",
+ env_name: "GYM_INCLUDE_BITCODE",
+ description: "Should the ipa include bitcode?",
+ is_string: false,
+ optional: true),
+ FastlaneCore::ConfigItem.new(key: :use_legacy_build_api,
+ env_name: "GYM_USE_LEGACY_BUILD_API",
+ description: "Don't use the new API because of https://openradar.appspot.com/radar?id=4952000420642816",
+ default_value: false,
+ is_string: false,
+ verify_block: proc do |value|
+ if value
+ UI.important "Using legacy build system - waiting for radar to be fixed: https://openradar.appspot.com/radar?id=4952000420642816"
+ end
+ end),
+ FastlaneCore::ConfigItem.new(key: :export_method,
+ short_option: "-j",
+ env_name: "GYM_EXPORT_METHOD",
+ description: "How should gym export the archive?",
+ is_string: true,
+ optional: true,
+ verify_block: proc do |value|
+ av = %w(app-store ad-hoc package enterprise development developer-id)
+ UI.user_error!("Unsupported export_method, must be: #{av}") unless av.include?(value)
+ end),
+ FastlaneCore::ConfigItem.new(key: :export_options,
+ env_name: "GYM_EXPORT_OPTIONS",
+ description: "Specifies path to export options plist. User xcodebuild -help to print the full set of available options",
+ is_string: false,
+ optional: true,
+ conflicting_options: [:use_legacy_build_api],
+ conflict_block: proc do |value|
+ UI.user_error!("'#{value.key}' must be false to use 'export_options'")
+ end),
+
+ # Very optional
+ FastlaneCore::ConfigItem.new(key: :build_path,
+ env_name: "GYM_BUILD_PATH",
+ description: "The directory in which the archive should be stored in",
+ optional: true),
+ FastlaneCore::ConfigItem.new(key: :archive_path,
+ short_option: "-b",
+ env_name: "GYM_ARCHIVE_PATH",
+ description: "The path to the created archive",
+ optional: true),
+ FastlaneCore::ConfigItem.new(key: :derived_data_path,
+ short_option: "-f",
+ env_name: "GYM_DERIVED_DATA_PATH",
+ description: "The directory where build products and other derived data will go",
+ optional: true),
+ FastlaneCore::ConfigItem.new(key: :result_bundle,
+ short_option: "-u",
+ env_name: "GYM_RESULT_BUNDLE",
+ is_string: false,
+ description: "Produce the result bundle describing what occurred will be placed",
+ optional: true),
+ FastlaneCore::ConfigItem.new(key: :buildlog_path,
+ short_option: "-l",
+ env_name: "GYM_BUILDLOG_PATH",
+ description: "The directory where to store the build log",
+ default_value: "~/Library/Logs/gym"),
+ FastlaneCore::ConfigItem.new(key: :sdk,
+ short_option: "-k",
+ env_name: "GYM_SDK",
+ description: "The SDK that should be used for building the application",
+ optional: true),
+ FastlaneCore::ConfigItem.new(key: :provisioning_profile_path,
+ short_option: "-e",
+ env_name: "GYM_PROVISIONING_PROFILE_PATH",
+ description: "The path to the provisioning profile (optional)",
+ optional: true,
+ verify_block: proc do |value|
+ UI.user_error!("Provisioning profile not found at path '#{File.expand_path(value)}'") unless File.exist?(value)
+ end),
FastlaneCore::ConfigItem.new(key: :destination,
short_option: "-d",
env_name: "GYM_DESTINATION",
description: "Use a custom destination for building the app",
optional: true),
+ FastlaneCore::ConfigItem.new(key: :export_team_id,
+ short_option: "-g",
+ env_name: "GYM_EXPORT_TEAM_ID",
+ description: "Optional: Sometimes you need to specify a team id when exporting the ipa file",
+ optional: true),
FastlaneCore::ConfigItem.new(key: :xcargs,
short_option: "-x",
env_name: "GYM_XCARGS",
@@ -99,17 +183,15 @@ def self.plain_options
description: "Use an extra XCCONFIG file to build your app",
optional: true,
verify_block: proc do |value|
- raise "File not found at path '#{File.expand_path(value)}'".red unless File.exist?(value)
+ UI.user_error!("File not found at path '#{File.expand_path(value)}'") unless File.exist?(value)
end),
- FastlaneCore::ConfigItem.new(key: :provisioning_profile_path,
- short_option: "-e",
- env_name: "GYM_PROVISIONING_PROFILE_PATH",
- description: "The path to the provisioning profile (optional)",
+ FastlaneCore::ConfigItem.new(key: :suppress_xcode_output,
+ short_option: "-r",
+ env_name: "SUPPRESS_OUTPUT",
+ description: "Suppress the output of xcodebuild to stdout. Output is still saved in buildlog_path",
optional: true,
- verify_block: proc do |value|
- raise "Provisioning profile not found at path '#{File.expand_path(value)}'".red unless File.exist?(value)
- end)
-
+ is_string: false
+ )
]
end
end
diff --git a/lib/gym/project.rb b/lib/gym/project.rb
deleted file mode 100644
index f98975e..0000000
--- a/lib/gym/project.rb
+++ /dev/null
@@ -1,155 +0,0 @@
-module Gym
- # Represents the Xcode project/workspace
- class Project
- # Path to the project/workspace
- attr_accessor :path
-
- attr_accessor :is_workspace
-
- def initialize(options)
- self.path = File.expand_path(options[:workspace] || options[:project])
- self.is_workspace = (options[:workspace].to_s.length > 0)
-
- if !path or !File.directory? path
- raise "Could not find project at path '#{path}'".red
- end
- end
-
- def workspace?
- self.is_workspace
- end
-
- # Get all available schemes in an array
- def schemes
- results = []
- output = raw_info.split("Schemes:").last.split(":").first
-
- if raw_info.include?("There are no schemes in workspace") or raw_info.include?("This project contains no schemes")
- return results
- end
-
- output.split("\n").each do |current|
- current = current.strip
-
- next if current.length == 0
- results << current
- end
-
- results
- end
-
- # Get all available configurations in an array
- def configurations
- results = []
- splitted = raw_info.split("Configurations:")
- return [] if splitted.count != 2 # probably a CocoaPods project
-
- output = splitted.last.split(":").first
- output.split("\n").each_with_index do |current, index|
- current = current.strip
-
- if current.length == 0
- next if index == 0
- break # as we want to break on the empty line
- end
-
- results << current
- end
-
- results
- end
-
- def app_name
- # WRAPPER_NAME: Example.app
- # WRAPPER_SUFFIX: .app
- name = build_settings(key: "WRAPPER_NAME")
-
- return name.gsub(build_settings(key: "WRAPPER_SUFFIX"), "") if name
- return "App" # default value
- end
-
- def mac?
- # Some projects have different values... we have to look for all of them
- return true if build_settings(key: "PLATFORM_NAME") == "macosx"
- return true if build_settings(key: "PLATFORM_DISPLAY_NAME") == "OS X"
- false
- end
-
- def ios?
- !mac?
- end
-
- #####################################################
- # @!group Raw Access
- #####################################################
-
- # Get the build settings for our project
- # this is used to properly get the DerivedData folder
- # @param [String] The key of which we want the value for (e.g. "PRODUCT_NAME")
- def build_settings(key: nil, optional: true)
- unless @build_settings
- # We also need to pass the workspace and scheme to this command
- command = "xcrun xcodebuild -showBuildSettings #{BuildCommandGenerator.project_path_array.join(' ')}"
- Helper.log.info command.yellow unless Gym.config[:silent]
- @build_settings = `#{command}`
- end
-
- begin
- result = @build_settings.split("\n").find { |c| c.include? key }
- return result.split(" = ").last
- rescue => ex
- return nil if optional # an optional value, we really don't care if something goes wrong
-
- Helper.log.error caller.join("\n\t")
- Helper.log.error "Could not fetch #{key} from project file: #{ex}"
- end
-
- nil
- end
-
- def raw_info
- # Examples:
-
- # Standard:
- #
- # Information about project "Example":
- # Targets:
- # Example
- # ExampleUITests
- #
- # Build Configurations:
- # Debug
- # Release
- #
- # If no build configuration is specified and -scheme is not passed then "Release" is used.
- #
- # Schemes:
- # Example
- # ExampleUITests
-
- # CococaPods
- #
- # Example.xcworkspace
- # Information about workspace "Example":
- # Schemes:
- # Example
- # HexColors
- # Pods-Example
-
- return @raw if @raw
-
- # Unfortunately since we pass the workspace we also get all the
- # schemes generated by CocoaPods
-
- options = BuildCommandGenerator.project_path_array.delete_if { |a| a.to_s.include? "scheme" }
- command = "xcrun xcodebuild -list #{options.join(' ')}"
- Helper.log.info command.yellow unless Gym.config[:silent]
-
- @raw = `#{command}`.to_s
-
- raise "Error parsing xcode file using `#{command}`".red if @raw.length == 0
-
- return @raw
- end
- end
-end
diff --git a/lib/gym/runner.rb b/lib/gym/runner.rb
index 4a4fc9e..3f90519 100644
--- a/lib/gym/runner.rb
+++ b/lib/gym/runner.rb
@@ -1,28 +1,33 @@
require 'pty'
require 'open3'
require 'fileutils'
+require 'shellwords'
module Gym
class Runner
# @return (String) The path to the resulting ipa
def run
clear_old_files
+
build_app
verify_archive
FileUtils.mkdir_p(Gym.config[:output_directory])
- if Gym.project.ios?
+ if Gym.project.ios? || Gym.project.tvos?
package_app
- Gym::XcodebuildFixes.swift_library_fix
- Gym::XcodebuildFixes.watchkit_fix
- Gym::XcodebuildFixes.clear_patched_package_application
-
+ fix_package
compress_and_move_dsym
- move_ipa
+ path = move_ipa
+ move_manifest
+ move_app_thinning
+ move_app_thinning_size_report
+ move_apps_folder
+
+ path
elsif Gym.project.mac?
compress_and_move_dsym
- move_mac_app
+ copy_mac_app
end
end
@@ -60,11 +65,24 @@ def print_command(command, title)
#####################################################
def clear_old_files
- if File.exist? PackageCommandGenerator.ipa_path
+ return unless Gym.config[:use_legacy_build_api]
+ if File.exist?(PackageCommandGenerator.ipa_path)
File.delete(PackageCommandGenerator.ipa_path)
end
end
+ def fix_package
+ return unless Gym.config[:use_legacy_build_api]
+ Gym::XcodebuildFixes.swift_library_fix
+ Gym::XcodebuildFixes.watchkit_fix
+ Gym::XcodebuildFixes.watchkit2_fix
+ end
+
+ def mark_archive_as_built_by_gym(archive_path)
+ escaped_archive_path = archive_path.shellescape
+ system("xattr -w info.fastlane.generated_by_gym 1 #{escaped_archive_path}")
+ end
+
# Builds the app and prepares the archive
def build_app
command = BuildCommandGenerator.generate
@@ -76,13 +94,15 @@ def build_app
ErrorHandler.handle_build_error(output)
end)
- Helper.log.info("Successfully stored the archive. You can find it in the Xcode Organizer.".green)
- Helper.log.info("Stored the archive in: ".green + BuildCommandGenerator.archive_path) if $verbose
+ mark_archive_as_built_by_gym(BuildCommandGenerator.archive_path)
+ UI.success "Successfully stored the archive. You can find it in the Xcode Organizer."
+ UI.verbose("Stored the archive in: " + BuildCommandGenerator.archive_path)
end
# Makes sure the archive is there and valid
def verify_archive
- if Dir[BuildCommandGenerator.archive_path + "/*"].count == 0
+ # from https://github.com/fastlane/gym/issues/115
+ if (Dir[BuildCommandGenerator.archive_path + "/*"]).count == 0
ErrorHandler.handle_empty_archive
end
end
@@ -104,42 +124,99 @@ def compress_and_move_dsym
# Compress and move the dsym file
containing_directory = File.expand_path("..", PackageCommandGenerator.dsym_path)
- file_name = File.basename(PackageCommandGenerator.dsym_path)
+
+ available_dsyms = Dir.glob("#{containing_directory}/*.dSYM")
+ UI.message "Compressing #{available_dsyms.count} dSYM(s)" unless Gym.config[:silent]
output_path = File.expand_path(File.join(Gym.config[:output_directory], Gym.config[:output_name] + ".app.dSYM.zip"))
- command = "cd '#{containing_directory}' && zip -r '#{output_path}' '#{file_name}'"
- Helper.log.info command.yellow unless Gym.config[:silent]
- command_result = `#{command}`
- Helper.log.info command_result if $verbose
+ command = "cd '#{containing_directory}' && zip -r '#{output_path}' *.dSYM"
+ Helper.backticks(command, print: !Gym.config[:silent])
puts "" # new line
- Helper.log.info "Successfully exported and compressed dSYM file.".green
+ UI.success "Successfully exported and compressed dSYM file"
end
# Moves over the binary and dsym file to the output directory
# @return (String) The path to the resulting ipa file
def move_ipa
- FileUtils.mv(PackageCommandGenerator.ipa_path, Gym.config[:output_directory], force: true)
+ FileUtils.mv(PackageCommandGenerator.ipa_path, File.expand_path(Gym.config[:output_directory]), force: true)
+ ipa_path = File.expand_path(File.join(Gym.config[:output_directory], File.basename(PackageCommandGenerator.ipa_path)))
- ipa_path = File.join(Gym.config[:output_directory], File.basename(PackageCommandGenerator.ipa_path))
-
- Helper.log.info "Successfully exported and signed the ipa file:".green
- Helper.log.info ipa_path
+ UI.success "Successfully exported and signed the ipa file:"
+ UI.message ipa_path
ipa_path
end
- # Move the .app from the archive into the output directory
- def move_mac_app
+ # Copies the .app from the archive into the output directory
+ def copy_mac_app
app_path = Dir[File.join(BuildCommandGenerator.archive_path, "Products/Applications/*.app")].last
- raise "Couldn't find application in '#{BuildCommandGenerator.archive_path}'".red unless app_path
+ UI.crash!("Couldn't find application in '#{BuildCommandGenerator.archive_path}'") unless app_path
- FileUtils.mv(app_path, Gym.config[:output_directory], force: true)
+ FileUtils.cp_r(app_path, File.expand_path(Gym.config[:output_directory]), remove_destination: true)
app_path = File.join(Gym.config[:output_directory], File.basename(app_path))
- Helper.log.info "Successfully exported the .app file:".green
- Helper.log.info app_path
+ UI.success "Successfully exported the .app file:"
+ UI.message app_path
app_path
end
+
+ # Move the manifest.plist if exists into the output directory
+ def move_manifest
+ if File.exist?(PackageCommandGenerator.manifest_path)
+ FileUtils.mv(PackageCommandGenerator.manifest_path, File.expand_path(Gym.config[:output_directory]), force: true)
+ manifest_path = File.join(File.expand_path(Gym.config[:output_directory]), File.basename(PackageCommandGenerator.manifest_path))
+
+ UI.success "Successfully exported the manifest.plist file:"
+ UI.message manifest_path
+ manifest_path
+ end
+ end
+
+ # Move the app-thinning.plist file into the output directory
+ def move_app_thinning
+ if File.exist?(PackageCommandGenerator.app_thinning_path)
+ FileUtils.mv(PackageCommandGenerator.app_thinning_path, File.expand_path(Gym.config[:output_directory]), force: true)
+ app_thinning_path = File.join(File.expand_path(Gym.config[:output_directory]), File.basename(PackageCommandGenerator.app_thinning_path))
+
+ UI.success "Successfully exported the app-thinning.plist file:"
+ UI.message app_thinning_path
+ app_thinning_path
+ end
+ end
+
+ # Move the App Thinning Size Report.txt file into the output directory
+ def move_app_thinning_size_report
+ if File.exist?(PackageCommandGenerator.app_thinning_size_report_path)
+ FileUtils.mv(PackageCommandGenerator.app_thinning_size_report_path, File.expand_path(Gym.config[:output_directory]), force: true)
+ app_thinning_size_report_path = File.join(File.expand_path(Gym.config[:output_directory]), File.basename(PackageCommandGenerator.app_thinning_size_report_path))
+
+ UI.success "Successfully exported the App Thinning Size Report.txt file:"
+ UI.message app_thinning_size_report_path
+ app_thinning_size_report_path
+ end
+ end
+
+ # Move the Apps folder to the output directory
+ def move_apps_folder
+ if Dir.exist?(PackageCommandGenerator.apps_path)
+ FileUtils.mv(PackageCommandGenerator.apps_path, File.expand_path(Gym.config[:output_directory]), force: true)
+ apps_path = File.join(File.expand_path(Gym.config[:output_directory]), File.basename(PackageCommandGenerator.apps_path))
+
+ UI.success "Successfully exported Apps folder:"
+ UI.message apps_path
+ apps_path
+ end
+ end
+
+ private
+
+ def find_archive_path
+ if Gym.config[:use_legacy_build_api]
+ BuildCommandGenerator.archive_path
+ else
+ Dir.glob(File.join(BuildCommandGenerator.build_path, "*.ipa")).last
+ end
+ end
end
end
diff --git a/lib/gym/version.rb b/lib/gym/version.rb
index 62f772a..37c81aa 100644
--- a/lib/gym/version.rb
+++ b/lib/gym/version.rb
@@ -1,4 +1,4 @@
module Gym
- VERSION = "0.4.6"
+ VERSION = "1.6.1"
DESCRIPTION = "Building your iOS apps has never been easier"
end
diff --git a/lib/gym/xcode.rb b/lib/gym/xcode.rb
new file mode 100644
index 0000000..ab7f7d4
--- /dev/null
+++ b/lib/gym/xcode.rb
@@ -0,0 +1,20 @@
+module Gym
+ class Xcode
+ class << self
+ def xcode_path
+ Helper.xcode_path
+ end
+
+ def xcode_version
+ Helper.xcode_version
+ end
+
+ # Below Xcode 7 (which offers a new nice API to sign the app)
+ def pre_7?
+ v = xcode_version
+ is_pre = v.split('.')[0].to_i < 7
+ is_pre
+ end
+ end
+ end
+end
diff --git a/lib/gym/xcodebuild_fixes/package_application_fix.rb b/lib/gym/xcodebuild_fixes/package_application_fix.rb
index 3b894ed..d9a8347 100644
--- a/lib/gym/xcodebuild_fixes/package_application_fix.rb
+++ b/lib/gym/xcodebuild_fixes/package_application_fix.rb
@@ -7,8 +7,8 @@ def patch_package_application
# Initialization
@patched_package_application_path = File.join("/tmp", "PackageApplication4Gym")
- # Remove any previous patched PackageApplication
- FileUtils.rm @patched_package_application_path if File.exist?(@patched_package_application_path)
+
+ return @patched_package_application_path if File.exist?(@patched_package_application_path)
Dir.mktmpdir do |tmpdir|
# Check current PackageApplication MD5
@@ -18,16 +18,16 @@ def patch_package_application
expected_md5 = File.read(path)
# If that location changes, search it using xcrun --sdk iphoneos -f PackageApplication
- package_application_path = "#{Gym.xcode_path}/Platforms/iPhoneOS.platform/Developer/usr/bin/PackageApplication"
+ package_application_path = "#{Xcode.xcode_path}/Platforms/iPhoneOS.platform/Developer/usr/bin/PackageApplication"
- raise "Unable to patch the `PackageApplication` script bundled in XCode. This is not supported." unless expected_md5 == Digest::MD5.file(package_application_path).hexdigest
+ UI.crash!("Unable to patch the `PackageApplication` script bundled in Xcode. This is not supported.") unless expected_md5 == Digest::MD5.file(package_application_path).hexdigest
# Duplicate PackageApplication script to PackageApplication4Gym
FileUtils.copy_file(package_application_path, @patched_package_application_path)
# Apply patches to PackageApplication4Gym from patches folder
Dir[File.join(Helper.gem_path("gym"), "lib/assets/package_application_patches/*.diff")].each do |patch|
- Helper.log.info "Applying Package Application patch: #{File.basename(patch)}" if $verbose
+ UI.verbose "Applying Package Application patch: #{File.basename(patch)}"
command = ["patch '#{@patched_package_application_path}' < '#{patch}'"]
Runner.new.print_command(command, "Applying Package Application patch: #{File.basename(patch)}") if $verbose
@@ -43,13 +43,10 @@ def patch_package_application
return @patched_package_application_path # Return path to the patched PackageApplication
end
- # Remove the patched script after it was used
- def clear_patched_package_application
- if @patched_package_application_path and File.exist?(@patched_package_application_path)
- Helper.log.debug "Removing patched PackageApplication file at path '#{@patched_package_application_path}'" if $verbose
- # force in the unlikely event that the file was deleted (e.g. by a concurrent job)
- FileUtils.rm @patched_package_application_path, force: true
- end
+ # Wrap xcodebuild to work-around ipatool dependecy to system ruby
+ def wrap_xcodebuild
+ require 'fileutils'
+ @wrapped_xcodebuild_path ||= File.join(Helper.gem_path("gym"), "lib/assets/wrap_xcodebuild/xcbuild-safe.sh")
end
end
end
diff --git a/lib/gym/xcodebuild_fixes/swift_fix.rb b/lib/gym/xcodebuild_fixes/swift_fix.rb
index eeffba4..cc60429 100644
--- a/lib/gym/xcodebuild_fixes/swift_fix.rb
+++ b/lib/gym/xcodebuild_fixes/swift_fix.rb
@@ -1,3 +1,7 @@
+# encoding: utf-8
+# from http://stackoverflow.com/a/9857493/445598
+# because of
+# `incompatible encoding regexp match (UTF-8 regexp with ASCII-8BIT string) (Encoding::CompatibilityError)`
require 'zip'
module Gym
@@ -10,7 +14,7 @@ def swift_library_fix
return if check_for_swift PackageCommandGenerator
- Helper.log.info "Packaging up the Swift Framework as the current app is a Swift app" if $verbose
+ UI.verbose "Packaging up the Swift Framework as the current app is a Swift app"
ipa_swift_frameworks = Dir["#{PackageCommandGenerator.appfile_path}/Frameworks/libswift*"]
Dir.mktmpdir do |tmpdir|
@@ -23,7 +27,7 @@ def swift_library_fix
ipa_swift_frameworks.each do |path|
framework = File.basename(path)
- FileUtils.copy_file("#{Gym.xcode_path}/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/iphoneos/#{framework}", File.join(swift_support, framework))
+ FileUtils.copy_file("#{Xcode.xcode_path}/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/iphoneos/#{framework}", File.join(swift_support, framework))
end
# Add "SwiftSupport" to the .ipa archive
@@ -45,7 +49,7 @@ def swift_library_fix
# @param the PackageCommandGenerator
# @return true if swift
def check_for_swift(pcg)
- Helper.log.info "Checking for Swift framework" if $verbose
+ UI.verbose "Checking for Swift framework"
default_swift_libs = "#{pcg.appfile_path}/Frameworks/libswift.*" # note the extra ., this is a string representation of a regexp
zip_entries_matching(pcg.ipa_path, /#{default_swift_libs}/).count > 0
end
@@ -57,7 +61,7 @@ def zip_entries_matching(zipfile, file_pattern)
files = []
Zip::File.open(zipfile) do |zip_file|
zip_file.each do |entry|
- files << entry.name if entry.name.match file_pattern
+ files << entry.name if entry.name.force_encoding("utf-8").match file_pattern
end
end
files
diff --git a/lib/gym/xcodebuild_fixes/watchkit2_fix.rb b/lib/gym/xcodebuild_fixes/watchkit2_fix.rb
new file mode 100644
index 0000000..58e5ebc
--- /dev/null
+++ b/lib/gym/xcodebuild_fixes/watchkit2_fix.rb
@@ -0,0 +1,35 @@
+module Gym
+ class XcodebuildFixes
+ class << self
+ # Determine whether this app has WatchKit2 support and manually package up the WatchKit2 framework
+ def watchkit2_fix
+ return unless watchkit2?
+
+ UI.verbose "Adding WatchKit2 support"
+
+ Dir.mktmpdir do |tmpdir|
+ # Make watchkit support directory
+ watchkit_support = File.join(tmpdir, "WatchKitSupport2")
+ Dir.mkdir(watchkit_support)
+
+ # Copy WK from Xcode into WatchKitSupport2
+ FileUtils.copy_file("#{Xcode.xcode_path}/Platforms/WatchOS.platform/Developer/SDKs/WatchOS.sdk/Library/Application Support/WatchKit/WK", File.join(watchkit_support, "WK"))
+
+ # Add "WatchKitSupport2" to the .ipa archive
+ Dir.chdir(tmpdir) do
+ abort unless system %(zip --recurse-paths "#{PackageCommandGenerator.ipa_path}" "WatchKitSupport2" > /dev/null)
+ end
+
+ UI.verbose "Successfully added WatchKit2 support"
+ end
+ end
+
+ # Does this application have a WatchKit target
+ def watchkit2?
+ Dir["#{PackageCommandGenerator.appfile_path}/**/*.plist"].any? do |plist_path|
+ `/usr/libexec/PlistBuddy -c 'Print DTSDKName' '#{plist_path}' 2>&1`.match(/^\s*watchos2\.\d+\s*$/)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gym/xcodebuild_fixes/watchkit_fix.rb b/lib/gym/xcodebuild_fixes/watchkit_fix.rb
index b9100d7..392ccc7 100644
--- a/lib/gym/xcodebuild_fixes/watchkit_fix.rb
+++ b/lib/gym/xcodebuild_fixes/watchkit_fix.rb
@@ -3,9 +3,9 @@ class XcodebuildFixes
class << self
# Determine whether this app has WatchKit support and manually package up the WatchKit framework
def watchkit_fix
- return unless watchkit?
+ return unless should_apply_watchkit1_fix?
- Helper.log.info "Adding WatchKit support" if $verbose
+ UI.verbose "Adding WatchKit support"
Dir.mktmpdir do |tmpdir|
# Make watchkit support directory
@@ -13,14 +13,14 @@ def watchkit_fix
Dir.mkdir(watchkit_support)
# Copy WK from Xcode into WatchKitSupport
- FileUtils.copy_file("#{Gym.xcode_path}/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/Library/Application Support/WatchKit/WK", File.join(watchkit_support, "WK"))
+ FileUtils.copy_file("#{Xcode.xcode_path}/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/Library/Application Support/WatchKit/WK", File.join(watchkit_support, "WK"))
# Add "WatchKitSupport" to the .ipa archive
Dir.chdir(tmpdir) do
abort unless system %(zip --recurse-paths "#{PackageCommandGenerator.ipa_path}" "WatchKitSupport" > /dev/null)
end
- Helper.log.info "Successfully added WatchKit support" if $verbose
+ UI.verbose "Successfully added WatchKit support"
end
end
@@ -30,6 +30,11 @@ def watchkit?
`/usr/libexec/PlistBuddy -c 'Print WKWatchKitApp' '#{plist_path}' 2>&1`.strip == 'true'
end
end
+
+ # Should only be applied if watchkit app is not a watchkit2 app
+ def should_apply_watchkit1_fix?
+ watchkit? && !(Gym::XcodebuildFixes.watchkit2?)
+ end
end
end
end
diff --git a/spec/build_command_generator_spec.rb b/spec/build_command_generator_spec.rb
index dec9edf..5f53f4f 100644
--- a/spec/build_command_generator_spec.rb
+++ b/spec/build_command_generator_spec.rb
@@ -2,11 +2,13 @@
describe Gym::BuildCommandGenerator do
it "raises an exception when project path wasn't found" do
expect do
- Gym.config = { project: "/notExistent" }
- end.to raise_error "Could not find project at path '/notExistent'".red
+ Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, { project: "/notExistent" })
+ end.to raise_error "Project file not found at path '/notExistent'"
end
it "supports additional parameters" do
+ log_path = File.expand_path("~/Library/Logs/gym/ExampleProductName-Example.log")
+
xcargs_hash = { DEBUG: "1", BUNDLE_NAME: "Example App" }
xcargs = xcargs_hash.map do |k, v|
"#{k.to_s.shellescape}=#{v.shellescape}"
@@ -20,13 +22,12 @@
"xcodebuild",
"-scheme 'Example'",
"-project './examples/standard/Example.xcodeproj'",
- "-configuration 'Release'",
"-sdk '9.0'",
"-destination 'generic/platform=iOS'",
"-archivePath '#{Gym::BuildCommandGenerator.archive_path}'",
"DEBUG=1 BUNDLE_NAME=Example\\ App",
:archive,
- "| xcpretty"
+ "| tee #{log_path.shellescape} | xcpretty"
])
end
@@ -37,17 +38,18 @@
end
it "uses the correct build command with the example project with no additional parameters" do
+ log_path = File.expand_path("~/Library/Logs/gym/ExampleProductName-Example.log")
+
result = Gym::BuildCommandGenerator.generate
expect(result).to eq([
"set -o pipefail &&",
"xcodebuild",
"-scheme 'Example'",
"-project './examples/standard/Example.xcodeproj'",
- "-configuration 'Release'",
"-destination 'generic/platform=iOS'",
"-archivePath '#{Gym::BuildCommandGenerator.archive_path}'",
:archive,
- "| xcpretty"
+ "| tee #{log_path.shellescape} | xcpretty"
])
end
@@ -56,17 +58,82 @@
expect(result).to eq(["-scheme 'Example'", "-project './examples/standard/Example.xcodeproj'"])
end
- it "#build_path" do
+ it "default #build_path" do
result = Gym::BuildCommandGenerator.build_path
regex = %r{Library/Developer/Xcode/Archives/\d\d\d\d\-\d\d\-\d\d}
expect(result).to match(regex)
end
+ it "user provided #build_path" do
+ options = { project: "./examples/standard/Example.xcodeproj", build_path: "/tmp/my/build_path" }
+ Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options)
+ result = Gym::BuildCommandGenerator.build_path
+ expect(result).to eq("/tmp/my/build_path")
+ end
+
it "#archive_path" do
result = Gym::BuildCommandGenerator.archive_path
regex = %r{Library/Developer/Xcode/Archives/\d\d\d\d\-\d\d\-\d\d/ExampleProductName \d\d\d\d\-\d\d\-\d\d \d\d\.\d\d\.\d\d.xcarchive}
expect(result).to match(regex)
end
+
+ it "#buildlog_path is used when provided" do
+ options = { project: "./examples/standard/Example.xcodeproj", buildlog_path: "/tmp/my/path" }
+ Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options)
+ result = Gym::BuildCommandGenerator.xcodebuild_log_path
+ expect(result).to include("/tmp/my/path")
+ end
+
+ it "#buildlog_path is not used when not provided" do
+ result = Gym::BuildCommandGenerator.xcodebuild_log_path
+ expect(result.to_s).to include("Library/Logs/gym")
+ end
+ end
+
+ describe "Derived Data Example" do
+ before do
+ options = { project: "./examples/standard/Example.xcodeproj", derived_data_path: "/tmp/my/derived_data" }
+ Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options)
+ end
+
+ it "uses the correct build command with the example project" do
+ log_path = File.expand_path("~/Library/Logs/gym/ExampleProductName-Example.log")
+
+ result = Gym::BuildCommandGenerator.generate
+ expect(result).to eq([
+ "set -o pipefail &&",
+ "xcodebuild",
+ "-scheme 'Example'",
+ "-project './examples/standard/Example.xcodeproj'",
+ "-destination 'generic/platform=iOS'",
+ "-archivePath '#{Gym::BuildCommandGenerator.archive_path}'",
+ "-derivedDataPath '/tmp/my/derived_data'",
+ :archive,
+ "| tee #{log_path.shellescape} | xcpretty"
+ ])
+ end
+ end
+
+ describe "Result Bundle Example" do
+ it "uses the correct build command with the example project" do
+ log_path = File.expand_path("~/Library/Logs/gym/ExampleProductName-Example.log")
+
+ options = { project: "./examples/standard/Example.xcodeproj", result_bundle: true }
+ Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options)
+
+ result = Gym::BuildCommandGenerator.generate
+ expect(result).to eq([
+ "set -o pipefail &&",
+ "xcodebuild",
+ "-scheme 'Example'",
+ "-project './examples/standard/Example.xcodeproj'",
+ "-destination 'generic/platform=iOS'",
+ "-archivePath '#{Gym::BuildCommandGenerator.archive_path}'",
+ "-resultBundlePath './ExampleProductName.result'",
+ :archive,
+ "| tee #{log_path.shellescape} | xcpretty"
+ ])
+ end
end
end
end
diff --git a/spec/error_handler.rb b/spec/error_handler.rb
new file mode 100644
index 0000000..7a0324e
--- /dev/null
+++ b/spec/error_handler.rb
@@ -0,0 +1,13 @@
+describe Gym do
+ describe Gym::ErrorHandler do
+ it "finds the standard output path" do
+ output = %(
+2015-12-15 13:00:57.177 xcodebuild[81544:4350404] [MT] IDEDistribution: -[IDEDistributionLogging _createLoggingBundleAtPath:]: Created bundle at path '/var/folders/88/l77k840955j0x55fkb3m6cdr0000gn/T/EventLink_2015-12-15_13-00-57.177.xcdistributionlogs'.
+2015-12-15 13:00:57.318 xcodebuild[81544:4350404] [MT] IDEDistribution: Failed to generate distribution items with error: Error Domain=DVTMachOErrorDomain Code=0 "Found an unexpected Mach-O header code: 0x72613c21" UserInfo={NSLocalizedDescription=Found an unexpected Mach-O header code: 0x72613c21, NSLocalizedRecoverySuggestion=}
+2015-12-15 13:00:57.318 xcodebuild[81544:4350404] [MT] IDEDistribution: Step failed: : Error Domain=DVTMachOErrorDomain Code=0 "Found an unexpected Mach-O header code: 0x72613c21" UserInfo={NSLocalizedDescription=Found an unexpected Mach-O header code: 0x72613c21, NSLocalizedRecoverySuggestion=}
+%)
+ expected = '/var/folders/88/l77k840955j0x55fkb3m6cdr0000gn/T/EventLink_2015-12-15_13-00-57.177.xcdistributionlogs/IDEDistribution.standard.log'
+ expect(Gym::ErrorHandler.find_standard_output_path output).to eq(expected)
+ end
+ end
+end
diff --git a/spec/fixtures/dummy.mobileprovision b/spec/fixtures/dummy.mobileprovision
new file mode 100644
index 0000000..e69de29
diff --git a/spec/gymfile_spec.rb b/spec/gymfile_spec.rb
index 9b8a0cc..55d7300 100644
--- a/spec/gymfile_spec.rb
+++ b/spec/gymfile_spec.rb
@@ -4,7 +4,7 @@
options = { project: "./examples/multipleSchemes/Example.xcodeproj" }
Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options,
options)
- @project = Gym::Project.new(Gym.config)
+ @project = FastlaneCore::Project.new(Gym.config)
end
it "#schemes returns all available schemes" do
diff --git a/spec/options_spec.rb b/spec/options_spec.rb
index df65f39..bb24da3 100644
--- a/spec/options_spec.rb
+++ b/spec/options_spec.rb
@@ -4,7 +4,7 @@
expect do
options = { project: "./examples/standard/Example.xcodeproj", workspace: "./examples/cocoapods/Example.xcworkspace" }
Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options)
- end.to raise_error "You can only pass either a workspace or a project path, not both".red
+ end.to raise_error "You can only pass either a 'project' or a 'workspace', not both"
end
it "removes the `ipa` from the output name if given" do
diff --git a/spec/package_command_generator_legacy_spec.rb b/spec/package_command_generator_legacy_spec.rb
new file mode 100644
index 0000000..0b6eab3
--- /dev/null
+++ b/spec/package_command_generator_legacy_spec.rb
@@ -0,0 +1,53 @@
+describe Gym do
+ describe Gym::PackageCommandGeneratorLegacy do
+ it "works with the example project with no additional parameters" do
+ options = { project: "./examples/standard/Example.xcodeproj" }
+ Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options)
+
+ result = Gym::PackageCommandGeneratorLegacy.generate
+ expect(result).to eq([
+ "/usr/bin/xcrun /tmp/PackageApplication4Gym -v",
+ "''",
+ "-o '#{Gym::PackageCommandGeneratorLegacy.ipa_path}'",
+ "exportFormat ipa",
+ ""
+ ])
+ end
+
+ it "works with the example project with no additional parameters and an apostrophe/single quote in the product name" do
+ options = { project: "./examples/standard/Example.xcodeproj" }
+ Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options)
+
+ allow(Gym::PackageCommandGeneratorLegacy).to receive(:appfile_path).and_return("Krause's App")
+
+ result = Gym::PackageCommandGeneratorLegacy.generate
+ expect(result).to eq([
+ "/usr/bin/xcrun /tmp/PackageApplication4Gym -v",
+ "Krause\\'s\\ App",
+ "-o '#{Gym::PackageCommandGeneratorLegacy.ipa_path}'",
+ "exportFormat ipa",
+ ""
+ ])
+ end
+
+ it "supports passing a path to a provisioning profile" do
+ # Profile Installation
+ expect(FastlaneCore::ProvisioningProfile).to receive(:install).with("./spec/fixtures/dummy.mobileprovision")
+ options = {
+ project: "./examples/standard/Example.xcodeproj",
+ provisioning_profile_path: "./spec/fixtures/dummy.mobileprovision"
+ }
+ Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options)
+
+ result = Gym::PackageCommandGeneratorLegacy.generate
+ expect(result).to eq([
+ "/usr/bin/xcrun /tmp/PackageApplication4Gym -v",
+ "''",
+ "-o '#{Gym::PackageCommandGeneratorLegacy.ipa_path}'",
+ "exportFormat ipa",
+ "--embed './spec/fixtures/dummy.mobileprovision'",
+ ""
+ ])
+ end
+ end
+end
diff --git a/spec/package_command_generator_spec.rb b/spec/package_command_generator_spec.rb
deleted file mode 100644
index 73a24ad..0000000
--- a/spec/package_command_generator_spec.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-describe Gym do
- describe Gym::PackageCommandGenerator do
- it "works with the example project with no additional parameters" do
- options = { project: "./examples/standard/Example.xcodeproj" }
- Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options)
-
- result = Gym::PackageCommandGenerator.generate
- expect(result).to eq([
- "/usr/bin/xcrun /tmp/PackageApplication4Gym -v",
- "''",
- "-o '#{Gym::PackageCommandGenerator.ipa_path}'",
- "exportFormat ipa",
- ""
- ])
- end
-
- it "works with the example project with no additional parameters and an apostrophe/single quote in the product name" do
- options = { project: "./examples/standard/Example.xcodeproj" }
- Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options)
-
- allow(Gym::PackageCommandGenerator).to receive(:appfile_path).and_return("Krause's App")
-
- result = Gym::PackageCommandGenerator.generate
- expect(result).to eq([
- "/usr/bin/xcrun /tmp/PackageApplication4Gym -v",
- "Krause\\'s\\ App",
- "-o '#{Gym::PackageCommandGenerator.ipa_path}'",
- "exportFormat ipa",
- ""
- ])
- end
- end
-end
diff --git a/spec/package_command_generator_xcode7_spec.rb b/spec/package_command_generator_xcode7_spec.rb
new file mode 100644
index 0000000..8486874
--- /dev/null
+++ b/spec/package_command_generator_xcode7_spec.rb
@@ -0,0 +1,149 @@
+describe Gym do
+ describe Gym::PackageCommandGeneratorXcode7 do
+ it "works with the example project with no additional parameters" do
+ options = { project: "./examples/standard/Example.xcodeproj" }
+ Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options)
+
+ result = Gym::PackageCommandGeneratorXcode7.generate
+ expect(result).to eq([
+ "/usr/bin/xcrun #{Gym::XcodebuildFixes.wrap_xcodebuild} -exportArchive",
+ "-exportOptionsPlist '#{Gym::PackageCommandGeneratorXcode7.config_path}'",
+ "-archivePath '#{Gym::BuildCommandGenerator.archive_path}'",
+ "-exportPath '#{Gym::PackageCommandGeneratorXcode7.temporary_output_path}'",
+ ""
+ ])
+ end
+
+ it "generates a valid plist file we need" do
+ options = { project: "./examples/standard/Example.xcodeproj" }
+ Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options)
+
+ result = Gym::PackageCommandGeneratorXcode7.generate
+ config_path = Gym::PackageCommandGeneratorXcode7.config_path
+
+ require 'plist'
+ expect(Plist.parse_xml(config_path)).to eq({
+ 'method' => "app-store",
+ 'uploadBitcode' => false,
+ 'uploadSymbols' => true
+ })
+ end
+
+ it "reads user export plist" do
+ options = { project: "./examples/standard/Example.xcodeproj", export_options: "./examples/standard/ExampleExport.plist" }
+ Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options)
+
+ result = Gym::PackageCommandGeneratorXcode7.generate
+ config_path = Gym::PackageCommandGeneratorXcode7.config_path
+
+ require 'plist'
+ expect(Plist.parse_xml(config_path)).to eq({
+ 'embedOnDemandResourcesAssetPacksInBundle' => true,
+ 'manifest' => {
+ 'appURL' => 'https://www.example.com/Example.ipa',
+ 'displayImageURL' => 'https://www.example.com/display.png',
+ 'fullSizeImageURL' => 'https://www.example.com/fullSize.png'
+ },
+ 'method' => 'ad-hoc'
+ })
+ expect(Gym.config[:export_method]).to eq("ad-hoc")
+ expect(Gym.config[:include_symbols]).to be_nil
+ expect(Gym.config[:include_bitcode]).to be_nil
+ expect(Gym.config[:export_team_id]).to be_nil
+ end
+
+ it "reads user export plist and override some parameters" do
+ options = {
+ project: "./examples/standard/Example.xcodeproj",
+ export_options: "./examples/standard/ExampleExport.plist",
+ export_method: "app-store",
+ include_symbols: false,
+ include_bitcode: true,
+ export_team_id: "1234567890"
+ }
+ Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options)
+
+ result = Gym::PackageCommandGeneratorXcode7.generate
+ config_path = Gym::PackageCommandGeneratorXcode7.config_path
+
+ require 'plist'
+ expect(Plist.parse_xml(config_path)).to eq({
+ 'embedOnDemandResourcesAssetPacksInBundle' => true,
+ 'manifest' => {
+ 'appURL' => 'https://www.example.com/Example.ipa',
+ 'displayImageURL' => 'https://www.example.com/display.png',
+ 'fullSizeImageURL' => 'https://www.example.com/fullSize.png'
+ },
+ 'method' => 'app-store',
+ 'uploadSymbols' => false,
+ 'uploadBitcode' => true,
+ 'teamID' => '1234567890'
+ })
+ end
+
+ it "reads export options from hash" do
+ options = {
+ project: "./examples/standard/Example.xcodeproj",
+ export_options: {
+ embedOnDemandResourcesAssetPacksInBundle: false,
+ manifest: {
+ appURL: "https://example.com/My App.ipa",
+ displayImageURL: "https://www.example.com/display image.png",
+ fullSizeImageURL: "https://www.example.com/fullSize image.png"
+ },
+ method: "enterprise",
+ uploadSymbols: false,
+ uploadBitcode: true,
+ teamID: "1234567890"
+ },
+ export_method: "app-store",
+ include_symbols: true,
+ include_bitcode: false,
+ export_team_id: "ASDFGHJK"
+ }
+ Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options)
+
+ result = Gym::PackageCommandGeneratorXcode7.generate
+ config_path = Gym::PackageCommandGeneratorXcode7.config_path
+
+ require 'plist'
+ expect(Plist.parse_xml(config_path)).to eq({
+ 'embedOnDemandResourcesAssetPacksInBundle' => false,
+ 'manifest' => {
+ 'appURL' => 'https://example.com/My%20App.ipa',
+ 'displayImageURL' => 'https://www.example.com/display%20image.png',
+ 'fullSizeImageURL' => 'https://www.example.com/fullSize%20image.png'
+ },
+ 'method' => 'app-store',
+ 'uploadSymbols' => true,
+ 'uploadBitcode' => false,
+ 'teamID' => 'ASDFGHJK'
+ })
+ end
+
+ it "doesn't store bitcode/symbols information for non app-store builds" do
+ options = { project: "./examples/standard/Example.xcodeproj", export_method: 'ad-hoc' }
+ Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options)
+
+ result = Gym::PackageCommandGeneratorXcode7.generate
+ config_path = Gym::PackageCommandGeneratorXcode7.config_path
+
+ require 'plist'
+ expect(Plist.parse_xml(config_path)).to eq({
+ 'method' => "ad-hoc"
+ })
+ end
+
+ it "uses a temporary folder to store the resulting ipa file" do
+ options = { project: "./examples/standard/Example.xcodeproj" }
+ Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options)
+
+ result = Gym::PackageCommandGeneratorXcode7.generate
+ expect(Gym::PackageCommandGeneratorXcode7.temporary_output_path).to match(%r{#{Dir.tmpdir}/gym.+\.gym_output})
+ expect(Gym::PackageCommandGeneratorXcode7.manifest_path).to match(%r{#{Dir.tmpdir}/gym.+\.gym_output/manifest.plist})
+ expect(Gym::PackageCommandGeneratorXcode7.app_thinning_path).to match(%r{#{Dir.tmpdir}/gym.+\.gym_output/app-thinning.plist})
+ expect(Gym::PackageCommandGeneratorXcode7.app_thinning_size_report_path).to match(%r{#{Dir.tmpdir}/gym.+\.gym_output/App Thinning Size Report.txt})
+ expect(Gym::PackageCommandGeneratorXcode7.apps_path).to match(%r{#{Dir.tmpdir}/gym.+\.gym_output/Apps})
+ end
+ end
+end
diff --git a/spec/project_spec.rb b/spec/project_spec.rb
deleted file mode 100644
index 302839e..0000000
--- a/spec/project_spec.rb
+++ /dev/null
@@ -1,84 +0,0 @@
-describe Gym do
- describe Gym::Project do
- it "raises an exception if path was not found" do
- expect do
- Gym::Project.new(project: "/tmp/notHere123")
- end.to raise_error "Could not find project at path '/tmp/notHere123'".red
- end
-
- describe "Valid Standard Project" do
- before do
- options = { project: "./examples/standard/Example.xcodeproj" }
- Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options)
-
- @project = Gym::Project.new(Gym.config)
- end
-
- it "#path" do
- expect(@project.path).to eq(File.expand_path("./examples/standard/Example.xcodeproj"))
- end
-
- it "#is_workspace" do
- expect(@project.is_workspace).to eq(false)
- end
-
- it "#schemes returns all available schemes" do
- expect(@project.schemes).to eq(["Example"])
- end
-
- it "#configurations returns all available configurations" do
- expect(@project.configurations).to eq(["Debug", "Release"])
- end
-
- it "#app_name" do
- expect(@project.app_name).to eq("ExampleProductName")
- end
-
- it "#mac?" do
- expect(@project.mac?).to eq(false)
- end
-
- it "#ios?" do
- expect(@project.ios?).to eq(true)
- end
- end
-
- describe "Valid CocoaPods Project" do
- before do
- options = { workspace: "./examples/cocoapods/Example.xcworkspace", scheme: "Example" }
- Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options)
-
- @workspace = Gym::Project.new(Gym.config)
- end
-
- it "#schemes returns all schemes" do
- expect(@workspace.schemes).to eq(["Example", "HexColors", "Pods-Example"])
- end
-
- it "#schemes returns all configurations" do
- expect(@workspace.configurations).to eq([])
- end
- end
-
- describe "Mac Project" do
- before do
- options = { project: "./examples/Mac/Mac.xcodeproj" }
- Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, options)
-
- @project = Gym::Project.new(Gym.config)
- end
-
- it "#mac?" do
- expect(@project.mac?).to eq(true)
- end
-
- it "#ios?" do
- expect(@project.ios?).to eq(false)
- end
-
- it "schemes" do
- expect(@project.schemes).to eq(["Mac"])
- end
- end
- end
-end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 40d7abd..29bfebe 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,8 +1,12 @@
require "coveralls"
Coveralls.wear! unless ENV["FASTLANE_SKIP_UPDATE_CHECK"]
+require 'webmock'
+
# This module is only used to check the environment is currently a testing env
module SpecHelper
end
require 'gym'
+
+WebMock.disable_net_connect!(allow: 'coveralls.io')
diff --git a/spec/xcodebuild_fixes/swift_fix_spec.rb b/spec/xcodebuild_fixes/swift_fix_spec.rb
index ee2467e..dbb1ea8 100644
--- a/spec/xcodebuild_fixes/swift_fix_spec.rb
+++ b/spec/xcodebuild_fixes/swift_fix_spec.rb
@@ -1,3 +1,7 @@
+require 'gym/xcodebuild_fixes/swift_fix'
+require 'gym/xcodebuild_fixes/watchkit_fix'
+require 'gym/xcodebuild_fixes/package_application_fix'
+
describe Gym do
describe Gym::XcodebuildFixes do
class FakePackageCommandGenerator