Merge: doc: fixed some typos and other misc. corrections
[nit.git] / src / platform / xcode_templates.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Templates and other services to create XCode projects
16 module xcode_templates
17
18 import template
19
20 import platform
21 import compiler::abstract_compiler
22
23 redef class Sys
24 # Map to identify the PBX file type for a given file extension
25 private var pbx_file_types: Map[String, String] is lazy do
26 var map = new HashMap[String, String]
27
28 # Source code
29 map["m"] = "sourcecode.c.objc"
30 map["c"] = "sourcecode.c.c"
31 map["h"] = "sourcecode.c.h"
32 map["cpp"] = "sourcecode.cpp.cpp"
33 map["hpp"] = "sourcecode.cpp.h"
34 map["vsh"] = "sourcecode.glsl"
35 map["fsh"] = "sourcecode.glsl"
36
37 # Images
38 map["png"] = "image.png"
39 map["gif"] = "image.gif"
40 map["jpg"] = "image.jpeg"
41 map["jpeg"] = "image.jpeg"
42 map["pdf"] = "image.pdf"
43 map["ico"] = "image.ico"
44
45 # Others
46 map["app"] = "wrapper.application"
47 map["plist"] = "text.plist.xml"
48 map["storyboard"] = "file.storyboard"
49 map["xib"] = "file.xib"
50 map["xcassets"] = "folder.assetcatalog"
51 map["xctest"] = "wrapper.cfbundle"
52
53 return map
54 end
55
56 # Generator of PBX UUIDs quique to an execution of the compiler
57 private var pbx_uuid_generator = new PbxUUIDGenerator is lazy
58 end
59
60 # Generator of PBX UUIDs
61 #
62 # PBX UUID are composed of 24 hex characters.
63 # They only need to be unique within the same project.
64 #
65 # This implementation simply counts upward from 0.
66 class PbxUUIDGenerator
67 private var seed = 0
68
69 # Generate a new UUID
70 fun next_uuid: String
71 do
72 seed += 1
73
74 var hex_val = seed.to_hex.to_upper
75 return "0"*(24-hex_val.length) + hex_val
76 end
77 end
78
79 # Reference to a file for the PBX format of a project file
80 #
81 # TODO create subclasses for different file types, this is currently for
82 # compilable source files only.
83 class PbxFile
84
85 # Path to `self`
86 var path: String
87
88 # Compiler flags for this source file
89 var cflags: String = "" is writable
90
91 # UUID for build elements
92 private var build_uuid: String = sys.pbx_uuid_generator.next_uuid is lazy
93
94 # File reference UUID
95 private var ref_uuid: String = sys.pbx_uuid_generator.next_uuid is lazy
96
97 # Documentation to add besides this file in the template
98 private fun doc: String do return path
99
100 # PBX file type for `self`
101 fun file_type: String
102 do
103 var map = sys.pbx_file_types
104 var ext = path.file_extension
105 if ext != null and map.keys.has(ext) then return map[ext]
106 return "unknown"
107 end
108
109 # PBX description of this file
110 private fun description: Writable
111 do
112 var extra = ""
113 var cflags = cflags
114 if not cflags.is_empty then extra = "\nsettings = \{COMPILER_FLAGS = \"{cflags}\"; \};"
115
116 return """
117 {{{ref_uuid}}} /* {{{doc}}} */ = {
118 isa = PBXFileReference;
119 fileEncoding = 4;
120 lastKnownFileType = {{{file_type}}};
121 path = '{{{path}}}';
122 sourceTree = "<group>";{{{extra}}}
123 };
124 """
125 end
126
127 private fun add_to_project(project: PbxprojectTemplate)
128 do
129 project.source_files.add self
130 project.files.add self
131 end
132 end
133
134 # Template for a PBX project file, usually a `project.pbcproj`
135 #
136 # This file list all information required to build an XCode project.
137 # It would usually be written and read by XCode.
138 # From the command line, xcodebuild can read this file but not write it.
139 #
140 # Information in the file (simplified list):
141 #
142 # * Compilable source files
143 # * Asset files
144 # * Build configurations (Release and debug modes, cflags, etc.)
145 # * List of files composing the project
146 class PbxprojectTemplate
147 super Template
148
149 # Name of the project
150 var name: String
151
152 # OTHER_CFLAGS
153 var cflags = "" is writable
154
155 # All body/implementation source files to be compiled
156 private var source_files = new Array[PbxFile]
157
158 # All asset files added to the app package
159 private var asset_files = new Array[PbxFile]
160
161 # All files used by this project
162 private var files = new Array[PbxFile]
163
164 # Add `file` to this project
165 fun add_file(file: PbxFile) do file.add_to_project(self)
166
167 redef fun rendering
168 do
169 add """
170 // !$*UTF8*$!
171 {
172 archiveVersion = 1;
173 classes = {
174 };
175 objectVersion = 46;
176 objects = {
177
178 /* Begin PBXBuildFile section */
179 """
180
181 # List build files (compilable sources and assets) with their reference UUID
182 for array in [source_files, asset_files] do for file in array do add """
183 {{{file.build_uuid}}} /* {{{file.doc}}} */ = {
184 isa = PBXBuildFile;
185 fileRef = {{{file.ref_uuid}}} /* {{{file.doc}}} */;
186 };
187 """
188
189 add """
190 0F4688411FDF8748004F34D4 /* assets in Resources */ = {isa = PBXBuildFile; fileRef = 0F4688401FDF8748004F34D4 /* assets */; };
191 0FDD07A21C6F8E0E006FF70E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0FDD07A11C6F8E0E006FF70E /* LaunchScreen.storyboard */; };
192 /* End PBXBuildFile section */
193
194 /* Begin PBXFileReference section */
195 /* Static generated files */
196 AF9F83CC1A5F0D21004B62C0 /* {{{name}}}.app */ = {
197 isa = PBXFileReference;
198 explicitFileType = wrapper.application;
199 includeInIndex = 0;
200 path = {{{name}}}.app;
201 sourceTree = BUILT_PRODUCTS_DIR;
202 };
203 AF9F83D01A5F0D21004B62C0 /* Info.plist */ = {
204 isa = PBXFileReference;
205 lastKnownFileType = text.plist.xml;
206 path = Info.plist;
207 sourceTree = "<group>";
208 };
209 AF9F83DE1A5F0D21004B62C0 /* Base */ = {
210 isa = PBXFileReference;
211 lastKnownFileType = file.storyboard;
212 name = Base;
213 path = Base.lproj/Main.storyboard;
214 sourceTree = "<group>";
215 };
216 AF9F83E01A5F0D21004B62C0 /* Images.xcassets */ = {
217 isa = PBXFileReference;
218 lastKnownFileType = folder.assetcatalog;
219 path = Images.xcassets;
220 sourceTree = "<group>";
221 };
222 AF9F83E31A5F0D21004B62C0 /* Base */ = {
223 isa = PBXFileReference;
224 lastKnownFileType = file.xib;
225 name = Base;
226 path = Base.lproj/LaunchScreen.xib;
227 sourceTree = "<group>";
228 };
229
230 /* Changing generated files */
231 """
232 # Describe all known files
233 for file in files do add file.description
234
235 add """
236 0F4688401FDF8748004F34D4 /* assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = assets; path = {{{name}}}/assets; sourceTree = SOURCE_ROOT; };
237 0FDD07A11C6F8E0E006FF70E /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
238 /* End PBXFileReference section */
239
240 /* Begin PBXFrameworksBuildPhase section */
241 AF9F83C91A5F0D21004B62C0 /* Frameworks */ = {
242 isa = PBXFrameworksBuildPhase;
243 buildActionMask = 2147483647;
244 files = (
245 );
246 runOnlyForDeploymentPostprocessing = 0;
247 };
248 /* End PBXFrameworksBuildPhase section */
249
250 /* Begin PBXGroup section */
251 AF9F83C31A5F0D21004B62C0 = {
252 isa = PBXGroup;
253 children = (
254 AF9F83CE1A5F0D21004B62C0 /* {{{name}}} */,
255 AF9F83CD1A5F0D21004B62C0 /* Products */,
256 0FDD07A11C6F8E0E006FF70E /* LaunchScreen.storyboard */,
257 );
258 sourceTree = "<group>";
259 };
260 AF9F83CD1A5F0D21004B62C0 /* Products */ = {
261 isa = PBXGroup;
262 children = (
263 AF9F83CC1A5F0D21004B62C0 /* {{{name}}}.app */,
264 );
265 name = Products;
266 sourceTree = "<group>";
267 };
268 AF9F83CE1A5F0D21004B62C0 /* {{{name}}} */ = {
269 isa = PBXGroup;
270 children = (
271 0F4688401FDF8748004F34D4 /* assets */,
272 """
273 # Reference all known files
274 for file in files do add """
275 {{{file.ref_uuid}}} /* {{{file.doc}}} */,
276 """
277
278 add """
279 );
280 path = {{{name}}};
281 sourceTree = "<group>";
282 };
283 /* End PBXGroup section */
284
285 /* Begin PBXNativeTarget section */
286 AF9F83CB1A5F0D21004B62C0 /* {{{name}}} */ = {
287 isa = PBXNativeTarget;
288 buildConfigurationList = AF9F83F31A5F0D21004B62C0 /* Build configuration list for PBXNativeTarget "{{{name}}}" */;
289 buildPhases = (
290 AF9F83C81A5F0D21004B62C0 /* Sources */,
291 AF9F83C91A5F0D21004B62C0 /* Frameworks */,
292 AF9F83CA1A5F0D21004B62C0 /* Resources */,
293 );
294 buildRules = (
295 );
296 dependencies = (
297 );
298 name = {{{name}}};
299 productName = {{{name}}};
300 productReference = AF9F83CC1A5F0D21004B62C0 /* {{{name}}}.app */;
301 productType = "com.apple.product-type.application";
302 };
303 /* End PBXNativeTarget section */
304
305 /* Begin PBXProject section */
306 AF9F83C41A5F0D21004B62C0 /* Project object */ = {
307 isa = PBXProject;
308 attributes = {
309 LastUpgradeCheck = 0610;
310 TargetAttributes = {
311 AF9F83CB1A5F0D21004B62C0 = {
312 CreatedOnToolsVersion = 6.1.1;
313 };
314 };
315 };
316 buildConfigurationList = AF9F83C71A5F0D21004B62C0 /* Build configuration list for PBXProject "{{{name}}}" */;
317 compatibilityVersion = "Xcode 3.2";
318 developmentRegion = English;
319 hasScannedForEncodings = 0;
320 knownRegions = (
321 en,
322 Base,
323 );
324 mainGroup = AF9F83C31A5F0D21004B62C0;
325 productRefGroup = AF9F83CD1A5F0D21004B62C0 /* Products */;
326 projectDirPath = "";
327 projectRoot = "";
328 targets = (
329 AF9F83CB1A5F0D21004B62C0 /* {{{name}}} */,
330 );
331 };
332 /* End PBXProject section */
333
334 /* Begin PBXResourcesBuildPhase section */
335 AF9F83CA1A5F0D21004B62C0 /* Resources */ = {
336 isa = PBXResourcesBuildPhase;
337 buildActionMask = 2147483647;
338 files = (
339 """
340 # Reference all asset files by their build UUID
341 for file in asset_files do add """
342 {{{file.build_uuid}}} /* {{{file.doc}}} */,
343 """
344
345 add """
346 0FDD07A21C6F8E0E006FF70E /* LaunchScreen.storyboard in Resources */,
347 0F4688411FDF8748004F34D4 /* assets in Resources */,
348 );
349 runOnlyForDeploymentPostprocessing = 0;
350 };
351 /* End PBXResourcesBuildPhase section */
352
353 /* Begin PBXSourcesBuildPhase section */
354 AF9F83C81A5F0D21004B62C0 /* Sources */ = {
355 isa = PBXSourcesBuildPhase;
356 buildActionMask = 2147483647;
357 files = (
358 """
359 # Reference all compilable source files by their build UUID
360 for file in source_files do add """
361 {{{file.build_uuid}}} /* {{{file.doc}}} */,
362 """
363 add """
364 );
365 runOnlyForDeploymentPostprocessing = 0;
366 };
367 /* End PBXSourcesBuildPhase section */
368
369 /* Begin XCBuildConfiguration section */
370 AF9F83F11A5F0D21004B62C0 /* Debug */ = {
371 isa = XCBuildConfiguration;
372 buildSettings = {
373 ALWAYS_SEARCH_USER_PATHS = NO;
374 CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
375 CLANG_CXX_LIBRARY = "libc++";
376 CLANG_ENABLE_MODULES = YES;
377 CLANG_ENABLE_OBJC_ARC = YES;
378 CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
379 CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
380 "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
381 COPY_PHASE_STRIP = NO;
382 ENABLE_STRICT_OBJC_MSGSEND = YES;
383 GCC_C_LANGUAGE_STANDARD = gnu99;
384 GCC_DYNAMIC_NO_PIC = NO;
385 GCC_OPTIMIZATION_LEVEL = 0;
386 GCC_PREPROCESSOR_DEFINITIONS = (
387 "DEBUG=1",
388 "$(inherited)",
389 );
390 GCC_SYMBOLS_PRIVATE_EXTERN = NO;
391 GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
392 GCC_WARN_UNDECLARED_SELECTOR = YES;
393 GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
394 IPHONEOS_DEPLOYMENT_TARGET = 8.1;
395 MTL_ENABLE_DEBUG_INFO = YES;
396 ONLY_ACTIVE_ARCH = YES;
397 SDKROOT = iphoneos;
398 TARGETED_DEVICE_FAMILY = "1,2";
399 };
400 name = Debug;
401 };
402 AF9F83F21A5F0D21004B62C0 /* Release */ = {
403 isa = XCBuildConfiguration;
404 buildSettings = {
405 ALWAYS_SEARCH_USER_PATHS = NO;
406 CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
407 CLANG_CXX_LIBRARY = "libc++";
408 CLANG_ENABLE_MODULES = YES;
409 CLANG_ENABLE_OBJC_ARC = YES;
410 CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
411 CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
412 "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
413 COPY_PHASE_STRIP = YES;
414 ENABLE_NS_ASSERTIONS = NO;
415 ENABLE_STRICT_OBJC_MSGSEND = YES;
416 GCC_C_LANGUAGE_STANDARD = gnu99;
417 GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
418 GCC_WARN_UNDECLARED_SELECTOR = YES;
419 GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
420 IPHONEOS_DEPLOYMENT_TARGET = 8.1;
421 MTL_ENABLE_DEBUG_INFO = NO;
422 SDKROOT = iphoneos;
423 TARGETED_DEVICE_FAMILY = "1,2";
424 VALIDATE_PRODUCT = YES;
425 };
426 name = Release;
427 };
428 AF9F83F41A5F0D21004B62C0 /* Debug */ = {
429 isa = XCBuildConfiguration;
430 buildSettings = {
431 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
432 INFOPLIST_FILE = {{{name}}}/Info.plist;
433 LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
434 OTHER_CFLAGS = "{{{cflags.escape_to_c}}}";
435 PRODUCT_NAME = "$(TARGET_NAME)";
436 };
437 name = Debug;
438 };
439 AF9F83F51A5F0D21004B62C0 /* Release */ = {
440 isa = XCBuildConfiguration;
441 buildSettings = {
442 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
443 INFOPLIST_FILE = {{{name}}}/Info.plist;
444 LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
445 OTHER_CFLAGS = "{{{cflags.escape_to_c}}}";
446 PRODUCT_NAME = "$(TARGET_NAME)";
447 };
448 name = Release;
449 };
450 /* End XCBuildConfiguration section */
451
452 /* Begin XCConfigurationList section */
453 AF9F83C71A5F0D21004B62C0 /* Build configuration list for PBXProject "{{{name}}}" */ = {
454 isa = XCConfigurationList;
455 buildConfigurations = (
456 AF9F83F11A5F0D21004B62C0 /* Debug */,
457 AF9F83F21A5F0D21004B62C0 /* Release */,
458 );
459 defaultConfigurationIsVisible = 0;
460 defaultConfigurationName = Release;
461 };
462 AF9F83F31A5F0D21004B62C0 /* Build configuration list for PBXNativeTarget "{{{name}}}" */ = {
463 isa = XCConfigurationList;
464 buildConfigurations = (
465 AF9F83F41A5F0D21004B62C0 /* Debug */,
466 AF9F83F51A5F0D21004B62C0 /* Release */,
467 );
468 defaultConfigurationIsVisible = 0;
469 };
470 /* End XCConfigurationList section */
471 };
472 rootObject = AF9F83C41A5F0D21004B62C0 /* Project object */;
473 }
474 """
475 end
476 end
477
478 # Template for a property list used by XCode for iOS projects
479 class PlistTemplate
480 super Template
481
482 # Value of CFBundleName, pretty name of the application
483 var product_name: String
484
485 # Value of CFBundleIdentifier, namespace of the app
486 var bundle_identifier: String
487
488 # Value of CFBundleShortVersionString, human readable version
489 var short_version: String
490
491 # Value of CFBundleVersion, often a revision number
492 var bundle_version: String
493
494 redef fun rendering
495 do
496 add """
497 <?xml version="1.0" encoding="UTF-8"?>
498 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
499 <plist version="1.0">
500 <dict>
501 <key>CFBundleDevelopmentRegion</key>
502 <string>en</string>
503 <key>CFBundleExecutable</key>
504 <string>$(EXECUTABLE_NAME)</string>
505 <key>CFBundleIdentifier</key>
506 <string>{{{bundle_identifier}}}</string>
507 <key>CFBundleInfoDictionaryVersion</key>
508 <string>6.0</string>
509 <key>CFBundleName</key>
510 <string>{{{product_name}}}</string>
511 <key>CFBundlePackageType</key>
512 <string>APPL</string>
513 <key>CFBundleShortVersionString</key>
514 <string>{{{short_version}}}</string>
515 <key>CFBundleSignature</key>
516 <string>????</string>
517 <key>CFBundleVersion</key>
518 <string>{{{bundle_version}}}</string>
519 <key>LSRequiresIPhoneOS</key>
520 <true/>
521 <key>UIRequiredDeviceCapabilities</key>
522 <array>
523 <string>armv7</string>
524 </array>
525 <key>UISupportedInterfaceOrientations</key>
526 <array>
527 <string>UIInterfaceOrientationPortrait</string>
528 <string>UIInterfaceOrientationLandscapeLeft</string>
529 <string>UIInterfaceOrientationLandscapeRight</string>
530 </array>
531 <key>UISupportedInterfaceOrientations~ipad</key>
532 <array>
533 <string>UIInterfaceOrientationPortrait</string>
534 <string>UIInterfaceOrientationPortraitUpsideDown</string>
535 <string>UIInterfaceOrientationLandscapeLeft</string>
536 <string>UIInterfaceOrientationLandscapeRight</string>
537 </array>
538
539 <key>UILaunchStoryboardName</key>
540 <string>LaunchScreen</string>
541
542 <key>NSAppTransportSecurity</key>
543 <dict>
544 <key>NSAllowsArbitraryLoads</key><true/>
545 </dict>
546 </dict>
547 </plist>
548 """
549 end
550 end
551
552 # Template for the loading screen to generate `LaunchScreen.storyboard`
553 class LaunchScreenStoryboardTemplate
554 super Template
555
556 # Large text to show in the center of the loading screen
557 var title = "" is writable
558
559 # Text to show at the bottom of the loading screen
560 var subtitle = "" is writable
561
562 # TODO make this more customizable by moving the subviews block as an attribute
563 # or by allowing to pass a custom file from the ` res/` folder.
564
565 redef fun rendering
566 do
567 add """
568 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
569 <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9531" systemVersion="15D21" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
570 <dependencies>
571 <deployment identifier="iOS"/>
572 <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
573 <capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
574 </dependencies>
575 <scenes>
576 <!--View Controller-->
577 <scene sceneID="EHf-IW-A2E">
578 <objects>
579 <viewController id="01J-lp-oVM" sceneMemberID="viewController">
580 <layoutGuides>
581 <viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
582 <viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
583 </layoutGuides>
584 <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
585 <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
586 <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
587 <subviews>
588 <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="{{{subtitle}}}" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="obG-Y5-kRd">
589 <rect key="frame" x="20" y="559" width="560" height="21"/>
590 <fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
591 <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
592 <nil key="highlightedColor"/>
593 </label>
594 <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="{{{title}}}" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="GJd-Yh-RWb">
595 <rect key="frame" x="20" y="176" width="560" height="43"/>
596 <fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
597 <color key="textColor" red="0.0" green="0.5" blue="1" alpha="1" colorSpace="calibratedRGB"/>
598 <nil key="highlightedColor"/>
599 </label>
600 </subviews>
601 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
602 <constraints>
603 <constraint firstAttribute="centerX" secondItem="obG-Y5-kRd" secondAttribute="centerX" id="5cz-MP-9tL"/>
604 <constraint firstAttribute="centerX" secondItem="GJd-Yh-RWb" secondAttribute="centerX" id="Q3B-4B-g5h"/>
605 <constraint firstItem="obG-Y5-kRd" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" constant="20" symbolic="YES" id="SfN-ll-jLj"/>
606 <constraint firstAttribute="bottom" secondItem="obG-Y5-kRd" secondAttribute="bottom" constant="20" id="Y44-ml-fuU"/>
607 <constraint firstItem="GJd-Yh-RWb" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="bottom" multiplier="1/3" constant="1" id="moa-c2-u7t"/>
608 <constraint firstItem="GJd-Yh-RWb" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" constant="20" symbolic="YES" id="x7j-FC-K8j"/>
609 </constraints>
610 </view>
611 </viewController>
612 <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
613 </objects>
614 <point key="canvasLocation" x="53" y="375"/>
615 </scene>
616 </scenes>
617 </document>"""
618 end
619 end