{"id":13906944,"url":"https://github.com/messeb/ios-project-env-setup","last_synced_at":"2025-04-07T14:12:56.917Z","repository":{"id":42444004,"uuid":"144632300","full_name":"messeb/ios-project-env-setup","owner":"messeb","description":"Setup your iOS project environment with a Shellscript, Makefile or Rakefile","archived":false,"fork":false,"pushed_at":"2022-10-06T01:53:43.000Z","size":435,"stargazers_count":326,"open_issues_count":5,"forks_count":17,"subscribers_count":17,"default_branch":"master","last_synced_at":"2025-03-31T12:06:45.276Z","etag":null,"topics":["devops","fastlane","ios","make","makefile","rake","rakefile","shell","shell-script"],"latest_commit_sha":null,"homepage":"https://to.messeb.com/2w3obUl","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/messeb.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-08-13T20:48:36.000Z","updated_at":"2023-12-17T03:23:40.000Z","dependencies_parsed_at":"2023-01-19T06:45:12.590Z","dependency_job_id":null,"html_url":"https://github.com/messeb/ios-project-env-setup","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/messeb%2Fios-project-env-setup","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/messeb%2Fios-project-env-setup/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/messeb%2Fios-project-env-setup/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/messeb%2Fios-project-env-setup/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/messeb","download_url":"https://codeload.github.com/messeb/ios-project-env-setup/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247666014,"owners_count":20975788,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["devops","fastlane","ios","make","makefile","rake","rakefile","shell","shell-script"],"created_at":"2024-08-06T23:01:45.187Z","updated_at":"2025-04-07T14:12:56.890Z","avatar_url":"https://github.com/messeb.png","language":"Swift","readme":"# Setup an iOS project environment\n\n  ![iOS Project setup](files/ios-project-setup.gif)\n\nNowadays an iOS project is more than only a `*.xcodeproj` file with some self-written Objective-C or Swift files. We have a lot of direct and indirect external dependencies in our projects and each new developer on the project or the build server have to get these. Developers need these before working on the app and the build server to build and deploy the app.\n\n#### Contact\n\n\u003ca href=\"https://to.messeb.com/twitter\"\u003e\u003cimg src=\"http://github.messeb.com.s3.amazonaws.com/assets/messeb-twitter-btn.png\" width=\"120\" height=\"34\" style=\"max-width:100%;\"\u003e\u003c/a\u003e \u003cbr\u003e\nFollow me on Twitter: [@_messeb](https://to.messeb.com/twitter) \n\n\u003ca href=\"https://to.messeb.com/contact\"\u003e\u003cimg src=\"http://github.messeb.com.s3.amazonaws.com/assets/messeb-linkedin-btn.png\" width=\"120\" height=\"34\" style=\"max-width:100%;\"\u003e\u003c/a\u003e \u003cbr\u003e\nContact me on LinkedIn: [@messingfeld](https://to.messeb.com/contact)\n\n--- \n#### Table of Content\n* [Types of dependencies](#types-of-dependencies)\n* [Solutions for code dependencies](#solutions-for-code-dependencies)\n* [Managing dependency chain](#managing-dependency-chain)\n* [Solutions](#solutions)\n    * [Shell script](#shell-script)\n    * [Makefile](#makefile)\n    * [Rakefile](#rakefile)\n* [Conclusion](#conclusion)\n* [Demo project](#demo-project)\n--- \n\n## Types of dependencies\n\nWe can separate the project dependencies into different categories:\n\n**Code**:  Because we don’t want reinvent the wheel for parts of our apps again and again, we use third-party libraries for common use cases. E.g. we use [Alamofire](https://github.com/Alamofire/Alamofire \"Alamofire\") for our network stack. Also, we want use the latest and hopefully greatest version of each dependency, to get the newest features and especially critical bug fixes almost automatically. To reach this goal you should use a dependency manager, which cares about these problems. The principle „never change a running“ system should not apply to third-party dependencies. Especially if these are responsible for critical parts of the app, like encryption.\n\n**Code Dependency Manager**:  To manage code dependencies in our project we currently have two famous dependency management systems in our iOS world: [Cocoapods](https://github.com/CocoaPods/CocoaPods \"Cocoapods\") and [Carthage](https://github.com/Carthage/Carthage \"Carthage\"). Both have almost the same feature set and care about two important requirements: \n1. Install the same versions of the dependencies on every system, so that every developer and the build server creates the same app artefact. \n1. Support updating a to dedicated or latest version of the dependencies\n\nBut neither _Cocoapods_ or _Carthage_ are bundled with macOS, therefore we have to install at least one of them. _Cocoapods_ is available as Ruby Gem and the preferred way to install _Carthage_ is via a _Homebrew_ package.\n\n**Dependency Manager Management**:  To manage our iOS dependency manager, we should use some kind of dependency manager, too. \n_Cocoapods_ is available as Ruby Gem. So we should create a `Gemfile` for these type of dependencies (a `Gemfile` is like the `Podfile` for Ruby developers). We then need to use the _bundler_ Ruby Gem to manage the dependencies in the `Gemfile`. Look at [https://rubygems.org](https://rubygems.org/ \"https://rubygems.org\") and [https://bundler.io/](https://bundler.io/) for detailed information.\nWe install _Carthage_ with _Homebrew_ via a shell command: `brew install carthage`. _Homebrew_ itself is only available through a Ruby installation script. (See [https://brew.sh](https://brew.sh/ \"https://brew.sh\"))\n\n**Ruby**:  The prime dependency in this dependency chain is Ruby. The good news is that it is directly available in the latest macOS, in a 'not so old' version too! If you want the latest or a special version of Ruby you have to install it another way. Besides compiling it from source code, you can use _RVM_ or _rbenv_, which provides an environment management for _Ruby_.\n\n## Solutions for code dependencies\n\nAfter we see what dependencies our iOS project really has, we could look at possible solutions for managing them:\n\n### Under version control\n\nIf you put your code dependencies in your version control system, you will have a compile-ready state of the project in your repository. Then it’s not needed, at least for the build server, to have a way to install all the other indirect dependencies, like _Cocoapods_. But a developer, who wants install new or update old code dependency, will need them. \n\n### Not under version control\n\nIf you not put the code dependencies under version control, you have to provide a way that your colleagues and the build server can resolve and fetch them. The most important part is that everyone gets the same versions of each dependency, which is ensured via the `*.lock`  / `*.resolved` files of each dependency manager. These files freeze the versions of used dependencies and you have to force update the dependency versions for newer versions.\nIn this solution, it will be also easy to add, update or remove a dependency in each step, because every developer has the needed environment for it.  E.g. _fastlane_ is also provided as a Ruby Gem, so you only need to modify the `Gemfile` of the project and update the `Gemfile.lock`.\nAn negative aspect is, that all of the dependencies have to always be available. Nowadays, most of the code dependency are public hosted on Github.com and will be consumed from there. If a developer decides to remove their library from Github.com you need to change the dependency or try to find another source for it.\n\n\nRegardless which way you choose, in my opinion you should provide an easy way to setup the whole project environment. \n\n## Managing dependency chain\n\nCurrently there is no single right way to manage the whole dependency chain for an iOS project environment. It depends on the project and what parts should be provided for the developers and what parts they want manage by their own. Especially for Xcode (Mac App Store or direct download from developer portal) and Ruby (_RVM_ or _rbenv_), each developer has their favourite way to manage it.\n\n\nSo, there is a part of the chain, which should already exists on the developers computer or the build server. For the rest, there are three common ways to install all the project dependencies: Shell script, Makefile and Rake script.\n\n### Base setup\n\nThe base setup, which should be already on the developers computers, normally contains Xcode, Ruby and Homebrew. It depends whether you use _Cocoapods_ or _Carthage_ if you need _Homebrew_. But we can use this as starting point.\n\n**Xcode**:  You can install the latest release version of Xcode via the Mac App Store ([https://itunes.apple.com/app/xcode/id497799835](https://itunes.apple.com/app/xcode/id497799835)), or via an direct download from the Apple Developer portal ([https://developer.apple.com/download/](https://developer.apple.com/download/)). If you use the Mac App Store version, you can auto update to the latest version, but keep in mind that not every project is directly ready to run with the latest Xcode. \n\n**Ruby**:  You can download the source code and compile it by your own ([https://www.ruby-lang.org/en/downloads/](https://www.ruby-lang.org/en/downloads/)). Or you use third party tools to manage it, like [rbenv](https://github.com/rbenv/rbenv) or [RVM](http://rvm.io/). With the third party tools you can easily update or switch the current used Ruby version. So, you should really have a look at it.\n\n**Homebrew**:  To install _Homebrew_ a script is provided on the project website [brew.sh](https://brew.sh/). If you have installed it already, _Homebrew_ can be self upgraded with the command: `brew update`\n\n### Dependency setup\n\n**Ruby dependencies**:  External dependencies for Ruby scripts are normally be managed via the packet manager system `RubyGems`. With the Gem _bundler_ it's possible to install Gems from a _Gemfile_ like this:\n\n```ruby\nruby \"~\u003e 2.5.1\"\n\nsource 'https://rubygems.org'\ngem 'cocoapods', '~\u003e 1.5.3'\ngem 'fastlane', '~\u003e 2.100.1'\n```\n\nTo install these dependencies, you only have to run `bundle install`. The first run of it produces also a `Gemfile.lock` file, which locks the version numbers for other clients. So it's guaranteed that the same artefact is produced on every system. Therefore the `Gemfile.lock` should be committed in the project Git repository.\n\n**Cocoapods dependencies**:  Cocoapods manages the dependencies in their  _Podfile_ and _Podfile.lock_ files. With a call of `pod install` you install all the right versions of the dependencies. With `pod update` you can update the _Podfile.lock_ after changes in _Podfile_. The _Podfile.lock_ also needs to be in you project Git repository.\n\n**Carthage dependencies**:  Like _Cocoapods_, _Carthage_ has it's _Cartfile_ and _Cartfile.resolved_ with the specified versions of the dependencies. Using `carthage bootstrap` you can build the frameworks. The _Cartfile.resolved_ should also be in your Git repository. \n\n\nTo install all the dependency a developer has to run the following commands:\n\n```bash\n# Installs bundler gem\ngem install bundler\n\n# Installs Gems with versions of Gemfile.lock\nbundle install\n\n# Installs Pods with versions of Podfile.lock\npod install\n\n# Builds the frameworks with code versions of Cartfile.resolved\ncarthage boostrap \n```\n\n## Solutions\n\nAfter you have the base setup of your iOS project environment you have to find an easy and predictable way to execute all the steps for setup the iOS project environment. You should prevent your developer to read a long potential outdated documentation.\n\nWe want do the following steps:\n* Check if Ruby is available\n* Check if Homebrew is available\n* Install Ruby Gems\n* Install Cocoapods dependencies\n* Install Carthage dependencies\n* Open for additional steps\n\nAdditional steps could be triggering the build process and run the unit test, like with fastlane:\n\n```bash\nfastlane test\n```\n\nSo the best way would be a solution where these steps are running with the other steps, but it should be also possible to run the additional steps only by their own, because you already could have setup your project environment.\n\n### Shell script\nThe shell script solution is inspired by the [Firefox iOS App](https://github.com/mozilla-mobile/firefox-ios/) and their solution can be found here: [bootstrap.sh](https://github.com/mozilla-mobile/firefox-ios/blob/master/bootstrap.sh)\n\nThe script will be executed from top to bottom and performs all the necessary steps to setup the project environment. \n\n**Pros**:\n* Same syntax as manual entered commands\n* Groups manual entered commands into one file\n* Can contain checks for dependencies\n* Customisable and extendible with functions\n\n**Cons**:\n* Bash syntax\n* No selective running of steps\n* No easy integration of optional additional steps\n\n\nI added the `command_exists` function, to check if a executable is available in the current shell path.\n\n```bash\n#!/bin/bash\n\n# Checks if executable exists in current path\ncommand_exists () {\n  command -v \"$1\" \u003e /dev/null 2\u003e\u00261;\n}\n\necho \"iOS project setup ...\"\n\n# Check if Ruby is installed\nif ! command_exists ruby\nthen\n  echo 'Ruby not found, please install it:'\n  echo 'https://www.ruby-lang.org/en/downloads/'\n  exit 1\nfi\n\n# Check if Homebrew is available\nif ! command_exists brew\nthen\n  echo 'Homebrew not found, please install it:'\n  echo 'https://brew.sh/'\n  exit 1\nelse\n  echo \"Update Homebrew ...\"\n  brew update\nfi\n\n# Install Bundler Gem\nif ! command_exists bundle\nthen\n  echo \"Bundler not found, installing it ...\"\n  gem install bundler -v '~\u003e 1.16.2'\nelse \n  echo \"Update Bundler\"\n  gem update bundler '~\u003e 1.16.2'\nfi\n\n# Install Ruby Gems\necho \"Install Ruby Gems ...\"\nbundle install\n\n# Install Cocopods dependencies\necho \"Install Cocoapods\"\npod install\n\n# Install Carthage\necho \"Install / Update carthage ...\"\nbrew unlink carthage || true\nbrew install carthage\nbrew link --overwrite carthage\n\n# Install Carthage dependencies\necho \"Install Carthage Dependencies ...\"\ncarthage bootstrap --platform ios --cache-builds\n\n```\n\nA new developer only needs to run `./project_setup.sh` to setup the iOS project environment.\n\nIf you want to add additional steps, you should write additional functions for each step. With parameters at the call of `./project_setup.sh`, you can control which step will be executed. For example, if we want to run our unit test, it would look like this\n\n```bash\n#!/bin/bash\n\necho \"iOS project setup ...\"\n\n# Check if user only wants to run unit tests\nonly_test=false\n[ \"$1\" == \"only_test\" ] \u0026\u0026 only_test=true\n\n# Check if user wants to create build environment\n# and execute the unit tests\nwith_test=false\n[ \"$1\" == \"with_test\" ] \u0026\u0026 with_test=true\n\n# Run fastlane unit tests\nunit_test() {\n  fastlane test\n}\n\n# Run only unit tests\nif $only_test \nthen\n  unit_test\n  exit 0\nfi\n\n#\n# All boostrapping steps\n#\n\n# Run unit tests after project setup\nif $with_test\nthen\n  unit_test\nfi\n```\n\nWe define a `unit_test` function, which will executed if the first parameter is `only_test` or `with_test`. You can call the shell script with\n\n```bash\n./project_setup.sh only_test\n```\n\nor\n\n```bash\n./project_setup.sh with_test\n```\n\nFor `only_test`, the function `unit_test` will be executed at the beginning and the script ends. For `with_test`, the whole bootstrapping steps will be executed and afterwards the function `unit_test`. Without parameters only the project setup will be executed.\n\n### Makefile\n\nInspired by the [Kickstarter](https://github.com/kickstarter/ios-oss \"Kickstarter\") ([Makefile](https://github.com/kickstarter/ios-oss/blob/master/Makefile \"Makefile\")) and [Wikipedia](https://github.com/wikimedia/wikipedia-ios \"Wikipedia\") ([Makefile](https://github.com/wikimedia/wikipedia-ios/blob/develop/Makefile \"Makefile\")) app, a Makefile can also be a solution to execute all the steps with one command.\n\nUnlike the shell script solution, it does not execute all the steps from top to bottom,. It executes a `target` block using it's name like:\n\n```bash\nmake target_name\n```\n\nOnly the commands in this `target` will be executed. But you can define other `targets` which should be executed before. So you have a chain of commands which will be executed one after the other which you can see it in the example.\n\nYou can also define a default `target` which will be executed if no `target` name is given.\n\n**Pros**:\n* Same syntax as manual entered commands\n* Groups manual entered commands in one file\n* Can contain checks for dependencies\n* Selective running of steps\n* Easy integration of optional additional steps\n\n**Cons**:\n* Makefile syntax\n* Limited customisable and extensible with targets\n\n\nA Makefile can look like the example below and contain setup steps for each project as target.\nThe `setup` target only has other targets as dependencies and doesn’t execute anything. Targets with a colon after the target name will be executed in top to bottom order. So you can manage the execution order of your steps. \n\nThe syntax of a Makefile is a little complicated, like the checks of the existing Ruby or Homebrew binaries shows, but normally you don’t need to know more. If you interested, read more in the [make documentation](https://www.gnu.org/software/make/manual/make.html \"make documentation\").\n\n```bash\n# Checks if executable exists in current path\nRUBY := $(shell command -v ruby 2\u003e/dev/null)\nHOMEBREW := $(shell command -v brew 2\u003e/dev/null)\nBUNDLER := $(shell command -v bundle 2\u003e/dev/null)\n\n# Default target, if no is provided\ndefault: setup\n\n# Steps for project environment setup\nsetup: \\\n    pre_setup \\\n    check_for_ruby \\\n    check_for_homebrew \\\n    update_homebrew \\\n    install_carthage \\\n    install_bundler_gem \\\n    install_ruby_gems \\\n    install_carthage_dependencies \\\n    install_cocoapods\n\n# Pre-setup steps\npre_setup:\n    $(info iOS project setup ...)\n\n# Check if Ruby is installed\ncheck_for_ruby:\n    $(info Checking for Ruby ...)\n\nifeq ($(RUBY),)\n    $(error Ruby is not installed)\nendif\n\n# Check if Homebrew is available\ncheck_for_homebrew:\n    $(info Checking for Homebrew ...)\n\nifeq ($(HOMEBREW),)\n    $(error Homebrew is not installed)\nendif\n\n# Update Homebrew\nupdate_homebrew:\n    $(info Update Homebrew ...)\n\n    brew update\n\n# Install Bundler Gem\ninstall_bundler_gem:\n    $(info Checking and install bundler ...)\n\nifeq ($(BUNDLER),)\n    gem install bundler -v '~\u003e 1.16'\nelse\n    gem update bundler '~\u003e 1.16'\nendif\n\n# Install Ruby Gems\ninstall_ruby_gems:\n    $(info Install RubyGems ...)\n\n    bundle install\n\n# Install Cocopods dependencies\ninstall_cocoapods:\n    $(info Install Cocoapods ...)\n\n    pod install\n\n# Install Carthage\ninstall_carthage:\n    $(info Install Carthage ...)\n\n    brew unlink carthage || true\n    brew install carthage\n    brew link --overwrite carthage\n\n# Install Carthage dependencies\ninstall_carthage_dependencies:\n    $(info Install Carthage Dependencies ...)\n\n    carthage bootstrap --platform ios --cache-builds\n\n```\n\nEach of the targets can also be execute on their own. You have to execute `make` with the specific target name, like `make install_ruby_gems`\n\nSo, it's also easy to add additional steps in our project setup. If you want to add a unit test to it, you can define an additional target (`unit_test`).\nIf you want to execute the `setup` and the `unit_test` target together, you can define an additional target with these targets as dependency.\n\n```bash\n# Combines project setup with unit tests\nsetup_with_unit_test: \\\n    setup \\\n    unit_test\n\n#\n# All other boostrapping steps\n#\n\n# Run fastlane unit tests\nunit_test:\n    $(info Run Unittests ...)\n\n    fastlane test\n```\n\nSo you can call \n\n```bash\nmake unit_test\n```\n\nto run only the unit tests, and \n\n```bash\nmake setup_with_unit_test\n```\n\nif you need also the project setup. Especially on an build server the last command is very useful.\n\n### Rakefile\n\nThe [Wordpress](https://github.com/wordpress-mobile/WordPress-iOS/ \"Wordpress\") ([Rakefile](https://github.com/wordpress-mobile/WordPress-iOS/blob/develop/Rakefile \"Rakefile\")) app uses a Rakefile for its project setup. This is similar to the Makefile solution, but it uses the Ruby variant of make: [rake](https://github.com/ruby/rake \"rake\")\n\nWe don’t need a check for Ruby because Ruby and `rake` are preconditions on the developers system to execute the Rakefile tasks.\n\nOtherwise, the Rakefile solution is very similar to a Makefile.\nEach project setup step is in a `task` block and can be execute by its name, e.g. `rake check_homebrew`.\nIt is also possible to have a default `task`, which will be execute if you only call `rake`, and each of the tasks can depend on others.\n\n**Pros**:\n* Groups manual entered commands in one file\n* Can contain checks for dependencies\n* Selective running of steps\n* Easy integration of optional additional steps\n* Customisation of build process via Ruby functionality\n\n**Cons**:\n* Needs rake on system\n* Rakefile syntax\n* Executes shell commands over an additional Ruby function `sh`\n\nYou can see an example below. The main task is `setup`, which has other tasks as dependencies. You can define dependencies with an arrow operator pointing to the list of the dependencies.\n\nEach of the tasks can contain any Ruby code. So if you are familiar with Ruby you can adapt this solution very quickly. But you can also see that you will mostly execute shell commands from your Ruby script. That’s why you should decide for yourself if you really need this additional abstraction layer.\n\n\n```ruby\n# Checks if executable exists in current path\ndef command?(command)\n    system(\"command -v #{command} \u003e /dev/null 2\u003e\u00261\")\nend\n\n# Default target, if no is provided\ntask default: [:setup]\n\n# Steps for project environment setup\ntask :setup =\u003e [\n    :pre_setup,\n    :check_for_homebrew, \n    :update_homebrew,\n    :install_bundler_gem,\n    :install_ruby_gems,\n    :install_carthage,\n    :install_cocoapods_dependencies,\n    :install_carthage_dependencies,\n]\n\n# Pre-setup steps\ntask :pre_setup do \n    puts \"iOS project setup ...\"\nend\n\n# Check if Homebrew is available\ntask :check_for_homebrew do\n    puts \"Checking Homebrew ...\"\n    if not command?('brew')\n        STDERR.puts \"Homebrew not found, please install it:\"\n        STDERR.puts \"https://brew.sh/\"\n        exit\n    end\nend\n\n# Update Homebrew\ntask :update_homebrew do \n    puts \"Updating Homebrew ...\"\n    sh \"brew update\"\nend\n\n# Install Bundler Gem\ntask :install_bundler_gem do \n    if not command?('bundle')\n        sh \"gem install bundler -v '~\u003e 1.16'\"\n    else\n        sh \"gem update bundler '~\u003e 1.16'\"\n    end\nend\n\n# Install Ruby Gems\ntask :install_ruby_gems do\n    sh \"bundle install\"\nend\n\n# Install Cocopods dependencies\ntask :install_cocoapods_dependencies do\n    sh \"pod install\"\nend\n\n# Install Carthage\ntask :install_carthage do \n    sh \"brew unlink carthage || true\"\n    sh \"brew install carthage\"\n    sh \"brew link --overwrite carthage\"\nend\n\n# Install Carthage dependencies\ntask :install_carthage_dependencies do\n    sh \"carthage bootstrap --platform ios --cache-builds\"\nend\n```\n\nTo add additional steps, you only have to add another task, like one for the unit tests\n\n```ruby\n# Run fastlane unit tests\ntask :unit_test do \n    sh \"fastlane test\"\nend\n```\n\nYou can call this directly with `rake unit_test`. To combine the project setup with the execution of the unit tests, you can define an extra task for it, which has the both tasks as dependencies.\n\n```ruby\n# Combines project setup with unit tests\ntask :setup_with_unit_test =\u003e [\n    :setup,\n    :unit_test\n]\n```\n\nThis can be execute with \n\n```ruby\nrake setup_with_unit_test\n```\n\n## Conclusion\n\nIf you use a shell script, a Makefile, a Rakefile or something else, you will provide an easy bootstrapping script for your iOS project. This makes it much easier for new developers to start and a build server needs only a one liner to build and deploy the app. The trouble with setting this up and learning some new script languages will it be worth it.\n\nNow, you can also easily use cloud continuous integration services like Travis CI, CircleCI or bitrise.io. Normally in the configuration of these services you will select an Xcode version and have also Ruby and Homebrew available. So your execution step will be the same as every developer does on their local machine: `make setup_with_unit_test`.\n\nMy preferred solution is a Makefile, because it has a integrated dependency management between the targets and is directly callable, which is not as easy in a shell script solution. It also relies on `make`, which comes with every macOS in contrast to `rake`.\nIf you need to execute more complex steps, which is not a strength of a Makefile, your can break the steps into multiple Shell or Ruby scripts and call them from your Makefile.\n\n## Demo project\n\nI have provided a demo project, where you can test all three solutions on your own.\n\n**Shell script**\n\nProject setup:\n\n```bash\n./project_setup.sh\n```\n\nProject setup with unit tests:\n\n```bash\n./project_setup.sh with_test\n```\n\nUnit tests:\n\n```bash\n./project_setup.sh only_test\n```\n\n\n**Makefile**\n\nProject setup:\n\n```bash\nmake setup\n```\n\nProject setup with unit tests:\n\n```bash\nmake setup_with_unit_test\n```\n\nUnit tests:\n\n```bash\nmake unit_test\n```\n\n\n**Rake**\n\nProject setup:\n\n```bash\nrake setup\n```\n\nProject setup with unit tests:\n\n```bash\nrake setup_with_unit_test\n```\n\nUnit tests:\n\n```bash\nrake unit_test\n```\n\n\nThe iOS project contains both, Cocoapods and Carthage dependencies to show the different steps. Normally you would only use one of these code dependency manager. A fastlane `test` lane is also provided to execute example unit tests.\n","funding_links":[],"categories":["HarmonyOS","Swift"],"sub_categories":["Windows Manager"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmesseb%2Fios-project-env-setup","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmesseb%2Fios-project-env-setup","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmesseb%2Fios-project-env-setup/lists"}