Created
January 25, 2018 09:59
-
-
Save 0xced/73d55c1d0be6953fb7b3199b6936f6a9 to your computer and use it in GitHub Desktop.
Generate constants from storyboard identifiers
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# Adapted from https://joris.kluivers.nl/blog/2014/02/10/storyboard-identifier-constants/ (https://github.com/kluivers/storyboard-constants) | |
# * Support for accessibility identifiers and accessibility labels | |
# * Easily extendable with xpath definitions | |
# * Valid identifiers | |
# * Namespaced constants, idea from https://www.mikeash.com/pyblog/friday-qa-2011-08-19-namespaced-constants-and-functions.html | |
PREFIX = '' | |
SPACING = '\t' | |
DEFINITIONS = { | |
'SegueIdentifier': [{'xpath': './/segue[@identifier]', 'attribute': 'identifier'}], | |
'ControllerIdentifier': [{'xpath': './/*[@storyboardIdentifier]', 'attribute': 'storyboardIdentifier'}], | |
'ReuseIdentifier': [{'xpath': './/*[@reuseIdentifier]', 'attribute': 'reuseIdentifier'}], | |
'AccessibilityIdentifier': [{'xpath': './/accessibility[@identifier]', 'attribute': 'identifier'}, {'xpath': './/userDefinedRuntimeAttribute[@keyPath="accessibilityIdentifier"]', 'attribute': 'value'}], | |
'AccessibilityLabel': [{'xpath': './/accessibility[@label]', 'attribute': 'label'}, {'xpath': './/userDefinedRuntimeAttribute[@keyPath="accessibilityLabel"]', 'attribute': 'value'}], | |
} | |
import sys, os, re, unicodedata | |
import xml.etree.ElementTree as ElementTree | |
# From http://stackoverflow.com/questions/517923/what-is-the-best-way-to-remove-accents-in-a-python-unicode-string/518232#518232 | |
def stripAccents(s): | |
return ''.join(c for c in unicodedata.normalize('NFD', unicode(s)) if unicodedata.category(c) != 'Mn') | |
def makeIdentifier(s): | |
return re.sub('[^\w]', '_', stripAccents(s)) | |
def processStoryboard(storyboardPath, identifiers): | |
root = ElementTree.parse(storyboardPath).getroot() | |
storyboardName = os.path.splitext(storyboardPath)[0] | |
for key,descriptors in DEFINITIONS.items(): | |
for descriptor in descriptors: | |
identifiers[key].extend(map((lambda e: e.get(descriptor['attribute'])), root.findall(descriptor['xpath']))) | |
return identifiers | |
def writeIdentifiers(path, identifiers, importHeader, structBegin, structContent, structEnd): | |
with open(path, 'w+') as f: | |
f.write('/* Generated file. DO NOT MODIFY */\n\n') | |
f.write('#import {0}\n\n'.format(importHeader)) | |
for key,identifiers in identifiers.items(): | |
if len(identifiers) == 0: | |
continue | |
f.write(structBegin(key)) | |
for identifier in sorted(identifiers): | |
f.write(structContent(identifier)) | |
f.write(structEnd(key)) | |
def main(headerPath, implementationPath): | |
identifiers = {key: [] for key in DEFINITIONS.keys()} | |
for n in range(int(os.environ['SCRIPT_INPUT_FILE_COUNT'])): | |
identifiers = processStoryboard(os.environ['SCRIPT_INPUT_FILE_' + str(n)], identifiers) | |
writeIdentifiers(headerPath, identifiers, '<Foundation/Foundation.h>', | |
(lambda kind: 'extern const struct {0}{1}Struct {{\n'.format(PREFIX, kind)), | |
(lambda value: '{0}__unsafe_unretained NSString *{1};\n'.format(SPACING, makeIdentifier(value))), | |
(lambda kind: '}} {0}{1};\n\n'.format(PREFIX, kind))) | |
writeIdentifiers(implementationPath, identifiers, '"' + os.path.basename(headerPath) + '"', | |
(lambda kind: 'const struct {0}{1}Struct {0}{1} = {{\n'.format(PREFIX, kind)), | |
(lambda value: '{0}.{1} = @"{2}",\n'.format(SPACING, makeIdentifier(value), value.encode('utf-8'))), | |
(lambda kind: '};\n\n')) | |
if __name__ == '__main__': | |
if len(sys.argv) != 3: | |
sys.exit('error: usage: {0} headerPath implementationPath'.format(os.path.basename(sys.argv[0]))) | |
main(sys.argv[1], sys.argv[2]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment