├── LICENSE ├── README.md ├── Shortcuts ├── Info.plist ├── IntentHandler.swift ├── Intents.intentdefinition └── Intents │ ├── makeUppercaseHandler.swift │ └── renameFilesHandler.swift ├── ShortcutsExamples.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcuserdata │ └── alexhay.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist └── ShortcutsExamples ├── AppDelegate.swift ├── Assets.xcassets ├── AppIcon.appiconset │ └── Contents.json └── Contents.json ├── Base.lproj └── LaunchScreen.storyboard ├── ContentView.swift ├── Info.plist ├── Preview Content └── Preview Assets.xcassets │ └── Contents.json └── SceneDelegate.swift /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Alex Hay 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Shortcuts Example Project 2 | 3 | A simple project demonstrating how to add new actions to Apple's Shortcuts app. 4 | 5 | ## Tutorial 6 | 7 | Step-by-step tutorials accompany this project: 8 | 9 | [Part 1:](https://toolboxpro.app/blog/adding-shortcuts-to-an-app-1) *Creating a project and adding the first action* 10 | 11 | [Part 2:](https://toolboxpro.app/blog/adding-shortcuts-to-an-app-2) *Exploring parameters: arrays, enums, calculated lists and files* 12 | 13 | [Part 3](https://toolboxpro.app/blog/adding-shortcuts-to-an-app-part-three): *In-app intent handling, custom output types, visual list API and using the SwiftUI app protocol* 14 | 15 | Part 3 has a new iOS 14 project, which you can see [here](https://github.com/mralexhay/Shortcuts-Example-iOS14). 16 | -------------------------------------------------------------------------------- /Shortcuts/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Shortcuts 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | NSExtension 24 | 25 | NSExtensionAttributes 26 | 27 | IntentsRestrictedWhileLocked 28 | 29 | IntentsRestrictedWhileProtectedDataUnavailable 30 | 31 | IntentsSupported 32 | 33 | MakeUppercaseIntent 34 | RenameFilesIntent 35 | 36 | 37 | NSExtensionPointIdentifier 38 | com.apple.intents-service 39 | NSExtensionPrincipalClass 40 | $(PRODUCT_MODULE_NAME).IntentHandler 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Shortcuts/IntentHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IntentHandler.swift 3 | // Shortcuts 4 | // 5 | // Created by Alex Hay on 04/04/2020. 6 | // Copyright © 2020 Alex Hay. All rights reserved. 7 | // 8 | 9 | import Intents 10 | 11 | class IntentHandler: INExtension { 12 | 13 | // When shortcuts are run, the relevant intent handler should to be returned 14 | override func handler(for intent: INIntent) -> Any { 15 | switch intent { 16 | case is MakeUppercaseIntent: 17 | return MakeUppercaseIntentHandler() 18 | case is RenameFilesIntent: 19 | return RenameFilesIntentHandler() 20 | default: 21 | // No intents should be unhandled so we'll throw an error by default 22 | fatalError("No handler for this intent") 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Shortcuts/Intents.intentdefinition: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | INEnums 6 | 7 | 8 | INEnumDisplayName 9 | Case 10 | INEnumDisplayNameID 11 | RJGk0k 12 | INEnumGeneratesHeader 13 | 14 | INEnumName 15 | RenameCase 16 | INEnumType 17 | Regular 18 | INEnumValues 19 | 20 | 21 | INEnumValueDisplayName 22 | unknown 23 | INEnumValueDisplayNameID 24 | 82oUdn 25 | INEnumValueName 26 | unknown 27 | 28 | 29 | INEnumValueDisplayName 30 | uppercase 31 | INEnumValueDisplayNameID 32 | sAJ94n 33 | INEnumValueIndex 34 | 1 35 | INEnumValueName 36 | uppercase 37 | 38 | 39 | INEnumValueDisplayName 40 | lowercase 41 | INEnumValueDisplayNameID 42 | TCYnwx 43 | INEnumValueIndex 44 | 2 45 | INEnumValueName 46 | lowercase 47 | 48 | 49 | 50 | 51 | INEnumDisplayName 52 | Position 53 | INEnumDisplayNameID 54 | rTOfHu 55 | INEnumGeneratesHeader 56 | 57 | INEnumName 58 | RenamePosition 59 | INEnumType 60 | Regular 61 | INEnumValues 62 | 63 | 64 | INEnumValueDisplayName 65 | unknown 66 | INEnumValueDisplayNameID 67 | g8gzOg 68 | INEnumValueName 69 | unknown 70 | 71 | 72 | INEnumValueDisplayName 73 | Prepend 74 | INEnumValueDisplayNameID 75 | x9TXCf 76 | INEnumValueIndex 77 | 1 78 | INEnumValueName 79 | prepend 80 | 81 | 82 | INEnumValueDisplayName 83 | Append 84 | INEnumValueDisplayNameID 85 | UOhRXd 86 | INEnumValueIndex 87 | 2 88 | INEnumValueName 89 | append 90 | 91 | 92 | 93 | 94 | INIntentDefinitionModelVersion 95 | 1.1 96 | INIntentDefinitionNamespace 97 | rztdGm 98 | INIntentDefinitionSystemVersion 99 | 19F53f 100 | INIntentDefinitionToolsBuildVersion 101 | 11E146 102 | INIntentDefinitionToolsVersion 103 | 11.4 104 | INIntents 105 | 106 | 107 | INIntentCategory 108 | generic 109 | INIntentConfigurable 110 | 111 | INIntentDescription 112 | Change the case of the input text to uppercase 113 | INIntentDescriptionID 114 | 2xhqX8 115 | INIntentIneligibleForSuggestions 116 | 117 | INIntentInput 118 | text 119 | INIntentKeyParameter 120 | text 121 | INIntentLastParameterTag 122 | 1 123 | INIntentManagedParameterCombinations 124 | 125 | text 126 | 127 | INIntentParameterCombinationSupportsBackgroundExecution 128 | 129 | INIntentParameterCombinationTitle 130 | Make ${text} uppercase 131 | INIntentParameterCombinationTitleID 132 | L23OrX 133 | INIntentParameterCombinationUpdatesLinked 134 | 135 | 136 | 137 | INIntentName 138 | MakeUppercase 139 | INIntentParameters 140 | 141 | 142 | INIntentParameterDisplayName 143 | Text 144 | INIntentParameterDisplayNameID 145 | ZuzB2d 146 | INIntentParameterDisplayPriority 147 | 1 148 | INIntentParameterMetadata 149 | 150 | INIntentParameterMetadataCapitalization 151 | Sentences 152 | 153 | INIntentParameterName 154 | text 155 | INIntentParameterPromptDialogs 156 | 157 | 158 | INIntentParameterPromptDialogCustom 159 | 160 | INIntentParameterPromptDialogFormatString 161 | Which text would you like to make uppercase? 162 | INIntentParameterPromptDialogFormatStringID 163 | YkN0TJ 164 | INIntentParameterPromptDialogType 165 | Primary 166 | 167 | 168 | INIntentParameterSupportsResolution 169 | 170 | INIntentParameterTag 171 | 1 172 | INIntentParameterType 173 | String 174 | INIntentParameterUnsupportedReasons 175 | 176 | 177 | INIntentParameterUnsupportedReasonCode 178 | noText 179 | INIntentParameterUnsupportedReasonCustom 180 | 181 | INIntentParameterUnsupportedReasonFormatString 182 | Please enter some text to make uppercase 183 | INIntentParameterUnsupportedReasonFormatStringID 184 | 1J3dOx 185 | 186 | 187 | 188 | 189 | INIntentResponse 190 | 191 | INIntentResponseCodes 192 | 193 | 194 | INIntentResponseCodeConciseFormatString 195 | ${result} 196 | INIntentResponseCodeConciseFormatStringID 197 | jV73hN 198 | INIntentResponseCodeFormatString 199 | ${result} 200 | INIntentResponseCodeFormatStringID 201 | 9LqbwS 202 | INIntentResponseCodeName 203 | success 204 | INIntentResponseCodeSuccess 205 | 206 | 207 | 208 | INIntentResponseCodeConciseFormatString 209 | ${error} 210 | INIntentResponseCodeConciseFormatStringID 211 | oqSEAN 212 | INIntentResponseCodeFormatString 213 | ${error} 214 | INIntentResponseCodeFormatStringID 215 | dn19Ht 216 | INIntentResponseCodeName 217 | failure 218 | 219 | 220 | INIntentResponseLastParameterTag 221 | 2 222 | INIntentResponseOutput 223 | result 224 | INIntentResponseParameters 225 | 226 | 227 | INIntentResponseParameterDisplayName 228 | Uppercase Text 229 | INIntentResponseParameterDisplayNameID 230 | 3tOQIH 231 | INIntentResponseParameterDisplayPriority 232 | 1 233 | INIntentResponseParameterName 234 | result 235 | INIntentResponseParameterTag 236 | 1 237 | INIntentResponseParameterType 238 | String 239 | 240 | 241 | INIntentResponseParameterDisplayName 242 | Error 243 | INIntentResponseParameterDisplayNameID 244 | 1Ujl5j 245 | INIntentResponseParameterDisplayPriority 246 | 2 247 | INIntentResponseParameterName 248 | error 249 | INIntentResponseParameterTag 250 | 2 251 | INIntentResponseParameterType 252 | String 253 | 254 | 255 | 256 | INIntentTitle 257 | Make Uppercase 258 | INIntentTitleID 259 | SF2n7q 260 | INIntentType 261 | Custom 262 | INIntentVerb 263 | Do 264 | 265 | 266 | INIntentCategory 267 | generic 268 | INIntentConfigurable 269 | 270 | INIntentDescription 271 | Append or prepend a date to the selected files 272 | INIntentDescriptionID 273 | e5pMxb 274 | INIntentIneligibleForSuggestions 275 | 276 | INIntentInput 277 | files 278 | INIntentKeyParameter 279 | files 280 | INIntentLastParameterTag 281 | 10 282 | INIntentManagedParameterCombinations 283 | 284 | files,dateFormat,changeCase,newCase,position 285 | 286 | INIntentParameterCombinationSupportsBackgroundExecution 287 | 288 | INIntentParameterCombinationTitle 289 | ${position} ${dateFormat} to names of ${files} 290 | INIntentParameterCombinationTitleID 291 | fQDQ7z 292 | INIntentParameterCombinationUpdatesLinked 293 | 294 | 295 | files,dateFormat,changeCase,position 296 | 297 | INIntentParameterCombinationSupportsBackgroundExecution 298 | 299 | INIntentParameterCombinationTitle 300 | ${position} ${dateFormat} to names of ${files} 301 | INIntentParameterCombinationTitleID 302 | 9iCgY7 303 | INIntentParameterCombinationUpdatesLinked 304 | 305 | 306 | 307 | INIntentName 308 | RenameFiles 309 | INIntentParameters 310 | 311 | 312 | INIntentParameterDisplayName 313 | Files 314 | INIntentParameterDisplayNameID 315 | sp0ZwK 316 | INIntentParameterDisplayPriority 317 | 1 318 | INIntentParameterMetadata 319 | 320 | INIntentParameterMetadataCustomUTIs 321 | 322 | 323 | UTI 324 | public.item 325 | 326 | 327 | INIntentParameterMetadataType 328 | Custom 329 | 330 | INIntentParameterName 331 | files 332 | INIntentParameterPromptDialogs 333 | 334 | 335 | INIntentParameterPromptDialogCustom 336 | 337 | INIntentParameterPromptDialogFormatString 338 | Which files would you like to rename? 339 | INIntentParameterPromptDialogFormatStringID 340 | h2Aj5I 341 | INIntentParameterPromptDialogType 342 | Primary 343 | 344 | 345 | INIntentParameterSupportsMultipleValues 346 | 347 | INIntentParameterSupportsResolution 348 | 349 | INIntentParameterTag 350 | 3 351 | INIntentParameterType 352 | File 353 | INIntentParameterUnsupportedReasons 354 | 355 | 356 | INIntentParameterUnsupportedReasonCode 357 | noFiles 358 | INIntentParameterUnsupportedReasonCustom 359 | 360 | INIntentParameterUnsupportedReasonFormatString 361 | Please select some files to rename 362 | INIntentParameterUnsupportedReasonFormatStringID 363 | CqrvE5 364 | 365 | 366 | 367 | 368 | INIntentParameterCustomDisambiguation 369 | 370 | INIntentParameterDisplayName 371 | Date Format 372 | INIntentParameterDisplayNameID 373 | lDOWDi 374 | INIntentParameterDisplayPriority 375 | 2 376 | INIntentParameterMetadata 377 | 378 | INIntentParameterMetadataCapitalization 379 | Sentences 380 | 381 | INIntentParameterName 382 | dateFormat 383 | INIntentParameterPromptDialogs 384 | 385 | 386 | INIntentParameterPromptDialogCustom 387 | 388 | INIntentParameterPromptDialogType 389 | Primary 390 | 391 | 392 | INIntentParameterPromptDialogCustom 393 | 394 | INIntentParameterPromptDialogFormatString 395 | There are ${count} options matching ‘${dateFormat}’. 396 | INIntentParameterPromptDialogFormatStringID 397 | ExuogR 398 | INIntentParameterPromptDialogType 399 | DisambiguationIntroduction 400 | 401 | 402 | INIntentParameterPromptDialogFormatString 403 | Which one? 404 | INIntentParameterPromptDialogFormatStringID 405 | yDGih4 406 | INIntentParameterPromptDialogType 407 | DisambiguationSelection 408 | 409 | 410 | INIntentParameterPromptDialogCustom 411 | 412 | INIntentParameterPromptDialogFormatString 413 | Just to confirm, you wanted ‘${dateFormat}’? 414 | INIntentParameterPromptDialogFormatStringID 415 | WqjRiL 416 | INIntentParameterPromptDialogType 417 | Confirmation 418 | 419 | 420 | INIntentParameterSupportsDynamicEnumeration 421 | 422 | INIntentParameterSupportsResolution 423 | 424 | INIntentParameterTag 425 | 4 426 | INIntentParameterType 427 | String 428 | INIntentParameterUnsupportedReasons 429 | 430 | 431 | INIntentParameterUnsupportedReasonCode 432 | empty 433 | INIntentParameterUnsupportedReasonCustom 434 | 435 | INIntentParameterUnsupportedReasonFormatString 436 | No date format selected 437 | INIntentParameterUnsupportedReasonFormatStringID 438 | fsJiPH 439 | 440 | 441 | 442 | 443 | INIntentParameterDisplayName 444 | Change Case 445 | INIntentParameterDisplayNameID 446 | iRmXTI 447 | INIntentParameterDisplayPriority 448 | 3 449 | INIntentParameterMetadata 450 | 451 | INIntentParameterMetadataFalseDisplayName 452 | false 453 | INIntentParameterMetadataFalseDisplayNameID 454 | nN7qDI 455 | INIntentParameterMetadataTrueDisplayName 456 | true 457 | INIntentParameterMetadataTrueDisplayNameID 458 | NiYgT5 459 | 460 | INIntentParameterName 461 | changeCase 462 | INIntentParameterPromptDialogs 463 | 464 | 465 | INIntentParameterPromptDialogCustom 466 | 467 | INIntentParameterPromptDialogFormatString 468 | Would you like to change the case of the title? 469 | INIntentParameterPromptDialogFormatStringID 470 | nhQaxM 471 | INIntentParameterPromptDialogType 472 | Primary 473 | 474 | 475 | INIntentParameterSupportsResolution 476 | 477 | INIntentParameterTag 478 | 6 479 | INIntentParameterType 480 | Boolean 481 | 482 | 483 | INIntentParameterCustomDisambiguation 484 | 485 | INIntentParameterDisplayName 486 | Case 487 | INIntentParameterDisplayNameID 488 | R1DaUC 489 | INIntentParameterDisplayPriority 490 | 4 491 | INIntentParameterEnumType 492 | RenameCase 493 | INIntentParameterEnumTypeNamespace 494 | rztdGm 495 | INIntentParameterMetadata 496 | 497 | INIntentParameterMetadataDefaultValue 498 | uppercase 499 | 500 | INIntentParameterName 501 | newCase 502 | INIntentParameterPromptDialogs 503 | 504 | 505 | INIntentParameterPromptDialogCustom 506 | 507 | INIntentParameterPromptDialogType 508 | Primary 509 | 510 | 511 | INIntentParameterPromptDialogCustom 512 | 513 | INIntentParameterPromptDialogFormatString 514 | There are ${count} options matching ‘${newCase}’. 515 | INIntentParameterPromptDialogFormatStringID 516 | 9lJZYq 517 | INIntentParameterPromptDialogType 518 | DisambiguationIntroduction 519 | 520 | 521 | INIntentParameterPromptDialogFormatString 522 | Which one? 523 | INIntentParameterPromptDialogFormatStringID 524 | vb2pF8 525 | INIntentParameterPromptDialogType 526 | DisambiguationSelection 527 | 528 | 529 | INIntentParameterPromptDialogCustom 530 | 531 | INIntentParameterPromptDialogFormatString 532 | Just to confirm, you wanted ‘${newCase}’? 533 | INIntentParameterPromptDialogFormatStringID 534 | 21FS8e 535 | INIntentParameterPromptDialogType 536 | Confirmation 537 | 538 | 539 | INIntentParameterRelationship 540 | 541 | INIntentParameterRelationshipParentName 542 | changeCase 543 | INIntentParameterRelationshipPredicateName 544 | BooleanHasExactValue 545 | INIntentParameterRelationshipPredicateValue 546 | 547 | 548 | INIntentParameterSupportsResolution 549 | 550 | INIntentParameterTag 551 | 8 552 | INIntentParameterType 553 | Integer 554 | 555 | 556 | INIntentParameterCustomDisambiguation 557 | 558 | INIntentParameterDisplayName 559 | Position 560 | INIntentParameterDisplayNameID 561 | PNwEUg 562 | INIntentParameterDisplayPriority 563 | 5 564 | INIntentParameterEnumType 565 | RenamePosition 566 | INIntentParameterEnumTypeNamespace 567 | rztdGm 568 | INIntentParameterMetadata 569 | 570 | INIntentParameterMetadataDefaultValue 571 | prepend 572 | 573 | INIntentParameterName 574 | position 575 | INIntentParameterPromptDialogs 576 | 577 | 578 | INIntentParameterPromptDialogCustom 579 | 580 | INIntentParameterPromptDialogType 581 | Primary 582 | 583 | 584 | INIntentParameterPromptDialogCustom 585 | 586 | INIntentParameterPromptDialogFormatString 587 | There are ${count} options matching ‘${position}’. 588 | INIntentParameterPromptDialogFormatStringID 589 | P9pvwh 590 | INIntentParameterPromptDialogType 591 | DisambiguationIntroduction 592 | 593 | 594 | INIntentParameterPromptDialogFormatString 595 | Which one? 596 | INIntentParameterPromptDialogFormatStringID 597 | XvRLXm 598 | INIntentParameterPromptDialogType 599 | DisambiguationSelection 600 | 601 | 602 | INIntentParameterPromptDialogCustom 603 | 604 | INIntentParameterPromptDialogFormatString 605 | Just to confirm, you wanted ‘${position}’? 606 | INIntentParameterPromptDialogFormatStringID 607 | eLdlF8 608 | INIntentParameterPromptDialogType 609 | Confirmation 610 | 611 | 612 | INIntentParameterSupportsResolution 613 | 614 | INIntentParameterTag 615 | 10 616 | INIntentParameterType 617 | Integer 618 | 619 | 620 | INIntentResponse 621 | 622 | INIntentResponseCodes 623 | 624 | 625 | INIntentResponseCodeConciseFormatString 626 | ${result} 627 | INIntentResponseCodeConciseFormatStringID 628 | 31TEIy 629 | INIntentResponseCodeFormatString 630 | ${result} 631 | INIntentResponseCodeFormatStringID 632 | P1J3bQ 633 | INIntentResponseCodeName 634 | success 635 | INIntentResponseCodeSuccess 636 | 637 | 638 | 639 | INIntentResponseCodeConciseFormatString 640 | ${error} 641 | INIntentResponseCodeConciseFormatStringID 642 | e8yrDN 643 | INIntentResponseCodeFormatString 644 | ${error} 645 | INIntentResponseCodeFormatStringID 646 | 0DcwfS 647 | INIntentResponseCodeName 648 | failure 649 | 650 | 651 | INIntentResponseLastParameterTag 652 | 4 653 | INIntentResponseOutput 654 | result 655 | INIntentResponseParameters 656 | 657 | 658 | INIntentResponseParameterDisplayName 659 | Renamed Files 660 | INIntentResponseParameterDisplayNameID 661 | UnzAGA 662 | INIntentResponseParameterDisplayPriority 663 | 1 664 | INIntentResponseParameterName 665 | result 666 | INIntentResponseParameterSupportsMultipleValues 667 | 668 | INIntentResponseParameterTag 669 | 3 670 | INIntentResponseParameterType 671 | File 672 | 673 | 674 | INIntentResponseParameterDisplayName 675 | Error 676 | INIntentResponseParameterDisplayNameID 677 | ppmT2J 678 | INIntentResponseParameterDisplayPriority 679 | 2 680 | INIntentResponseParameterName 681 | error 682 | INIntentResponseParameterTag 683 | 4 684 | INIntentResponseParameterType 685 | String 686 | 687 | 688 | 689 | INIntentTitle 690 | Rename Files 691 | INIntentTitleID 692 | ajwW3z 693 | INIntentType 694 | Custom 695 | INIntentVerb 696 | Do 697 | 698 | 699 | INTypes 700 | 701 | 702 | 703 | -------------------------------------------------------------------------------- /Shortcuts/Intents/makeUppercaseHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // makeUppercaseHandler.swift 3 | // Shortcuts 4 | // 5 | // Created by Alex Hay on 04/04/2020. 6 | // Copyright © 2020 Alex Hay. All rights reserved. 7 | // 8 | 9 | import Intents 10 | 11 | class MakeUppercaseIntentHandler: NSObject, MakeUppercaseIntentHandling { 12 | 13 | // To conform to MakeUppercaseIntentHandling, we need to resolve each parameter before handling the intent 14 | func resolveText(for intent: MakeUppercaseIntent, with completion: @escaping (MakeUppercaseTextResolutionResult) -> Void) { 15 | // Each parameter is an optional. We can do any neccessary validations at this stage and throw errors if required 16 | if let text = intent.text, !text.isEmpty { 17 | completion(MakeUppercaseTextResolutionResult.success(with: text)) 18 | } else { 19 | // This 'noText' code is defined in the Intent Definition file 20 | completion(MakeUppercaseTextResolutionResult.unsupported(forReason: .noText)) 21 | } 22 | } 23 | 24 | func handle(intent: MakeUppercaseIntent, completion: @escaping (MakeUppercaseIntentResponse) -> Void) { 25 | if let inputText = intent.text { 26 | let uppercaseText = inputText.uppercased() 27 | completion(MakeUppercaseIntentResponse.success(result: uppercaseText)) 28 | } else { 29 | // This text would show in the Shortcuts app if 'inputText' has no value 30 | completion(MakeUppercaseIntentResponse.failure(error: "The entered text was invalid")) 31 | } 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /Shortcuts/Intents/renameFilesHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // renameFilesHandler.swift 3 | // Shortcuts 4 | // 5 | // Created by Alex Hay on 09/04/2020. 6 | // Copyright © 2020 Alex Hay. All rights reserved. 7 | // 8 | 9 | import Intents 10 | 11 | class RenameFilesIntentHandler: NSObject, RenameFilesIntentHandling { 12 | 13 | func resolveFiles(for intent: RenameFilesIntent, with completion: @escaping ([RenameFilesFilesResolutionResult]) -> Void) { 14 | // For paramters that accept multiple files, we need to pass an array of Resolution Results to the completion handler 15 | var resultArray = [RenameFilesFilesResolutionResult]() 16 | let files = intent.files ?? [] 17 | if files.isEmpty { 18 | resultArray.append(RenameFilesFilesResolutionResult.unsupported(forReason: .noFiles)) 19 | } else { 20 | for file in files { 21 | resultArray.append(RenameFilesFilesResolutionResult.success(with: file)) 22 | } 23 | } 24 | completion(resultArray) 25 | } 26 | 27 | // this function will provide the drop-down list of options to choose from when tapping the "Date Format parameter in Shortcuts" 28 | func provideDateFormatOptions(for intent: RenameFilesIntent, with completion: @escaping ([String]?, Error?) -> Void) { 29 | let dateFormatter = DateFormatter() 30 | dateFormatter.locale = Locale.current 31 | dateFormatter.calendar = Calendar.current 32 | dateFormatter.dateFormat = "yyyy-MM-dd" 33 | 34 | let fullDate = dateFormatter.string(from: Date()) 35 | let yearsAndMonths = String(fullDate.dropLast(3)) 36 | let yearOnly = String(fullDate.dropLast(6)) 37 | 38 | let optionsArray: [String] = [fullDate, yearsAndMonths, yearOnly] 39 | 40 | completion(optionsArray, nil) 41 | } 42 | 43 | func resolveDateFormat(for intent: RenameFilesIntent, with completion: @escaping (RenameFilesDateFormatResolutionResult) -> Void) { 44 | if let dateFormat = intent.dateFormat { 45 | completion(RenameFilesDateFormatResolutionResult.success(with: dateFormat)) 46 | } else { 47 | completion(RenameFilesDateFormatResolutionResult.unsupported(forReason: .empty)) 48 | } 49 | } 50 | 51 | func resolveNewCase(for intent: RenameFilesIntent, with completion: @escaping (RenameCaseResolutionResult) -> Void) { 52 | let newCase = intent.newCase 53 | completion(RenameCaseResolutionResult.success(with: newCase)) 54 | } 55 | 56 | func resolvePosition(for intent: RenameFilesIntent, with completion: @escaping (RenamePositionResolutionResult) -> Void) { 57 | let position = intent.position 58 | completion(RenamePositionResolutionResult.success(with: position)) 59 | } 60 | 61 | func resolveChangeCase(for intent: RenameFilesIntent, with completion: @escaping (INBooleanResolutionResult) -> Void) { 62 | let changeCase = intent.changeCase?.boolValue ?? false 63 | completion(INBooleanResolutionResult.success(with: changeCase)) 64 | } 65 | 66 | func handle(intent: RenameFilesIntent, completion: @escaping (RenameFilesIntentResponse) -> Void) { 67 | let files = intent.files ?? [] 68 | let position = intent.position 69 | let changeCase = intent.changeCase?.boolValue ?? false 70 | guard let dateFormat = intent.dateFormat else { 71 | // We can display errors to the user when problems occur 72 | completion(RenameFilesIntentResponse.failure(error: "Please choose a valid date format")) 73 | return 74 | } 75 | 76 | // The intent response expects an array of INFiles 77 | var outputArray = [INFile]() 78 | 79 | for file in files { 80 | var newName = file.filename 81 | 82 | // change the case of the filename if selected 83 | if changeCase { 84 | let newCase = intent.newCase 85 | switch newCase { 86 | case .lowercase: 87 | newName = newName.lowercased() 88 | case .uppercase: 89 | newName = newName.uppercased() 90 | default: 91 | completion(RenameFilesIntentResponse.failure(error: "An invalid case was selected")) 92 | return 93 | } 94 | } 95 | 96 | // append or prepend the selected date value 97 | switch position { 98 | case .append: 99 | // if appending the date, we need to split the extension from the name first 100 | guard let fileURL = file.fileURL else { 101 | completion(RenameFilesIntentResponse.failure(error: "Couldn't get file URL of \(file.filename)")) 102 | return 103 | } 104 | let filePath = fileURL.deletingPathExtension().lastPathComponent 105 | let nameNoExt = FileManager.default.displayName(atPath: filePath) 106 | let ext = fileURL.pathExtension 107 | newName = "\(nameNoExt)_\(dateFormat).\(ext)" 108 | case .prepend: 109 | newName = "\(dateFormat)_\(newName)" 110 | default: 111 | // We'll show an error if for some reason one of our enum values hasn't been selected 112 | completion(RenameFilesIntentResponse.failure(error: "An invalid position was selected")) 113 | return 114 | } 115 | 116 | // construct a new INFile with identical data and type identifier and the new file name 117 | let renamedFile = INFile(data: file.data, filename: newName, typeIdentifier: file.typeIdentifier) 118 | outputArray.append(renamedFile) 119 | } 120 | completion(RenameFilesIntentResponse.success(result: outputArray)) 121 | } 122 | } 123 | 124 | -------------------------------------------------------------------------------- /ShortcutsExamples.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 8C06992F243F11B200831945 /* renameFilesHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C06992C243EA0E700831945 /* renameFilesHandler.swift */; }; 11 | 8CAB19C42438B2D00002AE8B /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CAB19C32438B2D00002AE8B /* AppDelegate.swift */; }; 12 | 8CAB19C62438B2D00002AE8B /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CAB19C52438B2D00002AE8B /* SceneDelegate.swift */; }; 13 | 8CAB19C82438B2D00002AE8B /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CAB19C72438B2D00002AE8B /* ContentView.swift */; }; 14 | 8CAB19CA2438B2D30002AE8B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8CAB19C92438B2D30002AE8B /* Assets.xcassets */; }; 15 | 8CAB19CD2438B2D30002AE8B /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8CAB19CC2438B2D30002AE8B /* Preview Assets.xcassets */; }; 16 | 8CAB19D02438B2D30002AE8B /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8CAB19CE2438B2D30002AE8B /* LaunchScreen.storyboard */; }; 17 | 8CAB19DE2438B3160002AE8B /* IntentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CAB19DD2438B3160002AE8B /* IntentHandler.swift */; }; 18 | 8CAB19E22438B3160002AE8B /* Shortcuts.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 8CAB19DB2438B3160002AE8B /* Shortcuts.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 19 | 8CAB19E82438B5570002AE8B /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 8CAB19E72438B5570002AE8B /* Intents.intentdefinition */; }; 20 | 8CAB19EA2438DE330002AE8B /* makeUppercaseHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CAB19E92438DE330002AE8B /* makeUppercaseHandler.swift */; }; 21 | 8CAB19EB2438E6860002AE8B /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 8CAB19E72438B5570002AE8B /* Intents.intentdefinition */; }; 22 | /* End PBXBuildFile section */ 23 | 24 | /* Begin PBXContainerItemProxy section */ 25 | 8CAB19E02438B3160002AE8B /* PBXContainerItemProxy */ = { 26 | isa = PBXContainerItemProxy; 27 | containerPortal = 8CAB19B82438B2CF0002AE8B /* Project object */; 28 | proxyType = 1; 29 | remoteGlobalIDString = 8CAB19DA2438B3160002AE8B; 30 | remoteInfo = Shortcuts; 31 | }; 32 | /* End PBXContainerItemProxy section */ 33 | 34 | /* Begin PBXCopyFilesBuildPhase section */ 35 | 8CAB19E62438B3160002AE8B /* Embed App Extensions */ = { 36 | isa = PBXCopyFilesBuildPhase; 37 | buildActionMask = 2147483647; 38 | dstPath = ""; 39 | dstSubfolderSpec = 13; 40 | files = ( 41 | 8CAB19E22438B3160002AE8B /* Shortcuts.appex in Embed App Extensions */, 42 | ); 43 | name = "Embed App Extensions"; 44 | runOnlyForDeploymentPostprocessing = 0; 45 | }; 46 | /* End PBXCopyFilesBuildPhase section */ 47 | 48 | /* Begin PBXFileReference section */ 49 | 8C06992C243EA0E700831945 /* renameFilesHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = renameFilesHandler.swift; sourceTree = ""; }; 50 | 8C069930243F2FDF00831945 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 51 | 8CAB19C02438B2D00002AE8B /* ShortcutsExamples.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ShortcutsExamples.app; sourceTree = BUILT_PRODUCTS_DIR; }; 52 | 8CAB19C32438B2D00002AE8B /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 53 | 8CAB19C52438B2D00002AE8B /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 54 | 8CAB19C72438B2D00002AE8B /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 55 | 8CAB19C92438B2D30002AE8B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 56 | 8CAB19CC2438B2D30002AE8B /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 57 | 8CAB19CF2438B2D30002AE8B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 58 | 8CAB19D12438B2D30002AE8B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 59 | 8CAB19DB2438B3160002AE8B /* Shortcuts.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = Shortcuts.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 60 | 8CAB19DD2438B3160002AE8B /* IntentHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntentHandler.swift; sourceTree = ""; }; 61 | 8CAB19DF2438B3160002AE8B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 62 | 8CAB19E72438B5570002AE8B /* Intents.intentdefinition */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; path = Intents.intentdefinition; sourceTree = ""; }; 63 | 8CAB19E92438DE330002AE8B /* makeUppercaseHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = makeUppercaseHandler.swift; sourceTree = ""; }; 64 | /* End PBXFileReference section */ 65 | 66 | /* Begin PBXFrameworksBuildPhase section */ 67 | 8CAB19BD2438B2D00002AE8B /* Frameworks */ = { 68 | isa = PBXFrameworksBuildPhase; 69 | buildActionMask = 2147483647; 70 | files = ( 71 | ); 72 | runOnlyForDeploymentPostprocessing = 0; 73 | }; 74 | 8CAB19D82438B3160002AE8B /* Frameworks */ = { 75 | isa = PBXFrameworksBuildPhase; 76 | buildActionMask = 2147483647; 77 | files = ( 78 | ); 79 | runOnlyForDeploymentPostprocessing = 0; 80 | }; 81 | /* End PBXFrameworksBuildPhase section */ 82 | 83 | /* Begin PBXGroup section */ 84 | 8CAB19B72438B2CF0002AE8B = { 85 | isa = PBXGroup; 86 | children = ( 87 | 8C069930243F2FDF00831945 /* README.md */, 88 | 8CAB19C22438B2D00002AE8B /* ShortcutsExamples */, 89 | 8CAB19DC2438B3160002AE8B /* Shortcuts */, 90 | 8CAB19C12438B2D00002AE8B /* Products */, 91 | ); 92 | sourceTree = ""; 93 | }; 94 | 8CAB19C12438B2D00002AE8B /* Products */ = { 95 | isa = PBXGroup; 96 | children = ( 97 | 8CAB19C02438B2D00002AE8B /* ShortcutsExamples.app */, 98 | 8CAB19DB2438B3160002AE8B /* Shortcuts.appex */, 99 | ); 100 | name = Products; 101 | sourceTree = ""; 102 | }; 103 | 8CAB19C22438B2D00002AE8B /* ShortcutsExamples */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | 8CAB19C32438B2D00002AE8B /* AppDelegate.swift */, 107 | 8CAB19C52438B2D00002AE8B /* SceneDelegate.swift */, 108 | 8CAB19C72438B2D00002AE8B /* ContentView.swift */, 109 | 8CAB19C92438B2D30002AE8B /* Assets.xcassets */, 110 | 8CAB19CE2438B2D30002AE8B /* LaunchScreen.storyboard */, 111 | 8CAB19D12438B2D30002AE8B /* Info.plist */, 112 | 8CAB19CB2438B2D30002AE8B /* Preview Content */, 113 | ); 114 | path = ShortcutsExamples; 115 | sourceTree = ""; 116 | }; 117 | 8CAB19CB2438B2D30002AE8B /* Preview Content */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | 8CAB19CC2438B2D30002AE8B /* Preview Assets.xcassets */, 121 | ); 122 | path = "Preview Content"; 123 | sourceTree = ""; 124 | }; 125 | 8CAB19DC2438B3160002AE8B /* Shortcuts */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | 8CAB19EC2438F0E90002AE8B /* Intents */, 129 | 8CAB19DD2438B3160002AE8B /* IntentHandler.swift */, 130 | 8CAB19DF2438B3160002AE8B /* Info.plist */, 131 | 8CAB19E72438B5570002AE8B /* Intents.intentdefinition */, 132 | ); 133 | path = Shortcuts; 134 | sourceTree = ""; 135 | }; 136 | 8CAB19EC2438F0E90002AE8B /* Intents */ = { 137 | isa = PBXGroup; 138 | children = ( 139 | 8CAB19E92438DE330002AE8B /* makeUppercaseHandler.swift */, 140 | 8C06992C243EA0E700831945 /* renameFilesHandler.swift */, 141 | ); 142 | path = Intents; 143 | sourceTree = ""; 144 | }; 145 | /* End PBXGroup section */ 146 | 147 | /* Begin PBXNativeTarget section */ 148 | 8CAB19BF2438B2D00002AE8B /* ShortcutsExamples */ = { 149 | isa = PBXNativeTarget; 150 | buildConfigurationList = 8CAB19D42438B2D30002AE8B /* Build configuration list for PBXNativeTarget "ShortcutsExamples" */; 151 | buildPhases = ( 152 | 8CAB19BC2438B2D00002AE8B /* Sources */, 153 | 8CAB19BD2438B2D00002AE8B /* Frameworks */, 154 | 8CAB19BE2438B2D00002AE8B /* Resources */, 155 | 8CAB19E62438B3160002AE8B /* Embed App Extensions */, 156 | ); 157 | buildRules = ( 158 | ); 159 | dependencies = ( 160 | 8CAB19E12438B3160002AE8B /* PBXTargetDependency */, 161 | ); 162 | name = ShortcutsExamples; 163 | productName = ShortcutsExamples; 164 | productReference = 8CAB19C02438B2D00002AE8B /* ShortcutsExamples.app */; 165 | productType = "com.apple.product-type.application"; 166 | }; 167 | 8CAB19DA2438B3160002AE8B /* Shortcuts */ = { 168 | isa = PBXNativeTarget; 169 | buildConfigurationList = 8CAB19E32438B3160002AE8B /* Build configuration list for PBXNativeTarget "Shortcuts" */; 170 | buildPhases = ( 171 | 8CAB19D72438B3160002AE8B /* Sources */, 172 | 8CAB19D82438B3160002AE8B /* Frameworks */, 173 | 8CAB19D92438B3160002AE8B /* Resources */, 174 | ); 175 | buildRules = ( 176 | ); 177 | dependencies = ( 178 | ); 179 | name = Shortcuts; 180 | productName = Shortcuts; 181 | productReference = 8CAB19DB2438B3160002AE8B /* Shortcuts.appex */; 182 | productType = "com.apple.product-type.app-extension"; 183 | }; 184 | /* End PBXNativeTarget section */ 185 | 186 | /* Begin PBXProject section */ 187 | 8CAB19B82438B2CF0002AE8B /* Project object */ = { 188 | isa = PBXProject; 189 | attributes = { 190 | LastSwiftUpdateCheck = 1140; 191 | LastUpgradeCheck = 1140; 192 | ORGANIZATIONNAME = "Alex Hay"; 193 | TargetAttributes = { 194 | 8CAB19BF2438B2D00002AE8B = { 195 | CreatedOnToolsVersion = 11.4; 196 | }; 197 | 8CAB19DA2438B3160002AE8B = { 198 | CreatedOnToolsVersion = 11.4; 199 | }; 200 | }; 201 | }; 202 | buildConfigurationList = 8CAB19BB2438B2CF0002AE8B /* Build configuration list for PBXProject "ShortcutsExamples" */; 203 | compatibilityVersion = "Xcode 9.3"; 204 | developmentRegion = en; 205 | hasScannedForEncodings = 0; 206 | knownRegions = ( 207 | en, 208 | Base, 209 | ); 210 | mainGroup = 8CAB19B72438B2CF0002AE8B; 211 | productRefGroup = 8CAB19C12438B2D00002AE8B /* Products */; 212 | projectDirPath = ""; 213 | projectRoot = ""; 214 | targets = ( 215 | 8CAB19BF2438B2D00002AE8B /* ShortcutsExamples */, 216 | 8CAB19DA2438B3160002AE8B /* Shortcuts */, 217 | ); 218 | }; 219 | /* End PBXProject section */ 220 | 221 | /* Begin PBXResourcesBuildPhase section */ 222 | 8CAB19BE2438B2D00002AE8B /* Resources */ = { 223 | isa = PBXResourcesBuildPhase; 224 | buildActionMask = 2147483647; 225 | files = ( 226 | 8CAB19D02438B2D30002AE8B /* LaunchScreen.storyboard in Resources */, 227 | 8CAB19CD2438B2D30002AE8B /* Preview Assets.xcassets in Resources */, 228 | 8CAB19CA2438B2D30002AE8B /* Assets.xcassets in Resources */, 229 | ); 230 | runOnlyForDeploymentPostprocessing = 0; 231 | }; 232 | 8CAB19D92438B3160002AE8B /* Resources */ = { 233 | isa = PBXResourcesBuildPhase; 234 | buildActionMask = 2147483647; 235 | files = ( 236 | ); 237 | runOnlyForDeploymentPostprocessing = 0; 238 | }; 239 | /* End PBXResourcesBuildPhase section */ 240 | 241 | /* Begin PBXSourcesBuildPhase section */ 242 | 8CAB19BC2438B2D00002AE8B /* Sources */ = { 243 | isa = PBXSourcesBuildPhase; 244 | buildActionMask = 2147483647; 245 | files = ( 246 | 8CAB19C42438B2D00002AE8B /* AppDelegate.swift in Sources */, 247 | 8CAB19C62438B2D00002AE8B /* SceneDelegate.swift in Sources */, 248 | 8CAB19EB2438E6860002AE8B /* Intents.intentdefinition in Sources */, 249 | 8CAB19C82438B2D00002AE8B /* ContentView.swift in Sources */, 250 | ); 251 | runOnlyForDeploymentPostprocessing = 0; 252 | }; 253 | 8CAB19D72438B3160002AE8B /* Sources */ = { 254 | isa = PBXSourcesBuildPhase; 255 | buildActionMask = 2147483647; 256 | files = ( 257 | 8C06992F243F11B200831945 /* renameFilesHandler.swift in Sources */, 258 | 8CAB19E82438B5570002AE8B /* Intents.intentdefinition in Sources */, 259 | 8CAB19EA2438DE330002AE8B /* makeUppercaseHandler.swift in Sources */, 260 | 8CAB19DE2438B3160002AE8B /* IntentHandler.swift in Sources */, 261 | ); 262 | runOnlyForDeploymentPostprocessing = 0; 263 | }; 264 | /* End PBXSourcesBuildPhase section */ 265 | 266 | /* Begin PBXTargetDependency section */ 267 | 8CAB19E12438B3160002AE8B /* PBXTargetDependency */ = { 268 | isa = PBXTargetDependency; 269 | target = 8CAB19DA2438B3160002AE8B /* Shortcuts */; 270 | targetProxy = 8CAB19E02438B3160002AE8B /* PBXContainerItemProxy */; 271 | }; 272 | /* End PBXTargetDependency section */ 273 | 274 | /* Begin PBXVariantGroup section */ 275 | 8CAB19CE2438B2D30002AE8B /* LaunchScreen.storyboard */ = { 276 | isa = PBXVariantGroup; 277 | children = ( 278 | 8CAB19CF2438B2D30002AE8B /* Base */, 279 | ); 280 | name = LaunchScreen.storyboard; 281 | sourceTree = ""; 282 | }; 283 | /* End PBXVariantGroup section */ 284 | 285 | /* Begin XCBuildConfiguration section */ 286 | 8CAB19D22438B2D30002AE8B /* Debug */ = { 287 | isa = XCBuildConfiguration; 288 | buildSettings = { 289 | ALWAYS_SEARCH_USER_PATHS = NO; 290 | CLANG_ANALYZER_NONNULL = YES; 291 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 292 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 293 | CLANG_CXX_LIBRARY = "libc++"; 294 | CLANG_ENABLE_MODULES = YES; 295 | CLANG_ENABLE_OBJC_ARC = YES; 296 | CLANG_ENABLE_OBJC_WEAK = YES; 297 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 298 | CLANG_WARN_BOOL_CONVERSION = YES; 299 | CLANG_WARN_COMMA = YES; 300 | CLANG_WARN_CONSTANT_CONVERSION = YES; 301 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 302 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 303 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 304 | CLANG_WARN_EMPTY_BODY = YES; 305 | CLANG_WARN_ENUM_CONVERSION = YES; 306 | CLANG_WARN_INFINITE_RECURSION = YES; 307 | CLANG_WARN_INT_CONVERSION = YES; 308 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 309 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 310 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 311 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 312 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 313 | CLANG_WARN_STRICT_PROTOTYPES = YES; 314 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 315 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 316 | CLANG_WARN_UNREACHABLE_CODE = YES; 317 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 318 | COPY_PHASE_STRIP = NO; 319 | DEBUG_INFORMATION_FORMAT = dwarf; 320 | ENABLE_STRICT_OBJC_MSGSEND = YES; 321 | ENABLE_TESTABILITY = YES; 322 | GCC_C_LANGUAGE_STANDARD = gnu11; 323 | GCC_DYNAMIC_NO_PIC = NO; 324 | GCC_NO_COMMON_BLOCKS = YES; 325 | GCC_OPTIMIZATION_LEVEL = 0; 326 | GCC_PREPROCESSOR_DEFINITIONS = ( 327 | "DEBUG=1", 328 | "$(inherited)", 329 | ); 330 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 331 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 332 | GCC_WARN_UNDECLARED_SELECTOR = YES; 333 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 334 | GCC_WARN_UNUSED_FUNCTION = YES; 335 | GCC_WARN_UNUSED_VARIABLE = YES; 336 | IPHONEOS_DEPLOYMENT_TARGET = 13.4; 337 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 338 | MTL_FAST_MATH = YES; 339 | ONLY_ACTIVE_ARCH = YES; 340 | SDKROOT = iphoneos; 341 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 342 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 343 | }; 344 | name = Debug; 345 | }; 346 | 8CAB19D32438B2D30002AE8B /* Release */ = { 347 | isa = XCBuildConfiguration; 348 | buildSettings = { 349 | ALWAYS_SEARCH_USER_PATHS = NO; 350 | CLANG_ANALYZER_NONNULL = YES; 351 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 352 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 353 | CLANG_CXX_LIBRARY = "libc++"; 354 | CLANG_ENABLE_MODULES = YES; 355 | CLANG_ENABLE_OBJC_ARC = YES; 356 | CLANG_ENABLE_OBJC_WEAK = YES; 357 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 358 | CLANG_WARN_BOOL_CONVERSION = YES; 359 | CLANG_WARN_COMMA = YES; 360 | CLANG_WARN_CONSTANT_CONVERSION = YES; 361 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 362 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 363 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 364 | CLANG_WARN_EMPTY_BODY = YES; 365 | CLANG_WARN_ENUM_CONVERSION = YES; 366 | CLANG_WARN_INFINITE_RECURSION = YES; 367 | CLANG_WARN_INT_CONVERSION = YES; 368 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 369 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 370 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 371 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 372 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 373 | CLANG_WARN_STRICT_PROTOTYPES = YES; 374 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 375 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 376 | CLANG_WARN_UNREACHABLE_CODE = YES; 377 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 378 | COPY_PHASE_STRIP = NO; 379 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 380 | ENABLE_NS_ASSERTIONS = NO; 381 | ENABLE_STRICT_OBJC_MSGSEND = YES; 382 | GCC_C_LANGUAGE_STANDARD = gnu11; 383 | GCC_NO_COMMON_BLOCKS = YES; 384 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 385 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 386 | GCC_WARN_UNDECLARED_SELECTOR = YES; 387 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 388 | GCC_WARN_UNUSED_FUNCTION = YES; 389 | GCC_WARN_UNUSED_VARIABLE = YES; 390 | IPHONEOS_DEPLOYMENT_TARGET = 13.4; 391 | MTL_ENABLE_DEBUG_INFO = NO; 392 | MTL_FAST_MATH = YES; 393 | SDKROOT = iphoneos; 394 | SWIFT_COMPILATION_MODE = wholemodule; 395 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 396 | VALIDATE_PRODUCT = YES; 397 | }; 398 | name = Release; 399 | }; 400 | 8CAB19D52438B2D30002AE8B /* Debug */ = { 401 | isa = XCBuildConfiguration; 402 | buildSettings = { 403 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 404 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 405 | CODE_SIGN_STYLE = Automatic; 406 | DEVELOPMENT_ASSET_PATHS = "\"ShortcutsExamples/Preview Content\""; 407 | DEVELOPMENT_TEAM = NWHDL7X5B3; 408 | ENABLE_PREVIEWS = YES; 409 | INFOPLIST_FILE = ShortcutsExamples/Info.plist; 410 | LD_RUNPATH_SEARCH_PATHS = ( 411 | "$(inherited)", 412 | "@executable_path/Frameworks", 413 | ); 414 | PRODUCT_BUNDLE_IDENTIFIER = com.alexhay.shortcutsExamples.ShortcutsExamples; 415 | PRODUCT_NAME = "$(TARGET_NAME)"; 416 | SWIFT_VERSION = 5.0; 417 | TARGETED_DEVICE_FAMILY = "1,2"; 418 | }; 419 | name = Debug; 420 | }; 421 | 8CAB19D62438B2D30002AE8B /* Release */ = { 422 | isa = XCBuildConfiguration; 423 | buildSettings = { 424 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 425 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 426 | CODE_SIGN_STYLE = Automatic; 427 | DEVELOPMENT_ASSET_PATHS = "\"ShortcutsExamples/Preview Content\""; 428 | DEVELOPMENT_TEAM = NWHDL7X5B3; 429 | ENABLE_PREVIEWS = YES; 430 | INFOPLIST_FILE = ShortcutsExamples/Info.plist; 431 | LD_RUNPATH_SEARCH_PATHS = ( 432 | "$(inherited)", 433 | "@executable_path/Frameworks", 434 | ); 435 | PRODUCT_BUNDLE_IDENTIFIER = com.alexhay.shortcutsExamples.ShortcutsExamples; 436 | PRODUCT_NAME = "$(TARGET_NAME)"; 437 | SWIFT_VERSION = 5.0; 438 | TARGETED_DEVICE_FAMILY = "1,2"; 439 | }; 440 | name = Release; 441 | }; 442 | 8CAB19E42438B3160002AE8B /* Debug */ = { 443 | isa = XCBuildConfiguration; 444 | buildSettings = { 445 | CODE_SIGN_STYLE = Automatic; 446 | DEVELOPMENT_TEAM = NWHDL7X5B3; 447 | INFOPLIST_FILE = Shortcuts/Info.plist; 448 | LD_RUNPATH_SEARCH_PATHS = ( 449 | "$(inherited)", 450 | "@executable_path/Frameworks", 451 | "@executable_path/../../Frameworks", 452 | ); 453 | PRODUCT_BUNDLE_IDENTIFIER = com.alexhay.shortcutsExamples.ShortcutsExamples.Shortcuts; 454 | PRODUCT_NAME = "$(TARGET_NAME)"; 455 | SKIP_INSTALL = YES; 456 | SWIFT_VERSION = 5.0; 457 | TARGETED_DEVICE_FAMILY = "1,2"; 458 | }; 459 | name = Debug; 460 | }; 461 | 8CAB19E52438B3160002AE8B /* Release */ = { 462 | isa = XCBuildConfiguration; 463 | buildSettings = { 464 | CODE_SIGN_STYLE = Automatic; 465 | DEVELOPMENT_TEAM = NWHDL7X5B3; 466 | INFOPLIST_FILE = Shortcuts/Info.plist; 467 | LD_RUNPATH_SEARCH_PATHS = ( 468 | "$(inherited)", 469 | "@executable_path/Frameworks", 470 | "@executable_path/../../Frameworks", 471 | ); 472 | PRODUCT_BUNDLE_IDENTIFIER = com.alexhay.shortcutsExamples.ShortcutsExamples.Shortcuts; 473 | PRODUCT_NAME = "$(TARGET_NAME)"; 474 | SKIP_INSTALL = YES; 475 | SWIFT_VERSION = 5.0; 476 | TARGETED_DEVICE_FAMILY = "1,2"; 477 | }; 478 | name = Release; 479 | }; 480 | /* End XCBuildConfiguration section */ 481 | 482 | /* Begin XCConfigurationList section */ 483 | 8CAB19BB2438B2CF0002AE8B /* Build configuration list for PBXProject "ShortcutsExamples" */ = { 484 | isa = XCConfigurationList; 485 | buildConfigurations = ( 486 | 8CAB19D22438B2D30002AE8B /* Debug */, 487 | 8CAB19D32438B2D30002AE8B /* Release */, 488 | ); 489 | defaultConfigurationIsVisible = 0; 490 | defaultConfigurationName = Release; 491 | }; 492 | 8CAB19D42438B2D30002AE8B /* Build configuration list for PBXNativeTarget "ShortcutsExamples" */ = { 493 | isa = XCConfigurationList; 494 | buildConfigurations = ( 495 | 8CAB19D52438B2D30002AE8B /* Debug */, 496 | 8CAB19D62438B2D30002AE8B /* Release */, 497 | ); 498 | defaultConfigurationIsVisible = 0; 499 | defaultConfigurationName = Release; 500 | }; 501 | 8CAB19E32438B3160002AE8B /* Build configuration list for PBXNativeTarget "Shortcuts" */ = { 502 | isa = XCConfigurationList; 503 | buildConfigurations = ( 504 | 8CAB19E42438B3160002AE8B /* Debug */, 505 | 8CAB19E52438B3160002AE8B /* Release */, 506 | ); 507 | defaultConfigurationIsVisible = 0; 508 | defaultConfigurationName = Release; 509 | }; 510 | /* End XCConfigurationList section */ 511 | }; 512 | rootObject = 8CAB19B82438B2CF0002AE8B /* Project object */; 513 | } 514 | -------------------------------------------------------------------------------- /ShortcutsExamples.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ShortcutsExamples.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ShortcutsExamples.xcodeproj/xcuserdata/alexhay.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Shortcuts.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 1 11 | 12 | ShortcutsExamples.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 0 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /ShortcutsExamples/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // ShortcutsExamples 4 | // 5 | // Created by Alex Hay on 04/04/2020. 6 | // Copyright © 2020 Alex Hay. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 17 | // Override point for customization after application launch. 18 | return true 19 | } 20 | 21 | // MARK: UISceneSession Lifecycle 22 | 23 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 24 | // Called when a new scene session is being created. 25 | // Use this method to select a configuration to create the new scene with. 26 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 27 | } 28 | 29 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 30 | // Called when the user discards a scene session. 31 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 32 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 33 | } 34 | 35 | 36 | } 37 | 38 | -------------------------------------------------------------------------------- /ShortcutsExamples/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /ShortcutsExamples/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /ShortcutsExamples/Base.lproj/LaunchScreen.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 | -------------------------------------------------------------------------------- /ShortcutsExamples/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // ShortcutsExamples 4 | // 5 | // Created by Alex Hay on 04/04/2020. 6 | // Copyright © 2020 Alex Hay. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | 11 | struct ContentView: View { 12 | var body: some View { 13 | Text("Hello, World!") 14 | } 15 | } 16 | 17 | struct ContentView_Previews: PreviewProvider { 18 | static var previews: some View { 19 | ContentView() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ShortcutsExamples/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | NSUserActivityTypes 24 | 25 | MakeUppercaseIntent 26 | RenameFilesIntent 27 | 28 | UIApplicationSceneManifest 29 | 30 | UIApplicationSupportsMultipleScenes 31 | 32 | UISceneConfigurations 33 | 34 | UIWindowSceneSessionRoleApplication 35 | 36 | 37 | UISceneConfigurationName 38 | Default Configuration 39 | UISceneDelegateClassName 40 | $(PRODUCT_MODULE_NAME).SceneDelegate 41 | 42 | 43 | 44 | 45 | UILaunchStoryboardName 46 | LaunchScreen 47 | UIRequiredDeviceCapabilities 48 | 49 | armv7 50 | 51 | UISupportedInterfaceOrientations 52 | 53 | UIInterfaceOrientationPortrait 54 | UIInterfaceOrientationLandscapeLeft 55 | UIInterfaceOrientationLandscapeRight 56 | 57 | UISupportedInterfaceOrientations~ipad 58 | 59 | UIInterfaceOrientationPortrait 60 | UIInterfaceOrientationPortraitUpsideDown 61 | UIInterfaceOrientationLandscapeLeft 62 | UIInterfaceOrientationLandscapeRight 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /ShortcutsExamples/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /ShortcutsExamples/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // ShortcutsExamples 4 | // 5 | // Created by Alex Hay on 04/04/2020. 6 | // Copyright © 2020 Alex Hay. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftUI 11 | 12 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 18 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 19 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 20 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 21 | 22 | // Create the SwiftUI view that provides the window contents. 23 | let contentView = ContentView() 24 | 25 | // Use a UIHostingController as window root view controller. 26 | if let windowScene = scene as? UIWindowScene { 27 | let window = UIWindow(windowScene: windowScene) 28 | window.rootViewController = UIHostingController(rootView: contentView) 29 | self.window = window 30 | window.makeKeyAndVisible() 31 | } 32 | } 33 | 34 | func sceneDidDisconnect(_ scene: UIScene) { 35 | // Called as the scene is being released by the system. 36 | // This occurs shortly after the scene enters the background, or when its session is discarded. 37 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 38 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). 39 | } 40 | 41 | func sceneDidBecomeActive(_ scene: UIScene) { 42 | // Called when the scene has moved from an inactive state to an active state. 43 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 44 | } 45 | 46 | func sceneWillResignActive(_ scene: UIScene) { 47 | // Called when the scene will move from an active state to an inactive state. 48 | // This may occur due to temporary interruptions (ex. an incoming phone call). 49 | } 50 | 51 | func sceneWillEnterForeground(_ scene: UIScene) { 52 | // Called as the scene transitions from the background to the foreground. 53 | // Use this method to undo the changes made on entering the background. 54 | } 55 | 56 | func sceneDidEnterBackground(_ scene: UIScene) { 57 | // Called as the scene transitions from the foreground to the background. 58 | // Use this method to save data, release shared resources, and store enough scene-specific state information 59 | // to restore the scene back to its current state. 60 | } 61 | 62 | 63 | } 64 | 65 | --------------------------------------------------------------------------------