├── .github ├── dependabot.yml └── workflows │ ├── ci.yml │ └── pre-commit-update.yml ├── .gitignore ├── .pre-commit-config.yaml ├── CONTRIBUTING.md ├── LICENSE ├── README.rst ├── cookiecutter.json └── {{ cookiecutter.format }} ├── Support └── README ├── briefcase.toml ├── {{ cookiecutter.class_name }} ├── Images.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── icon-1024.png │ │ ├── icon-120.png │ │ ├── icon-152.png │ │ ├── icon-167.png │ │ ├── icon-180.png │ │ ├── icon-20.png │ │ ├── icon-29.png │ │ ├── icon-40.png │ │ ├── icon-58.png │ │ ├── icon-60.png │ │ ├── icon-76.png │ │ ├── icon-80.png │ │ └── icon-87.png │ └── Splash.imageset │ │ ├── Contents.json │ │ ├── splash-1280.png │ │ ├── splash-1920.png │ │ └── splash-640.png ├── Launch Screen.storyboard ├── app │ └── README ├── app_packages.iphoneos │ └── README ├── app_packages.iphonesimulator │ └── README ├── dylib-Info-template.plist ├── en.lproj │ └── InfoPlist.strings ├── main.m ├── {{ cookiecutter.class_name }}-Info.plist └── {{ cookiecutter.class_name }}-Prefix.pch └── {{ cookiecutter.formal_name }}.xcodeproj └── project.pbxproj /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | 2 | version: 2 3 | updates: 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | # Check for updates on Sunday, 8PM UTC 8 | interval: "weekly" 9 | day: "sunday" 10 | time: "20:00" 11 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - main 7 | workflow_call: 8 | 9 | # Cancel active CI runs for a PR before starting another run 10 | concurrency: 11 | group: ${{ github.ref }} 12 | cancel-in-progress: true 13 | 14 | defaults: 15 | run: 16 | shell: bash 17 | 18 | env: 19 | FORCE_COLOR: "1" 20 | 21 | jobs: 22 | pre-commit: 23 | name: Pre-commit checks 24 | uses: beeware/.github/.github/workflows/pre-commit-run.yml@main 25 | with: 26 | pre-commit-source: pre-commit 27 | 28 | verify-apps: 29 | name: Build apps 30 | needs: pre-commit 31 | uses: beeware/.github/.github/workflows/app-build-verify.yml@main 32 | with: 33 | python-version: ${{ matrix.python-version }} 34 | runner-os: macos-latest 35 | framework: ${{ matrix.framework }} 36 | target-platform: iOS 37 | target-format: Xcode 38 | strategy: 39 | fail-fast: false 40 | matrix: 41 | python-version: [ "3.9", "3.10", "3.11", "3.12", "3.13" ] 42 | framework: [ "toga" ] 43 | -------------------------------------------------------------------------------- /.github/workflows/pre-commit-update.yml: -------------------------------------------------------------------------------- 1 | name: Update pre-commit 2 | 3 | on: 4 | schedule: 5 | - cron: "0 20 * * SUN" # Sunday @ 2000 UTC 6 | workflow_dispatch: 7 | 8 | jobs: 9 | pre-commit-update: 10 | name: Update pre-commit 11 | uses: beeware/.github/.github/workflows/pre-commit-update.yml@main 12 | secrets: inherit 13 | with: 14 | pre-commit-source: pre-commit 15 | create-changenote: false 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .python-version 3 | .vscode 4 | .idea 5 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v5.0.0 4 | hooks: 5 | - id: check-yaml 6 | - id: check-json 7 | - id: check-xml 8 | - id: check-case-conflict 9 | - id: end-of-file-fixer 10 | - id: trailing-whitespace 11 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | BeeWare <3's contributions! 4 | 5 | Please be aware, BeeWare operates under a Code of Conduct. 6 | 7 | See [CONTRIBUTING to BeeWare](http://beeware.org/contributing) for details. 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Russell Keith-Magee. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Briefcase iOS Xcode Template 2 | ============================ 3 | 4 | A `Cookiecutter `__ template for 5 | building Python apps that will run under iOS. 6 | 7 | Using this template 8 | ------------------- 9 | 10 | The easiest way to use this project is to not use it at all - at least, not 11 | directly. `Briefcase `__ is a tool that 12 | uses this template, rolling it out using data extracted from a 13 | ``pyproject.toml`` configuration file. 14 | 15 | However, if you *do* want use this template directly... 16 | 17 | 1. Install `cookiecutter`_. This is a tool used to bootstrap complex project 18 | templates:: 19 | 20 | $ pip install cookiecutter 21 | 22 | 2. Run ``cookiecutter`` on the template:: 23 | 24 | $ cookiecutter https://github.com/beeware/briefcase-iOS-Xcode-template 25 | 26 | This will ask you for a number of details of your application, including the 27 | `name` of your application (which should be a valid PyPI identifier), and 28 | the `Formal Name` of your application (the full name you use to describe 29 | your app). The remainder of these instructions will assume a `name` of 30 | ``my-project``, and a formal name of ``My Project``. 31 | 32 | 3. `Obtain a Python Apple support package for iOS`_, and extract it into 33 | the ``My Project`` directory generated by the template. This will give you a 34 | ``My Project/Support`` directory containing a self contained Python install. 35 | 36 | 4. Add your code to the template, into the ``My Project/my-project/app``. 37 | directory. At the very minimum, you need to have an 38 | ``app//__main__.py`` file that defines a ``PythonAppDelegate`` 39 | class. 40 | 41 | If your code has any dependencies, they should be installed into the 42 | ``My Project/my-project/app_packages`` directory. 43 | 44 | If you've done this correctly, a project with a formal name of ``My Project``, 45 | with an app name of ``my-project`` should have a directory structure that 46 | looks something like:: 47 | 48 | My Project/ 49 | my-project/ 50 | app/ 51 | my_project/ 52 | __init__.py 53 | app.py (declares PythonAppDelegate) 54 | app_packages/ 55 | ... 56 | ... 57 | My Project.xcodeproj/ 58 | ... 59 | Support/ 60 | ... 61 | briefcase.toml 62 | 63 | You're now ready to open the XCode project file, build and run your project! 64 | 65 | Next steps 66 | ---------- 67 | 68 | Of course, running Python code isn't very interesting by itself - you'll be 69 | able to output to the console, and see that output in XCode, but if you tap the 70 | app icon on your phone, you won't see anything - because there isn't a visible 71 | console on an iPhone. 72 | 73 | To do something interesting, you'll need to work with the native iOS system 74 | libraries to draw widgets and respond to screen taps. The `Rubicon`_ Objective 75 | C bridging library can be used to interface with the iOS system libraries. 76 | Alternatively, you could use a cross-platform widget toolkit that supports iOS 77 | (such as `Toga`_) to provide a GUI for your application. 78 | 79 | Regardless of whether you use Toga, or you write an application natively, the 80 | template project will try to instantiate a ``UIApplicationMain`` instance, 81 | using a class named ``PythonAppDelegate`` as the App delegate. If a class of 82 | that name can't be instantiated, the error raised will be logged, and the 83 | Python interpreter will be shut down. 84 | 85 | If you have any external library dependencies (like Toga, or anything other 86 | third-party library), you should install the library code into the 87 | ``app_packages`` directory. This directory is the same as a ``site_packages`` 88 | directory on a desktop Python install. 89 | 90 | .. _cookiecutter: https://github.com/cookiecutter/cookiecutter 91 | .. _Obtain a Python Apple support package for iOS: https://github.com/beeware/Python-Apple-support 92 | .. _Rubicon: https://github.com/beeware/rubicon-objc 93 | .. _Toga: https://beeware.org/project/projects/libraries/toga 94 | -------------------------------------------------------------------------------- /cookiecutter.json: -------------------------------------------------------------------------------- 1 | { 2 | "format": "Xcode", 3 | "formal_name": "App Name", 4 | "app_name": "{{ cookiecutter.formal_name|lower|replace(' ', '-') }}", 5 | "class_name": "{{ cookiecutter.formal_name.title().replace(' ','').replace('-','').replace('!','').replace('.','').replace(',','') }}", 6 | "module_name": "{{ cookiecutter.app_name|replace('-', '_') }}", 7 | "author": "Example Corporation", 8 | "bundle": "com.example", 9 | "bundle_identifier": "{{ cookiecutter.bundle }}.{{ cookiecutter.app_name|replace('_', '-') }}", 10 | "splash_background_color": "#FFFFFF", 11 | "info": "", 12 | "version": "1.0", 13 | "build": "1", 14 | "python_version": "3.X.0", 15 | "min_os_version": "13.0", 16 | "uses_non_exempt_encryption": false, 17 | "_copy_without_render": [ 18 | ".gitignore", 19 | "*.png" 20 | ], 21 | "_extensions": [ 22 | "briefcase.integrations.cookiecutter.PythonVersionExtension", 23 | "briefcase.integrations.cookiecutter.RGBExtension", 24 | "briefcase.integrations.cookiecutter.PListExtension" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /{{ cookiecutter.format }}/Support/README: -------------------------------------------------------------------------------- 1 | This directory will contain the support libraries for the project. 2 | -------------------------------------------------------------------------------- /{{ cookiecutter.format }}/briefcase.toml: -------------------------------------------------------------------------------- 1 | # Generated using Python {{ cookiecutter.python_version }} 2 | [briefcase] 3 | # This is the start of the PEP 730 framework era. 4 | target_version = "0.3.20" 5 | 6 | [paths] 7 | app_path = "{{ cookiecutter.class_name }}/app" 8 | app_packages_path = "{{ cookiecutter.class_name }}/app_packages" 9 | info_plist_path = "{{ cookiecutter.class_name }}/{{ cookiecutter.class_name }}-Info.plist" 10 | support_path = "Support" 11 | {{ { 12 | "3.9": "support_revision = 16", 13 | "3.10": "support_revision = 12", 14 | "3.11": "support_revision = 7", 15 | "3.12": "support_revision = 7", 16 | "3.13": "support_revision = 6", 17 | "3.14": "support_revision = 2", 18 | }.get(cookiecutter.python_version|py_tag, "") }} 19 | 20 | icon.20 = "{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-20.png" 21 | icon.29 = "{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-29.png" 22 | icon.40 = "{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-40.png" 23 | icon.58 = "{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-58.png" 24 | icon.60 = "{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-60.png" 25 | icon.76 = "{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-76.png" 26 | icon.80 = "{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-80.png" 27 | icon.87 = "{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-87.png" 28 | icon.120 = "{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-120.png" 29 | icon.152 = "{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-152.png" 30 | icon.167 = "{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-167.png" 31 | icon.180 = "{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-180.png" 32 | icon.640 = "{{ cookiecutter.class_name }}/Images.xcassets/Splash.imageset/splash-640.png" 33 | icon.1024 = "{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-1024.png" 34 | icon.1280 = "{{ cookiecutter.class_name }}/Images.xcassets/Splash.imageset/splash-1280.png" 35 | icon.1920 = "{{ cookiecutter.class_name }}/Images.xcassets/Splash.imageset/splash-1920.png" 36 | -------------------------------------------------------------------------------- /{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "icon-40.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "icon-60.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "icon-58.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "icon-87.png", 25 | "scale" : "3x" 26 | }, 27 | { 28 | "size" : "40x40", 29 | "idiom" : "iphone", 30 | "filename" : "icon-80.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "icon-120.png", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "icon-120.png", 43 | "scale" : "2x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "icon-180.png", 49 | "scale" : "3x" 50 | }, 51 | { 52 | "idiom" : "ipad", 53 | "size" : "20x20", 54 | "filename" : "icon-20.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "icon-40.png", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "29x29", 66 | "filename" : "icon-29.png", 67 | "scale" : "1x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "icon-58.png", 73 | "scale" : "2x" 74 | }, 75 | { 76 | "idiom" : "ipad", 77 | "size" : "40x40", 78 | "filename" : "icon-40.png", 79 | "scale" : "1x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "icon-80.png", 85 | "scale" : "2x" 86 | }, 87 | { 88 | "idiom" : "ipad", 89 | "size" : "76x76", 90 | "filename" : "icon-76.png", 91 | "scale" : "1x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "icon-152.png", 97 | "scale" : "2x" 98 | }, 99 | { 100 | "size" : "83.5x83.5", 101 | "idiom" : "ipad", 102 | "filename" : "icon-167.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "1024x1024", 107 | "idiom" : "ios-marketing", 108 | "filename" : "icon-1024.png", 109 | "scale" : "1x" 110 | } 111 | ], 112 | "info" : { 113 | "version" : 1, 114 | "author" : "xcode" 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beeware/briefcase-iOS-Xcode-template/1aeeb214d7c5455a03345a4031452ad4959677b8/{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-1024.png -------------------------------------------------------------------------------- /{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beeware/briefcase-iOS-Xcode-template/1aeeb214d7c5455a03345a4031452ad4959677b8/{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-120.png -------------------------------------------------------------------------------- /{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beeware/briefcase-iOS-Xcode-template/1aeeb214d7c5455a03345a4031452ad4959677b8/{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-152.png -------------------------------------------------------------------------------- /{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beeware/briefcase-iOS-Xcode-template/1aeeb214d7c5455a03345a4031452ad4959677b8/{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-167.png -------------------------------------------------------------------------------- /{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beeware/briefcase-iOS-Xcode-template/1aeeb214d7c5455a03345a4031452ad4959677b8/{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-180.png -------------------------------------------------------------------------------- /{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beeware/briefcase-iOS-Xcode-template/1aeeb214d7c5455a03345a4031452ad4959677b8/{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-20.png -------------------------------------------------------------------------------- /{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beeware/briefcase-iOS-Xcode-template/1aeeb214d7c5455a03345a4031452ad4959677b8/{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-29.png -------------------------------------------------------------------------------- /{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beeware/briefcase-iOS-Xcode-template/1aeeb214d7c5455a03345a4031452ad4959677b8/{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-40.png -------------------------------------------------------------------------------- /{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beeware/briefcase-iOS-Xcode-template/1aeeb214d7c5455a03345a4031452ad4959677b8/{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-58.png -------------------------------------------------------------------------------- /{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beeware/briefcase-iOS-Xcode-template/1aeeb214d7c5455a03345a4031452ad4959677b8/{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-60.png -------------------------------------------------------------------------------- /{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beeware/briefcase-iOS-Xcode-template/1aeeb214d7c5455a03345a4031452ad4959677b8/{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-76.png -------------------------------------------------------------------------------- /{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beeware/briefcase-iOS-Xcode-template/1aeeb214d7c5455a03345a4031452ad4959677b8/{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-80.png -------------------------------------------------------------------------------- /{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-87.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beeware/briefcase-iOS-Xcode-template/1aeeb214d7c5455a03345a4031452ad4959677b8/{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Images.xcassets/AppIcon.appiconset/icon-87.png -------------------------------------------------------------------------------- /{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Images.xcassets/Splash.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "splash-640.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "splash-1280.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "splash-1920.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Images.xcassets/Splash.imageset/splash-1280.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beeware/briefcase-iOS-Xcode-template/1aeeb214d7c5455a03345a4031452ad4959677b8/{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Images.xcassets/Splash.imageset/splash-1280.png -------------------------------------------------------------------------------- /{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Images.xcassets/Splash.imageset/splash-1920.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beeware/briefcase-iOS-Xcode-template/1aeeb214d7c5455a03345a4031452ad4959677b8/{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Images.xcassets/Splash.imageset/splash-1920.png -------------------------------------------------------------------------------- /{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Images.xcassets/Splash.imageset/splash-640.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beeware/briefcase-iOS-Xcode-template/1aeeb214d7c5455a03345a4031452ad4959677b8/{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Images.xcassets/Splash.imageset/splash-640.png -------------------------------------------------------------------------------- /{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/Launch Screen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/app/README: -------------------------------------------------------------------------------- 1 | Your application code should be placed in this directory. 2 | 3 | The native code will be looking for a {{ cookiecutter.module_name }}/__main__.py file as the entry point. 4 | -------------------------------------------------------------------------------- /{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/app_packages.iphoneos/README: -------------------------------------------------------------------------------- 1 | This directory exists so that 3rd party iPhone device packages can be installed here. 2 | -------------------------------------------------------------------------------- /{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/app_packages.iphonesimulator/README: -------------------------------------------------------------------------------- 1 | This directory exists so that 3rd party iPhone Simulator packages can be installed here. 2 | -------------------------------------------------------------------------------- /{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/dylib-Info-template.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | 9 | CFBundleIdentifier 10 | 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | APPL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSupportedPlatforms 18 | 19 | iPhoneOS 20 | 21 | MinimumOSVersion 22 | {{ cookiecutter.min_os_version }} 23 | CFBundleVersion 24 | 1 25 | 26 | 27 | -------------------------------------------------------------------------------- /{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | -------------------------------------------------------------------------------- /{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // A main module for starting Python projects under iOS. 4 | // 5 | 6 | #import 7 | #import 8 | #include 9 | #include 10 | 11 | 12 | void crash_dialog(NSString *); 13 | NSString * format_traceback(PyObject *type, PyObject *value, PyObject *traceback); 14 | 15 | int main(int argc, char *argv[]) { 16 | int ret = 0; 17 | PyStatus status; 18 | PyPreConfig preconfig; 19 | PyConfig config; 20 | NSString *python_tag; 21 | NSString *python_home; 22 | NSString *app_module_name; 23 | NSString *path; 24 | NSString *traceback_str; 25 | wchar_t *wtmp_str; 26 | wchar_t *app_packages_path_str; 27 | const char* app_module_str; 28 | const char* nslog_script; 29 | PyObject *app_packages_path; 30 | PyObject *app_module; 31 | PyObject *module; 32 | PyObject *module_attr; 33 | PyObject *method_args; 34 | PyObject *result; 35 | PyObject *exc_type; 36 | PyObject *exc_value; 37 | PyObject *exc_traceback; 38 | PyObject *systemExit_code; 39 | 40 | @autoreleasepool { 41 | NSString *resourcePath = [[NSBundle mainBundle] resourcePath]; 42 | 43 | // Generate an isolated Python configuration. 44 | NSLog(@"Configuring isolated Python..."); 45 | PyPreConfig_InitIsolatedConfig(&preconfig); 46 | PyConfig_InitIsolatedConfig(&config); 47 | 48 | // Configure the Python interpreter: 49 | // Enforce UTF-8 encoding for stderr, stdout, file-system encoding and locale. 50 | // See https://docs.python.org/3/library/os.html#python-utf-8-mode. 51 | preconfig.utf8_mode = 1; 52 | // Don't buffer stdio. We want output to appears in the log immediately 53 | config.buffered_stdio = 0; 54 | // Don't write bytecode; we can't modify the app bundle 55 | // after it has been signed. 56 | config.write_bytecode = 0; 57 | // Isolated apps need to set the full PYTHONPATH manually. 58 | config.module_search_paths_set = 1; 59 | // For debugging - enable verbose mode. 60 | // config.verbose = 1; 61 | 62 | NSLog(@"Pre-initializing Python runtime..."); 63 | status = Py_PreInitialize(&preconfig); 64 | if (PyStatus_Exception(status)) { 65 | crash_dialog([NSString stringWithFormat:@"Unable to pre-initialize Python interpreter: %s", status.err_msg, nil]); 66 | PyConfig_Clear(&config); 67 | Py_ExitStatusException(status); 68 | } 69 | 70 | // Set the home for the Python interpreter 71 | python_tag = @"{{ cookiecutter.python_version|py_tag }}"; 72 | python_home = [NSString stringWithFormat:@"%@/python", resourcePath, nil]; 73 | NSLog(@"PythonHome: %@", python_home); 74 | wtmp_str = Py_DecodeLocale([python_home UTF8String], NULL); 75 | status = PyConfig_SetString(&config, &config.home, wtmp_str); 76 | if (PyStatus_Exception(status)) { 77 | crash_dialog([NSString stringWithFormat:@"Unable to set PYTHONHOME: %s", status.err_msg, nil]); 78 | PyConfig_Clear(&config); 79 | Py_ExitStatusException(status); 80 | } 81 | PyMem_RawFree(wtmp_str); 82 | 83 | // Determine the app module name 84 | app_module_name = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"MainModule"]; 85 | if (app_module_name == NULL) { 86 | NSLog(@"Unable to identify app module name."); 87 | } 88 | app_module_str = [app_module_name UTF8String]; 89 | status = PyConfig_SetBytesString(&config, &config.run_module, app_module_str); 90 | if (PyStatus_Exception(status)) { 91 | crash_dialog([NSString stringWithFormat:@"Unable to set app module name: %s", status.err_msg, nil]); 92 | PyConfig_Clear(&config); 93 | Py_ExitStatusException(status); 94 | } 95 | 96 | // Read the site config 97 | status = PyConfig_Read(&config); 98 | if (PyStatus_Exception(status)) { 99 | crash_dialog([NSString stringWithFormat:@"Unable to read site config: %s", status.err_msg, nil]); 100 | PyConfig_Clear(&config); 101 | Py_ExitStatusException(status); 102 | } 103 | 104 | // Set the full module path. This includes the stdlib, site-packages, and app code. 105 | NSLog(@"PYTHONPATH:"); 106 | // The unpacked form of the stdlib 107 | path = [NSString stringWithFormat:@"%@/lib/python%@", python_home, python_tag, nil]; 108 | NSLog(@"- %@", path); 109 | wtmp_str = Py_DecodeLocale([path UTF8String], NULL); 110 | status = PyWideStringList_Append(&config.module_search_paths, wtmp_str); 111 | if (PyStatus_Exception(status)) { 112 | crash_dialog([NSString stringWithFormat:@"Unable to set unpacked form of stdlib path: %s", status.err_msg, nil]); 113 | PyConfig_Clear(&config); 114 | Py_ExitStatusException(status); 115 | } 116 | PyMem_RawFree(wtmp_str); 117 | 118 | // The binary modules in the stdlib 119 | path = [NSString stringWithFormat:@"%@/lib/python%@/lib-dynload", python_home, python_tag, nil]; 120 | NSLog(@"- %@", path); 121 | wtmp_str = Py_DecodeLocale([path UTF8String], NULL); 122 | status = PyWideStringList_Append(&config.module_search_paths, wtmp_str); 123 | if (PyStatus_Exception(status)) { 124 | crash_dialog([NSString stringWithFormat:@"Unable to set unpacked form of stdlib path: %s", status.err_msg, nil]); 125 | PyConfig_Clear(&config); 126 | Py_ExitStatusException(status); 127 | } 128 | PyMem_RawFree(wtmp_str); 129 | 130 | // Add the app path 131 | path = [NSString stringWithFormat:@"%@/app", resourcePath, nil]; 132 | NSLog(@"- %@", path); 133 | wtmp_str = Py_DecodeLocale([path UTF8String], NULL); 134 | status = PyWideStringList_Append(&config.module_search_paths, wtmp_str); 135 | if (PyStatus_Exception(status)) { 136 | crash_dialog([NSString stringWithFormat:@"Unable to set app path: %s", status.err_msg, nil]); 137 | PyConfig_Clear(&config); 138 | Py_ExitStatusException(status); 139 | } 140 | PyMem_RawFree(wtmp_str); 141 | 142 | NSLog(@"Configure argc/argv..."); 143 | status = PyConfig_SetBytesArgv(&config, argc, argv); 144 | if (PyStatus_Exception(status)) { 145 | crash_dialog([NSString stringWithFormat:@"Unable to configure argc/argv: %s", status.err_msg, nil]); 146 | PyConfig_Clear(&config); 147 | Py_ExitStatusException(status); 148 | } 149 | 150 | NSLog(@"Initializing Python runtime..."); 151 | status = Py_InitializeFromConfig(&config); 152 | if (PyStatus_Exception(status)) { 153 | crash_dialog([NSString stringWithFormat:@"Unable to initialize Python interpreter: %s", status.err_msg, nil]); 154 | PyConfig_Clear(&config); 155 | Py_ExitStatusException(status); 156 | } 157 | 158 | @try { 159 | // Set the name of the python NSLog bootstrap script 160 | nslog_script = [ 161 | [[NSBundle mainBundle] pathForResource:@"app_packages/nslog" 162 | ofType:@"py"] cStringUsingEncoding:NSUTF8StringEncoding]; 163 | if (nslog_script == NULL) { 164 | NSLog(@"No Python NSLog handler found. stdout/stderr will not be captured."); 165 | NSLog(@"To capture stdout/stderr, add 'std-nslog' to your app dependencies."); 166 | } else { 167 | NSLog(@"Installing Python NSLog handler..."); 168 | FILE* fd = fopen(nslog_script, "r"); 169 | if (fd == NULL) { 170 | crash_dialog(@"Unable to open nslog.py"); 171 | exit(-1); 172 | } 173 | 174 | ret = PyRun_SimpleFileEx(fd, nslog_script, 1); 175 | fclose(fd); 176 | if (ret != 0) { 177 | crash_dialog(@"Unable to install Python NSLog handler"); 178 | exit(ret); 179 | } 180 | } 181 | 182 | 183 | // Adding the app_packages as site directory. 184 | // 185 | // This adds app_packages to sys.path and executes any .pth 186 | // files in that directory. 187 | path = [NSString stringWithFormat:@"%@/app_packages", resourcePath, nil]; 188 | app_packages_path_str = Py_DecodeLocale([path UTF8String], NULL); 189 | 190 | NSLog(@"Adding app_packages as site directory: %@", path); 191 | 192 | module = PyImport_ImportModule("site"); 193 | if (module == NULL) { 194 | crash_dialog(@"Could not import site module"); 195 | exit(-11); 196 | } 197 | 198 | module_attr = PyObject_GetAttrString(module, "addsitedir"); 199 | if (module_attr == NULL || !PyCallable_Check(module_attr)) { 200 | crash_dialog(@"Could not access site.addsitedir"); 201 | exit(-12); 202 | } 203 | 204 | app_packages_path = PyUnicode_FromWideChar(app_packages_path_str, wcslen(app_packages_path_str)); 205 | if (app_packages_path == NULL) { 206 | crash_dialog(@"Could not convert app_packages path to unicode"); 207 | exit(-13); 208 | } 209 | PyMem_RawFree(app_packages_path_str); 210 | 211 | method_args = Py_BuildValue("(O)", app_packages_path); 212 | if (method_args == NULL) { 213 | crash_dialog(@"Could not create arguments for site.addsitedir"); 214 | exit(-14); 215 | } 216 | 217 | result = PyObject_CallObject(module_attr, method_args); 218 | if (result == NULL) { 219 | crash_dialog(@"Could not add app_packages directory using site.addsitedir"); 220 | exit(-15); 221 | } 222 | 223 | 224 | // Start the app module. 225 | // 226 | // From here to Py_ObjectCall(runmodule...) is effectively 227 | // a copy of Py_RunMain() (and, more specifically, the 228 | // pymain_run_module() method); we need to re-implement it 229 | // because we need to be able to inspect the error state of 230 | // the interpreter, not just the return code of the module. 231 | NSLog(@"Running app module: %@", app_module_name); 232 | module = PyImport_ImportModule("runpy"); 233 | if (module == NULL) { 234 | crash_dialog(@"Could not import runpy module"); 235 | exit(-2); 236 | } 237 | 238 | module_attr = PyObject_GetAttrString(module, "_run_module_as_main"); 239 | if (module_attr == NULL) { 240 | crash_dialog(@"Could not access runpy._run_module_as_main"); 241 | exit(-3); 242 | } 243 | 244 | app_module = PyUnicode_FromString(app_module_str); 245 | if (app_module == NULL) { 246 | crash_dialog(@"Could not convert module name to unicode"); 247 | exit(-3); 248 | } 249 | 250 | method_args = Py_BuildValue("(Oi)", app_module, 0); 251 | if (method_args == NULL) { 252 | crash_dialog(@"Could not create arguments for runpy._run_module_as_main"); 253 | exit(-4); 254 | } 255 | 256 | // Print a separator to differentiate Python startup logs from app logs 257 | NSLog(@"---------------------------------------------------------------------------"); 258 | 259 | // Invoke the app module 260 | result = PyObject_Call(module_attr, method_args, NULL); 261 | 262 | if (result == NULL) { 263 | // Retrieve the current error state of the interpreter. 264 | PyErr_Fetch(&exc_type, &exc_value, &exc_traceback); 265 | PyErr_NormalizeException(&exc_type, &exc_value, &exc_traceback); 266 | 267 | if (exc_traceback == NULL) { 268 | crash_dialog(@"Could not retrieve traceback"); 269 | exit(-5); 270 | } 271 | 272 | traceback_str = NULL; 273 | if (PyErr_GivenExceptionMatches(exc_value, PyExc_SystemExit)) { 274 | systemExit_code = PyObject_GetAttrString(exc_value, "code"); 275 | if (systemExit_code == NULL) { 276 | traceback_str = @"Could not determine exit code"; 277 | ret = -10; 278 | } else if (systemExit_code == Py_None) { 279 | // SystemExit with a code of None; documented as a 280 | // return code of 0. 281 | ret = 0; 282 | } else if (PyLong_Check(systemExit_code)) { 283 | // SystemExit with error code 284 | ret = (int) PyLong_AsLong(systemExit_code); 285 | } else { 286 | // Any other SystemExit value - convert to a string, and 287 | // use the string as the traceback, and use the 288 | // documented SystemExit return value of 1. 289 | ret = 1; 290 | traceback_str = [NSString stringWithUTF8String:PyUnicode_AsUTF8(PyObject_Str(systemExit_code))]; 291 | } 292 | } else { 293 | // Non-SystemExit; likely an uncaught exception 294 | NSLog(@"---------------------------------------------------------------------------\n"); 295 | NSLog(@"Application quit abnormally (Exit code %d)!", ret); 296 | ret = -6; 297 | traceback_str = format_traceback(exc_type, exc_value, exc_traceback); 298 | } 299 | 300 | if (traceback_str != NULL) { 301 | // Display stack trace in the crash dialog. 302 | crash_dialog(traceback_str); 303 | } 304 | } else { 305 | // In a normal iOS application, the following line is what 306 | // actually runs the application. It requires that the 307 | // Objective-C runtime environment has a class named 308 | // "PythonAppDelegate". This project doesn't define 309 | // one, because Objective-C bridging isn't something 310 | // Python does out of the box. You'll need to use 311 | // a library like Rubicon-ObjC [1], Pyobjus [2] or 312 | // PyObjC [3] if you want to run an *actual* iOS app. 313 | // [1] http://beeware.org/rubicon 314 | // [2] http://pyobjus.readthedocs.org/ 315 | // [3] https://pythonhosted.org/pyobjc/ 316 | UIApplicationMain(argc, argv, nil, @"PythonAppDelegate"); 317 | } 318 | } 319 | @catch (NSException *exception) { 320 | crash_dialog([NSString stringWithFormat:@"Python runtime error: %@", [exception reason]]); 321 | ret = -7; 322 | } 323 | @finally { 324 | Py_Finalize(); 325 | } 326 | } 327 | 328 | exit(ret); 329 | return ret; 330 | } 331 | 332 | /** 333 | * Construct and display a modal dialog to the user that contains 334 | * details of an error during application execution (usually a traceback). 335 | */ 336 | void crash_dialog(NSString *details) { 337 | NSLog(@"Application has crashed!"); 338 | NSLog(@"========================\n%@", details); 339 | // TODO - acutally make this a dialog 340 | // NSString *full_message = [NSString stringWithFormat:@"An unexpected error occurred.\n%@", details]; 341 | // // Create a stack trace dialog 342 | // [UIAlertController alertControllerWithTitle:@"Application has crashed" 343 | // message:full_message 344 | // preferredStyle:UIAlertControllerStyleAlert]; 345 | } 346 | 347 | /** 348 | * Convert a Python traceback object into a user-suitable string, stripping off 349 | * stack context that comes from this stub binary. 350 | * 351 | * If any error occurs processing the traceback, the error message returned 352 | * will describe the mode of failure. 353 | */ 354 | NSString *format_traceback(PyObject *type, PyObject *value, PyObject *traceback) { 355 | NSRegularExpression *regex; 356 | NSString *traceback_str; 357 | PyObject *traceback_list; 358 | PyObject *traceback_module; 359 | PyObject *format_exception; 360 | PyObject *traceback_unicode; 361 | PyObject *inner_traceback; 362 | 363 | // Drop the top two stack frames; these are internal 364 | // wrapper logic, and not in the control of the user. 365 | for (int i = 0; i < 2; i++) { 366 | inner_traceback = PyObject_GetAttrString(traceback, "tb_next"); 367 | if (inner_traceback != NULL) { 368 | traceback = inner_traceback; 369 | } 370 | } 371 | 372 | // Format the traceback. 373 | traceback_module = PyImport_ImportModule("traceback"); 374 | if (traceback_module == NULL) { 375 | NSLog(@"Could not import traceback"); 376 | return @"Could not import traceback"; 377 | } 378 | 379 | format_exception = PyObject_GetAttrString(traceback_module, "format_exception"); 380 | if (format_exception && PyCallable_Check(format_exception)) { 381 | traceback_list = PyObject_CallFunctionObjArgs(format_exception, type, value, traceback, NULL); 382 | } else { 383 | NSLog(@"Could not find 'format_exception' in 'traceback' module"); 384 | return @"Could not find 'format_exception' in 'traceback' module"; 385 | } 386 | if (traceback_list == NULL) { 387 | NSLog(@"Could not format traceback"); 388 | return @"Could not format traceback"; 389 | } 390 | 391 | traceback_unicode = PyUnicode_Join(PyUnicode_FromString(""), traceback_list); 392 | traceback_str = [NSString stringWithUTF8String:PyUnicode_AsUTF8(PyObject_Str(traceback_unicode))]; 393 | 394 | // Take the opportunity to clean up the source path, 395 | // so paths only refer to the "app local" path. 396 | regex = [NSRegularExpression regularExpressionWithPattern:@"^ File \"/.*/(.*?).app/Library" 397 | options:NSRegularExpressionAnchorsMatchLines 398 | error:nil]; 399 | traceback_str = [regex stringByReplacingMatchesInString:traceback_str 400 | options:0 401 | range:NSMakeRange(0, [traceback_str length]) 402 | withTemplate:@" File \"$1.app/Library"]; 403 | 404 | return traceback_str; 405 | } 406 | -------------------------------------------------------------------------------- /{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/{{ cookiecutter.class_name }}-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | {{ cookiecutter.bundle_identifier }} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | {{ cookiecutter.version }} 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | {{ cookiecutter.build }} 25 | ITSAppUsesNonExemptEncryption 26 | {{ cookiecutter.uses_non_exempt_encryption|plist_value }} 27 | LSRequiresIPhoneOS 28 | 29 | UIRequiresFullScreen 30 | 31 | UILaunchStoryboardName 32 | Launch Screen 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | MainModule 47 | {{ cookiecutter.module_name }} 48 | UIApplicationSceneManifest 49 | 50 | UIApplicationSupportsMultipleScenes 51 | 52 | UISceneConfigurations 53 | 54 | 55 | {%- if cookiecutter.info -%} 56 | {%- for permission, value in cookiecutter.info.items() %} 57 | {{ permission }} 58 | {{ value|plist_value }} 59 | {%- endfor -%} 60 | {%- endif %} 61 | 62 | 63 | -------------------------------------------------------------------------------- /{{ cookiecutter.format }}/{{ cookiecutter.class_name }}/{{ cookiecutter.class_name }}-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_3_0 10 | #warning "This project uses features only available in iOS SDK 3.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #endif 17 | -------------------------------------------------------------------------------- /{{ cookiecutter.format }}/{{ cookiecutter.formal_name }}.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 55; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 600000000000000000000800 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 610000000000000000000800 /* CoreGraphics.framework */; }; 11 | 600000000000000000000900 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 610000000000000000000900 /* Foundation.framework */; }; 12 | 600000000000000000000A00 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 610000000000000000000A00 /* UIKit.framework */; }; 13 | 600000000000000000100000 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 60796EEE19190F4100A9926B /* InfoPlist.strings */; }; 14 | 600000000000000000100100 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 610000000000000000100300 /* main.m */; }; 15 | 600000000000000000100200 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 610000000000000000100800 /* Images.xcassets */; }; 16 | 600000000000000000100300 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 610000000000000000100900 /* Launch Screen.storyboard */; }; 17 | 60754F822AA5926A0013A4FB /* dylib-Info-template.plist in Resources */ = {isa = PBXBuildFile; fileRef = 60754F802AA57C440013A4FB /* dylib-Info-template.plist */; }; 18 | 60813D372B02EED200EFB492 /* Python.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60813D352B02EBFC00EFB492 /* Python.xcframework */; }; 19 | 60813D382B02EED200EFB492 /* Python.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 60813D352B02EBFC00EFB492 /* Python.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 20 | 60A04BC728B35FD000DAA9E5 /* app in Resources */ = {isa = PBXBuildFile; fileRef = 610000000000000000100100 /* app */; }; 21 | 60C0057928F7DB9A008B9E84 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60C0057828F7DB9A008B9E84 /* WebKit.framework */; }; 22 | /* End PBXBuildFile section */ 23 | 24 | /* Begin PBXCopyFilesBuildPhase section */ 25 | 6060E7A12AF8BF4400C04AE0 /* Embed Frameworks */ = { 26 | isa = PBXCopyFilesBuildPhase; 27 | buildActionMask = 2147483647; 28 | dstPath = ""; 29 | dstSubfolderSpec = 10; 30 | files = ( 31 | 60813D382B02EED200EFB492 /* Python.xcframework in Embed Frameworks */, 32 | ); 33 | name = "Embed Frameworks"; 34 | runOnlyForDeploymentPostprocessing = 0; 35 | }; 36 | /* End PBXCopyFilesBuildPhase section */ 37 | 38 | /* Begin PBXFileReference section */ 39 | 60754F802AA57C440013A4FB /* dylib-Info-template.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "dylib-Info-template.plist"; sourceTree = ""; }; 40 | 60813D352B02EBFC00EFB492 /* Python.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = Python.xcframework; sourceTree = ""; }; 41 | 60A421FC2AB4256300E46BC3 /* app_packages.iphonesimulator */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app_packages.iphonesimulator; sourceTree = ""; }; 42 | 60A421FD2AB4256300E46BC3 /* app_packages.iphoneos */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app_packages.iphoneos; sourceTree = ""; }; 43 | 60C0057828F7DB9A008B9E84 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; }; 44 | 610000000000000000000800 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 45 | 610000000000000000000900 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 46 | 610000000000000000000A00 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 47 | 610000000000000000100100 /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app; sourceTree = ""; }; 48 | 610000000000000000100300 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 49 | 610000000000000000100400 /* {{ cookiecutter.formal_name }}.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "{{ cookiecutter.formal_name }}.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 50 | 610000000000000000100500 /* {{ cookiecutter.class_name }}-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "{{ cookiecutter.class_name }}-Info.plist"; sourceTree = ""; }; 51 | 610000000000000000100600 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 52 | 610000000000000000100700 /* {{ cookiecutter.class_name }}-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "{{ cookiecutter.class_name }}-Prefix.pch"; sourceTree = ""; }; 53 | 610000000000000000100800 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 54 | 610000000000000000100900 /* Launch Screen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = "Launch Screen.storyboard"; sourceTree = ""; }; 55 | /* End PBXFileReference section */ 56 | 57 | /* Begin PBXFrameworksBuildPhase section */ 58 | 60796EDF19190F4100A9926B /* Frameworks */ = { 59 | isa = PBXFrameworksBuildPhase; 60 | buildActionMask = 2147483647; 61 | files = ( 62 | 600000000000000000000800 /* CoreGraphics.framework in Frameworks */, 63 | 600000000000000000000900 /* Foundation.framework in Frameworks */, 64 | 60813D372B02EED200EFB492 /* Python.xcframework in Frameworks */, 65 | 600000000000000000000A00 /* UIKit.framework in Frameworks */, 66 | 60C0057928F7DB9A008B9E84 /* WebKit.framework in Frameworks */, 67 | ); 68 | runOnlyForDeploymentPostprocessing = 0; 69 | }; 70 | /* End PBXFrameworksBuildPhase section */ 71 | 72 | /* Begin PBXGroup section */ 73 | 60796ED919190F4100A9926B = { 74 | isa = PBXGroup; 75 | children = ( 76 | 60A04BC228B35E9F00DAA9E5 /* Support */, 77 | 60796EEB19190F4100A9926B /* {{ cookiecutter.class_name }} */, 78 | 60796EE419190F4100A9926B /* Frameworks */, 79 | 60796EE319190F4100A9926B /* Products */, 80 | ); 81 | sourceTree = ""; 82 | }; 83 | 60796EE319190F4100A9926B /* Products */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | 610000000000000000100400 /* {{ cookiecutter.formal_name }}.app */, 87 | ); 88 | name = Products; 89 | sourceTree = ""; 90 | }; 91 | 60796EE419190F4100A9926B /* Frameworks */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | 610000000000000000000800 /* CoreGraphics.framework */, 95 | 610000000000000000000900 /* Foundation.framework */, 96 | 610000000000000000000A00 /* UIKit.framework */, 97 | 60C0057828F7DB9A008B9E84 /* WebKit.framework */, 98 | ); 99 | name = Frameworks; 100 | sourceTree = ""; 101 | }; 102 | 60796EEB19190F4100A9926B /* {{ cookiecutter.class_name }} */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | 610000000000000000100100 /* app */, 106 | 60A421FD2AB4256300E46BC3 /* app_packages.iphoneos */, 107 | 60A421FC2AB4256300E46BC3 /* app_packages.iphonesimulator */, 108 | 610000000000000000100800 /* Images.xcassets */, 109 | 60796EEC19190F4100A9926B /* Supporting Files */, 110 | ); 111 | path = "{{ cookiecutter.class_name }}"; 112 | sourceTree = ""; 113 | }; 114 | 60796EEC19190F4100A9926B /* Supporting Files */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | 610000000000000000100300 /* main.m */, 118 | 610000000000000000100500 /* {{ cookiecutter.class_name }}-Info.plist */, 119 | 610000000000000000100700 /* {{ cookiecutter.class_name }}-Prefix.pch */, 120 | 60796EEE19190F4100A9926B /* InfoPlist.strings */, 121 | 610000000000000000100900 /* Launch Screen.storyboard */, 122 | 60754F802AA57C440013A4FB /* dylib-Info-template.plist */, 123 | ); 124 | name = "Supporting Files"; 125 | sourceTree = ""; 126 | }; 127 | 60A04BC228B35E9F00DAA9E5 /* Support */ = { 128 | isa = PBXGroup; 129 | children = ( 130 | 60813D352B02EBFC00EFB492 /* Python.xcframework */, 131 | ); 132 | path = Support; 133 | sourceTree = ""; 134 | }; 135 | /* End PBXGroup section */ 136 | 137 | /* Begin PBXNativeTarget section */ 138 | 60796EE119190F4100A9926B /* {{ cookiecutter.formal_name }} */ = { 139 | isa = PBXNativeTarget; 140 | buildConfigurationList = 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "{{ cookiecutter.formal_name }}" */; 141 | buildPhases = ( 142 | 60796EDE19190F4100A9926B /* Sources */, 143 | 60796EDF19190F4100A9926B /* Frameworks */, 144 | 60796EE019190F4100A9926B /* Resources */, 145 | 609384A628B5B958005B2A5D /* Install target specific Python modules */, 146 | 609384A528B45C86005B2A5D /* Sign Python Binary Modules */, 147 | 6060E7A12AF8BF4400C04AE0 /* Embed Frameworks */, 148 | ); 149 | buildRules = ( 150 | ); 151 | dependencies = ( 152 | ); 153 | name = "{{ cookiecutter.formal_name }}"; 154 | productName = {{ cookiecutter.app_name }}; 155 | productReference = 610000000000000000100400 /* {{ cookiecutter.formal_name }}.app */; 156 | productType = "com.apple.product-type.application"; 157 | }; 158 | /* End PBXNativeTarget section */ 159 | 160 | /* Begin PBXProject section */ 161 | 60796EDA19190F4100A9926B /* Project object */ = { 162 | isa = PBXProject; 163 | attributes = { 164 | BuildIndependentTargetsInParallel = YES; 165 | LastUpgradeCheck = 1630; 166 | ORGANIZATIONNAME = "Russell Keith-Magee"; 167 | }; 168 | buildConfigurationList = 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "{{ cookiecutter.formal_name }}" */; 169 | compatibilityVersion = "Xcode 13.0"; 170 | developmentRegion = en; 171 | hasScannedForEncodings = 0; 172 | knownRegions = ( 173 | en, 174 | Base, 175 | ); 176 | mainGroup = 60796ED919190F4100A9926B; 177 | productRefGroup = 60796EE319190F4100A9926B /* Products */; 178 | projectDirPath = ""; 179 | projectRoot = ""; 180 | targets = ( 181 | 60796EE119190F4100A9926B /* {{ cookiecutter.formal_name }} */, 182 | ); 183 | }; 184 | /* End PBXProject section */ 185 | 186 | /* Begin PBXResourcesBuildPhase section */ 187 | 60796EE019190F4100A9926B /* Resources */ = { 188 | isa = PBXResourcesBuildPhase; 189 | buildActionMask = 2147483647; 190 | files = ( 191 | 600000000000000000100000 /* InfoPlist.strings in Resources */, 192 | 600000000000000000100200 /* Images.xcassets in Resources */, 193 | 600000000000000000100300 /* Launch Screen.storyboard in Resources */, 194 | 60A04BC728B35FD000DAA9E5 /* app in Resources */, 195 | 60754F822AA5926A0013A4FB /* dylib-Info-template.plist in Resources */, 196 | ); 197 | runOnlyForDeploymentPostprocessing = 0; 198 | }; 199 | /* End PBXResourcesBuildPhase section */ 200 | 201 | /* Begin PBXShellScriptBuildPhase section */ 202 | 609384A528B45C86005B2A5D /* Sign Python Binary Modules */ = { 203 | isa = PBXShellScriptBuildPhase; 204 | alwaysOutOfDate = 1; 205 | buildActionMask = 2147483647; 206 | files = ( 207 | ); 208 | inputFileListPaths = ( 209 | ); 210 | inputPaths = ( 211 | ); 212 | name = "Sign Python Binary Modules"; 213 | outputFileListPaths = ( 214 | ); 215 | outputPaths = ( 216 | ); 217 | runOnlyForDeploymentPostprocessing = 0; 218 | shellPath = /bin/sh; 219 | shellScript = "set -e\n\ninstall_dylib () {\n INSTALL_BASE=$1\n FULL_EXT=$2\n\n # The name of the extension file\n EXT=$(basename \"$FULL_EXT\")\n # The name and location of the module\n MODULE_PATH=$(dirname \"$FULL_EXT\") \n MODULE_NAME=$(echo $EXT | cut -d \".\" -f 1) \n # The location of the extension file, relative to the bundle\n RELATIVE_EXT=${FULL_EXT#$CODESIGNING_FOLDER_PATH/} \n # The path to the extension file, relative to the install base\n PYTHON_EXT=${RELATIVE_EXT/$INSTALL_BASE/}\n # The full dotted name of the extension module, constructed from the file path.\n FULL_MODULE_NAME=$(echo $PYTHON_EXT | cut -d \".\" -f 1 | tr \"/\" \".\"); \n # A bundle identifier; not actually used, but required by Xcode framework packaging\n FRAMEWORK_BUNDLE_ID=$(echo $PRODUCT_BUNDLE_IDENTIFIER.$FULL_MODULE_NAME | tr \"_\" \"-\")\n # The name of the framework folder.\n FRAMEWORK_FOLDER=\"Frameworks/$FULL_MODULE_NAME.framework\"\n\n # If the framework folder doesn't exist, create it.\n if [ ! -d \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\" ]; then\n echo \"Creating framework for $RELATIVE_EXT\" \n mkdir -p \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\"\n\n cp \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n plutil -replace CFBundleExecutable -string \"$FULL_MODULE_NAME\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n plutil -replace CFBundleIdentifier -string \"$FRAMEWORK_BUNDLE_ID\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n fi\n \n echo \"Installing binary for $FRAMEWORK_FOLDER/$FULL_MODULE_NAME\" \n mv \"$FULL_EXT\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME\"\n if [ -e \"$MODULE_PATH/$MODULE_NAME.xcprivacy\" ];then \n echo \"Installing XCPrivacy file for $FRAMEWORK_FOLDER/$FULL_MODULE_NAME\"\n XCPRIVACY_FILE=\"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/PrivacyInfo.xcprivacy\"\n if [ -e \"$XCPRIVACY_FILE\" ]; then\n rm -rf \"$XCPRIVACY_FILE\"\n fi\n mv \"$MODULE_PATH/$MODULE_NAME.xcprivacy\" \"$XCPRIVACY_FILE\"\n fi\n if [ -e \"$FULL_EXT.dSYM\" ];then \n echo \"Installing dSYM file for $FRAMEWORK_FOLDER/$FULL_MODULE_NAME\"\n DSYM_FILE=\"$BUILT_PRODUCTS_DIR/$FULL_MODULE_NAME.dSYM\"\n if [ -e \"$DSYM_FILE\" ]; then\n rm -rf \"$DSYM_FILE\"\n fi\n mv \"$FULL_EXT.dSYM\" \"$DSYM_FILE\"\n plutil -replace CFBundleIdentifier -string \"$FRAMEWORK_BUNDLE_ID\" \"$BUILT_PRODUCTS_DIR/$FULL_MODULE_NAME.dSYM/Contents/Info.plist\"\n mv \"$BUILT_PRODUCTS_DIR/$FULL_MODULE_NAME.dSYM/Contents/Resources/DWARF/$EXT\" \"$BUILT_PRODUCTS_DIR/$FULL_MODULE_NAME.dSYM/Contents/Resources/DWARF/$FULL_MODULE_NAME\"\n find \"$BUILT_PRODUCTS_DIR/$FULL_MODULE_NAME.dSYM/Contents/Resources\" -name \"$EXT.*\" | while read YML; do\n mv \"$YML\" \"$(dirname \"$YML\")/$FULL_MODULE_NAME.yml\"\n done\n fi\n # Create a placeholder .fwork file where the .so was\n echo \"$FRAMEWORK_FOLDER/$FULL_MODULE_NAME\" > ${FULL_EXT%.so}.fwork\n # Create a back reference to the .so file location in the framework\n echo \"${RELATIVE_EXT%.so}.fwork\" > \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME.origin\" \n}\n\necho \"Install standard library extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/python/lib/python{{ cookiecutter.python_version|py_tag }}/lib-dynload\" -name \"*.so\" -not -path \"*/*.so.dSYM/*\" | while read FULL_EXT; do\n install_dylib python/lib/python{{ cookiecutter.python_version|py_tag }}/lib-dynload/ \"$FULL_EXT\"\ndone\necho \"Install app package extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/app_packages\" -name \"*.so\" -not -path \"*/*.so.dSYM/*\" | while read FULL_EXT; do\n install_dylib app_packages/ \"$FULL_EXT\"\ndone\necho \"Install app extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/app\" -name \"*.so\" -not -path \"*/*.so.dSYM/*\" | while read FULL_EXT; do\n install_dylib app/ \"$FULL_EXT\"\ndone\n\n# Clean up dylib template \nrm -f \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\"\n\necho \"Signing frameworks as $EXPANDED_CODE_SIGN_IDENTITY_NAME ($EXPANDED_CODE_SIGN_IDENTITY)...\"\nfind \"$CODESIGNING_FOLDER_PATH/Frameworks\" -name \"*.framework\" -exec /usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" ${OTHER_CODE_SIGN_FLAGS:-} -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der \"{}\" \\; \n"; 220 | }; 221 | 609384A628B5B958005B2A5D /* Install target specific Python modules */ = { 222 | isa = PBXShellScriptBuildPhase; 223 | alwaysOutOfDate = 1; 224 | buildActionMask = 2147483647; 225 | files = ( 226 | ); 227 | inputFileListPaths = ( 228 | ); 229 | inputPaths = ( 230 | ); 231 | name = "Install target specific Python modules"; 232 | outputFileListPaths = ( 233 | ); 234 | outputPaths = ( 235 | ); 236 | runOnlyForDeploymentPostprocessing = 0; 237 | shellPath = /bin/sh; 238 | shellScript = "set -e\n\nmkdir -p \"$CODESIGNING_FOLDER_PATH/python/lib\"\nif [ \"$EFFECTIVE_PLATFORM_NAME\" = \"-iphonesimulator\" ]; then\n echo \"Installing Python modules for iOS Simulator\"\n PYTHON_SLICE=\"$PROJECT_DIR/Support/Python.xcframework/ios-arm64_x86_64-simulator\"\n PACKAGES_PATH=\"$PROJECT_DIR/{{ cookiecutter.class_name }}/app_packages.iphonesimulator\"\nelse\n echo \"Installing Python modules for iOS Device\"\n PYTHON_SLICE=\"$PROJECT_DIR/Support/Python.xcframework/ios-arm64-simulator\"\n PACKAGES_PATH=\"$PROJECT_DIR/{{ cookiecutter.class_name }}/app_packages.iphoneos\"\nfi\n\nrsync -au --delete \"$PYTHON_SLICE/lib/\" \"$CODESIGNING_FOLDER_PATH/python/lib/\" \nif [ -e \"$PYTHON_SLICE/Python.dSYM\" ]; then \n rsync -au --delete \"$PYTHON_SLICE/Python.dSYM\" \"$BUILT_PRODUCTS_DIR\"\nfi\nrsync -au --delete \"$PACKAGES_PATH/\" \"$CODESIGNING_FOLDER_PATH/app_packages\"\n"; 239 | showEnvVarsInLog = 0; 240 | }; 241 | /* End PBXShellScriptBuildPhase section */ 242 | 243 | /* Begin PBXSourcesBuildPhase section */ 244 | 60796EDE19190F4100A9926B /* Sources */ = { 245 | isa = PBXSourcesBuildPhase; 246 | buildActionMask = 2147483647; 247 | files = ( 248 | 600000000000000000100100 /* main.m in Sources */, 249 | ); 250 | runOnlyForDeploymentPostprocessing = 0; 251 | }; 252 | /* End PBXSourcesBuildPhase section */ 253 | 254 | /* Begin PBXVariantGroup section */ 255 | 60796EEE19190F4100A9926B /* InfoPlist.strings */ = { 256 | isa = PBXVariantGroup; 257 | children = ( 258 | 610000000000000000100600 /* en */, 259 | ); 260 | name = InfoPlist.strings; 261 | sourceTree = ""; 262 | }; 263 | /* End PBXVariantGroup section */ 264 | 265 | /* Begin XCBuildConfiguration section */ 266 | 60796F0C19190F4100A9926B /* Debug */ = { 267 | isa = XCBuildConfiguration; 268 | buildSettings = { 269 | ALWAYS_SEARCH_USER_PATHS = NO; 270 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 271 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 272 | CLANG_CXX_LIBRARY = "libc++"; 273 | CLANG_ENABLE_MODULES = YES; 274 | CLANG_ENABLE_OBJC_ARC = YES; 275 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 276 | CLANG_WARN_BOOL_CONVERSION = YES; 277 | CLANG_WARN_COMMA = YES; 278 | CLANG_WARN_CONSTANT_CONVERSION = YES; 279 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 280 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 281 | CLANG_WARN_EMPTY_BODY = YES; 282 | CLANG_WARN_ENUM_CONVERSION = YES; 283 | CLANG_WARN_INFINITE_RECURSION = YES; 284 | CLANG_WARN_INT_CONVERSION = YES; 285 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 286 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 287 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 288 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 289 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 290 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 291 | CLANG_WARN_STRICT_PROTOTYPES = YES; 292 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 293 | CLANG_WARN_UNREACHABLE_CODE = YES; 294 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 295 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 296 | COPY_PHASE_STRIP = NO; 297 | ENABLE_STRICT_OBJC_MSGSEND = YES; 298 | ENABLE_TESTABILITY = YES; 299 | ENABLE_USER_SCRIPT_SANDBOXING = NO; 300 | FRAMEWORK_SEARCH_PATHS = "\"$(PROJECT_DIR)\""; 301 | GCC_C_LANGUAGE_STANDARD = gnu99; 302 | GCC_DYNAMIC_NO_PIC = NO; 303 | GCC_NO_COMMON_BLOCKS = YES; 304 | GCC_OPTIMIZATION_LEVEL = 0; 305 | GCC_PREPROCESSOR_DEFINITIONS = ( 306 | "DEBUG=1", 307 | "$(inherited)", 308 | ); 309 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 310 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 311 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 312 | GCC_WARN_UNDECLARED_SELECTOR = YES; 313 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 314 | GCC_WARN_UNUSED_FUNCTION = YES; 315 | GCC_WARN_UNUSED_VARIABLE = YES; 316 | HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\""; 317 | IPHONEOS_DEPLOYMENT_TARGET = {{ cookiecutter.min_os_version }}; 318 | ONLY_ACTIVE_ARCH = YES; 319 | SDKROOT = iphoneos; 320 | TARGETED_DEVICE_FAMILY = "1,2"; 321 | }; 322 | name = Debug; 323 | }; 324 | 60796F0D19190F4100A9926B /* Release */ = { 325 | isa = XCBuildConfiguration; 326 | buildSettings = { 327 | ALWAYS_SEARCH_USER_PATHS = NO; 328 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 329 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 330 | CLANG_CXX_LIBRARY = "libc++"; 331 | CLANG_ENABLE_MODULES = YES; 332 | CLANG_ENABLE_OBJC_ARC = YES; 333 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 334 | CLANG_WARN_BOOL_CONVERSION = YES; 335 | CLANG_WARN_COMMA = YES; 336 | CLANG_WARN_CONSTANT_CONVERSION = YES; 337 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 338 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 339 | CLANG_WARN_EMPTY_BODY = YES; 340 | CLANG_WARN_ENUM_CONVERSION = YES; 341 | CLANG_WARN_INFINITE_RECURSION = YES; 342 | CLANG_WARN_INT_CONVERSION = YES; 343 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 344 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 345 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 346 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 347 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 348 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 349 | CLANG_WARN_STRICT_PROTOTYPES = YES; 350 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 351 | CLANG_WARN_UNREACHABLE_CODE = YES; 352 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 353 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 354 | COPY_PHASE_STRIP = YES; 355 | ENABLE_NS_ASSERTIONS = NO; 356 | ENABLE_STRICT_OBJC_MSGSEND = YES; 357 | ENABLE_TESTABILITY = YES; 358 | ENABLE_USER_SCRIPT_SANDBOXING = NO; 359 | FRAMEWORK_SEARCH_PATHS = "\"$(PROJECT_DIR)\""; 360 | GCC_C_LANGUAGE_STANDARD = gnu99; 361 | GCC_NO_COMMON_BLOCKS = YES; 362 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 363 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 364 | GCC_WARN_UNDECLARED_SELECTOR = YES; 365 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 366 | GCC_WARN_UNUSED_FUNCTION = YES; 367 | GCC_WARN_UNUSED_VARIABLE = YES; 368 | HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\""; 369 | IPHONEOS_DEPLOYMENT_TARGET = {{ cookiecutter.min_os_version }}; 370 | SDKROOT = iphoneos; 371 | TARGETED_DEVICE_FAMILY = "1,2"; 372 | VALIDATE_PRODUCT = YES; 373 | }; 374 | name = Release; 375 | }; 376 | 60796F0F19190F4100A9926B /* Debug */ = { 377 | isa = XCBuildConfiguration; 378 | buildSettings = { 379 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 380 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; 381 | ENABLE_BITCODE = NO; 382 | FRAMEWORK_SEARCH_PATHS = "\"$(PROJECT_DIR)/Support\""; 383 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 384 | GCC_PREFIX_HEADER = "{{ cookiecutter.class_name }}/{{ cookiecutter.class_name }}-Prefix.pch"; 385 | HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\""; 386 | INFOPLIST_FILE = "{{ cookiecutter.class_name }}/{{ cookiecutter.class_name }}-Info.plist"; 387 | IPHONEOS_DEPLOYMENT_TARGET = {{ cookiecutter.min_os_version }}; 388 | LD_RUNPATH_SEARCH_PATHS = ( 389 | "$(inherited)", 390 | "@executable_path/Frameworks", 391 | ); 392 | PRODUCT_BUNDLE_IDENTIFIER = "{{ cookiecutter.bundle_identifier }}"; 393 | PRODUCT_NAME = "$(TARGET_NAME)"; 394 | WRAPPER_EXTENSION = app; 395 | }; 396 | name = Debug; 397 | }; 398 | 60796F1019190F4100A9926B /* Release */ = { 399 | isa = XCBuildConfiguration; 400 | buildSettings = { 401 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 402 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; 403 | ENABLE_BITCODE = NO; 404 | ENABLE_TESTABILITY = YES; 405 | FRAMEWORK_SEARCH_PATHS = "\"$(PROJECT_DIR)/Support\""; 406 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 407 | GCC_PREFIX_HEADER = "{{ cookiecutter.class_name }}/{{ cookiecutter.class_name }}-Prefix.pch"; 408 | HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\""; 409 | INFOPLIST_FILE = "{{ cookiecutter.class_name }}/{{ cookiecutter.class_name }}-Info.plist"; 410 | IPHONEOS_DEPLOYMENT_TARGET = {{ cookiecutter.min_os_version }}; 411 | LD_RUNPATH_SEARCH_PATHS = ( 412 | "$(inherited)", 413 | "@executable_path/Frameworks", 414 | ); 415 | PRODUCT_BUNDLE_IDENTIFIER = "{{ cookiecutter.bundle_identifier }}"; 416 | PRODUCT_NAME = "$(TARGET_NAME)"; 417 | WRAPPER_EXTENSION = app; 418 | }; 419 | name = Release; 420 | }; 421 | /* End XCBuildConfiguration section */ 422 | 423 | /* Begin XCConfigurationList section */ 424 | 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "{{ cookiecutter.formal_name }}" */ = { 425 | isa = XCConfigurationList; 426 | buildConfigurations = ( 427 | 60796F0C19190F4100A9926B /* Debug */, 428 | 60796F0D19190F4100A9926B /* Release */, 429 | ); 430 | defaultConfigurationIsVisible = 0; 431 | defaultConfigurationName = Release; 432 | }; 433 | 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "{{ cookiecutter.formal_name }}" */ = { 434 | isa = XCConfigurationList; 435 | buildConfigurations = ( 436 | 60796F0F19190F4100A9926B /* Debug */, 437 | 60796F1019190F4100A9926B /* Release */, 438 | ); 439 | defaultConfigurationIsVisible = 0; 440 | defaultConfigurationName = Release; 441 | }; 442 | /* End XCConfigurationList section */ 443 | }; 444 | rootObject = 60796EDA19190F4100A9926B /* Project object */; 445 | } 446 | --------------------------------------------------------------------------------