├── .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 |
--------------------------------------------------------------------------------