Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
A kinda hacky script to set up iOS env config for RN, Objective-C, and preprocessor defines.
from plistlib import readPlist as read_plist
from subprocess import check_output
import socket
import json
from datetime import datetime
import os
# Export your version of these for testing the script from the console directly:
# During build, this is run in the iOS directory by the Environment Switch Task
# target.
# SCHEME_FILE is set by an environment variable on the YOURAPP project and is written
# as a pre-build command by the individual schemes.
# To edit the environment variable, go to the bottom of YOURAPP -> Build Settings in the
# project tab of XCode.
# To modify the writing of a temp file that stores the currently-building scheme name, go to
# Edit Schemes -> Build -> Pre-build.
scheme = check_output(['cat', os.environ['SCHEME_FILE']]).strip()
# Whitelist scheme names:
if scheme not in ['Development', 'Testflight', 'Production']:
raise'Scheme must be one of Development, Testflight, or Production. If you need another, whitelist it in iOS/Configuration/')
# Get directories:
project_dir = os.environ['PROJECT_DIR']
config_dir = os.path.join(project_dir,'iOS','Configuration')
# This is the location of, say "environment_SchemeName.plist". We gitignored
# "environment_Development.plist" and instead committed "environment_Development.plist.sample"
# So that each dev's config doesn't get committed.
env_plist = os.path.join(config_dir, 'environment_%s.plist' % scheme)
# Read the environment plist, e.g. environment_Development.plist:
conf = read_plist(env_plist)
# Put the scheme and project directory into env config:
conf['FS_ENVIRONMENT'] = scheme
conf['FS_PROJECT_DIR'] = os.environ['PROJECT_DIR']
# Override for using localhost in ip address lookup:
if 'FS_FORCE_LOCALHOST' not in conf:
conf['FS_FORCE_LOCALHOST'] = False
# Override for using localhost in ip address lookup:
if 'FS_USE_API_HOST' not in conf:
conf['FS_FORCE_LOCALHOST'] = 'FS_API_HOST' in conf
if 'FS_USE_BUNDLED_JS' not in conf:
conf['FS_USE_BUNDLED_JS'] = False
# Get the local ip address:
host = None
def get_host(port=None):
# cache the ip address even though I think it does that internally:
global host
if host is None:
# Localhost override:
host = 'localhost'
# Look up the ip address, attempting to exclude virtual hosts:
host_ex = socket.gethostbyname_ex(socket.gethostname())
host = host_ex[-1][-1] #[host_ex.index(socket.gethostname())]
# Prepend http://
result = 'http://%s' % host
# Append optional port:
if port is not None:
result += ':%s' % port
return result
# If it's not set manually, compute the api host address:
if 'FS_API_HOST' not in conf or not conf['FS_USE_API_HOST']:
conf['FS_API_HOST'] = '%s/api/v7' % get_host(3000)
# Ditto code host:
if not conf['FS_USE_BUNDLED_JS'] and 'FS_CODE_HOST' not in conf:
conf['FS_CODE_HOST'] = '%s/index.ios.bundle?platform=ios&dev=true' % get_host(8081)
conf['FS_CODE_HOST'] = ''
# This is a header file so the iOS knows about preprocessor defines:
def export_to_header(filename, data, NSString=False):
prefix,suffix = ('@"','"') if NSString else ('','')
f = open(filename,'w')
f.write('// Auto generated file\n')
f.write('// Created %s\n' % datetime.strftime(,'%c'))
for key in data:
val = data[key]
if isinstance(val,bool):
val = 'true' if val else 'false'
val = '%s%s%s' % (prefix,str(val),suffix)
f.write('#define %-30s %s\n' % (key, val))
def export_to_json(filename,data):
f = open(filename,'w')
f.write('module.exports=%s' % json.dumps(data))
# Export a file for use in the main Info.plist. This let's you do fun stuff like
# set the app name dynamically so that it can be scheme-dependent. To use this, find
# the 'Info.plist Preprocessor Prefix File' build setting for your project and set it
# to this file. Then set 'Preprocess Info.plist File' = Yes.
export_to_header('environment_for_infolist_prefix.h', conf, False)
# This is for plain old objective-c preprocessor defines. The only difference is that
# it needs extra @"" NSString stuff. For regular c #includes.
export_to_header('environment.h', conf, True)
# This is the version that gets exported to json so that json knows about the same config:
export_to_json('../app/config/environment.ios.js', conf)

rreusser commented May 13, 2016

To use this, you'll need a couple extra tweaks.

  1. In pre-build settings for each scheme, I manually added, say for the Development scheme, echo Development > ${SCHEME_FILE}. This strategy is sooo ugly.
  2. Add the location of this file as a SCHEME_FILE variable to the User-Defined section of the Build Settings tab of your main project
  3. Add a new XCode target of type Aggregate ("other" section) to your project. Call it, maybe, "Environment Setup Task"
  4. Add this target as a build dependency of your main app target (YOUR APP Target -> Build Phases -> Target Dependencies)
  5. Add the a 'Run Script' build phase to "Environment Setup Task"
  6. Add the following to the script:
/usr/bin/python path/to/your/ || exit 1

Input: path/to/your/environment_${SCHEME_NAME}.plist, e.g. ios/Configuration/environment_Development.plist.


  • environment_for_infolist_prefix.h so that you can use env vars in your main Info.plist (so cool! this lets you do things like changing your app name depending on the environment so that it's properly called YourApp-Dev when installed as dev.)
  • environment.h so that you can use env vars in Objective-C.
  • app/config/environment.ios.js so that you can use env vars in JavaScript.

NB: this is kind of a hacky script that worked great for us. But don't expect it to work without fixing up paths and minor details just a bit. Let me know if you run into trouble.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment