├── .github ├── ISSUE_TEMPLATE.md ├── ISSUE_TEMPLATE │ ├── bug-report.md │ └── feature-request.md └── lock.yml ├── .gitignore ├── .gitmodules ├── CONTRIBUTING.md ├── DEPRECATIONS.md ├── LICENSE ├── README.md ├── applesimutils ├── applesimutils.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── applesimutils.xcscheme └── applesimutils │ ├── ClearKeychain.h │ ├── ClearKeychain.m │ ├── ClearMedia.h │ ├── ClearMedia.m │ ├── DTXLoggingSubsystem.h │ ├── NSTask+InputOutput.h │ ├── NSTask+InputOutput.m │ ├── PrefixHeader.pch │ ├── SetHealthKitPermission.h │ ├── SetHealthKitPermission.m │ ├── SetLocationPermission.h │ ├── SetLocationPermission.m │ ├── SetNotificationsPermission.h │ ├── SetNotificationsPermission.m │ ├── SetServicePermission.h │ ├── SetServicePermission.m │ ├── SetSimulatorLocation.h │ ├── SetSimulatorLocation.m │ ├── SimUtils.h │ ├── SimUtils.m │ ├── Swiftier.h │ ├── main.m │ └── version.h ├── buildForBrew.sh ├── releaseVersion.sh └── sample └── PermissionsTest ├── PermissionsTest.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── PermissionsTest.xcscheme └── PermissionsTest ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets ├── AppIcon.appiconset │ └── Contents.json └── Contents.json ├── Base.lproj ├── LaunchScreen.storyboard └── Main.storyboard ├── Info.plist ├── PermissionsTest.entitlements ├── ViewController.h ├── ViewController.m └── main.m /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | #### Description 7 | 10 | 11 | #### Steps to Reproduce 12 | 16 | 17 | #### Device, macOS and Xcode Versions 18 | 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Create a report to help us improve AppleSimulatorUtils 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Description** 11 | A clear and concise description of what the bug is. If you are seeing a regression, try to provide the last known version where the issue did not reproduce. 12 | 13 | If you are describing an issue with `brew install`: 14 | - [ ] I've run `brew doctor` and fixed all issues 15 | 16 | **Steps to Reproduce** 17 | Steps to reproduce the behavior: 18 | 19 | **Expected Behavior** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Screenshots** 23 | If applicable, add screenshots to help explain your problem. 24 | 25 | **Environment** 26 | - macOS version: [e.g. 10.14.4] 27 | - Xcode version: [e.g. 10.2] 28 | 29 | **Additional Context** 30 | Add any other context about the problem here. 31 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/lock.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before a closed issue or pull request is locked 2 | daysUntilLock: 3 3 | 4 | # Issues and pull requests with these labels will not be locked. Set to `[]` to disable 5 | exemptLabels: [] 6 | 7 | # Label to add before locking, such as `outdated`. Set to `false` to disable 8 | lockLabel: false 9 | 10 | # Comment to post before locking. Set to `false` to disable 11 | lockComment: false 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## IDEs 6 | .idea/ 7 | 8 | ## Build generated 9 | build/ 10 | DerivedData/ 11 | Index/ 12 | 13 | ## Various settings 14 | *.pbxuser 15 | !default.pbxuser 16 | *.mode1v3 17 | !default.mode1v3 18 | *.mode2v3 19 | !default.mode2v3 20 | *.perspectivev3 21 | !default.perspectivev3 22 | xcuserdata/ 23 | 24 | ## Other 25 | *.moved-aside 26 | *.xcuserstate 27 | 28 | ## Obj-C/Swift specific 29 | *.hmap 30 | *.ipa 31 | *.dSYM.zip 32 | *.dSYM 33 | 34 | # CocoaPods 35 | # 36 | # We recommend against adding the Pods directory to your .gitignore. However 37 | # you should judge for yourself, the pros and cons are mentioned at: 38 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 39 | # 40 | # Pods/ 41 | 42 | # Carthage 43 | # 44 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 45 | # Carthage/Checkouts 46 | 47 | Carthage/Build 48 | 49 | # fastlane 50 | # 51 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 52 | # screenshots whenever they are needed. 53 | # For more information about the recommended setup visit: 54 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 55 | 56 | fastlane/report.xml 57 | fastlane/screenshots 58 | 59 | #Code Injection 60 | # 61 | # After new code Injection tools there's a generated folder /iOSInjectionProject 62 | # https://github.com/johnno1962/injectionforxcode 63 | 64 | iOSInjectionProject/ 65 | _tmp_release_notes.md 66 | /bottle 67 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "homebrew-brew"] 2 | path = homebrew-brew 3 | url = git@github.com:wix/homebrew-brew.git 4 | [submodule "ObjCCLIInfra"] 5 | path = ObjCCLIInfra 6 | url = git@github.com:wix/ObjCCLIInfra.git 7 | [submodule "fmdb"] 8 | path = fmdb 9 | url = git@github.com:ccgus/fmdb.git 10 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to AppleSimulatorUtils (ASU) 2 | So, you want to contribute to ASU? Great! 👏 3 | Here are a few guidelines that will help you along the way. There are many ways to contribute, and we appreciate all of them. 4 | 5 | ## Before you start 6 | 7 | ASU is a collection of utils for Apple simulators. 8 | It is a command line tool that allows you to set permissions for apps running on simulators, clear the simulator's keychain and media, and more. 9 | 10 | We created this tool on Wix to help us with our development and testing, and it is mainly used by our testing framework, [Detox](https://wix.github.io/Detox/) (which is also an open source project), but can be used independently. 11 | 12 | ### Asking questions 13 | If you have a question, please open an issue in the Issues section of the project. Feel free to ask anything, even if you're not sure it's a bug or an enhancement. We'll do our best to answer, or redirect you to the right place. 14 | The more information you provide, the better we can help you. 15 | 16 | ### Reporting bugs 17 | If you find a bug, please report it in the [issues](http://github.com/wix/AppleSimulatorUtils/issues) section of the project. 18 | 19 | ### Suggesting enhancements 20 | For feature requests or suggestions, please open an issue in the Issues section of the project. Please provide as much information as possible, to help us understand what you're looking for. 21 | If you'd like to implement a new feature, please submit an issue with a proposal for your work first, to be sure that we can use it. When you are ready to start, please follow the instructions in the [Contributing code](#contributing-code) section below. 22 | 23 | ### Help others 24 | Help others by answering questions in the Issues section. If you see a question that you know the answer to, please help out! It's a great way to learn, and to help others. 25 | 26 | ### We ❤️ Pull Requests! 27 | **We are happy to accept contributions to ASU from the community!** 28 | Please read the following [guidelines](#contributing-code) before you start working on a pull request. 29 | 30 | 31 | ## Contributing code 32 | 33 | ASU is a homebrew package, therefore it can be installed using the `brew` command: 34 | ```shell 35 | brew tap wix/brew 36 | brew install applesimutils 37 | ``` 38 | 39 | However, if you are interested in changing ASU code and to contribute to it, the following steps are required. 40 | 1. Clone the repository to your local machine. 41 | 2. Make sure you have Xcode installed. 42 | 3. Open the `applesimutils.xcodeproj` file in Xcode. 43 | 4. Make your changes. 44 | 5. Build the project from the Xcode: `Product > Build` or `⌘B`. 45 | 6. Set the scheme launch arguments to the desired command line arguments you want to test (e.g. `--list`). This can be done by clicking on the scheme name in the top left corner of Xcode, and then selecting `Edit Scheme...`. In the `Arguments` tab, select or add the desired arguments to the `Arguments Passed On Launch` section. 46 | 7. Run and play with the tool from the Xcode: `Product > Run` or `⌘R`. 47 | 1. Set breakpoints to debug your changes. 48 | 2. Plan a manual test scenario and run it (unfortunately, we don't have automated tests for this project). Make sure the output is as expected. 49 | 8. Commit your changes and open a pull request. 50 | 1. Add a description of your changes. 51 | 2. Describe how to test them (how you tested them). 52 | 3. Add a link to the issue you are fixing, if there is one. 53 | 9. Wait for the pull request to be reviewed our team and merged. 54 | 10. If everything went well, your changes will be available in the next release of ASU. 55 | 56 | Thank you for your contribution! 🎉 57 | -------------------------------------------------------------------------------- /DEPRECATIONS.md: -------------------------------------------------------------------------------- 1 | # Deprecations in AppleSimulatorUtils 2 | 3 | As of January 2024, we've identified overlapping functionalities between **AppleSimulatorUtils** and **`xcrun simctl`** command in the latest **Command Line Tools for Xcode**. 4 | This has led to the deprecation of several commands in **AppleSimulatorUtils**. 5 | 6 | For detailed usage of the **`xcrun simctl`** command, run `xcrun simctl --help`. 7 | 8 | ## Deprecated Commands 9 | 10 | The commands listed below are now deprecated in **AppleSimulatorUtils**, and can be replaced with corresponding **`xcrun simctl`** commands: 11 | 12 | ### `--setPermissions` Options 13 | 14 | The following `--setPermissions` options in **AppleSimulatorUtils** are deprecated and can be replaced with corresponding `xcrun simctl privacy` commands: 15 | 16 | - `calendar` 17 | - `contacts` 18 | - `location` 19 | - `photos` 20 | - `medialibrary` _(Use `media-library` in `xcrun simctl privacy`)_ 21 | - `microphone` 22 | - `motion` 23 | - `reminders` 24 | - `siri` 25 | 26 | For detailed usage of the simctl privacy commands, run `xcrun simctl privacy --help`. 27 | 28 | ### `--setLocation` Command 29 | 30 | The `--setLocation` command is deprecated. Use `xcrun simctl location` instead. 31 | 32 | For detailed usage of the location command, run `xcrun simctl location --help`. 33 | 34 | ## Additional Notes 35 | 36 | While most of the deprecated commands are still available and function in **AppleSimulatorUtils**, 37 | some may show regressions (especially in newer versions of iOS and Xcode). 38 | 39 | If you encounter any issues, 40 | the recommended solution is to use **`xcrun simctl`** instead, 41 | as it is more up-to-date and maintained by Apple. 42 | 43 | --- 44 | 45 | **We will continue to maintain AppleSimulatorUtils for non-overlapping functionalities only.** 46 | 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Wix.com 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 | 23 | JPSimulatorHacks 24 | ---------------- 25 | 26 | The MIT License (MIT) 27 | 28 | Copyright (c) 2014 Johannes Plunien 29 | 30 | Permission is hereby granted, free of charge, to any person obtaining a copy of 31 | this software and associated documentation files (the "Software"), to deal in 32 | the Software without restriction, including without limitation the rights to 33 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 34 | the Software, and to permit persons to whom the Software is furnished to do so, 35 | subject to the following conditions: 36 | 37 | The above copyright notice and this permission notice shall be included in all 38 | copies or substantial portions of the Software. 39 | 40 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 41 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 42 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 43 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 44 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 45 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 46 | 47 | GBCli 48 | ----- 49 | 50 | Copyright (C) 2012 by Tomaz Kragelj 51 | 52 | Permission is hereby granted, free of charge, to any person obtaining a copy 53 | of this software and associated documentation files (the "Software"), to deal 54 | in the Software without restriction, including without limitation the rights 55 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 56 | copies of the Software, and to permit persons to whom the Software is 57 | furnished to do so, subject to the following conditions: 58 | 59 | The above copyright notice and this permission notice shall be included in 60 | all copies or substantial portions of the Software. 61 | 62 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 63 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 64 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 65 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 66 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 67 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 68 | THE SOFTWARE. 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AppleSimulatorUtils 2 | A collection of utils for Apple simulators. 3 | 4 | ## Deprecation Notice 5 | 6 | **AppleSimulatorUtils** remains an actively maintained project. 7 | However, we have deprecated certain functionalities that overlap with features provided by the **`xcrun simctl`** command in the latest **Command Line Tools for Xcode**. 8 | In addition to avoid redundancy, it is better to use the **official** tool provided by Apple. 9 | 10 | For a comprehensive list of deprecated commands and their `xcrun simctl` alternatives, please refer to our [Deprecations Document](./DEPRECATIONS.md). 11 | 12 | 13 | ## Installing 14 | 15 | Install [brew](https://brew.sh), then: 16 | 17 | ```shell 18 | brew tap wix/brew 19 | brew install applesimutils 20 | ``` 21 | 22 | ## Usage 23 | 24 | ``` 25 | A collection of utils for Apple simulators. 26 | 27 | Usage Examples: 28 | applesimutils --byId --bundle --setPermissions ", , ..." 29 | applesimutils --byName --byOS --bundle --setPermissions ", , ..." 30 | applesimutils --list [--byName ] [--byOS ] [--byType ] [--maxResults ] [--fields ] 31 | applesimutils --booted --biometricEnrollment 32 | applesimutils --booted --biometricMatch 33 | 34 | Options: 35 | --byId, -id Filters simulators by unique device identifier (UDID) 36 | --byName, -n Filters simulators by name 37 | --byType, -t Filters simulators by device type 38 | --byOS, -o Filters simulators by operating system 39 | --booted, -bt Filters simulators by booted status 40 | 41 | --list, -l Lists available simulators 42 | --bundle, -b The app bundle identifier 43 | --maxResults Limits the number of results returned from --list 44 | --fields Comma-separated list of fields to include in --list output (e.g. "udid,os,identifier") 45 | 46 | --setPermissions, -sp Sets the specified permissions and restarts SpringBoard for the changes to take effect 47 | --clearKeychain, -ck Clears the simulator's keychain 48 | --clearMedia, -cm Clears the simulator's media 49 | --restartSB, -sb Restarts SpringBoard 50 | 51 | --biometricEnrollment, -be Enables or disables biometric (Face ID/Touch ID) enrollment. 52 | --biometricMatch, -bm Approves a biometric authentication request with a matching biometric feature (e.g. face or finger) 53 | --biometricNonmatch, -bnm Fails a biometric authentication request with a non-matching biometric feature (e.g. face or finger) 54 | 55 | --version, -v Prints version 56 | --help, -h Prints usage 57 | 58 | Available Permissions: 59 | calendar=YES|NO|unset 60 | camera=YES|NO|unset 61 | contacts=YES|NO|unset 62 | faceid=YES|NO|unset 63 | health=YES|NO|unset (iOS/tvOS 12.0 and above) 64 | homekit=YES|NO|unset 65 | location=always|inuse|never|unset 66 | medialibrary=YES|NO|unset 67 | microphone=YES|NO|unset 68 | motion=YES|NO|unset 69 | notifications=YES|NO|critical|unset 70 | photos=YES|NO|limited|unset (“limited” supported on iOS/tvOS 14.0 and above) 71 | reminders=YES|NO|unset 72 | siri=YES|NO|unset 73 | speech=YES|NO|unset 74 | userTracking=YES|NO|unset (iOS/tvOS 14.0 and above) 75 | ``` 76 | 77 | ## Troubleshooting 78 | 79 | - In case an installation fails, make sure to update your command line tools in the System Update system preference pane of your Mac 80 | - If Homebrew complains about a conflict in the `wix/brew` tap, run `brew untap wix/brew && brew tap wix/brew` and try installing again 81 | - If installation still fails, **run `brew doctor` and fix all issues & warnings** 82 | 83 | ### Contributing 84 | 85 | See [CONTRIBUTING.md](CONTRIBUTING.md) for more information. 86 | -------------------------------------------------------------------------------- /applesimutils/applesimutils.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 3909866525700D400099E93D /* ClearMedia.m in Sources */ = {isa = PBXBuildFile; fileRef = 3909866425700D400099E93D /* ClearMedia.m */; }; 11 | 391596E01E8CBAA900FDD6F5 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 391596DF1E8CBAA900FDD6F5 /* main.m */; }; 12 | 391597021E8CDD1D00FDD6F5 /* SetNotificationsPermission.m in Sources */ = {isa = PBXBuildFile; fileRef = 391597011E8CDD1D00FDD6F5 /* SetNotificationsPermission.m */; }; 13 | 391EBF311E911CAA0009AACD /* SetServicePermission.m in Sources */ = {isa = PBXBuildFile; fileRef = 391EBF301E911CAA0009AACD /* SetServicePermission.m */; }; 14 | 391EBF381E91212B0009AACD /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 391EBF371E91212B0009AACD /* libsqlite3.tbd */; }; 15 | 392AFBB122BAB9E0007E0F15 /* SetHealthKitPermission.m in Sources */ = {isa = PBXBuildFile; fileRef = 392AFBB022BAB9E0007E0F15 /* SetHealthKitPermission.m */; }; 16 | 393510391FCB9972001FB466 /* LNOptionsParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 393510361FCB9972001FB466 /* LNOptionsParser.m */; }; 17 | 3935103A1FCB9972001FB466 /* LNLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 393510381FCB9972001FB466 /* LNLog.m */; }; 18 | 393510451FCB9A1E001FB466 /* GBSettings.m in Sources */ = {isa = PBXBuildFile; fileRef = 3935103E1FCB9A1E001FB466 /* GBSettings.m */; }; 19 | 393510461FCB9A1E001FB466 /* GBOptionsHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 3935103F1FCB9A1E001FB466 /* GBOptionsHelper.m */; }; 20 | 393510471FCB9A1E001FB466 /* GBCommandLineParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 393510401FCB9A1E001FB466 /* GBCommandLineParser.m */; }; 21 | 393510481FCB9A1E001FB466 /* GBPrint.m in Sources */ = {isa = PBXBuildFile; fileRef = 393510431FCB9A1E001FB466 /* GBPrint.m */; }; 22 | 39470A6C22BA879F00BDFA5C /* libFMDB.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 39470A6122BA878700BDFA5C /* libFMDB.a */; }; 23 | 395046D51EA725FF00557294 /* SetLocationPermission.m in Sources */ = {isa = PBXBuildFile; fileRef = 395046D41EA725FF00557294 /* SetLocationPermission.m */; }; 24 | 395046D81EA72F1000557294 /* SimUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 395046D71EA72F1000557294 /* SimUtils.m */; }; 25 | 39676AD61F94BE4100018548 /* ClearKeychain.m in Sources */ = {isa = PBXBuildFile; fileRef = 39676AD51F94BE4100018548 /* ClearKeychain.m */; }; 26 | 3976EA0225F8EEE800E4BD78 /* SetSimulatorLocation.m in Sources */ = {isa = PBXBuildFile; fileRef = 3976E9FA25F8EEE800E4BD78 /* SetSimulatorLocation.m */; }; 27 | 39A57000258F3FF90067D86B /* DTXLogging.m in Sources */ = {isa = PBXBuildFile; fileRef = 39A56FFF258F3FF90067D86B /* DTXLogging.m */; }; 28 | 39EAF80624E2F008003F86C3 /* NSTask+InputOutput.m in Sources */ = {isa = PBXBuildFile; fileRef = 39EAF80524E2F008003F86C3 /* NSTask+InputOutput.m */; }; 29 | /* End PBXBuildFile section */ 30 | 31 | /* Begin PBXContainerItemProxy section */ 32 | 39470A5E22BA878700BDFA5C /* PBXContainerItemProxy */ = { 33 | isa = PBXContainerItemProxy; 34 | containerPortal = 39470A5422BA878700BDFA5C /* fmdb.xcodeproj */; 35 | proxyType = 2; 36 | remoteGlobalIDString = 8DD76FA10486AA7600D96B5E; 37 | remoteInfo = fmdb; 38 | }; 39 | 39470A6022BA878700BDFA5C /* PBXContainerItemProxy */ = { 40 | isa = PBXContainerItemProxy; 41 | containerPortal = 39470A5422BA878700BDFA5C /* fmdb.xcodeproj */; 42 | proxyType = 2; 43 | remoteGlobalIDString = EE4290EF12B42F870088BD94; 44 | remoteInfo = FMDB; 45 | }; 46 | 39470A6222BA878700BDFA5C /* PBXContainerItemProxy */ = { 47 | isa = PBXContainerItemProxy; 48 | containerPortal = 39470A5422BA878700BDFA5C /* fmdb.xcodeproj */; 49 | proxyType = 2; 50 | remoteGlobalIDString = BF5D041618416BB2008C5AA9; 51 | remoteInfo = Tests; 52 | }; 53 | 39470A6422BA878700BDFA5C /* PBXContainerItemProxy */ = { 54 | isa = PBXContainerItemProxy; 55 | containerPortal = 39470A5422BA878700BDFA5C /* fmdb.xcodeproj */; 56 | proxyType = 2; 57 | remoteGlobalIDString = 6290CBB5188FE836009790F8; 58 | remoteInfo = "FMDB-IOS"; 59 | }; 60 | 39470A6622BA878700BDFA5C /* PBXContainerItemProxy */ = { 61 | isa = PBXContainerItemProxy; 62 | containerPortal = 39470A5422BA878700BDFA5C /* fmdb.xcodeproj */; 63 | proxyType = 2; 64 | remoteGlobalIDString = 83C73EFE1C326AB000FFC730; 65 | remoteInfo = "FMDB iOS"; 66 | }; 67 | 39470A6822BA878700BDFA5C /* PBXContainerItemProxy */ = { 68 | isa = PBXContainerItemProxy; 69 | containerPortal = 39470A5422BA878700BDFA5C /* fmdb.xcodeproj */; 70 | proxyType = 2; 71 | remoteGlobalIDString = 83C73F0B1C326ADA00FFC730; 72 | remoteInfo = "FMDB MacOS"; 73 | }; 74 | 39470A6A22BA878700BDFA5C /* PBXContainerItemProxy */ = { 75 | isa = PBXContainerItemProxy; 76 | containerPortal = 39470A5422BA878700BDFA5C /* fmdb.xcodeproj */; 77 | proxyType = 2; 78 | remoteGlobalIDString = 2CD2426D1FCC09CA00479FDE; 79 | remoteInfo = "FMDB watchOS"; 80 | }; 81 | 39470A6D22BA8C1900BDFA5C /* PBXContainerItemProxy */ = { 82 | isa = PBXContainerItemProxy; 83 | containerPortal = 39470A5422BA878700BDFA5C /* fmdb.xcodeproj */; 84 | proxyType = 1; 85 | remoteGlobalIDString = EE4290EE12B42F870088BD94; 86 | remoteInfo = FMDB; 87 | }; 88 | /* End PBXContainerItemProxy section */ 89 | 90 | /* Begin PBXCopyFilesBuildPhase section */ 91 | 391596DA1E8CBAA900FDD6F5 /* CopyFiles */ = { 92 | isa = PBXCopyFilesBuildPhase; 93 | buildActionMask = 2147483647; 94 | dstPath = /usr/share/man/man1/; 95 | dstSubfolderSpec = 0; 96 | files = ( 97 | ); 98 | runOnlyForDeploymentPostprocessing = 1; 99 | }; 100 | /* End PBXCopyFilesBuildPhase section */ 101 | 102 | /* Begin PBXFileReference section */ 103 | 3909866325700D400099E93D /* ClearMedia.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ClearMedia.h; sourceTree = ""; }; 104 | 3909866425700D400099E93D /* ClearMedia.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ClearMedia.m; sourceTree = ""; }; 105 | 391596DC1E8CBAA900FDD6F5 /* applesimutils */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = applesimutils; sourceTree = BUILT_PRODUCTS_DIR; }; 106 | 391596DF1E8CBAA900FDD6F5 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 107 | 391597001E8CDD1D00FDD6F5 /* SetNotificationsPermission.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SetNotificationsPermission.h; sourceTree = ""; }; 108 | 391597011E8CDD1D00FDD6F5 /* SetNotificationsPermission.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SetNotificationsPermission.m; sourceTree = ""; }; 109 | 391EBF2F1E911CAA0009AACD /* SetServicePermission.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SetServicePermission.h; sourceTree = ""; }; 110 | 391EBF301E911CAA0009AACD /* SetServicePermission.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SetServicePermission.m; sourceTree = ""; }; 111 | 391EBF371E91212B0009AACD /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; }; 112 | 392AFBA522BA9149007E0F15 /* Swiftier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Swiftier.h; sourceTree = ""; }; 113 | 392AFBA622BA915A007E0F15 /* PrefixHeader.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PrefixHeader.pch; sourceTree = ""; }; 114 | 392AFBAF22BAB9E0007E0F15 /* SetHealthKitPermission.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SetHealthKitPermission.h; sourceTree = ""; }; 115 | 392AFBB022BAB9E0007E0F15 /* SetHealthKitPermission.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SetHealthKitPermission.m; sourceTree = ""; }; 116 | 393510351FCB9972001FB466 /* LNOptionsParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LNOptionsParser.h; path = ../../ObjCCLIInfra/LNOptionsParser.h; sourceTree = ""; }; 117 | 393510361FCB9972001FB466 /* LNOptionsParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = LNOptionsParser.m; path = ../../ObjCCLIInfra/LNOptionsParser.m; sourceTree = ""; }; 118 | 393510371FCB9972001FB466 /* LNLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LNLog.h; path = ../../ObjCCLIInfra/LNLog.h; sourceTree = ""; }; 119 | 393510381FCB9972001FB466 /* LNLog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = LNLog.m; path = ../../ObjCCLIInfra/LNLog.m; sourceTree = ""; }; 120 | 3935103C1FCB9A1E001FB466 /* GBPrint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GBPrint.h; path = ../../ObjCCLIInfra/GBCli/GBCli/src/GBPrint.h; sourceTree = ""; }; 121 | 3935103D1FCB9A1E001FB466 /* GBCommandLineParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GBCommandLineParser.h; path = ../../ObjCCLIInfra/GBCli/GBCli/src/GBCommandLineParser.h; sourceTree = ""; }; 122 | 3935103E1FCB9A1E001FB466 /* GBSettings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GBSettings.m; path = ../../ObjCCLIInfra/GBCli/GBCli/src/GBSettings.m; sourceTree = ""; }; 123 | 3935103F1FCB9A1E001FB466 /* GBOptionsHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GBOptionsHelper.m; path = ../../ObjCCLIInfra/GBCli/GBCli/src/GBOptionsHelper.m; sourceTree = ""; }; 124 | 393510401FCB9A1E001FB466 /* GBCommandLineParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GBCommandLineParser.m; path = ../../ObjCCLIInfra/GBCli/GBCli/src/GBCommandLineParser.m; sourceTree = ""; }; 125 | 393510411FCB9A1E001FB466 /* GBCli.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GBCli.h; path = ../../ObjCCLIInfra/GBCli/GBCli/src/GBCli.h; sourceTree = ""; }; 126 | 393510421FCB9A1E001FB466 /* GBOptionsHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GBOptionsHelper.h; path = ../../ObjCCLIInfra/GBCli/GBCli/src/GBOptionsHelper.h; sourceTree = ""; }; 127 | 393510431FCB9A1E001FB466 /* GBPrint.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GBPrint.m; path = ../../ObjCCLIInfra/GBCli/GBCli/src/GBPrint.m; sourceTree = ""; }; 128 | 393510441FCB9A1E001FB466 /* GBSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GBSettings.h; path = ../../ObjCCLIInfra/GBCli/GBCli/src/GBSettings.h; sourceTree = ""; }; 129 | 39470A5422BA878700BDFA5C /* fmdb.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = fmdb.xcodeproj; path = ../../fmdb/fmdb.xcodeproj; sourceTree = ""; }; 130 | 395046D31EA725FF00557294 /* SetLocationPermission.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SetLocationPermission.h; sourceTree = ""; }; 131 | 395046D41EA725FF00557294 /* SetLocationPermission.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SetLocationPermission.m; sourceTree = ""; }; 132 | 395046D61EA72F1000557294 /* SimUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SimUtils.h; sourceTree = ""; }; 133 | 395046D71EA72F1000557294 /* SimUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SimUtils.m; sourceTree = ""; }; 134 | 39676AD41F94BE4100018548 /* ClearKeychain.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ClearKeychain.h; sourceTree = ""; }; 135 | 39676AD51F94BE4100018548 /* ClearKeychain.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ClearKeychain.m; sourceTree = ""; }; 136 | 3976E9FA25F8EEE800E4BD78 /* SetSimulatorLocation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SetSimulatorLocation.m; sourceTree = ""; }; 137 | 3976EA0125F8EEE800E4BD78 /* SetSimulatorLocation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SetSimulatorLocation.h; sourceTree = ""; }; 138 | 39A56FF8258F3FF90067D86B /* DTXLogging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DTXLogging.h; path = ../../ObjCCLIInfra/DTXLoggingInfra/DTXLogging.h; sourceTree = ""; }; 139 | 39A56FFF258F3FF90067D86B /* DTXLogging.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DTXLogging.m; path = ../../ObjCCLIInfra/DTXLoggingInfra/DTXLogging.m; sourceTree = ""; }; 140 | 39A57002258F416F0067D86B /* DTXLoggingSubsystem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DTXLoggingSubsystem.h; sourceTree = ""; }; 141 | 39EAF80424E2F008003F86C3 /* NSTask+InputOutput.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSTask+InputOutput.h"; sourceTree = ""; }; 142 | 39EAF80524E2F008003F86C3 /* NSTask+InputOutput.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSTask+InputOutput.m"; sourceTree = ""; }; 143 | 39FE99C31F8A6B17006DF8EF /* version.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = version.h; sourceTree = ""; }; 144 | /* End PBXFileReference section */ 145 | 146 | /* Begin PBXFrameworksBuildPhase section */ 147 | 391596D91E8CBAA900FDD6F5 /* Frameworks */ = { 148 | isa = PBXFrameworksBuildPhase; 149 | buildActionMask = 2147483647; 150 | files = ( 151 | 39470A6C22BA879F00BDFA5C /* libFMDB.a in Frameworks */, 152 | 391EBF381E91212B0009AACD /* libsqlite3.tbd in Frameworks */, 153 | ); 154 | runOnlyForDeploymentPostprocessing = 0; 155 | }; 156 | /* End PBXFrameworksBuildPhase section */ 157 | 158 | /* Begin PBXGroup section */ 159 | 391596D31E8CBAA900FDD6F5 = { 160 | isa = PBXGroup; 161 | children = ( 162 | 391596DE1E8CBAA900FDD6F5 /* applesimutils */, 163 | 391596DD1E8CBAA900FDD6F5 /* Products */, 164 | 391EBF361E91212B0009AACD /* Frameworks */, 165 | ); 166 | sourceTree = ""; 167 | usesTabs = 1; 168 | }; 169 | 391596DD1E8CBAA900FDD6F5 /* Products */ = { 170 | isa = PBXGroup; 171 | children = ( 172 | 391596DC1E8CBAA900FDD6F5 /* applesimutils */, 173 | ); 174 | name = Products; 175 | sourceTree = ""; 176 | }; 177 | 391596DE1E8CBAA900FDD6F5 /* applesimutils */ = { 178 | isa = PBXGroup; 179 | children = ( 180 | 39470A5322BA878000BDFA5C /* fmdb */, 181 | 391596FF1E8CDD0500FDD6F5 /* Handlers */, 182 | 3935103B1FCB9A0D001FB466 /* GBCli */, 183 | 39A56FF8258F3FF90067D86B /* DTXLogging.h */, 184 | 39A56FFF258F3FF90067D86B /* DTXLogging.m */, 185 | 39A57002258F416F0067D86B /* DTXLoggingSubsystem.h */, 186 | 393510371FCB9972001FB466 /* LNLog.h */, 187 | 393510381FCB9972001FB466 /* LNLog.m */, 188 | 393510351FCB9972001FB466 /* LNOptionsParser.h */, 189 | 393510361FCB9972001FB466 /* LNOptionsParser.m */, 190 | 391596DF1E8CBAA900FDD6F5 /* main.m */, 191 | 39FE99C31F8A6B17006DF8EF /* version.h */, 192 | 392AFBA522BA9149007E0F15 /* Swiftier.h */, 193 | 392AFBA622BA915A007E0F15 /* PrefixHeader.pch */, 194 | 39EAF80424E2F008003F86C3 /* NSTask+InputOutput.h */, 195 | 39EAF80524E2F008003F86C3 /* NSTask+InputOutput.m */, 196 | ); 197 | path = applesimutils; 198 | sourceTree = ""; 199 | }; 200 | 391596FF1E8CDD0500FDD6F5 /* Handlers */ = { 201 | isa = PBXGroup; 202 | children = ( 203 | 39676AD41F94BE4100018548 /* ClearKeychain.h */, 204 | 39676AD51F94BE4100018548 /* ClearKeychain.m */, 205 | 3909866325700D400099E93D /* ClearMedia.h */, 206 | 3909866425700D400099E93D /* ClearMedia.m */, 207 | 392AFBAF22BAB9E0007E0F15 /* SetHealthKitPermission.h */, 208 | 392AFBB022BAB9E0007E0F15 /* SetHealthKitPermission.m */, 209 | 395046D31EA725FF00557294 /* SetLocationPermission.h */, 210 | 395046D41EA725FF00557294 /* SetLocationPermission.m */, 211 | 391597001E8CDD1D00FDD6F5 /* SetNotificationsPermission.h */, 212 | 391597011E8CDD1D00FDD6F5 /* SetNotificationsPermission.m */, 213 | 391EBF2F1E911CAA0009AACD /* SetServicePermission.h */, 214 | 391EBF301E911CAA0009AACD /* SetServicePermission.m */, 215 | 395046D61EA72F1000557294 /* SimUtils.h */, 216 | 395046D71EA72F1000557294 /* SimUtils.m */, 217 | 3976EA0125F8EEE800E4BD78 /* SetSimulatorLocation.h */, 218 | 3976E9FA25F8EEE800E4BD78 /* SetSimulatorLocation.m */, 219 | ); 220 | name = Handlers; 221 | sourceTree = ""; 222 | }; 223 | 391EBF361E91212B0009AACD /* Frameworks */ = { 224 | isa = PBXGroup; 225 | children = ( 226 | 391EBF371E91212B0009AACD /* libsqlite3.tbd */, 227 | ); 228 | name = Frameworks; 229 | sourceTree = ""; 230 | }; 231 | 3935103B1FCB9A0D001FB466 /* GBCli */ = { 232 | isa = PBXGroup; 233 | children = ( 234 | 393510411FCB9A1E001FB466 /* GBCli.h */, 235 | 3935103D1FCB9A1E001FB466 /* GBCommandLineParser.h */, 236 | 393510401FCB9A1E001FB466 /* GBCommandLineParser.m */, 237 | 393510421FCB9A1E001FB466 /* GBOptionsHelper.h */, 238 | 3935103F1FCB9A1E001FB466 /* GBOptionsHelper.m */, 239 | 3935103C1FCB9A1E001FB466 /* GBPrint.h */, 240 | 393510431FCB9A1E001FB466 /* GBPrint.m */, 241 | 393510441FCB9A1E001FB466 /* GBSettings.h */, 242 | 3935103E1FCB9A1E001FB466 /* GBSettings.m */, 243 | ); 244 | name = GBCli; 245 | sourceTree = ""; 246 | }; 247 | 39470A5322BA878000BDFA5C /* fmdb */ = { 248 | isa = PBXGroup; 249 | children = ( 250 | 39470A5422BA878700BDFA5C /* fmdb.xcodeproj */, 251 | ); 252 | name = fmdb; 253 | sourceTree = ""; 254 | }; 255 | 39470A5522BA878700BDFA5C /* Products */ = { 256 | isa = PBXGroup; 257 | children = ( 258 | 39470A5F22BA878700BDFA5C /* fmdb */, 259 | 39470A6122BA878700BDFA5C /* libFMDB.a */, 260 | 39470A6322BA878700BDFA5C /* Tests.xctest */, 261 | 39470A6522BA878700BDFA5C /* libFMDB-IOS.a */, 262 | 39470A6722BA878700BDFA5C /* FMDB.framework */, 263 | 39470A6922BA878700BDFA5C /* FMDB.framework */, 264 | 39470A6B22BA878700BDFA5C /* FMDB.framework */, 265 | ); 266 | name = Products; 267 | sourceTree = ""; 268 | }; 269 | /* End PBXGroup section */ 270 | 271 | /* Begin PBXNativeTarget section */ 272 | 391596DB1E8CBAA900FDD6F5 /* applesimutils */ = { 273 | isa = PBXNativeTarget; 274 | buildConfigurationList = 391596E31E8CBAA900FDD6F5 /* Build configuration list for PBXNativeTarget "applesimutils" */; 275 | buildPhases = ( 276 | 391596D81E8CBAA900FDD6F5 /* Sources */, 277 | 391596D91E8CBAA900FDD6F5 /* Frameworks */, 278 | 391596DA1E8CBAA900FDD6F5 /* CopyFiles */, 279 | ); 280 | buildRules = ( 281 | ); 282 | dependencies = ( 283 | 39470A6E22BA8C1900BDFA5C /* PBXTargetDependency */, 284 | ); 285 | name = applesimutils; 286 | productName = applesimutils; 287 | productReference = 391596DC1E8CBAA900FDD6F5 /* applesimutils */; 288 | productType = "com.apple.product-type.tool"; 289 | }; 290 | /* End PBXNativeTarget section */ 291 | 292 | /* Begin PBXProject section */ 293 | 391596D41E8CBAA900FDD6F5 /* Project object */ = { 294 | isa = PBXProject; 295 | attributes = { 296 | LastUpgradeCheck = 1230; 297 | ORGANIZATIONNAME = Wix; 298 | TargetAttributes = { 299 | 391596DB1E8CBAA900FDD6F5 = { 300 | CreatedOnToolsVersion = 8.3; 301 | ProvisioningStyle = Automatic; 302 | }; 303 | }; 304 | }; 305 | buildConfigurationList = 391596D71E8CBAA900FDD6F5 /* Build configuration list for PBXProject "applesimutils" */; 306 | compatibilityVersion = "Xcode 3.2"; 307 | developmentRegion = en; 308 | hasScannedForEncodings = 0; 309 | knownRegions = ( 310 | en, 311 | Base, 312 | ); 313 | mainGroup = 391596D31E8CBAA900FDD6F5; 314 | productRefGroup = 391596DD1E8CBAA900FDD6F5 /* Products */; 315 | projectDirPath = ""; 316 | projectReferences = ( 317 | { 318 | ProductGroup = 39470A5522BA878700BDFA5C /* Products */; 319 | ProjectRef = 39470A5422BA878700BDFA5C /* fmdb.xcodeproj */; 320 | }, 321 | ); 322 | projectRoot = ""; 323 | targets = ( 324 | 391596DB1E8CBAA900FDD6F5 /* applesimutils */, 325 | ); 326 | }; 327 | /* End PBXProject section */ 328 | 329 | /* Begin PBXReferenceProxy section */ 330 | 39470A5F22BA878700BDFA5C /* fmdb */ = { 331 | isa = PBXReferenceProxy; 332 | fileType = "compiled.mach-o.executable"; 333 | path = fmdb; 334 | remoteRef = 39470A5E22BA878700BDFA5C /* PBXContainerItemProxy */; 335 | sourceTree = BUILT_PRODUCTS_DIR; 336 | }; 337 | 39470A6122BA878700BDFA5C /* libFMDB.a */ = { 338 | isa = PBXReferenceProxy; 339 | fileType = archive.ar; 340 | path = libFMDB.a; 341 | remoteRef = 39470A6022BA878700BDFA5C /* PBXContainerItemProxy */; 342 | sourceTree = BUILT_PRODUCTS_DIR; 343 | }; 344 | 39470A6322BA878700BDFA5C /* Tests.xctest */ = { 345 | isa = PBXReferenceProxy; 346 | fileType = wrapper.cfbundle; 347 | path = Tests.xctest; 348 | remoteRef = 39470A6222BA878700BDFA5C /* PBXContainerItemProxy */; 349 | sourceTree = BUILT_PRODUCTS_DIR; 350 | }; 351 | 39470A6522BA878700BDFA5C /* libFMDB-IOS.a */ = { 352 | isa = PBXReferenceProxy; 353 | fileType = archive.ar; 354 | path = "libFMDB-IOS.a"; 355 | remoteRef = 39470A6422BA878700BDFA5C /* PBXContainerItemProxy */; 356 | sourceTree = BUILT_PRODUCTS_DIR; 357 | }; 358 | 39470A6722BA878700BDFA5C /* FMDB.framework */ = { 359 | isa = PBXReferenceProxy; 360 | fileType = wrapper.framework; 361 | path = FMDB.framework; 362 | remoteRef = 39470A6622BA878700BDFA5C /* PBXContainerItemProxy */; 363 | sourceTree = BUILT_PRODUCTS_DIR; 364 | }; 365 | 39470A6922BA878700BDFA5C /* FMDB.framework */ = { 366 | isa = PBXReferenceProxy; 367 | fileType = wrapper.framework; 368 | path = FMDB.framework; 369 | remoteRef = 39470A6822BA878700BDFA5C /* PBXContainerItemProxy */; 370 | sourceTree = BUILT_PRODUCTS_DIR; 371 | }; 372 | 39470A6B22BA878700BDFA5C /* FMDB.framework */ = { 373 | isa = PBXReferenceProxy; 374 | fileType = wrapper.framework; 375 | path = FMDB.framework; 376 | remoteRef = 39470A6A22BA878700BDFA5C /* PBXContainerItemProxy */; 377 | sourceTree = BUILT_PRODUCTS_DIR; 378 | }; 379 | /* End PBXReferenceProxy section */ 380 | 381 | /* Begin PBXSourcesBuildPhase section */ 382 | 391596D81E8CBAA900FDD6F5 /* Sources */ = { 383 | isa = PBXSourcesBuildPhase; 384 | buildActionMask = 2147483647; 385 | files = ( 386 | 3909866525700D400099E93D /* ClearMedia.m in Sources */, 387 | 3935103A1FCB9972001FB466 /* LNLog.m in Sources */, 388 | 393510471FCB9A1E001FB466 /* GBCommandLineParser.m in Sources */, 389 | 393510451FCB9A1E001FB466 /* GBSettings.m in Sources */, 390 | 395046D51EA725FF00557294 /* SetLocationPermission.m in Sources */, 391 | 392AFBB122BAB9E0007E0F15 /* SetHealthKitPermission.m in Sources */, 392 | 393510461FCB9A1E001FB466 /* GBOptionsHelper.m in Sources */, 393 | 391EBF311E911CAA0009AACD /* SetServicePermission.m in Sources */, 394 | 39EAF80624E2F008003F86C3 /* NSTask+InputOutput.m in Sources */, 395 | 393510481FCB9A1E001FB466 /* GBPrint.m in Sources */, 396 | 39676AD61F94BE4100018548 /* ClearKeychain.m in Sources */, 397 | 3976EA0225F8EEE800E4BD78 /* SetSimulatorLocation.m in Sources */, 398 | 391596E01E8CBAA900FDD6F5 /* main.m in Sources */, 399 | 395046D81EA72F1000557294 /* SimUtils.m in Sources */, 400 | 391597021E8CDD1D00FDD6F5 /* SetNotificationsPermission.m in Sources */, 401 | 39A57000258F3FF90067D86B /* DTXLogging.m in Sources */, 402 | 393510391FCB9972001FB466 /* LNOptionsParser.m in Sources */, 403 | ); 404 | runOnlyForDeploymentPostprocessing = 0; 405 | }; 406 | /* End PBXSourcesBuildPhase section */ 407 | 408 | /* Begin PBXTargetDependency section */ 409 | 39470A6E22BA8C1900BDFA5C /* PBXTargetDependency */ = { 410 | isa = PBXTargetDependency; 411 | name = FMDB; 412 | targetProxy = 39470A6D22BA8C1900BDFA5C /* PBXContainerItemProxy */; 413 | }; 414 | /* End PBXTargetDependency section */ 415 | 416 | /* Begin XCBuildConfiguration section */ 417 | 391596E11E8CBAA900FDD6F5 /* Debug */ = { 418 | isa = XCBuildConfiguration; 419 | buildSettings = { 420 | ALWAYS_SEARCH_USER_PATHS = NO; 421 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 422 | CLANG_ANALYZER_NONNULL = YES; 423 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 424 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 425 | CLANG_CXX_LIBRARY = "libc++"; 426 | CLANG_ENABLE_MODULES = YES; 427 | CLANG_ENABLE_OBJC_ARC = YES; 428 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 429 | CLANG_WARN_BOOL_CONVERSION = YES; 430 | CLANG_WARN_COMMA = YES; 431 | CLANG_WARN_CONSTANT_CONVERSION = YES; 432 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 433 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 434 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 435 | CLANG_WARN_EMPTY_BODY = YES; 436 | CLANG_WARN_ENUM_CONVERSION = YES; 437 | CLANG_WARN_INFINITE_RECURSION = YES; 438 | CLANG_WARN_INT_CONVERSION = YES; 439 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 440 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 441 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 442 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 443 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 444 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 445 | CLANG_WARN_STRICT_PROTOTYPES = YES; 446 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 447 | CLANG_WARN_UNREACHABLE_CODE = YES; 448 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 449 | CODE_SIGN_IDENTITY = "-"; 450 | COPY_PHASE_STRIP = NO; 451 | DEBUG_INFORMATION_FORMAT = dwarf; 452 | ENABLE_STRICT_OBJC_MSGSEND = YES; 453 | ENABLE_TESTABILITY = YES; 454 | GCC_C_LANGUAGE_STANDARD = gnu99; 455 | GCC_DYNAMIC_NO_PIC = NO; 456 | GCC_NO_COMMON_BLOCKS = YES; 457 | GCC_OPTIMIZATION_LEVEL = 0; 458 | GCC_PREPROCESSOR_DEFINITIONS = ( 459 | "DEBUG=1", 460 | "$(inherited)", 461 | ); 462 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 463 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 464 | GCC_WARN_UNDECLARED_SELECTOR = YES; 465 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 466 | GCC_WARN_UNUSED_FUNCTION = YES; 467 | GCC_WARN_UNUSED_VARIABLE = YES; 468 | MACOSX_DEPLOYMENT_TARGET = 10.12; 469 | MTL_ENABLE_DEBUG_INFO = YES; 470 | ONLY_ACTIVE_ARCH = YES; 471 | SDKROOT = macosx; 472 | STRIP_INSTALLED_PRODUCT = NO; 473 | STRIP_SWIFT_SYMBOLS = NO; 474 | }; 475 | name = Debug; 476 | }; 477 | 391596E21E8CBAA900FDD6F5 /* Release */ = { 478 | isa = XCBuildConfiguration; 479 | buildSettings = { 480 | ALWAYS_SEARCH_USER_PATHS = NO; 481 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 482 | CLANG_ANALYZER_NONNULL = YES; 483 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 484 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 485 | CLANG_CXX_LIBRARY = "libc++"; 486 | CLANG_ENABLE_MODULES = YES; 487 | CLANG_ENABLE_OBJC_ARC = YES; 488 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 489 | CLANG_WARN_BOOL_CONVERSION = YES; 490 | CLANG_WARN_COMMA = YES; 491 | CLANG_WARN_CONSTANT_CONVERSION = YES; 492 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 493 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 494 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 495 | CLANG_WARN_EMPTY_BODY = YES; 496 | CLANG_WARN_ENUM_CONVERSION = YES; 497 | CLANG_WARN_INFINITE_RECURSION = YES; 498 | CLANG_WARN_INT_CONVERSION = YES; 499 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 500 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 501 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 502 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 503 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 504 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 505 | CLANG_WARN_STRICT_PROTOTYPES = YES; 506 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 507 | CLANG_WARN_UNREACHABLE_CODE = YES; 508 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 509 | CODE_SIGN_IDENTITY = "-"; 510 | COPY_PHASE_STRIP = NO; 511 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 512 | ENABLE_NS_ASSERTIONS = NO; 513 | ENABLE_STRICT_OBJC_MSGSEND = YES; 514 | GCC_C_LANGUAGE_STANDARD = gnu99; 515 | GCC_NO_COMMON_BLOCKS = YES; 516 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 517 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 518 | GCC_WARN_UNDECLARED_SELECTOR = YES; 519 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 520 | GCC_WARN_UNUSED_FUNCTION = YES; 521 | GCC_WARN_UNUSED_VARIABLE = YES; 522 | MACOSX_DEPLOYMENT_TARGET = 10.12; 523 | MTL_ENABLE_DEBUG_INFO = NO; 524 | SDKROOT = macosx; 525 | STRIP_INSTALLED_PRODUCT = NO; 526 | STRIP_SWIFT_SYMBOLS = NO; 527 | }; 528 | name = Release; 529 | }; 530 | 391596E41E8CBAA900FDD6F5 /* Debug */ = { 531 | isa = XCBuildConfiguration; 532 | buildSettings = { 533 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = NO; 534 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = NO; 535 | CODE_SIGN_IDENTITY = "-"; 536 | GCC_C_LANGUAGE_STANDARD = "compiler-default"; 537 | GCC_PREFIX_HEADER = applesimutils/PrefixHeader.pch; 538 | GCC_TREAT_WARNINGS_AS_ERRORS = YES; 539 | MACOSX_DEPLOYMENT_TARGET = 10.11; 540 | PRODUCT_NAME = "$(TARGET_NAME)"; 541 | }; 542 | name = Debug; 543 | }; 544 | 391596E51E8CBAA900FDD6F5 /* Release */ = { 545 | isa = XCBuildConfiguration; 546 | buildSettings = { 547 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = NO; 548 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = NO; 549 | CODE_SIGN_IDENTITY = "-"; 550 | GCC_C_LANGUAGE_STANDARD = "compiler-default"; 551 | GCC_PREFIX_HEADER = applesimutils/PrefixHeader.pch; 552 | GCC_TREAT_WARNINGS_AS_ERRORS = YES; 553 | MACOSX_DEPLOYMENT_TARGET = 10.11; 554 | PRODUCT_NAME = "$(TARGET_NAME)"; 555 | }; 556 | name = Release; 557 | }; 558 | /* End XCBuildConfiguration section */ 559 | 560 | /* Begin XCConfigurationList section */ 561 | 391596D71E8CBAA900FDD6F5 /* Build configuration list for PBXProject "applesimutils" */ = { 562 | isa = XCConfigurationList; 563 | buildConfigurations = ( 564 | 391596E11E8CBAA900FDD6F5 /* Debug */, 565 | 391596E21E8CBAA900FDD6F5 /* Release */, 566 | ); 567 | defaultConfigurationIsVisible = 0; 568 | defaultConfigurationName = Release; 569 | }; 570 | 391596E31E8CBAA900FDD6F5 /* Build configuration list for PBXNativeTarget "applesimutils" */ = { 571 | isa = XCConfigurationList; 572 | buildConfigurations = ( 573 | 391596E41E8CBAA900FDD6F5 /* Debug */, 574 | 391596E51E8CBAA900FDD6F5 /* Release */, 575 | ); 576 | defaultConfigurationIsVisible = 0; 577 | defaultConfigurationName = Release; 578 | }; 579 | /* End XCConfigurationList section */ 580 | }; 581 | rootObject = 391596D41E8CBAA900FDD6F5 /* Project object */; 582 | } 583 | -------------------------------------------------------------------------------- /applesimutils/applesimutils.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /applesimutils/applesimutils.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /applesimutils/applesimutils.xcodeproj/xcshareddata/xcschemes/applesimutils.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 57 | 58 | 61 | 62 | 65 | 66 | 69 | 70 | 73 | 74 | 77 | 78 | 81 | 82 | 85 | 86 | 89 | 90 | 93 | 94 | 97 | 98 | 101 | 102 | 105 | 106 | 109 | 110 | 113 | 114 | 117 | 118 | 121 | 122 | 125 | 126 | 129 | 130 | 133 | 134 | 137 | 138 | 141 | 142 | 145 | 146 | 147 | 148 | 154 | 156 | 162 | 163 | 164 | 165 | 167 | 168 | 171 | 172 | 173 | -------------------------------------------------------------------------------- /applesimutils/applesimutils/ClearKeychain.h: -------------------------------------------------------------------------------- 1 | // 2 | // ClearKeychain.h 3 | // applesimutils 4 | // 5 | // Created by Leo Natan (Wix) on 16/10/2017. 6 | // Copyright © 2017 Wix. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | extern NSURL* securitydURL(NSURL* runtimeBundleURL); 12 | 13 | extern void performClearKeychainPass(NSString* simulatorIdentifier, NSURL* runtimeBundleURL); 14 | -------------------------------------------------------------------------------- /applesimutils/applesimutils/ClearKeychain.m: -------------------------------------------------------------------------------- 1 | // 2 | // ClearKeychain.m 3 | // applesimutils 4 | // 5 | // Created by Leo Natan (Wix) on 16/10/2017. 6 | // Copyright © 2017 Wix. All rights reserved. 7 | // 8 | 9 | #import "ClearKeychain.h" 10 | #import "SimUtils.h" 11 | 12 | extern NSURL* securitydURL(NSURL* runtimeBundleURL) 13 | { 14 | static NSURL *securitydDaemonURL; 15 | static dispatch_once_t onceToken; 16 | dispatch_once(&onceToken, ^{ 17 | securitydDaemonURL = [SimUtils launchDaemonPlistURLForDaemon:@"com.apple.securityd" runtimeBundleURL:runtimeBundleURL]; 18 | }); 19 | return securitydDaemonURL; 20 | } 21 | 22 | static void securitydCtl(NSString* simulatorId, NSURL* runtimeBundleURL, BOOL stop) 23 | { 24 | NSURL *locationdDaemonURL = securitydURL(runtimeBundleURL); 25 | NSCAssert(locationdDaemonURL != nil, @"Launch daemon “com.apple.securityd” not found. Please open an issue."); 26 | 27 | NSTask* rebootTask = [NSTask new]; 28 | rebootTask.launchPath = SimUtils.xcrunURL.path; 29 | rebootTask.arguments = @[@"simctl", @"spawn", simulatorId, @"launchctl", stop ? @"unload" : @"load", locationdDaemonURL.path]; 30 | [rebootTask launch]; 31 | [rebootTask waitUntilExit]; 32 | } 33 | 34 | void performClearKeychainPass(NSString* simulatorIdentifier, NSURL* runtimeBundleURL) 35 | { 36 | securitydCtl(simulatorIdentifier, runtimeBundleURL, YES); 37 | 38 | NSURL* keychainDirURL = [[SimUtils libraryURLForSimulatorId:simulatorIdentifier] URLByAppendingPathComponent:@"Keychains"]; 39 | [[NSFileManager.defaultManager contentsOfDirectoryAtURL:keychainDirURL includingPropertiesForKeys:nil options:0 error:NULL] enumerateObjectsUsingBlock:^(NSURL * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 40 | [NSFileManager.defaultManager removeItemAtURL:obj error:NULL]; 41 | }]; 42 | 43 | securitydCtl(simulatorIdentifier, runtimeBundleURL, NO); 44 | } 45 | -------------------------------------------------------------------------------- /applesimutils/applesimutils/ClearMedia.h: -------------------------------------------------------------------------------- 1 | // 2 | // ClearMedia.h 3 | // applesimutils 4 | // 5 | // Created by Leo Natan on 11/26/20. 6 | // Copyright © 2020 Wix. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | extern void performClearMediaPass(NSString* simulatorIdentifier, NSURL* runtimeBundleURL); 12 | -------------------------------------------------------------------------------- /applesimutils/applesimutils/ClearMedia.m: -------------------------------------------------------------------------------- 1 | // 2 | // ClearMedia.m 3 | // applesimutils 4 | // 5 | // Created by Leo Natan on 11/26/20. 6 | // Copyright © 2020 Wix. All rights reserved. 7 | // 8 | 9 | #import "ClearMedia.h" 10 | #import "SimUtils.h" 11 | 12 | extern NSURL* assetsdURL(NSURL* runtimeBundleURL) 13 | { 14 | static NSURL *assetsdURL; 15 | static dispatch_once_t onceToken; 16 | dispatch_once(&onceToken, ^{ 17 | assetsdURL = [SimUtils launchDaemonPlistURLForDaemon:@"com.apple.assetsd" runtimeBundleURL:runtimeBundleURL]; 18 | }); 19 | return assetsdURL; 20 | } 21 | 22 | static void assetsdCtl(NSString* simulatorId, NSURL* runtimeBundleURL, BOOL stop) 23 | { 24 | NSURL *locationdDaemonURL = assetsdURL(runtimeBundleURL); 25 | NSCAssert(locationdDaemonURL != nil, @"Launch daemon “com.apple.mobileassetd” not found. Please open an issue."); 26 | 27 | NSTask* rebootTask = [NSTask new]; 28 | rebootTask.launchPath = SimUtils.xcrunURL.path; 29 | rebootTask.arguments = @[@"simctl", @"spawn", simulatorId, @"launchctl", stop ? @"unload" : @"load", locationdDaemonURL.path]; 30 | [rebootTask launch]; 31 | [rebootTask waitUntilExit]; 32 | } 33 | 34 | void performClearMediaPass(NSString* simulatorIdentifier, NSURL* runtimeBundleURL) 35 | { 36 | assetsdCtl(simulatorIdentifier, runtimeBundleURL, YES); 37 | NSURL* mediaURL = [[SimUtils dataURLForSimulatorId:simulatorIdentifier] URLByAppendingPathComponent:@"Media"]; 38 | [NSFileManager.defaultManager removeItemAtURL:[mediaURL URLByAppendingPathComponent:@"DCIM"] error:NULL]; 39 | [NSFileManager.defaultManager removeItemAtURL:[mediaURL URLByAppendingPathComponent:@"PhotoData"] error:NULL]; 40 | assetsdCtl(simulatorIdentifier, runtimeBundleURL, NO); 41 | } 42 | -------------------------------------------------------------------------------- /applesimutils/applesimutils/DTXLoggingSubsystem.h: -------------------------------------------------------------------------------- 1 | // 2 | // DTXLoggingSubsystem.h 3 | // applesimutils 4 | // 5 | // Created by Leo Natan (Wix) on 12/10/17. 6 | // Copyright © 2017 Wix. All rights reserved. 7 | // 8 | 9 | #ifndef DTX_LOG_SUBSYSTEM 10 | #define DTX_LOG_SUBSYSTEM "com.wix.applesimutils" 11 | #endif /* DTX_LOG_SUBSYSTEM */ 12 | -------------------------------------------------------------------------------- /applesimutils/applesimutils/NSTask+InputOutput.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSTask+InputOutput.h 3 | // applesimutils 4 | // 5 | // Created by Leo Natan (Wix) on 8/11/20. 6 | // Copyright © 2020 Wix. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface NSTask (InputOutput) 14 | 15 | - (int)launchAndWaitUntilExitReturningStandardOutputData:(out NSData* __autoreleasing __nullable * __nullable)stdOutData standardErrorData:(out NSData *__autoreleasing _Nullable * __nullable)stdErrData; 16 | - (int)launchAndWaitUntilExitReturningStandardOutput:(out NSString* __autoreleasing __nullable * __nullable)stdOut standardRrror:(out NSString *__autoreleasing _Nullable * __nullable)stdErr; 17 | 18 | @end 19 | 20 | NS_ASSUME_NONNULL_END 21 | -------------------------------------------------------------------------------- /applesimutils/applesimutils/NSTask+InputOutput.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSTask+InputOutput.m 3 | // applesimutils 4 | // 5 | // Created by Leo Natan (Wix) on 8/11/20. 6 | // Copyright © 2020 Wix. All rights reserved. 7 | // 8 | 9 | #import "NSTask+InputOutput.h" 10 | 11 | @implementation NSTask (InputOutput) 12 | 13 | - (int)launchAndWaitUntilExitReturningStandardOutputData:(out NSData* __autoreleasing __nullable * __nullable)stdOutData standardErrorData:(out NSData *__autoreleasing _Nullable * __nullable)stdErrData 14 | { 15 | dispatch_semaphore_t waitForStdout = dispatch_semaphore_create(0); 16 | dispatch_semaphore_t waitForStderr = dispatch_semaphore_create(0); 17 | dispatch_semaphore_t waitForTaskTermination = dispatch_semaphore_create(0); 18 | 19 | NSPipe* outPipe = [NSPipe pipe]; 20 | NSMutableData* outData = [NSMutableData new]; 21 | self.standardOutput = outPipe; 22 | outPipe.fileHandleForReading.readabilityHandler = ^(NSFileHandle * _Nonnull fileHandle) { 23 | NSData* newData = [fileHandle readDataOfLength:NSUIntegerMax]; 24 | 25 | if (newData.length == 0) { 26 | dispatch_semaphore_signal(waitForStdout); 27 | } else { 28 | [outData appendData:newData]; 29 | } 30 | }; 31 | 32 | NSPipe* errPipe = [NSPipe pipe]; 33 | NSMutableData* errData = [NSMutableData new]; 34 | self.standardError = errPipe; 35 | errPipe.fileHandleForReading.readabilityHandler = ^(NSFileHandle * _Nonnull fileHandle) { 36 | NSData* newData = [fileHandle readDataOfLength:NSUIntegerMax]; 37 | 38 | if (newData.length == 0) { 39 | dispatch_semaphore_signal(waitForStderr); 40 | } else { 41 | [errData appendData:newData]; 42 | } 43 | }; 44 | 45 | self.terminationHandler = ^(NSTask* _Nonnull task) { 46 | dispatch_semaphore_signal(waitForTaskTermination); 47 | }; 48 | 49 | LNLog(LNLogLevelDebug, @"Running “%@”%@", self.launchPath, self.arguments.count > 0 ? [NSString stringWithFormat:@" with argument%@: “%@”", self.arguments.count > 1 ? @"s" : @"", [self.arguments componentsJoinedByString:@" "]] : @""); 50 | 51 | [self launch]; 52 | 53 | dispatch_semaphore_wait(waitForStdout, DISPATCH_TIME_FOREVER); 54 | dispatch_semaphore_wait(waitForStderr, DISPATCH_TIME_FOREVER); 55 | // also wait for the NSTask to terminate, otherwise we can't read terminationStatus 56 | dispatch_semaphore_wait(waitForTaskTermination, DISPATCH_TIME_FOREVER); 57 | 58 | outPipe.fileHandleForReading.readabilityHandler = nil; 59 | errPipe.fileHandleForReading.readabilityHandler = nil; 60 | 61 | NSString* stdOutStr = [[[NSString alloc] initWithData:outData encoding:NSUTF8StringEncoding] stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet]; 62 | if(stdOutStr.length > 0) 63 | { 64 | LNLog(LNLogLevelDebug, @"Got output:\n%@", stdOutStr); 65 | } 66 | 67 | if(stdOutData != NULL) 68 | { 69 | *stdOutData = outData; 70 | } 71 | 72 | NSString* stdErrStr = [[[NSString alloc] initWithData:errData encoding:NSUTF8StringEncoding] stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet]; 73 | if(self.terminationStatus != 0 && stdErrStr.length > 0) 74 | { 75 | LNLog(LNLogLevelError, @"Got error:\n%@", stdErrStr); 76 | } 77 | 78 | if(stdErrData != NULL) 79 | { 80 | *stdErrData = errData; 81 | } 82 | 83 | return self.terminationStatus; 84 | } 85 | 86 | - (int)launchAndWaitUntilExitReturningStandardOutput:(out NSString* __autoreleasing __nullable *)stdOut standardRrror:(out NSString *__autoreleasing _Nullable *)stdErr 87 | { 88 | NSData* stdOutData; 89 | NSData* stdErrData; 90 | 91 | int rv = [self launchAndWaitUntilExitReturningStandardOutputData:&stdOutData standardErrorData:&stdErrData]; 92 | 93 | if(stdOutData.length > 0 && stdOut != NULL) 94 | { 95 | *stdOut = [[[NSString alloc] initWithData:stdOutData encoding:NSUTF8StringEncoding] stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet]; 96 | } 97 | 98 | if(stdErrData.length > 0 && stdErr != NULL) 99 | { 100 | *stdErr = [[[NSString alloc] initWithData:stdErrData encoding:NSUTF8StringEncoding] stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet]; 101 | } 102 | 103 | return rv; 104 | } 105 | 106 | @end 107 | -------------------------------------------------------------------------------- /applesimutils/applesimutils/PrefixHeader.pch: -------------------------------------------------------------------------------- 1 | // 2 | // PrefixHeader.pch 3 | // applesimutils 4 | // 5 | // Created by Leo Natan (Wix) on 6/19/19. 6 | // Copyright © 2019 Wix. All rights reserved. 7 | // 8 | 9 | #ifndef PrefixHeader_pch 10 | #define PrefixHeader_pch 11 | 12 | #import "LNLog.h" 13 | 14 | #define debug_log(message) LNLog(LNLogLevelDebug, @"%@", message) 15 | #define logcontinue(message) if(error) { *error = [NSError errorWithDomain:@"" code:0 userInfo:@{NSLocalizedDescriptionKey:(message)}]; }; debug_log((message)); continue 16 | #define logreturn(message) if(error) { *error = [NSError errorWithDomain:@"" code:0 userInfo:@{NSLocalizedDescriptionKey:(message)}]; }; debug_log((message)); return 17 | 18 | #import "Swiftier.h" 19 | 20 | #endif /* PrefixHeader_pch */ 21 | -------------------------------------------------------------------------------- /applesimutils/applesimutils/SetHealthKitPermission.h: -------------------------------------------------------------------------------- 1 | // 2 | // SetHealthKitPermission.h 3 | // applesimutils 4 | // 5 | // Created by Leo Natan (Wix) on 6/19/19. 6 | // Copyright © 2019 Wix. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef enum : NSUInteger { 12 | HealthKitPermissionStatusUnset, 13 | HealthKitPermissionStatusAllow, 14 | HealthKitPermissionStatusDeny, 15 | } HealthKitPermissionStatus; 16 | 17 | @interface SetHealthKitPermission : NSObject 18 | 19 | + (NSURL*)healthdbURLForSimulatorId:(NSString*)simulatorId osVersion:(NSOperatingSystemVersion)isVersion; 20 | 21 | + (BOOL)setHealthKitPermission:(HealthKitPermissionStatus)permission forBundleIdentifier:(NSString*)bundleIdentifier simulatorIdentifier:(NSString*)simulatorId osVersion:(NSOperatingSystemVersion)osVersion needsSBRestart:(BOOL*)needsSBRestart error:(NSError**)error; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /applesimutils/applesimutils/SetHealthKitPermission.m: -------------------------------------------------------------------------------- 1 | // 2 | // SetHealthKitPermission.m 3 | // applesimutils 4 | // 5 | // Created by Leo Natan (Wix) on 6/19/19. 6 | // Copyright © 2019 Wix. All rights reserved. 7 | // 8 | 9 | #import "SetHealthKitPermission.h" 10 | #import 11 | #import "SimUtils.h" 12 | 13 | #define logcontinue_query_error(db) \ 14 | auto msg = [NSString stringWithFormat:@"Health database failed to execute query: %@", [db lastErrorMessage]]; \ 15 | logcontinue(msg) 16 | 17 | @implementation SetHealthKitPermission 18 | 19 | + (NSURL*)healthdbURLForSimulatorId:(NSString*)simulatorId osVersion:(NSOperatingSystemVersion)isVersion 20 | { 21 | return [[SimUtils libraryURLForSimulatorId:simulatorId] URLByAppendingPathComponent:@"Health/healthdb.sqlite"]; 22 | } 23 | 24 | + (BOOL)setHealthKitPermission:(HealthKitPermissionStatus)permission forBundleIdentifier:(NSString*)bundleIdentifier simulatorIdentifier:(NSString*)simulatorId osVersion:(NSOperatingSystemVersion)osVersion needsSBRestart:(BOOL*)needsSBRestart error:(NSError**)error 25 | { 26 | LNLog(LNLogLevelDebug, @"Setting HealthKit permission"); 27 | 28 | if(osVersion.majorVersion < 12) 29 | { 30 | *error = [NSError errorWithDomain:@"SetHealthKitPermissionError" code:0 userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Setting health permission is supported for iOS 12 simulators and above (got %@.%@)", @(osVersion.majorVersion), @(osVersion.minorVersion)]}]; 31 | 32 | return NO; 33 | } 34 | 35 | *needsSBRestart |= YES; 36 | 37 | __block BOOL success = NO; 38 | NSDate* start = [NSDate date]; 39 | 40 | while (!success) 41 | { 42 | dtx_defer { 43 | if(success == NO) 44 | { 45 | debug_log(@"Retrying in one second"); 46 | [NSThread sleepForTimeInterval:1]; 47 | } 48 | }; 49 | 50 | NSTimeInterval elapsed = [[NSDate date] timeIntervalSinceDate:start]; 51 | if (elapsed > AppleSimUtilsRetryTimeout) break; 52 | 53 | NSURL* healthURL = [self healthdbURLForSimulatorId:simulatorId osVersion:osVersion]; 54 | if ([healthURL checkResourceIsReachableAndReturnError:error] == NO) 55 | { 56 | logcontinue(@"Health database not found"); 57 | } 58 | 59 | FMDatabase* db = [[FMDatabase alloc] initWithURL:healthURL]; 60 | dtx_defer { 61 | if(db.isOpen == YES) 62 | { 63 | [db close]; 64 | } 65 | }; 66 | 67 | if([db open] == NO) 68 | { 69 | logcontinue(@"Health database failed to open"); 70 | } 71 | 72 | FMResultSet* resultSet; 73 | id rowID = nil; 74 | dtx_defer { 75 | if(resultSet != nil) 76 | { 77 | [resultSet close]; 78 | } 79 | }; 80 | 81 | auto bundleIdQuery = osVersion.majorVersion >= 16 ? 82 | @"select ROWID from sources where logical_source_id IN(select ROWID from logical_sources where bundle_id == :bundle_id)" : 83 | @"select ROWID from sources where bundle_id == :bundle_id"; 84 | 85 | if((resultSet = [db executeQuery:bundleIdQuery withParameterDictionary:@{@"bundle_id": bundleIdentifier}]) == nil) 86 | { 87 | logcontinue_query_error(db); 88 | } 89 | 90 | BOOL didHaveRow = [resultSet nextWithError:error]; 91 | 92 | if(didHaveRow == NO && permission == HealthKitPermissionStatusUnset) 93 | { 94 | //No need to do anything, 95 | return YES; 96 | } 97 | 98 | if(didHaveRow == NO) 99 | { 100 | NSUUID* uuid = NSUUID.UUID; 101 | NSMutableData* uuidData = [[NSMutableData alloc] initWithLength:sizeof(uuid_t)]; 102 | [uuid getUUIDBytes:uuidData.mutableBytes]; 103 | 104 | FMResultSet* syncAnchorResultSet = nil; 105 | dtx_defer { 106 | if(syncAnchorResultSet != nil) 107 | { 108 | [syncAnchorResultSet close]; 109 | } 110 | }; 111 | NSNumber* syncAnchor = @1; 112 | if((syncAnchorResultSet = [db executeQuery:@"select MAX(sync_anchor) from sources"]) == nil) 113 | { 114 | logcontinue_query_error(db); 115 | } 116 | 117 | if([syncAnchorResultSet next] != NO) 118 | { 119 | syncAnchor = @([syncAnchorResultSet intForColumnIndex:0] + 1); 120 | } 121 | 122 | if(osVersion.majorVersion >= 16) 123 | { 124 | [db beginTransaction]; 125 | if([db executeUpdate:@"insert into logical_sources (bundle_id) VALUES (:bundle_id)" withParameterDictionary:@{@"bundle_id": bundleIdentifier}] == NO) 126 | { 127 | logcontinue_query_error(db); 128 | } 129 | 130 | NSMutableString* query = @"uuid, name, source_options, local_device, product_type, deleted, mod_date, provenance, sync_anchor, logical_source_id, sync_identity".mutableCopy; 131 | NSMutableString* values = @":uuid, :name, :source_options, :local_device, :product_type, :deleted, :mod_date, :provenance, :sync_anchor, (select ROWID from logical_sources where bundle_id == :bundle_id), :sync_identity".mutableCopy; 132 | NSMutableDictionary* params = @{@"uuid": uuidData, @"name": bundleIdentifier, @"source_options": @5, @"local_device": @0, @"product_type": @"", @"deleted": @0, @"mod_date": @(NSDate.date.timeIntervalSinceReferenceDate), @"provenance": @0, @"sync_anchor": syncAnchor, @"bundle_id": bundleIdentifier, @"sync_identity": @1}.mutableCopy; 133 | 134 | if([db executeUpdate:[NSString stringWithFormat:@"insert into sources (%@) VALUES (%@)", query, values] withParameterDictionary:params] == NO) 135 | { 136 | logcontinue_query_error(db); 137 | } 138 | [db commit]; 139 | } 140 | else 141 | { 142 | NSMutableString* query = @"uuid, bundle_id, name, source_options, local_device, product_type, deleted, mod_date, provenance, sync_anchor".mutableCopy; 143 | NSMutableString* values = @":uuid, :bundle_id, :name, :source_options, :local_device, :product_type, :deleted, :mod_date, :provenance, :sync_anchor".mutableCopy; 144 | NSMutableDictionary* params = @{@"uuid": uuidData, @"bundle_id": bundleIdentifier, @"name": bundleIdentifier, @"source_options": @5, @"local_device": @0, @"product_type": @"", @"deleted": @0, @"mod_date": @(NSDate.date.timeIntervalSinceReferenceDate), @"provenance": @0, @"sync_anchor": syncAnchor}.mutableCopy; 145 | 146 | if([db executeUpdate:[NSString stringWithFormat:@"insert into sources (%@) VALUES (%@)", query, values] withParameterDictionary:params] == NO) 147 | { 148 | logcontinue_query_error(db); 149 | } 150 | } 151 | 152 | [resultSet close]; 153 | if((resultSet = [db executeQuery:bundleIdQuery withParameterDictionary:@{@"bundle_id": bundleIdentifier}]) == nil) 154 | { 155 | logcontinue_query_error(db); 156 | } 157 | [resultSet nextWithError:error]; 158 | } 159 | 160 | rowID = [resultSet objectForColumn:@"ROWID"]; 161 | 162 | if(rowID == nil) 163 | { 164 | logcontinue(@"No row ID found");; 165 | } 166 | 167 | __unused BOOL b = [db executeUpdate:@"delete from authorization where source_id == :source_id" withParameterDictionary:@{@"source_id": rowID}]; 168 | 169 | if(permission == HealthKitPermissionStatusUnset) 170 | { 171 | if(osVersion.majorVersion >= 16) 172 | { 173 | [db beginTransaction]; 174 | if([db executeUpdate:@"delete from sources where (select ROWID from logical_sources where bundle_id == :bundle_id)" withParameterDictionary:@{@"bundle_id": bundleIdentifier}] == NO) 175 | { 176 | logcontinue_query_error(db); 177 | } 178 | if([db executeUpdate:@"delete from logical_sources where bundle_id == :bundle_id" withParameterDictionary:@{@"bundle_id": bundleIdentifier}] == NO) 179 | { 180 | logcontinue_query_error(db); 181 | } 182 | [db commit]; 183 | } 184 | else 185 | { 186 | if([db executeUpdate:@"delete from sources where bundle_id == :bundle_id" withParameterDictionary:@{@"bundle_id": bundleIdentifier}] == NO) 187 | { 188 | logcontinue_query_error(db); 189 | } 190 | } 191 | } 192 | else 193 | { 194 | NSMutableString* query; 195 | NSMutableString* values; 196 | NSMutableDictionary* baseParams; 197 | if (osVersion.majorVersion > 16 || (osVersion.majorVersion == 16 && osVersion.minorVersion > 1)) 198 | { 199 | query = @"source_id, object_type, status, request, mode, date_modified, modification_epoch, provenance, deleted_object_anchor, object_limit_anchor, object_limit_modified, sync_identity".mutableCopy; 200 | values = @":source_id, :object_type, :status, :request, :mode, :date_modified, :modification_epoch, :provenance, :deleted_object_anchor, :object_limit_anchor, :object_limit_modified, :sync_identity".mutableCopy; 201 | baseParams = [@{@"source_id": rowID, @"status": permission == HealthKitPermissionStatusAllow ? @101 : @104, @"request": @203, @"mode": @0, @"date_modified": @(NSDate.date.timeIntervalSinceReferenceDate), @"modification_epoch": @1, @"provenance": @0, @"deleted_object_anchor": @0, @"object_limit_anchor": @0, @"object_limit_modified": NSNull.null, @"sync_identity": @1} mutableCopy]; 202 | } 203 | else 204 | { 205 | query = @"source_id, object_type, status, request, mode, date_modified, modification_epoch, provenance, deleted_object_anchor, object_limit_anchor, object_limit_modified".mutableCopy; 206 | values = @":source_id, :object_type, :status, :request, :mode, :date_modified, :modification_epoch, :provenance, :deleted_object_anchor, :object_limit_anchor, :object_limit_modified".mutableCopy; 207 | baseParams = [@{@"source_id": rowID, @"status": permission == HealthKitPermissionStatusAllow ? @101 : @104, @"request": @203, @"mode": @0, @"date_modified": @(NSDate.date.timeIntervalSinceReferenceDate), @"modification_epoch": @1, @"provenance": @0, @"deleted_object_anchor": @0, @"object_limit_anchor": @0, @"object_limit_modified": NSNull.null} mutableCopy]; 208 | } 209 | for(int i = 0; i < 200; i++) 210 | { 211 | baseParams[@"object_type"] = @(i); 212 | if ([db executeUpdate:[NSString stringWithFormat:@"insert into authorization (%@) VALUES (%@)", query, values] withParameterDictionary:baseParams] == NO) 213 | { 214 | logcontinue_query_error(db); 215 | } 216 | } 217 | } 218 | 219 | success = YES; 220 | } 221 | 222 | return success; 223 | } 224 | 225 | @end 226 | -------------------------------------------------------------------------------- /applesimutils/applesimutils/SetLocationPermission.h: -------------------------------------------------------------------------------- 1 | // 2 | // SetLocationPermission.h 3 | // applesimutils 4 | // 5 | // Created by Leo Natan (Wix) on 19/04/2017. 6 | // Copyright © 2017 Wix. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SetLocationPermission : NSObject 12 | 13 | + (NSURL*)locationdURLForRuntimeBundleURL:(NSURL*)runtimeBundleURL; 14 | 15 | + (BOOL)setLocationPermission:(NSString*)permission forBundleIdentifier:(NSString*)bundleIdentifier simulatorIdentifier:(NSString*)simulatorId runtimeBundleURL:(NSURL*)runtimeBundleURL error:(NSError**)error; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /applesimutils/applesimutils/SetLocationPermission.m: -------------------------------------------------------------------------------- 1 | // 2 | // SetLocationPermission.m 3 | // applesimutils 4 | // 5 | // Created by Leo Natan (Wix) on 19/04/2017. 6 | // Copyright © 2017 Wix. All rights reserved. 7 | // 8 | 9 | #import "SetLocationPermission.h" 10 | #import "SimUtils.h" 11 | 12 | static void startStopLocationdCtl(NSString* simulatorId, NSURL* runtimeBundleURL, BOOL stop) 13 | { 14 | NSURL *locationdDaemonURL = [SetLocationPermission locationdURLForRuntimeBundleURL:runtimeBundleURL]; 15 | NSCAssert(locationdDaemonURL != nil, @"Launch daemon “com.apple.locationd” not found. Please open an issue."); 16 | 17 | NSTask* rebootTask = [NSTask new]; 18 | rebootTask.launchPath = [SimUtils xcrunURL].path; 19 | rebootTask.arguments = @[@"simctl", @"spawn", simulatorId, @"launchctl", stop ? @"unload" : @"load", locationdDaemonURL.path]; 20 | [rebootTask launch]; 21 | [rebootTask waitUntilExit]; 22 | } 23 | 24 | @implementation SetLocationPermission 25 | 26 | + (NSURL*)locationdURLForRuntimeBundleURL:(NSURL*)runtimeBundleURL 27 | { 28 | static NSURL *locationdDaemonURL; 29 | static dispatch_once_t onceToken; 30 | dispatch_once(&onceToken, ^{ 31 | locationdDaemonURL = [SimUtils launchDaemonPlistURLForDaemon:@"com.apple.locationd" runtimeBundleURL:runtimeBundleURL]; 32 | }); 33 | return locationdDaemonURL; 34 | } 35 | 36 | + (BOOL)setLocationPermission:(NSString*)permission forBundleIdentifier:(NSString*)bundleIdentifier simulatorIdentifier:(NSString*)simulatorId runtimeBundleURL:(NSURL*)runtimeBundleURL error:(NSError**)error 37 | { 38 | LNLog(LNLogLevelDebug, @"Setting location permission"); 39 | 40 | NSURL* plistURL = [[SimUtils libraryURLForSimulatorId:simulatorId] URLByAppendingPathComponent:@"Caches/locationd/clients.plist"]; 41 | 42 | NSData* plistData = [NSData dataWithContentsOfURL:plistURL]; 43 | NSMutableDictionary* locationClients; 44 | if(plistData == nil) 45 | { 46 | locationClients = [NSMutableDictionary new]; 47 | } 48 | else 49 | { 50 | locationClients = [NSPropertyListSerialization propertyListWithData:plistData options:NSPropertyListMutableContainers format:nil error:error]; 51 | } 52 | 53 | if(locationClients == nil) 54 | { 55 | *error = [NSError errorWithDomain:@"SetLocationPermissionsError" code:0 userInfo:@{NSLocalizedDescriptionKey: @"Unable to parse clients.plist"}]; 56 | return NO; 57 | } 58 | 59 | if([permission isEqualToString:@"unset"]) 60 | { 61 | [locationClients removeObjectForKey:bundleIdentifier]; 62 | } 63 | else 64 | { 65 | NSMutableDictionary* bundlePermissions = locationClients[bundleIdentifier]; 66 | if(bundlePermissions == nil) 67 | { 68 | bundlePermissions = [NSMutableDictionary new]; 69 | } 70 | 71 | NSDictionary* permissionMapping = @{@"never": @1, @"inuse": @2, @"always": @4}; 72 | 73 | bundlePermissions[@"AuthorizationUpgradeAvailable"] = @NO; 74 | bundlePermissions[@"SupportedAuthorizationMask"] = @7; 75 | bundlePermissions[@"Authorization"] = permissionMapping[permission]; 76 | bundlePermissions[@"BundleId"] = bundleIdentifier; 77 | bundlePermissions[@"Whitelisted"] = @NO; 78 | 79 | NSURL* binaryURL = [SimUtils binaryURLForBundleId:bundleIdentifier simulatorId:simulatorId]; 80 | NSString* path = binaryURL != nil ? binaryURL.path : @""; 81 | 82 | bundlePermissions[@"Executable"] = path; 83 | bundlePermissions[@"Registered"] = path; 84 | bundlePermissions[@"TrialPeriodBegin"] = @([NSDate.date timeIntervalSinceReferenceDate]); 85 | 86 | locationClients[bundleIdentifier] = bundlePermissions; 87 | } 88 | 89 | startStopLocationdCtl(simulatorId, runtimeBundleURL, YES); 90 | 91 | [[NSPropertyListSerialization dataWithPropertyList:locationClients format:NSPropertyListBinaryFormat_v1_0 options:0 error:NULL] writeToURL:plistURL atomically:YES]; 92 | 93 | startStopLocationdCtl(simulatorId, runtimeBundleURL, NO); 94 | 95 | return YES; 96 | } 97 | 98 | @end 99 | -------------------------------------------------------------------------------- /applesimutils/applesimutils/SetNotificationsPermission.h: -------------------------------------------------------------------------------- 1 | // 2 | // SetNotificationsPermission.h 3 | // applesimutils 4 | // 5 | // Created by Leo Natan (Wix) on 30/03/2017. 6 | // Copyright © 2017 Wix. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SetNotificationsPermission : NSObject 12 | 13 | + (BOOL)setNotificationsStatus:(NSString*)enabled forBundleIdentifier:(NSString*)bundleIdentifier displayName:(NSString*)displayName simulatorIdentifier:(NSString*)simulatorId error:(NSError**)error; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /applesimutils/applesimutils/SetNotificationsPermission.m: -------------------------------------------------------------------------------- 1 | // 2 | // SetNotificationsPermission.m 3 | // applesimutils 4 | // 5 | // Created by Leo Natan (Wix) on 30/03/2017. 6 | // Copyright © 2017 Wix. All rights reserved. 7 | // 8 | 9 | #import "SetNotificationsPermission.h" 10 | #import "SimUtils.h" 11 | 12 | @implementation SetNotificationsPermission 13 | 14 | + (BOOL)setNotificationsStatus:(NSString*)status forBundleIdentifier:(NSString*)bundleIdentifier displayName:(NSString*)displayName simulatorIdentifier:(NSString*)simulatorId error:(NSError**)error 15 | { 16 | LNLog(LNLogLevelDebug, @"Setting notification permission"); 17 | 18 | if([status isEqualToString:@"unset"]) 19 | { 20 | return [self _setSectionInfoData:nil forBundleIdentifier:bundleIdentifier displayName:displayName simulatorIdentifier:simulatorId error:error]; 21 | } 22 | 23 | BOOL isCriticalEnabled = [status isEqualToString:@"critical"]; 24 | BOOL enabled = [status boolValue] || isCriticalEnabled; 25 | 26 | static NSString* const b64S = @"YnBsaXN0MDDUAQIDBAUGTU5YJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKgHCDAxQUgfSVUkbnVsbN8QFQkKCwwNDg8QERITFBUWFxgZGhscHR4fHiEiIx4jJicfHygjIh8jIyMjI18QFHN1cHByZXNzRnJvbVNldHRpbmdzXxASc3VwcHJlc3NlZFNldHRpbmdzWmhpZGVXZWVBcHBZc2VjdGlvbklEW2Rpc3BsYXlOYW1lVGljb25fEBlkaXNwbGF5c0NyaXRpY2FsQnVsbGV0aW5zW3N1YnNlY3Rpb25zXxATc2VjdGlvbkluZm9TZXR0aW5nc1YkY2xhc3NfEA9zZWN0aW9uQ2F0ZWdvcnlfEBJzdWJzZWN0aW9uUHJpb3JpdHlXdmVyc2lvbl8QGm1hbmFnZWRTZWN0aW9uSW5mb1NldHRpbmdzV2FwcE5hbWVbc2VjdGlvblR5cGVfEBBmYWN0b3J5U2VjdGlvbklEXxAPZGF0YVByb3ZpZGVySURzXHN1YnNlY3Rpb25JRFdmaWx0ZXJzXxAYcGF0aFRvV2VlQXBwUGx1Z2luQnVuZGxlCBAACIACgAWAAAiAAIADgAeABoAAgAWAAIAAgACAAIAAXxAmY29tLkxlb05hdGFuLkxOUG9wdXBDb250cm9sbGVyRXhhbXBsZS3ZMjM0NTY3Ejg5Ojs7Ox8fPjtAXHB1c2hTZXR0aW5nc18QGXNob3dzSW5Ob3RpZmljYXRpb25DZW50ZXJfEBNhbGxvd3NOb3RpZmljYXRpb25zXxAWc2hvd3NPbkV4dGVybmFsRGV2aWNlc18QFWNvbnRlbnRQcmV2aWV3U2V0dGluZ15jYXJQbGF5U2V0dGluZ18QEXNob3dzSW5Mb2NrU2NyZWVuWWFsZXJ0VHlwZRA/CQkJgAQJEAHSQkNERVokY2xhc3NuYW1lWCRjbGFzc2VzXxAVQkJTZWN0aW9uSW5mb1NldHRpbmdzokZHXxAVQkJTZWN0aW9uSW5mb1NldHRpbmdzWE5TT2JqZWN0V0xOUG9wdXDSQkNKS11CQlNlY3Rpb25JbmZvokxHXUJCU2VjdGlvbkluZm9fEA9OU0tleWVkQXJjaGl2ZXLRT1BUcm9vdIABAAgAEQAaACMALQAyADcAQABGAHMAigCfAKoAtADAAMUA4QDtAQMBCgEcATEBOQFWAV4BagF9AY8BnAGkAb8BwAHCAcMBxQHHAckBygHMAc4B0AHSAdQB1gHYAdoB3AHeAeACCQIcAikCRQJbAnQCjAKbAq8CuQK7ArwCvQK+AsACwQLDAsgC0wLcAvQC9wMPAxgDIAMlAzMDNgNEA1YDWQNeAAAAAAAAAgEAAAAAAAAAUQAAAAAAAAAAAAAAAAAAA2A="; 27 | 28 | NSData* b64 = [[NSData alloc] initWithBase64EncodedString:b64S options:0]; 29 | 30 | NSDictionary* propList = CFBridgingRelease(CFPropertyListCreateWithData(NULL, (__bridge CFDataRef)b64, kCFPropertyListMutableContainersAndLeaves, NULL, NULL)); 31 | 32 | propList[@"$objects"][2] = bundleIdentifier; 33 | propList[@"$objects"][3][@"allowsNotifications"] = @(enabled); 34 | propList[@"$objects"][3][@"criticalAlertSetting"] = isCriticalEnabled ? @2 : @0; 35 | propList[@"$objects"][5] = displayName; 36 | 37 | NSData* sectionInfoData = CFBridgingRelease(CFPropertyListCreateData(NULL, (__bridge CFTypeRef)propList, kCFPropertyListBinaryFormat_v1_0, 0, NULL)); 38 | 39 | return [self _setSectionInfoData:sectionInfoData forBundleIdentifier:bundleIdentifier displayName:displayName simulatorIdentifier:simulatorId error:error]; 40 | } 41 | 42 | + (BOOL)_ensurePermissionSet:(NSString*)path bundleIdentifier:(NSString*)bundleIdentifier sectionInfoData:(id)sectionInfoData 43 | { 44 | NSMutableDictionary* bulletinVersionedSectionInfo = [NSMutableDictionary dictionaryWithContentsOfFile:path]; 45 | 46 | return bulletinVersionedSectionInfo[@"sectionInfo"][bundleIdentifier] == sectionInfoData || [bulletinVersionedSectionInfo[@"sectionInfo"][bundleIdentifier] isEqual:sectionInfoData]; 47 | } 48 | 49 | static void _setImmutable(NSString* path, BOOL immutable) 50 | { 51 | [NSFileManager.defaultManager setAttributes:@{NSFileImmutable: @(immutable)} ofItemAtPath:path error:NULL]; 52 | } 53 | 54 | + (BOOL)_setSectionInfoData:(id)sectionInfoData forBundleIdentifier:(NSString*)bundleIdentifier displayName:(NSString*)displayName simulatorIdentifier:(NSString*)simulatorId error:(NSError**)error 55 | { 56 | NSURL* simulatorLibraryURL = [SimUtils libraryURLForSimulatorId:simulatorId]; 57 | 58 | BOOL success = NO; 59 | NSDate *start = [NSDate date]; 60 | 61 | while (!success) 62 | { 63 | NSTimeInterval elapsed = [[NSDate date] timeIntervalSinceDate:start]; 64 | if (elapsed > AppleSimUtilsRetryTimeout) break; 65 | 66 | //Legacy 67 | NSFileManager *fileManager = [NSFileManager defaultManager]; 68 | NSString* sectionInfoPath = [simulatorLibraryURL.path stringByAppendingPathComponent:@"BulletinBoard/SectionInfo.plist"]; 69 | if([fileManager fileExistsAtPath:sectionInfoPath]) 70 | { 71 | NSMutableDictionary* bulletinSectionInfo = [NSMutableDictionary dictionaryWithContentsOfFile:sectionInfoPath]; 72 | if(sectionInfoData == nil) 73 | { 74 | [bulletinSectionInfo removeObjectForKey:bundleIdentifier]; 75 | } 76 | else 77 | { 78 | bulletinSectionInfo[bundleIdentifier] = sectionInfoData; 79 | } 80 | [bulletinSectionInfo writeToFile:sectionInfoPath atomically:YES]; 81 | 82 | return YES; 83 | } 84 | 85 | //Xcode 9 support 86 | NSString* versionedSectionInfoPath = [simulatorLibraryURL.path stringByAppendingPathComponent:@"BulletinBoard/VersionedSectionInfo.plist"]; 87 | if([fileManager fileExistsAtPath:versionedSectionInfoPath]) 88 | { 89 | _setImmutable(versionedSectionInfoPath, YES); 90 | NSMutableDictionary* bulletinVersionedSectionInfo = [NSMutableDictionary dictionaryWithContentsOfFile:versionedSectionInfoPath]; 91 | if(sectionInfoData == nil) 92 | { 93 | [bulletinVersionedSectionInfo[@"sectionInfo"] removeObjectForKey:bundleIdentifier]; 94 | } 95 | else 96 | { 97 | bulletinVersionedSectionInfo[@"sectionInfo"][bundleIdentifier] = sectionInfoData; 98 | } 99 | 100 | _setImmutable(versionedSectionInfoPath, NO); 101 | [bulletinVersionedSectionInfo writeToFile:versionedSectionInfoPath atomically:YES]; 102 | _setImmutable(versionedSectionInfoPath, YES); 103 | 104 | if([self _ensurePermissionSet:versionedSectionInfoPath bundleIdentifier:bundleIdentifier sectionInfoData:sectionInfoData]) 105 | { 106 | //Pass only if file is verified to include the permission as expected. 107 | [SimUtils registerCleanupBlock:^{ 108 | _setImmutable(versionedSectionInfoPath, NO); 109 | }]; 110 | 111 | return YES; 112 | } 113 | } 114 | 115 | //Add a retry mechanism to be able to cope with a device which is in the process of booting while this runs. 116 | debug_log(@"Retrying in one second"); 117 | [NSThread sleepForTimeInterval:1]; 118 | } 119 | 120 | if(error) 121 | { 122 | *error = [NSError errorWithDomain:@"SetNotificationsPermissionError" code:0 userInfo:@{NSLocalizedDescriptionKey: @"BulletinBoard property list not found."}]; 123 | } 124 | 125 | return NO; 126 | } 127 | 128 | @end 129 | -------------------------------------------------------------------------------- /applesimutils/applesimutils/SetServicePermission.h: -------------------------------------------------------------------------------- 1 | // 2 | // SetServicePermission.h 3 | // applesimutils 4 | // 5 | // Created by Leo Natan (Wix) on 02/04/2017. 6 | // Copyright © 2017 Wix. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SetServicePermission : NSObject 12 | 13 | + (BOOL)isSimulatorReadyForPersmissions:(NSString*)simulatorId; 14 | + (BOOL)setPermisionStatus:(NSString*)status forService:(NSString*)service bundleIdentifier:(NSString*)bundleIdentifier simulatorIdentifier:(NSString*)simulatorId operatingSystemVersion:(NSOperatingSystemVersion)operatingSystemVersion error:(NSError**)error; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /applesimutils/applesimutils/SetServicePermission.m: -------------------------------------------------------------------------------- 1 | // 2 | // SetServicePermission.m 3 | // applesimutils 4 | // 5 | // Created by Leo Natan (Wix) on 02/04/2017. 6 | // Copyright © 2017 Wix. All rights reserved. 7 | // 8 | 9 | #import "SetServicePermission.h" 10 | #import 11 | #import "SimUtils.h" 12 | 13 | @implementation SetServicePermission 14 | 15 | + (NSURL *)_tccURLForSimulatorId:(NSString*)simulatorId 16 | { 17 | return [[SimUtils libraryURLForSimulatorId:simulatorId] URLByAppendingPathComponent:@"TCC/TCC.db"]; 18 | } 19 | 20 | #pragma mark - Helper 21 | 22 | + (BOOL)_changeAccessToService:(NSString*)service 23 | simulatorId:(NSString*)simulatorId 24 | operatingSystemVersion:(NSOperatingSystemVersion)version 25 | bundleIdentifier:(NSString*)bundleIdentifier 26 | status:(NSString*)status 27 | error:(NSError**)error 28 | { 29 | __block BOOL success = NO; 30 | NSDate *start = [NSDate date]; 31 | 32 | while (!success) 33 | { 34 | dtx_defer { 35 | if(success == NO) 36 | { 37 | debug_log(@"Retrying in one second"); 38 | [NSThread sleepForTimeInterval:1]; 39 | } 40 | }; 41 | 42 | NSTimeInterval elapsed = [[NSDate date] timeIntervalSinceDate:start]; 43 | if (elapsed > AppleSimUtilsRetryTimeout) break; 44 | 45 | NSURL* tccURL = [self _tccURLForSimulatorId:simulatorId]; 46 | 47 | if ([tccURL checkResourceIsReachableAndReturnError:error] == NO) 48 | { 49 | logcontinue(@"TCC database is not found"); 50 | } 51 | 52 | FMDatabase* db = [[FMDatabase alloc] initWithURL:tccURL]; 53 | dtx_defer { 54 | if(db.isOpen) 55 | { 56 | [db close]; 57 | } 58 | }; 59 | 60 | if (![db open]) 61 | { 62 | auto msg = [NSString stringWithFormat:@"TCC database failed to open: %@", [db lastErrorMessage]]; 63 | logcontinue(msg); 64 | } 65 | 66 | NSString *query; 67 | NSDictionary *parameters; 68 | 69 | query = @"DELETE FROM access WHERE service = :service AND client = :client AND client_type = :client_type"; 70 | parameters = @{@"service": service, @"client": bundleIdentifier, @"client_type": @"0"}; 71 | 72 | if ([db executeUpdate:query withParameterDictionary:parameters]) 73 | { 74 | success = YES; 75 | 76 | if([status isEqualToString:@"unset"] == NO) 77 | { 78 | if(version.majorVersion >= 14) 79 | { 80 | query = @"INSERT INTO access (service, client, client_type, auth_value, auth_reason, auth_version, flags) VALUES (:service, :client, :client_type, :auth_value, :auth_reason, :auth_version, :flags)"; 81 | 82 | NSString* auth_value = nil; 83 | 84 | NSString* auth_version = @"1"; 85 | if([service isEqualToString:@"kTCCServicePhotos"]) 86 | { 87 | auth_version = @"2"; 88 | if([status isEqualToString:@"limited"]) 89 | { 90 | auth_value = @"3"; 91 | } 92 | } 93 | 94 | if(auth_value == nil) 95 | { 96 | auth_value = [status boolValue] ? @"2" : @"0"; 97 | } 98 | 99 | parameters = @{@"service": service, @"client": bundleIdentifier, @"client_type": @"0", @"auth_value": auth_value, @"auth_reason": @"2", @"auth_version": auth_version, @"flags": @"0"}; 100 | } 101 | else 102 | { 103 | if([service isEqualToString:@"kTCCServicePhotos"] && [status isEqualToString:@"limited"]) 104 | { 105 | *error = [NSError errorWithDomain:@"SetServicePermissionError" code:0 userInfo:@{NSLocalizedDescriptionKey: @"Limited photos permission is only supported for simulators running iOS/tvOS 14 and above"}]; 106 | return NO; 107 | } 108 | 109 | BOOL allowed = [status boolValue]; 110 | 111 | query = @"REPLACE INTO access (service, client, client_type, allowed, prompt_count) VALUES (:service, :client, :client_type, :allowed, :prompt_count)"; 112 | parameters = @{@"service": service, @"client": bundleIdentifier, @"client_type": @"0", @"allowed": [@(allowed) stringValue], @"prompt_count": @"1"}; 113 | } 114 | 115 | if ([db executeUpdate:query withParameterDictionary:parameters] == NO) 116 | { 117 | success = NO; 118 | auto msg = [NSString stringWithFormat:@"TCC database failed to update: %@", [db lastErrorMessage]]; 119 | logcontinue(msg); 120 | } 121 | } 122 | } 123 | else 124 | { 125 | auto msg = [NSString stringWithFormat:@"TCC database failed to update: %@", [db lastErrorMessage]]; 126 | logcontinue(msg); 127 | } 128 | } 129 | 130 | if(success == NO && error && *error == nil) 131 | { 132 | *error = [NSError errorWithDomain:@"SetServicePermissionError" code:0 userInfo:@{NSLocalizedDescriptionKey: @"Unknown set service permission error"}]; 133 | } 134 | 135 | return success; 136 | } 137 | 138 | + (BOOL)setPermisionStatus:(NSString*)status forService:(NSString*)service bundleIdentifier:(NSString*)bundleIdentifier simulatorIdentifier:(NSString*)simulatorId operatingSystemVersion:(NSOperatingSystemVersion)operatingSystemVersion error:(NSError**)error; 139 | { 140 | LNLog(LNLogLevelDebug, @"Setting service “%@” permission", service); 141 | 142 | return [self _changeAccessToService:service simulatorId:simulatorId operatingSystemVersion:operatingSystemVersion bundleIdentifier:bundleIdentifier status:status error:error]; 143 | } 144 | 145 | + (BOOL)isSimulatorReadyForPersmissions:(NSString *)simulatorId 146 | { 147 | NSURL* tccURL = [self _tccURLForSimulatorId:simulatorId]; 148 | 149 | return [tccURL checkResourceIsReachableAndReturnError:NULL]; 150 | } 151 | 152 | @end 153 | -------------------------------------------------------------------------------- /applesimutils/applesimutils/SetSimulatorLocation.h: -------------------------------------------------------------------------------- 1 | // 2 | // SetSimulatorLocation.h 3 | // applesimutils 4 | // 5 | // Created by Leo Natan on 3/10/21. 6 | // Copyright © 2017-2021 Leo Natan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface SetSimulatorLocation : NSObject 14 | 15 | + (void)setLatitude:(double)latitude longitude:(double)longitude forSimulatorUDIDs:(NSArray*)udids; 16 | + (void)clearLocationForSimulatorUDIDs:(NSArray*)udids; 17 | 18 | @end 19 | 20 | NS_ASSUME_NONNULL_END 21 | -------------------------------------------------------------------------------- /applesimutils/applesimutils/SetSimulatorLocation.m: -------------------------------------------------------------------------------- 1 | // 2 | // SetSimulatorLocation.m 3 | // applesimutils 4 | // 5 | // Created by Leo Natan on 3/10/21. 6 | // Copyright © 2017-2021 Leo Natan. All rights reserved. 7 | // 8 | 9 | #import "SetSimulatorLocation.h" 10 | 11 | static NSString * const kLocationNotificationLatitudeKey = @"simulateLocationLatitude"; 12 | static NSString * const kLocationNotificationLongitudeKey = @"simulateLocationLongitude"; 13 | static NSString * const kLocationNotificationDevicesKey = @"simulateLocationDevices"; 14 | static NSString * const kLocationNotificationName = @"com.apple.iphonesimulator.simulateLocation"; 15 | 16 | @implementation SetSimulatorLocation 17 | 18 | + (void)setLatitude:(double)latitude longitude:(double)longitude 19 | forSimulatorUDIDs:(NSArray *)udids { 20 | [self postNewLocationNotification:@{ 21 | kLocationNotificationLatitudeKey: @(latitude), 22 | kLocationNotificationLongitudeKey: @(longitude), 23 | kLocationNotificationDevicesKey: udids 24 | }]; 25 | } 26 | 27 | + (void)clearLocationForSimulatorUDIDs:(NSArray *)udids { 28 | [self postNewLocationNotification:@{ 29 | kLocationNotificationDevicesKey: udids 30 | }]; 31 | } 32 | 33 | /// Post the new location configurations over the distributed notification center. 34 | /// 35 | /// @note The notification is posted twice as a workaround to overcome the issue of not notifying 36 | /// the app for the new location that was set on the first attempt after setting a new location 37 | /// permissions. We post the new location notifications twice, and the location is set on the 38 | /// second attempt. The mentioned bug also happens when setting the location directly through the 39 | /// Simulator app (without AppleSimUtils). 40 | + (void)postNewLocationNotification:(NSDictionary *)info { 41 | auto notificationCenter = NSDistributedNotificationCenter.defaultCenter; 42 | 43 | for (uint i = 0; i < 2; i ++) { 44 | [notificationCenter postNotificationName:kLocationNotificationName object:nil userInfo:info 45 | deliverImmediately:YES]; 46 | } 47 | } 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /applesimutils/applesimutils/SimUtils.h: -------------------------------------------------------------------------------- 1 | // 2 | // SimUtils.h 3 | // applesimutils 4 | // 5 | // Created by Leo Natan (Wix) on 19/04/2017. 6 | // Copyright © 2017 Wix. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | extern const NSTimeInterval AppleSimUtilsRetryTimeout; 12 | 13 | @interface SimUtils : NSObject 14 | 15 | + (NSURL*)xcrunURL; 16 | + (NSURL*)developerURL; 17 | + (NSURL*)URLForSimulatorId:(NSString*)simulatorId; 18 | + (NSURL*)dataURLForSimulatorId:(NSString*)simulatorId; 19 | + (NSURL*)libraryURLForSimulatorId:(NSString*)simulatorId; 20 | + (NSURL*)binaryURLForBundleId:(NSString*)bundleId simulatorId:(NSString*)simulatorId; 21 | + (NSURL*)launchDaemonPlistURLForDaemon:(NSString*)daemon runtimeBundleURL:(NSURL*)runtimeBundleURL; 22 | + (void)restartSpringBoardForSimulatorId:(NSString*)simulatorId; 23 | 24 | + (void)registerCleanupBlock:(dispatch_block_t)block; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /applesimutils/applesimutils/SimUtils.m: -------------------------------------------------------------------------------- 1 | // 2 | // SimUtils.m 3 | // applesimutils 4 | // 5 | // Created by Leo Natan (Wix) on 19/04/2017. 6 | // Copyright © 2017 Wix. All rights reserved. 7 | // 8 | 9 | #import "SimUtils.h" 10 | #import "NSTask+InputOutput.h" 11 | #import "LNOptionsParser.h" 12 | 13 | const NSTimeInterval AppleSimUtilsRetryTimeout = 30.0f; 14 | 15 | @implementation SimUtils 16 | 17 | + (NSURL*)_whichURLForBinaryName:(NSString*)binaryName 18 | { 19 | NSParameterAssert(binaryName != nil); 20 | 21 | // NSString* shellPath = NSProcessInfo.processInfo.environment[@"SHELL"] ?: @"/bin/zsh"; 22 | // 23 | // NSTask* whichTask = [NSTask new]; 24 | // whichTask.launchPath = shellPath; 25 | // whichTask.arguments = @[@"-l", @"-c", [NSString stringWithFormat:@" %@", binaryName]]; 26 | 27 | NSTask* whichTask = [NSTask new]; 28 | whichTask.launchPath = @"/usr/bin/which"; 29 | whichTask.arguments = @[binaryName]; 30 | 31 | NSString* whichResponse; 32 | NSString* whichError; 33 | if(0 != [whichTask launchAndWaitUntilExitReturningStandardOutput:&whichResponse standardRrror:&whichError]) 34 | { 35 | LNUsagePrintMessage(whichError, LNLogLevelError); 36 | exit(-1); 37 | } 38 | 39 | return [NSURL fileURLWithPath:whichResponse]; 40 | } 41 | 42 | + (NSURL*)xcrunURL 43 | { 44 | static NSURL* xcrunURL; 45 | static dispatch_once_t onceToken; 46 | dispatch_once(&onceToken, ^{ 47 | xcrunURL = [self _whichURLForBinaryName:@"xcrun"]; 48 | }); 49 | 50 | return xcrunURL; 51 | } 52 | 53 | + (NSURL*)xcodeSelectURL 54 | { 55 | static NSURL* xcodeSelectURL; 56 | static dispatch_once_t onceToken; 57 | dispatch_once(&onceToken, ^{ 58 | xcodeSelectURL = [self _whichURLForBinaryName:@"xcode-select"]; 59 | }); 60 | 61 | return xcodeSelectURL; 62 | } 63 | 64 | + (NSURL*)developerURL 65 | { 66 | NSTask* developerToolsPrintTask = [NSTask new]; 67 | developerToolsPrintTask.launchPath = [self xcodeSelectURL].path; 68 | developerToolsPrintTask.arguments = @[@"-p"]; 69 | 70 | NSString* devToolsPath; 71 | [developerToolsPrintTask launchAndWaitUntilExitReturningStandardOutput:&devToolsPath standardRrror:NULL]; 72 | 73 | NSURL* devToolsURL = [NSURL fileURLWithPath:devToolsPath]; 74 | 75 | return devToolsURL; 76 | } 77 | 78 | + (NSURL*)URLForSimulatorId:(NSString*)simulatorId 79 | { 80 | return [[[NSURL fileURLWithPath:NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES).firstObject] URLByAppendingPathComponent:@"Developer/CoreSimulator/Devices/"] URLByAppendingPathComponent:simulatorId]; 81 | } 82 | 83 | + (NSURL*)dataURLForSimulatorId:(NSString*)simulatorId 84 | { 85 | return [[self URLForSimulatorId:simulatorId] URLByAppendingPathComponent:@"data/"]; 86 | } 87 | 88 | + (NSURL *)libraryURLForSimulatorId:(NSString*)simulatorId 89 | { 90 | return [[self dataURLForSimulatorId:simulatorId] URLByAppendingPathComponent:@"Library/"]; 91 | } 92 | 93 | + (NSURL *)binaryURLForBundleId:(NSString*)bundleId simulatorId:(NSString*)simulatorId 94 | { 95 | NSTask* getBundlePathTask = [NSTask new]; 96 | getBundlePathTask.launchPath = [self xcrunURL].path; 97 | getBundlePathTask.arguments = @[@"simctl", @"get_app_container", simulatorId, bundleId, @"app"]; 98 | 99 | NSString* bundlePath; 100 | if(0 != [getBundlePathTask launchAndWaitUntilExitReturningStandardOutput:&bundlePath standardRrror:NULL]) 101 | { 102 | return nil; 103 | } 104 | 105 | NSURL* bundleURL = [NSURL fileURLWithPath:bundlePath]; 106 | 107 | [getBundlePathTask waitUntilExit]; 108 | 109 | if(bundleURL == nil || [bundleURL checkResourceIsReachableAndReturnError:NULL] == NO) 110 | { 111 | return nil; 112 | } 113 | 114 | NSDictionary* infoPlist = [NSPropertyListSerialization propertyListWithData:[NSData dataWithContentsOfURL:[bundleURL URLByAppendingPathComponent:@"Info.plist"]] options:0 format:nil error:NULL]; 115 | NSString* executableName = infoPlist[@"CFBundleExecutable"]; 116 | 117 | return [bundleURL URLByAppendingPathComponent:executableName]; 118 | } 119 | 120 | + (NSURL*)launchDaemonPlistURLForDaemon:(NSString*)daemon runtimeBundleURL:(NSURL*)runtimeBundleURL 121 | { 122 | if([daemon hasSuffix:@".plist"] == NO) 123 | { 124 | daemon = [daemon stringByAppendingString:@".plist"]; 125 | } 126 | 127 | //Xcode 14 and 15 (using runtimeBundleURL) 128 | NSURL *daemonURL = [runtimeBundleURL URLByAppendingPathComponent:[NSString stringWithFormat:@"Contents/Resources/RuntimeRoot/System/Library/LaunchDaemons/%@", daemon]]; 129 | 130 | //Xcode 11 131 | NSURL* developerTools = [SimUtils developerURL]; 132 | 133 | if ([daemonURL checkResourceIsReachableAndReturnError:NULL] == NO) 134 | { 135 | daemonURL = [developerTools URLByAppendingPathComponent:[NSString stringWithFormat:@"Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/LaunchDaemons/%@", daemon]]; 136 | } 137 | 138 | //Xcode 9 139 | if ([daemonURL checkResourceIsReachableAndReturnError:NULL] == NO) 140 | { 141 | daemonURL = [developerTools URLByAppendingPathComponent:[NSString stringWithFormat:@"Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/LaunchDaemons/%@", daemon]]; 142 | } 143 | 144 | //Older 145 | if ([daemonURL checkResourceIsReachableAndReturnError:NULL] == NO) 146 | { 147 | daemonURL = [developerTools URLByAppendingPathComponent:[NSString stringWithFormat:@"Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/LaunchDaemons/%@", daemon]]; 148 | } 149 | 150 | if ([daemonURL checkResourceIsReachableAndReturnError:NULL] == NO) 151 | { 152 | return nil; 153 | } 154 | 155 | return daemonURL; 156 | } 157 | 158 | + (void)restartSpringBoardForSimulatorId:(NSString*)simulatorId 159 | { 160 | NSTask* respringTask = [NSTask new]; 161 | respringTask.launchPath = [SimUtils xcrunURL].path; 162 | respringTask.arguments = @[@"simctl", @"spawn", simulatorId, @"launchctl", @"stop", @"com.apple.SpringBoard"]; 163 | [respringTask launch]; 164 | [respringTask waitUntilExit]; 165 | } 166 | 167 | static NSMutableArray* _blocks; 168 | 169 | + (void)registerCleanupBlock:(dispatch_block_t)block 170 | { 171 | static dispatch_once_t onceToken; 172 | dispatch_once(&onceToken, ^{ 173 | _blocks = [NSMutableArray new]; 174 | }); 175 | [_blocks addObject:block]; 176 | } 177 | 178 | __attribute__((destructor)) 179 | static void cleanup(void) 180 | { 181 | [_blocks enumerateObjectsUsingBlock:^(dispatch_block_t _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 182 | obj(); 183 | }]; 184 | } 185 | 186 | @end 187 | -------------------------------------------------------------------------------- /applesimutils/applesimutils/Swiftier.h: -------------------------------------------------------------------------------- 1 | // 2 | // Swiftier.h 3 | // DetoxInstruments 4 | // 5 | // Created by Leo Natan (Wix) on 11/22/17. 6 | // Copyright © 2017-2019 Wix. All rights reserved. 7 | // 8 | 9 | #ifndef Swiftier_h 10 | #define Swiftier_h 11 | 12 | #ifndef DTX_NOTHROW 13 | #define DTX_NOTHROW __attribute__((__nothrow__)) 14 | #endif 15 | #ifndef DTX_ALWAYS_INLINE 16 | #define DTX_ALWAYS_INLINE __attribute__((__always_inline__)) 17 | #endif 18 | #ifndef DTX_ALWAYS_INLINE 19 | #define DTX_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) 20 | #endif 21 | 22 | #if ! defined(__cplusplus) 23 | #import 24 | 25 | #if ! defined(thread_local) 26 | #define thread_local _Thread_local 27 | #endif 28 | 29 | #define auto __auto_type 30 | #endif 31 | 32 | typedef _Atomic(void*) atomic_voidptr; 33 | typedef _Atomic(const void*) atomic_constvoidptr; 34 | typedef _Atomic(double) atomic_double; 35 | 36 | #if __has_include() 37 | #import 38 | typedef _Atomic(thread_t) atomic_thread; 39 | #endif 40 | 41 | #define dtx_defer_block_name_with_prefix(prefix, suffix) prefix ## suffix 42 | #define dtx_defer_block_name(suffix) dtx_defer_block_name_with_prefix(defer_, suffix) 43 | #define dtx_defer __strong void(^dtx_defer_block_name(__LINE__))(void) __attribute__((cleanup(defer_cleanup_block), unused)) = ^ 44 | #pragma clang diagnostic push 45 | #pragma clang diagnostic ignored "-Wunused-function" 46 | static void defer_cleanup_block(__strong void(^*block)(void)) { 47 | (*block)(); 48 | } 49 | #pragma clang diagnostic pop 50 | 51 | #ifdef __OBJC__ 52 | #define NS(x) ((__bridge id)x) 53 | #define CF(x) ((__bridge CFTypeRef)x) 54 | #define PTR(x) ((__bridge void*)x) 55 | 56 | #ifdef __cplusplus 57 | #import 58 | #else 59 | @import Foundation; 60 | #endif 61 | 62 | @interface NSArray (PSPDFSafeCopy) 63 | - (NSArray *)copy; 64 | - (NSMutableArray *)mutableCopy; 65 | @end 66 | 67 | @interface NSSet (PSPDFSafeCopy) 68 | - (NSSet *)copy; 69 | - (NSMutableSet *)mutableCopy; 70 | @end 71 | 72 | @interface NSDictionary (PSPDFSafeCopy) 73 | - (NSDictionary *)copy; 74 | - (NSMutableDictionary *)mutableCopy; 75 | @end 76 | 77 | @interface NSOrderedSet (PSPDFSafeCopy) 78 | - (NSOrderedSet *)copy; 79 | - (NSMutableOrderedSet *)mutableCopy; 80 | @end 81 | 82 | @interface NSHashTable (PSPDFSafeCopy) 83 | - (NSHashTable *)copy; 84 | @end 85 | 86 | @interface NSMapTable (PSPDFSafeCopy) 87 | - (NSMapTable *)copy; 88 | @end 89 | 90 | #endif 91 | 92 | #endif /* Swiftier_pch */ 93 | -------------------------------------------------------------------------------- /applesimutils/applesimutils/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // applesimutils 4 | // 5 | // Created by Leo Natan (Wix) on 30/03/2017. 6 | // Copyright © 2017 Wix. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SetNotificationsPermission.h" 11 | #import "SetServicePermission.h" 12 | #import "SetLocationPermission.h" 13 | #import "SetHealthKitPermission.h" 14 | #import "SetSimulatorLocation.h" 15 | #import "ClearMedia.h" 16 | #import "ClearKeychain.h" 17 | #import "LNOptionsParser.h" 18 | #import "SimUtils.h" 19 | #import "NSTask+InputOutput.h" 20 | 21 | static char* const __version = 22 | #include "version.h" 23 | ; 24 | 25 | static void bootSimulator(NSString* simulatorId) 26 | { 27 | LNLog(LNLogLevelDebug, @"Booting simulator “%@”", simulatorId); 28 | 29 | NSTask* bootTask = [NSTask new]; 30 | bootTask.launchPath = [SimUtils xcrunURL].path; 31 | bootTask.arguments = @[@"simctl", @"boot", simulatorId]; 32 | [bootTask launch]; 33 | 34 | NSTask* bootStatusTask = [NSTask new]; 35 | bootStatusTask.launchPath = [SimUtils xcrunURL].path; 36 | bootStatusTask.arguments = @[@"simctl", @"bootstatus", simulatorId]; 37 | 38 | NSPipe* devNullPipe = [NSPipe new]; 39 | bootStatusTask.standardOutput = devNullPipe; 40 | bootStatusTask.standardError = devNullPipe; 41 | 42 | [bootStatusTask launch]; 43 | [bootStatusTask waitUntilExit]; 44 | 45 | [bootTask waitUntilExit]; 46 | } 47 | 48 | static void shutdownSimulator(NSString* simulatorId) 49 | { 50 | LNLog(LNLogLevelDebug, @"Shutting down simulator “%@”", simulatorId); 51 | 52 | NSTask* shutdownTask = [NSTask new]; 53 | shutdownTask.launchPath = [SimUtils xcrunURL].path; 54 | shutdownTask.arguments = @[@"simctl", @"shutdown", simulatorId]; 55 | [shutdownTask launch]; 56 | [shutdownTask waitUntilExit]; 57 | } 58 | 59 | static NSArray* simulatorDevicesList(void) 60 | { 61 | LNLog(LNLogLevelDebug, @"Obtaining simulator device list"); 62 | 63 | NSTask* listTask = [NSTask new]; 64 | listTask.launchPath = SimUtils.xcrunURL.path; 65 | listTask.arguments = @[@"simctl", @"list", @"--json"]; 66 | 67 | NSData* jsonData; 68 | [listTask launchAndWaitUntilExitReturningStandardOutputData:&jsonData standardErrorData:NULL]; 69 | 70 | NSError* error; 71 | NSDictionary* list = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error]; 72 | 73 | if(list == nil) 74 | { 75 | LNUsagePrintMessage([NSString stringWithFormat:@"Error: %@.", error.localizedDescription], LNLogLevelError); 76 | 77 | return nil; 78 | } 79 | 80 | NSArray*>* deviceTypes = list[@"devicetypes"]; 81 | NSMutableDictionary* deviceTypeMaps = [NSMutableDictionary new]; 82 | [deviceTypes enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 83 | deviceTypeMaps[obj[@"identifier"]] = obj; 84 | }]; 85 | 86 | NSPredicate* availabilityPredicate = [NSPredicate predicateWithFormat:@"availability == \"(available)\" OR isAvailable == \"YES\" OR isAvailable == 1"]; 87 | 88 | NSArray* runtimes = [list[@"runtimes"] filteredArrayUsingPredicate:availabilityPredicate]; 89 | NSDictionary* devices = list[@"devices"]; 90 | 91 | NSMutableArray*>* allDevices = [NSMutableArray new]; 92 | 93 | [runtimes enumerateObjectsUsingBlock:^(NSDictionary* _Nonnull runtime, NSUInteger idx, BOOL * _Nonnull stop) { 94 | NSString* runtimeName = runtime[@"name"]; 95 | NSString* runtimeIdentifier = runtime[@"identifier"]; 96 | NSArray* nameDevices = devices[runtimeName] ?: @[]; 97 | NSArray* identifierDevices = devices[runtimeIdentifier] ?: @[]; 98 | NSArray* runtimeDevices = [nameDevices arrayByAddingObjectsFromArray:identifierDevices]; 99 | NSArray* filteredDevices = [runtimeDevices filteredArrayUsingPredicate:availabilityPredicate]; 100 | [filteredDevices setValue:runtime forKey:@"os"]; 101 | [allDevices addObjectsFromArray:runtimeDevices]; 102 | }]; 103 | 104 | [allDevices sortUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"os.version" ascending:NO comparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { 105 | return [obj1 compare:obj2 options:NSNumericSearch]; 106 | }], [NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES]]]; 107 | 108 | [allDevices enumerateObjectsUsingBlock:^(NSMutableDictionary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 109 | NSURL* url = [[SimUtils URLForSimulatorId:obj[@"udid"]] URLByAppendingPathComponent:@"device.plist"]; 110 | NSMutableDictionary* metadata = [[NSMutableDictionary alloc] initWithContentsOfURL:url]; 111 | obj[@"deviceType"] = deviceTypeMaps[metadata[@"deviceType"]]; 112 | }]; 113 | 114 | return allDevices; 115 | } 116 | 117 | static NSPredicate* predicateByBooted(void) 118 | { 119 | return [NSPredicate predicateWithFormat:@"state ==[c] %@", @"Booted"]; 120 | } 121 | 122 | static NSPredicate* predicateByName(NSString* simName) 123 | { 124 | return [NSPredicate predicateWithFormat:@"name ==[cd] %@", simName]; 125 | } 126 | 127 | static NSPredicate* predicateByOS(NSString* osVer) 128 | { 129 | return [NSPredicate predicateWithFormat:@"(os.version == %@ || os.name == %@)", osVer, osVer]; 130 | } 131 | 132 | static NSPredicate* predicateById(NSString* simId) 133 | { 134 | return [NSPredicate predicateWithFormat:@"udid == %@", simId]; 135 | } 136 | 137 | static NSPredicate* predicateByType(NSString* deviceType) 138 | { 139 | return [NSPredicate predicateWithFormat:@"(deviceType.identifier == %@ || deviceType.name == %@)", deviceType, deviceType]; 140 | } 141 | 142 | static NSArray* filteredDeviceList(NSArray* simulatorDevices, NSPredicate* filterPredicate) 143 | { 144 | if(simulatorDevices == nil) 145 | { 146 | return nil; 147 | } 148 | 149 | if(filterPredicate != nil) 150 | { 151 | return [simulatorDevices filteredArrayUsingPredicate:filterPredicate]; 152 | } 153 | 154 | return simulatorDevices; 155 | } 156 | 157 | 158 | /* 159 | strings /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/TCC.framework/TCC | grep kTCCService 160 | 161 | As of iOS 14.2: 162 | 163 | kTCCServiceAll 164 | kTCCServiceAddressBook 165 | kTCCServiceContactsLimited 166 | kTCCServiceContactsFull 167 | kTCCServiceCalendar 168 | kTCCServiceReminders 169 | kTCCServiceTwitter 170 | kTCCServiceFacebook 171 | kTCCServiceSinaWeibo 172 | kTCCServiceLiverpool 173 | kTCCServiceUbiquity 174 | kTCCServiceTencentWeibo 175 | kTCCServiceShareKit 176 | kTCCServicePhotos 177 | kTCCServicePhotosAdd 178 | kTCCServiceMicrophone 179 | kTCCServiceCamera 180 | kTCCServiceWillow 181 | kTCCServiceMediaLibrary 182 | kTCCServiceSiri 183 | kTCCServiceMotion 184 | kTCCServiceSpeechRecognition 185 | kTCCServiceUserTracking 186 | kTCCServiceBluetoothAlways 187 | kTCCServiceWebKitIntelligentTrackingPrevention 188 | kTCCServicePrototype3Rights 189 | kTCCServicePrototype4Rights 190 | kTCCServiceBluetoothPeripheral 191 | kTCCServiceBluetoothWhileInUse 192 | kTCCServiceKeyboardNetwork 193 | kTCCServiceMSO 194 | kTCCServiceCalls 195 | kTCCServiceFaceID 196 | kTCCServiceSensorKitMotion 197 | kTCCServiceSensorKitWatchMotion 198 | kTCCServiceSensorKitLocationMetrics 199 | kTCCServiceSensorKitAmbientLightSensor 200 | kTCCServiceSensorKitWatchAmbientLightSensor 201 | kTCCServiceSensorKitWatchHeartRate 202 | kTCCServiceSensorKitWatchOnWristState 203 | kTCCServiceSensorKitKeyboardMetrics 204 | kTCCServiceSensorKitWatchPedometer 205 | kTCCServiceSensorKitPedometer 206 | kTCCServiceSensorKitWatchFallStats 207 | kTCCServiceSensorKitWatchForegroundAppCategory 208 | kTCCServiceSensorKitForegroundAppCategory 209 | kTCCServiceSensorKitWatchSpeechMetrics 210 | kTCCServiceSensorKitSpeechMetrics 211 | kTCCServiceSensorKitMotionHeartRate 212 | kTCCServiceSensorKitOdometer 213 | kTCCServiceSensorKitElevation 214 | kTCCServiceSensorKitStrideCalibration 215 | kTCCServiceSensorKitDeviceUsage 216 | kTCCServiceSensorKitPhoneUsage 217 | kTCCServiceSensorKitMessageUsage 218 | kTCCServiceSensorKitFacialMetrics 219 | kTCCServiceExposureNotification 220 | kTCCServiceExposureNotificationRegion 221 | */ 222 | 223 | static void assertStringInArrayValues(NSString* str, NSArray* values, int errorCode, NSString* failureMessage) 224 | { 225 | if([[values valueForKey:@"lowercaseString"] containsObject:str.lowercaseString] == NO) 226 | { 227 | LNUsagePrintMessage(failureMessage, LNLogLevelError); 228 | 229 | exit(errorCode); 230 | } 231 | } 232 | 233 | static NSOperatingSystemVersion operatingSystemFromSimulator(NSDictionary* simulator) 234 | { 235 | NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:@"([0-9]+)\\.([0-9]+)(\\.([0-9]+))?" options:0 error:NULL]; 236 | 237 | NSString* version = [simulator valueForKeyPath:@"os.version"]; 238 | __unused NSArray* results = [regex matchesInString:version options:0 range:NSMakeRange(0, version.length)]; 239 | 240 | if(results.count != 1 || results.firstObject.numberOfRanges != 5) 241 | { 242 | LNUsagePrintMessage([NSString stringWithFormat:@"Unable to parse simulator version: %@.", version], LNLogLevelError); 243 | 244 | exit(-10); 245 | } 246 | 247 | NSOperatingSystemVersion rv = {0}; 248 | rv.majorVersion = [[version substringWithRange:(NSRange)[results.firstObject rangeAtIndex:1]] integerValue]; 249 | rv.minorVersion = [[version substringWithRange:(NSRange)[results.firstObject rangeAtIndex:2]] integerValue]; 250 | if([results.firstObject rangeAtIndex:4].location != NSNotFound) 251 | { 252 | rv.patchVersion = [[version substringWithRange:(NSRange)[results.firstObject rangeAtIndex:4]] integerValue]; 253 | } 254 | 255 | return rv; 256 | } 257 | 258 | static BOOL performPermissionsPass(NSString* permissionsArgument, NSString* simulatorIdentifier, NSString* bundleIdentifier, NSDictionary* simulator) 259 | { 260 | LNLog(LNLogLevelDebug, @"Performing permission pass"); 261 | 262 | NSDictionary* argumentToAppleService = @{ 263 | @"calendar": @"kTCCServiceCalendar", 264 | @"camera": @"kTCCServiceCamera", 265 | @"contacts": @"kTCCServiceAddressBook", 266 | @"faceid": @"kTCCServiceFaceID", 267 | @"homekit": @"kTCCServiceWillow", 268 | @"microphone": @"kTCCServiceMicrophone", 269 | @"photos": @"kTCCServicePhotos", 270 | @"reminders": @"kTCCServiceReminders", 271 | @"medialibrary": @"kTCCServiceMediaLibrary", 272 | @"motion": @"kTCCServiceMotion", 273 | @"siri": @"kTCCServiceSiri", 274 | @"speech": @"kTCCServiceSpeechRecognition", 275 | @"userTracking": @"kTCCServiceUserTracking", 276 | }; 277 | NSURL *runtimeBundleURL = [NSURL fileURLWithPath:simulator[@"os"][@"bundlePath"]]; 278 | 279 | NSArray* parsedArguments = [permissionsArgument componentsSeparatedByString:@","]; 280 | 281 | __block NSError* err; 282 | __block BOOL success = YES; 283 | 284 | __block BOOL needsSpringBoardRestart = NO; 285 | 286 | [parsedArguments enumerateObjectsUsingBlock:^(NSString * _Nonnull argument, NSUInteger idx, BOOL * _Nonnull stop) { 287 | NSArray* split = [argument componentsSeparatedByString:@"="]; 288 | if(split.count != 2) 289 | { 290 | LNUsagePrintMessage([NSString stringWithFormat:@"Error: Permission argument cannot be parsed: “%@”.", argument], LNLogLevelError); 291 | exit(-10); 292 | } 293 | 294 | NSString* permission = [split.firstObject stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; 295 | NSString* value = [split.lastObject stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; 296 | 297 | if([permission isEqualToString:@"health"]) 298 | { 299 | assertStringInArrayValues(value, @[@"YES", @"NO", @"unset"], -10, [NSString stringWithFormat:@"Error: Illegal value “%@” parsed for permission “%@”.", value, permission]); 300 | 301 | NSDictionary* map = @{ 302 | @"YES": @(HealthKitPermissionStatusAllow), 303 | @"NO": @(HealthKitPermissionStatusDeny), 304 | @"unset": @(HealthKitPermissionStatusUnset) 305 | }; 306 | 307 | success = [SetHealthKitPermission setHealthKitPermission:[map[value] unsignedIntegerValue] forBundleIdentifier:bundleIdentifier simulatorIdentifier:simulatorIdentifier osVersion:operatingSystemFromSimulator(simulator) needsSBRestart:&needsSpringBoardRestart error:&err]; 308 | } 309 | else if([permission isEqualToString:@"notifications"]) 310 | { 311 | assertStringInArrayValues(value, @[@"YES", @"NO", @"critical", @"unset"], -10, [NSString stringWithFormat:@"Error: Illegal value “%@” parsed for permission “%@”.", value, permission]); 312 | 313 | success = [SetNotificationsPermission setNotificationsStatus:value forBundleIdentifier:bundleIdentifier displayName:bundleIdentifier simulatorIdentifier:simulatorIdentifier error:&err]; 314 | 315 | needsSpringBoardRestart |= YES; 316 | } 317 | else if([permission isEqualToString:@"location"]) 318 | { 319 | assertStringInArrayValues(value, @[@"never", @"always", @"inuse", @"unset"], -10, [NSString stringWithFormat:@"Error: Illegal value “%@” parsed for permission “%@”.", value, permission]); 320 | 321 | success = [SetLocationPermission setLocationPermission:value forBundleIdentifier:bundleIdentifier simulatorIdentifier:simulatorIdentifier runtimeBundleURL:runtimeBundleURL error:&err]; 322 | 323 | needsSpringBoardRestart |= NO; 324 | } 325 | else if([permission isEqualToString:@"photos"]) 326 | { 327 | assertStringInArrayValues(value, @[@"YES", @"NO", @"limited", @"unset"], -10, [NSString stringWithFormat:@"Error: Illegal value “%@” parsed for permission “%@”.", value, permission]); 328 | 329 | success = [SetServicePermission setPermisionStatus:value forService:argumentToAppleService[permission] bundleIdentifier:bundleIdentifier simulatorIdentifier:simulatorIdentifier operatingSystemVersion:operatingSystemFromSimulator(simulator) error:&err]; 330 | 331 | needsSpringBoardRestart |= NO; 332 | } 333 | else 334 | { 335 | assertStringInArrayValues(value, @[@"YES", @"NO", @"unset"], -10, [NSString stringWithFormat:@"Error: Illegal value “%@” parsed for permission “%@”.", value, permission]); 336 | 337 | NSString* appleService = argumentToAppleService[permission]; 338 | if(appleService == nil) 339 | { 340 | LNLog(LNLogLevelWarning, @"Warning: Unknown permission “%@”; ignoring", permission); 341 | return; 342 | } 343 | 344 | success = [SetServicePermission setPermisionStatus:value forService:appleService bundleIdentifier:bundleIdentifier simulatorIdentifier:simulatorIdentifier operatingSystemVersion:operatingSystemFromSimulator(simulator) error:&err]; 345 | 346 | needsSpringBoardRestart |= NO; 347 | } 348 | 349 | if(success == NO) 350 | { 351 | *stop = YES; 352 | } 353 | }]; 354 | 355 | if(success) { 356 | LNUsagePrintMessage( 357 | [NSString stringWithFormat:@"Permissions settings performed successfully: %@.", 358 | permissionsArgument], 359 | LNLogLevelDebug); 360 | } else { 361 | if(err == nil) { 362 | err = [NSError errorWithDomain:@"AppleSimUtilsError" code:0 userInfo:@{NSLocalizedDescriptionKey: @"Unknown permission pass error"}]; 363 | } 364 | 365 | LNUsagePrintMessage([NSString stringWithFormat:@"Error: %@.", err.localizedDescription], LNLogLevelError); 366 | exit(-3); 367 | } 368 | 369 | return needsSpringBoardRestart; 370 | } 371 | 372 | static NSPredicate* predicateByAppendingOrCreatingPredicate(NSPredicate* orig, NSPredicate* append) 373 | { 374 | if(append == nil) 375 | { 376 | return orig; 377 | } 378 | 379 | if(orig == nil) 380 | { 381 | return append; 382 | } 383 | 384 | return [NSCompoundPredicate andPredicateWithSubpredicates:@[orig, append]]; 385 | } 386 | 387 | /* 388 | com.apple.BiometricKit.enrollmentChanged 389 | */ 390 | static void setBiometricEnrollment(NSString* simulatorId, BOOL enrolled) 391 | { 392 | NSTask* setNotifyValueTask = [NSTask new]; 393 | setNotifyValueTask.launchPath = [SimUtils xcrunURL].path; 394 | setNotifyValueTask.arguments = @[@"simctl", @"spawn", simulatorId, @"notifyutil", @"-s", @"com.apple.BiometricKit.enrollmentChanged", enrolled ? @"1" : @"0"]; 395 | [setNotifyValueTask launch]; 396 | [setNotifyValueTask waitUntilExit]; 397 | 398 | NSTask* postNotifyTask = [NSTask new]; 399 | postNotifyTask.launchPath = [SimUtils xcrunURL].path; 400 | postNotifyTask.arguments = @[@"simctl", @"spawn", simulatorId, @"notifyutil", @"-p", @"com.apple.BiometricKit.enrollmentChanged"]; 401 | [postNotifyTask launch]; 402 | [postNotifyTask waitUntilExit]; 403 | } 404 | 405 | typedef NS_ENUM(NSUInteger, ASUBiometricType) { 406 | ASUBiometricTypeFinger, 407 | ASUBiometricTypeFace, 408 | }; 409 | 410 | static id filterObject(id obj, NSSet* allowedKeys) { 411 | if ([obj isKindOfClass:[NSDictionary class]]) { 412 | NSMutableDictionary* filtered = [NSMutableDictionary new]; 413 | [(NSDictionary*)obj enumerateKeysAndObjectsUsingBlock:^(NSString* key, id value, BOOL* stop) { 414 | if ([allowedKeys containsObject:key]) { 415 | if ([value isKindOfClass:[NSDictionary class]] || [value isKindOfClass:[NSArray class]]) { 416 | id filteredValue = filterObject(value, allowedKeys); 417 | if (filteredValue) { 418 | filtered[key] = filteredValue; 419 | } 420 | } else { 421 | filtered[key] = value; 422 | } 423 | } 424 | }]; 425 | return filtered; 426 | } else if ([obj isKindOfClass:[NSArray class]]) { 427 | NSMutableArray* filtered = [NSMutableArray new]; 428 | [(NSArray*)obj enumerateObjectsUsingBlock:^(id item, NSUInteger idx, BOOL* stop) { 429 | id filteredItem = filterObject(item, allowedKeys); 430 | if (filteredItem) { 431 | [filtered addObject:filteredItem]; 432 | } 433 | }]; 434 | return filtered; 435 | } 436 | return obj; 437 | } 438 | 439 | /* 440 | com.apple.BiometricKit_Sim.fingerTouch.match 441 | com.apple.BiometricKit_Sim.fingerTouch.nomatch 442 | com.apple.BiometricKit_Sim.pearl.match 443 | com.apple.BiometricKit_Sim.pearl.nomatch 444 | */ 445 | static void sendBiometricMatch(NSString* simulatorId, ASUBiometricType biometricType, BOOL matching) 446 | { 447 | NSMutableString* keyName = [@"com.apple.BiometricKit_Sim." mutableCopy]; 448 | switch (biometricType) { 449 | case ASUBiometricTypeFinger: 450 | [keyName appendString:@"fingerTouch."]; 451 | break; 452 | case ASUBiometricTypeFace: 453 | [keyName appendString:@"pearl."]; 454 | break; 455 | default: 456 | exit(-666); 457 | break; 458 | } 459 | 460 | if(matching) 461 | { 462 | [keyName appendString:@"match"]; 463 | } 464 | else 465 | { 466 | [keyName appendString:@"nomatch"]; 467 | } 468 | 469 | NSTask* postNotifyTask = [NSTask new]; 470 | postNotifyTask.launchPath = [SimUtils xcrunURL].path; 471 | postNotifyTask.arguments = @[@"simctl", @"spawn", simulatorId, @"notifyutil", @"-p", keyName]; 472 | [postNotifyTask launch]; 473 | [postNotifyTask waitUntilExit]; 474 | } 475 | 476 | int main(int argc, const char* argv[]) { 477 | @autoreleasepool { 478 | LNUsageSetIntroStrings(@[@"A collection of utils for Apple simulators."]); 479 | 480 | LNUsageSetExampleStrings(@[ 481 | @"%@ --byId --bundle --setPermissions \", , ...\"", 482 | @"%@ --byName --byOS --bundle --setPermissions \", , ...\"", 483 | @"%@ --list [--byName ] [--byOS ] [--byType ] [--maxResults ] [--fields ]", 484 | @"%@ --booted --biometricEnrollment ", 485 | @"%@ --booted --biometricMatch", 486 | @"%@ --booted --setLocation \"[51.51915, -0.12907]\"" 487 | ]); 488 | 489 | LNUsageSetOptions(@[ 490 | [LNUsageOption optionWithName:@"byId" shortcut:@"id" valueRequirement:LNUsageOptionRequirementRequired description:@"Filters simulators by unique device identifier (UDID)"], 491 | [LNUsageOption optionWithName:@"byName" shortcut:@"n" valueRequirement:LNUsageOptionRequirementRequired description:@"Filters simulators by name"], 492 | [LNUsageOption optionWithName:@"byType" shortcut:@"t" valueRequirement:LNUsageOptionRequirementRequired description:@"Filters simulators by device type"], 493 | [LNUsageOption optionWithName:@"byOS" shortcut:@"o" valueRequirement:LNUsageOptionRequirementRequired description:@"Filters simulators by operating system"], 494 | [LNUsageOption optionWithName:@"booted" shortcut:@"bt" valueRequirement:LNUsageOptionRequirementNone description:@"Filters simulators by booted status"], 495 | LNUsageOption.emptyOption, 496 | [LNUsageOption optionWithName:@"list" shortcut:@"l" valueRequirement:LNUsageOptionRequirementOptional description:@"Lists available simulators"], 497 | [LNUsageOption optionWithName:@"bundle" shortcut:@"b" valueRequirement:LNUsageOptionRequirementRequired description:@"The app bundle identifier"], 498 | [LNUsageOption optionWithName:@"maxResults" valueRequirement:LNUsageOptionRequirementRequired description:@"Limits the number of results returned from --list"], 499 | [LNUsageOption optionWithName:@"fields" valueRequirement:LNUsageOptionRequirementRequired description:@"Comma-separated list of fields to include in --list output (e.g. \"udid,os,identifier\")"], 500 | LNUsageOption.emptyOption, 501 | [LNUsageOption optionWithName:@"setPermissions" shortcut:@"sp" valueRequirement:LNUsageOptionRequirementRequired description:@"Sets the specified permissions and restarts SpringBoard for the changes to take effect"], 502 | [LNUsageOption optionWithName:@"clearKeychain" shortcut:@"ck" valueRequirement:LNUsageOptionRequirementNone description:@"Clears the simulator's keychain"], 503 | [LNUsageOption optionWithName:@"clearMedia" shortcut:@"cm" valueRequirement:LNUsageOptionRequirementNone description:@"Clears the simulator's media"], 504 | [LNUsageOption optionWithName:@"restartSB" shortcut:@"sb" valueRequirement:LNUsageOptionRequirementNone description:@"Restarts SpringBoard"], 505 | LNUsageOption.emptyOption, 506 | [LNUsageOption optionWithName:@"biometricEnrollment" shortcut:@"be" valueRequirement:LNUsageOptionRequirementRequired description:@"Enables or disables biometric (Face ID/Touch ID) enrollment"], 507 | [LNUsageOption optionWithName:@"biometricMatch" shortcut:@"bm" valueRequirement:LNUsageOptionRequirementNone description:@"Approves a biometric authentication request with a matching biometric feature (e.g. face or finger)"], 508 | [LNUsageOption optionWithName:@"biometricNonmatch" shortcut:@"bnm" valueRequirement:LNUsageOptionRequirementNone description:@"Fails a biometric authentication request with a non-matching biometric feature (e.g. face or finger)"], 509 | LNUsageOption.emptyOption, 510 | [LNUsageOption optionWithName:@"setLocation" shortcut:@"sl" valueRequirement:LNUsageOptionRequirementRequired description:@"Sets the simulated location; the latitude and longitude should be provided as two numbers in JSON array, or \"none\" to clear the simulated location"], 511 | LNUsageOption.emptyOption, 512 | [LNUsageOption optionWithName:@"version" shortcut:@"v" valueRequirement:LNUsageOptionRequirementNone description:@"Prints version"], 513 | ]); 514 | 515 | LNUsageSetHiddenOptions(@[ 516 | [LNUsageOption optionWithName:@"byID" valueRequirement:LNUsageOptionRequirementRequired description:@"Filters simulators by unique device identifier (UDID)"], 517 | [LNUsageOption optionWithName:@"byUDID" valueRequirement:LNUsageOptionRequirementRequired description:@"Filters simulators by unique device identifier (UDID)"], 518 | 519 | [LNUsageOption optionWithName:@"matchFace" shortcut:@"mf" valueRequirement:LNUsageOptionRequirementNone description:@"Approves a Face ID authentication request with a matching face"], 520 | [LNUsageOption optionWithName:@"unmatchFace" shortcut:@"uf" valueRequirement:LNUsageOptionRequirementNone description:@"Fails a Face ID authentication request with a non-matching face"], 521 | [LNUsageOption optionWithName:@"matchFinger" valueRequirement:LNUsageOptionRequirementNone description:@"Approves a Touch ID authentication request with a matching finger"], 522 | [LNUsageOption optionWithName:@"unmatchFinger" valueRequirement:LNUsageOptionRequirementNone description:@"Fails a Touch ID authentication request with a non-matching finger"], 523 | [LNUsageOption optionWithName:@"paths" shortcut:@"p" valueRequirement:LNUsageOptionRequirementOptional description:@"Prints important paths for the selected simulator"], 524 | ]); 525 | 526 | LNUsageSetAdditionalTopics(@[ 527 | @{ 528 | @"Available Permissions": 529 | @[ 530 | @"calendar=YES|NO|unset", 531 | @"camera=YES|NO|unset", 532 | @"contacts=YES|NO|unset", 533 | @"faceid=YES|NO|unset", 534 | @"health=YES|NO|unset (iOS/tvOS 12.0 and above)", 535 | @"homekit=YES|NO|unset", 536 | @"location=always|inuse|never|unset", 537 | @"medialibrary=YES|NO|unset", 538 | @"microphone=YES|NO|unset", 539 | @"motion=YES|NO|unset", 540 | @"notifications=YES|NO|critical|unset", 541 | @"photos=YES|NO|limited|unset (“limited” supported on iOS/tvOS 14.0 and above)", 542 | @"reminders=YES|NO|unset", 543 | @"siri=YES|NO|unset", 544 | @"speech=YES|NO|unset", 545 | @"userTracking=YES|NO|unset (iOS/tvOS 14.0 and above)" 546 | ] 547 | }]); 548 | 549 | LNUsageSetAdditionalStrings(@[ 550 | @"", 551 | @"For more features, open an issue at https://github.com/wix/AppleSimulatorUtils", 552 | @"Pull-requests are always welcome!" 553 | ]); 554 | 555 | id settings = LNUsageParseArguments(argc, argv); 556 | 557 | if(![settings boolForKey:@"version"] && 558 | ![settings objectForKey:@"setPermissions"] && 559 | ![settings boolForKey:@"restartSB"] && 560 | ![settings boolForKey:@"clearKeychain"] && 561 | ![settings boolForKey:@"clearMedia"] && 562 | ![settings objectForKey:@"list"] && 563 | ![settings objectForKey:@"paths"] && 564 | ![settings objectForKey:@"biometricEnrollment"] && 565 | ![settings boolForKey:@"biometricMatch"] && 566 | ![settings boolForKey:@"biometricNonmatch"] && 567 | ![settings boolForKey:@"matchFace"] && 568 | ![settings boolForKey:@"unmatchFace"] && 569 | ![settings boolForKey:@"matchFinger"] && 570 | ![settings boolForKey:@"unmatchFinger"] && 571 | ![settings objectForKey:@"setLocation"] 572 | ) 573 | { 574 | LNUsagePrintMessage(nil, LNLogLevelStdOut); 575 | exit(-1); 576 | } 577 | 578 | if([settings boolForKey:@"version"]) 579 | { 580 | LNLog(LNLogLevelStdOut, @"%@ version %s", NSProcessInfo.processInfo.arguments.firstObject.lastPathComponent, __version); 581 | exit(0); 582 | } 583 | 584 | @try 585 | { 586 | NSArray* simulatorDevices = simulatorDevicesList(); 587 | 588 | if(simulatorDevices == nil) 589 | { 590 | LNUsagePrintMessage(@"Error: Unable to obtain a list of simulators.", LNLogLevelError); 591 | exit(-1); 592 | } 593 | 594 | NSPredicate* filter = nil; 595 | 596 | NSString* udid = [settings objectForKey:@"byId"] ?: [settings objectForKey:@"byID"] ?: [settings objectForKey:@"byUDID"]; 597 | if(udid) 598 | { 599 | NSPredicate* predicate = predicateById(udid); 600 | filter = predicateByAppendingOrCreatingPredicate(filter, predicate); 601 | } 602 | if([settings objectForKey:@"booted"]) 603 | { 604 | NSPredicate* predicate = predicateByBooted(); 605 | filter = predicateByAppendingOrCreatingPredicate(filter, predicate); 606 | } 607 | if([settings objectForKey:@"byName"]) 608 | { 609 | NSString* fStr = [settings objectForKey:@"byName"]; 610 | NSPredicate* predicate = predicateByName(fStr); 611 | filter = predicateByAppendingOrCreatingPredicate(filter, predicate); 612 | } 613 | if([settings objectForKey:@"byType"]) 614 | { 615 | NSString* fStr = [settings objectForKey:@"byType"]; 616 | NSPredicate* predicate = predicateByType(fStr); 617 | filter = predicateByAppendingOrCreatingPredicate(filter, predicate); 618 | } 619 | if([settings objectForKey:@"byOS"]) 620 | { 621 | NSString* fStr = [settings objectForKey:@"byOS"]; 622 | NSPredicate* predicate = predicateByOS(fStr); 623 | filter = predicateByAppendingOrCreatingPredicate(filter, predicate); 624 | } 625 | 626 | NSArray* filteredSimulators = filteredDeviceList(simulatorDevices, filter); 627 | 628 | if([settings objectForKey:@"list"] != nil) 629 | { 630 | if(filteredSimulators == nil) 631 | { 632 | LNUsagePrintMessage(@"Error: Unable to filter simulators.", LNLogLevelError); 633 | } 634 | 635 | NSUInteger maxResults = NSUIntegerMax; 636 | if([settings objectForKey:@"maxResults"]) 637 | { 638 | maxResults = [settings unsignedIntegerForKey:@"maxResults"]; 639 | } 640 | 641 | if(maxResults < 1) 642 | { 643 | LNUsagePrintMessage(@"Error: Invalid value for --maxResults.", LNLogLevelError); 644 | } 645 | 646 | if(maxResults != NSUIntegerMax) 647 | { 648 | filteredSimulators = [filteredSimulators subarrayWithRange:NSMakeRange(0, MIN(filteredSimulators.count, maxResults))]; 649 | } 650 | 651 | // Apply field filtering if --fields option is provided 652 | NSArray* compactedSimulators = filteredSimulators; 653 | NSString* fieldsParam = [settings objectForKey:@"fields"]; 654 | if(fieldsParam != nil && fieldsParam.length > 0) 655 | { 656 | NSArray* fieldNames = [fieldsParam componentsSeparatedByString:@","]; 657 | NSMutableSet* allowedKeys = [NSMutableSet new]; 658 | [fieldNames enumerateObjectsUsingBlock:^(NSString* fieldName, NSUInteger idx, BOOL* stop) { 659 | NSString* trimmedField = [fieldName stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; 660 | if(trimmedField.length > 0) 661 | { 662 | [allowedKeys addObject:trimmedField]; 663 | } 664 | }]; 665 | 666 | if(allowedKeys.count > 0) 667 | { 668 | compactedSimulators = filterObject(filteredSimulators, allowedKeys); 669 | } 670 | } 671 | 672 | LNLog(LNLogLevelStdOut, @"%@", [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:compactedSimulators options:NSJSONWritingPrettyPrinted error:NULL] encoding:NSUTF8StringEncoding]); 673 | 674 | exit(0); 675 | } 676 | 677 | if(filteredSimulators.count == 0) 678 | { 679 | if(filter == nil) 680 | { 681 | LNUsagePrintMessage([NSString stringWithFormat:@"Error: No simulator found."], LNLogLevelError); 682 | } 683 | else 684 | { 685 | LNUsagePrintMessage([NSString stringWithFormat:@"Error: No simulator found matching “%@”.", filter], LNLogLevelError); 686 | } 687 | 688 | exit(-1); 689 | } 690 | 691 | if([settings objectForKey:@"paths"] != nil) 692 | { 693 | [filteredSimulators enumerateObjectsUsingBlock:^(NSDictionary* _Nonnull simulator, NSUInteger idx, BOOL * _Nonnull stop) { 694 | NSString* simulatorId = simulator[@"udid"]; 695 | NSURL *runtimeBundleURL = [NSURL fileURLWithPath:simulator[@"os"][@"bundlePath"]]; 696 | 697 | NSString* title = [NSString stringWithFormat:@"%@ (%@, %@)", simulator[@"name"], simulatorId, simulator[@"state"]]; 698 | NSString* underline = [@"" stringByPaddingToLength:title.length withString:@"-" startingAtIndex:0]; 699 | LNLog(LNLogLevelStdOut, @"%@\n%@", title, underline); 700 | 701 | NSURL* url = [SimUtils URLForSimulatorId:simulatorId]; 702 | if(url.path) 703 | { 704 | LNLog(LNLogLevelStdOut, @"Path: %@", url.path); 705 | } 706 | 707 | NSMutableDictionary* simPaths = [NSMutableDictionary new]; 708 | 709 | url = [SimUtils libraryURLForSimulatorId:simulatorId]; 710 | if(url.path) 711 | { 712 | simPaths[@"Library Path"] = url.path; 713 | 714 | NSFileManager *fileManager = [NSFileManager defaultManager]; 715 | NSString* sectionInfoPath = [url.path stringByAppendingPathComponent:@"BulletinBoard/SectionInfo.plist"]; 716 | if([fileManager fileExistsAtPath:sectionInfoPath]) 717 | { 718 | simPaths[@"BulletinBoard Section Info Plist Path"] = sectionInfoPath; 719 | } 720 | else 721 | { 722 | NSString* versionedSectionInfoPath = [url.path stringByAppendingPathComponent:@"BulletinBoard/VersionedSectionInfo.plist"]; 723 | if([fileManager fileExistsAtPath:versionedSectionInfoPath]) 724 | { 725 | simPaths[@"BulletinBoard Versioned Section Info Plist Path"] = versionedSectionInfoPath; 726 | } 727 | } 728 | 729 | simPaths[@"TCC Database Path"] = [url URLByAppendingPathComponent:@"TCC/TCC.db"].path; 730 | } 731 | 732 | url = [SetLocationPermission locationdURLForRuntimeBundleURL:runtimeBundleURL]; 733 | if(url.path != nil) 734 | { 735 | simPaths[@"locationd Daemon Info Plist Path"] = url.path; 736 | } 737 | 738 | url = securitydURL(runtimeBundleURL); 739 | if(url.path != nil) 740 | { 741 | simPaths[@"securityd Daemon Info Plist Path"] = url.path; 742 | } 743 | 744 | url = [SetHealthKitPermission healthdbURLForSimulatorId:simulatorId osVersion:operatingSystemFromSimulator(simulator)]; 745 | if(url.path != nil) 746 | { 747 | simPaths[@"Health Database Path"] = url.path; 748 | } 749 | 750 | NSString* bundleId = [settings objectForKey:@"bundle"]; 751 | if(bundleId != nil && (url = [SimUtils binaryURLForBundleId:bundleId simulatorId:simulatorId]).path != nil) 752 | { 753 | simPaths[@"App Binary Path"] = url.path; 754 | } 755 | 756 | NSArray* keys = [simPaths.allKeys sortedArrayUsingSelector:@selector(compare:)]; 757 | [keys enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 758 | LNLog(LNLogLevelStdOut, @"%@: %@", obj, simPaths[obj]); 759 | }]; 760 | 761 | LNLog(LNLogLevelStdOut, @"\n"); 762 | }]; 763 | 764 | exit(0); 765 | } 766 | 767 | NSString* setLocationParam = [settings objectForKey:@"setLocation"]; 768 | if(setLocationParam != nil && filteredSimulators.count > 0) 769 | { 770 | NSArray* simulatorUDIDs = [filteredSimulators valueForKey:@"udid"]; 771 | 772 | if([setLocationParam isKindOfClass:NSString.class] && [setLocationParam isEqualToString:@"none"]) 773 | { 774 | [SetSimulatorLocation clearLocationForSimulatorUDIDs:simulatorUDIDs]; 775 | } 776 | else 777 | { 778 | NSError* jsonError = nil; 779 | NSArray* locationArgs = [NSJSONSerialization JSONObjectWithData:[setLocationParam dataUsingEncoding:NSUTF8StringEncoding] options:0 error:&jsonError]; 780 | if(jsonError != nil) 781 | { 782 | LNUsagePrintMessage([NSString stringWithFormat:@"Error: Unable to parse location JSON: %@.", jsonError.localizedDescription], LNLogLevelError); 783 | 784 | exit(-2); 785 | } 786 | if(locationArgs.count != 2) 787 | { 788 | LNUsagePrintMessage(@"Error: Invalid number of arguments in JSON.", LNLogLevelError); 789 | 790 | exit(-2); 791 | } 792 | [SetSimulatorLocation setLatitude:[locationArgs.firstObject doubleValue] longitude:[locationArgs.lastObject doubleValue] forSimulatorUDIDs:simulatorUDIDs]; 793 | } 794 | } 795 | 796 | [filteredSimulators enumerateObjectsUsingBlock:^(NSDictionary* _Nonnull simulator, NSUInteger idx, BOOL * _Nonnull stop) { 797 | NSString* simulatorId = simulator[@"udid"]; 798 | NSURL *runtimeBundleURL = [NSURL fileURLWithPath:simulator[@"os"][@"bundlePath"]]; 799 | 800 | BOOL needsSimShutdown = NO; 801 | if([simulator[@"state"] isEqualToString:@"Shutdown"] && [settings objectForKey:@"setPermissions"] != nil) 802 | { 803 | needsSimShutdown = YES; 804 | 805 | bootSimulator(simulatorId); 806 | } 807 | 808 | BOOL needsSpringBoardRestart = NO; 809 | 810 | NSString* permissions = [settings objectForKey:@"setPermissions"]; 811 | if(permissions != nil) 812 | { 813 | NSString* bundleId = [settings objectForKey:@"bundle"]; 814 | if(bundleId.length == 0) 815 | { 816 | LNUsagePrintMessage(@"Error: No app bundle identifier provided.", LNLogLevelError); 817 | 818 | exit(-2); 819 | } 820 | 821 | needsSpringBoardRestart = performPermissionsPass(permissions, simulatorId, bundleId, simulator); 822 | } 823 | 824 | if([settings boolForKey:@"clearKeychain"]) 825 | { 826 | performClearKeychainPass(simulatorId, runtimeBundleURL); 827 | 828 | needsSpringBoardRestart = YES; 829 | } 830 | 831 | if([settings boolForKey:@"clearMedia"]) 832 | { 833 | performClearMediaPass(simulatorId, runtimeBundleURL); 834 | 835 | needsSpringBoardRestart = YES; 836 | } 837 | 838 | if([settings boolForKey:@"restartSB"]) 839 | { 840 | needsSpringBoardRestart = YES; 841 | } 842 | 843 | NSString* biometricEnrollment = [settings objectForKey:@"biometricEnrollment"]; 844 | if(biometricEnrollment) 845 | { 846 | assertStringInArrayValues(biometricEnrollment, @[@"YES", @"NO"], -10, [NSString stringWithFormat:@"Error: Value “%@” cannot be parsed for biometricEnrollment; expected YES|NO.", biometricEnrollment]); 847 | 848 | setBiometricEnrollment(simulatorId, [biometricEnrollment boolValue]); 849 | } 850 | 851 | if([settings boolForKey:@"biometricMatch"]) 852 | { 853 | sendBiometricMatch(simulatorId, ASUBiometricTypeFace, YES); 854 | } 855 | if([settings boolForKey:@"biometricNonmatch"]) 856 | { 857 | sendBiometricMatch(simulatorId, ASUBiometricTypeFace, NO); 858 | } 859 | if([settings boolForKey:@"matchFace"]) 860 | { 861 | sendBiometricMatch(simulatorId, ASUBiometricTypeFace, YES); 862 | } 863 | if([settings boolForKey:@"unmatchFace"]) 864 | { 865 | sendBiometricMatch(simulatorId, ASUBiometricTypeFace, NO); 866 | } 867 | if([settings boolForKey:@"matchFinger"]) 868 | { 869 | sendBiometricMatch(simulatorId, ASUBiometricTypeFinger, YES); 870 | } 871 | if([settings boolForKey:@"unmatchFinger"]) 872 | { 873 | sendBiometricMatch(simulatorId, ASUBiometricTypeFinger, NO); 874 | } 875 | 876 | if(needsSpringBoardRestart == YES && needsSimShutdown == NO) 877 | { 878 | [SimUtils restartSpringBoardForSimulatorId:simulatorId]; 879 | } 880 | 881 | if(needsSimShutdown == YES) 882 | { 883 | shutdownSimulator(simulatorId); 884 | } 885 | }]; 886 | } 887 | @catch (NSException *exception) 888 | { 889 | LNUsagePrintMessage([NSString stringWithFormat:@"%@.", [exception.reason stringByReplacingCharactersInRange:NSMakeRange(0,1) withString:[[exception.reason substringToIndex:1] capitalizedString]]], LNLogLevelError); 890 | exit(-1); 891 | } 892 | } 893 | exit(0); 894 | } 895 | -------------------------------------------------------------------------------- /applesimutils/applesimutils/version.h: -------------------------------------------------------------------------------- 1 | "0.9.10" 2 | -------------------------------------------------------------------------------- /buildForBrew.sh: -------------------------------------------------------------------------------- 1 | targetDir="$1" 2 | 3 | echo "\n\n\033[1;33mIf you see issues while installing applesimutils, make sure to update your Xcode Commandline Tools from System Preferences\033[0m\n\n" 4 | 5 | export CODE_SIGNING_REQUIRED=NO && xcodebuild clean build -project applesimutils/applesimutils.xcodeproj -scheme applesimutils -configuration Release -derivedDataPath ./build BUILD_DIR=../build/Build/Products 6 | mkdir -p "$targetDir"/bin 7 | cp build/Build/Products/Release/applesimutils "$targetDir"/bin 8 | -------------------------------------------------------------------------------- /releaseVersion.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | ### User running this script must have git permission, and have https://cli.github.com/ installed and authenticated. 5 | 6 | # Assumes gh is installed and logged in 7 | 8 | if [ "$#" -ne 1 ]; then 9 | echo -e >&2 "\033[1;31mIllegal number of parameters\033[0m" 10 | echo -e >&2 "\033[1;31mreleaseVersion.sh \033[0m" 11 | exit -1 12 | fi 13 | 14 | if [[ -n $(git status --porcelain) ]]; then 15 | echo -e >&2 "\033[1;31mCannot release version because there are unstaged changes:\033[0m" 16 | git status --short 17 | exit -2 18 | fi 19 | 20 | if [[ -n $(git tag --contains $(git rev-parse --verify HEAD)) ]]; then 21 | echo -e >&2 "\033[1;31mThe latest commit is already contained in the following releases:\033[0m" 22 | git tag --contains $(git rev-parse --verify HEAD) 23 | exit -3 24 | fi 25 | 26 | if [[ -n $(git log --branches --not --remotes) ]]; then 27 | echo -e "\033[1;34mPushing commits to git\033[0m" 28 | git push 29 | fi 30 | 31 | echo -e "\033[1;34mCreating release notes\033[0m" 32 | 33 | RELEASE_NOTES_FILE=_tmp_release_notes.md 34 | 35 | touch "${RELEASE_NOTES_FILE}" 36 | open -Wn "${RELEASE_NOTES_FILE}" 37 | 38 | if ! [ -s "${RELEASE_NOTES_FILE}" ]; then 39 | echo -e >&2 "\033[1;31mNo release notes provided, aborting.\033[0m" 40 | rm -f "${RELEASE_NOTES_FILE}" 41 | exit -1 42 | fi 43 | 44 | echo -e "\033[1;34mCreating commit for version\033[0m" 45 | 46 | VERSION="$1" 47 | 48 | echo "\"${VERSION}\"" > applesimutils/applesimutils/version.h 49 | 50 | echo -e "\033[1;34mCreating a compressed tarball of the source\033[0m" 51 | 52 | SRC_TGZ_FILE="AppleSimulatorUtils-${VERSION}.tar.gz" 53 | 54 | mkdir -p build 55 | tar --exclude="releaseVersion.sh" --exclude=".git" --exclude="build" --exclude="bottle" --exclude "_tmp_release_notes.md" --exclude=".github" --exclude="homebrew-brew" -cvzf "build/${SRC_TGZ_FILE}" . 56 | 57 | echo -e "\033[1;34mCreating Homebrew bottles" 58 | 59 | rm -fr bottle 60 | BOTTLE_DIR="bottle/applesimutils/${VERSION}/" 61 | mkdir -p "${BOTTLE_DIR}" 62 | ./buildForBrew.sh "${BOTTLE_DIR}" 63 | pushd . 64 | cd bottle 65 | 66 | BOTTLES=( "catalina" "mojave" "high_sierra" "sierra" "big_sur" "arm64_big_sur" ) 67 | for BOTTLE in "${BOTTLES[@]}" 68 | do 69 | BOTTLE_TGZ_FILE="applesimutils-${VERSION}.${BOTTLE}.bottle.tar.gz" 70 | tar -cvzf "${BOTTLE_TGZ_FILE}" applesimutils 71 | done 72 | 73 | popd 74 | 75 | echo -e "\033[1;34mUpdating applesimutils.rb with latest hashes\033[0m" 76 | 77 | cd homebrew-brew 78 | 79 | git checkout master 80 | git fetch 81 | git pull --rebase 82 | sed -i '' -e 's/^\ \ url .*/\ \ url '"'https:\/\/github.com\/wix\/AppleSimulatorUtils\/releases\/download\/${VERSION}\/${SRC_TGZ_FILE}'"'/g' Formula/applesimutils.rb 83 | sed -i '' -e 's/^\ \ \ \ root\_url .*/\ \ \ \ root\_url '"'https:\/\/github.com\/wix\/AppleSimulatorUtils\/releases\/download\/${VERSION}'"'/g' Formula/applesimutils.rb 84 | sed -i '' -e 's/^\ \ sha256 .*/\ \ sha256 '"'"$(shasum -b -a 256 ../build/${SRC_TGZ_FILE} | awk '{ print $1 }')"'"'/g' Formula/applesimutils.rb 85 | 86 | for BOTTLE in "${BOTTLES[@]}" 87 | do 88 | BOTTLE_TGZ_FILE="applesimutils-${VERSION}.${BOTTLE}.bottle.tar.gz" 89 | sed -i '' -e "s/^ sha256 .* => :${BOTTLE}/ sha256 '$(shasum -b -a 256 ../bottle/${BOTTLE_TGZ_FILE} | awk '{ print $1 }')' => :${BOTTLE}/g" Formula/applesimutils.rb 90 | done 91 | 92 | git add -A 93 | git commit -m "Apple Simulator Utils ${VERSION}" 94 | git push 95 | 96 | cd .. 97 | 98 | echo -e "\033[1;34mPushing changes to AppleSimUtils\033[0m" 99 | 100 | git add -A 101 | git commit -m "${VERSION}" 102 | git tag "${VERSION}" 103 | 104 | git push 105 | git push --tags 106 | 107 | echo -e "\033[1;34mCreating a GitHub release\033[0m" 108 | 109 | gh release create --repo wix/AppleSimulatorUtils "$VERSION" --title "$VERSION" --notes-file "${RELEASE_NOTES_FILE}" 110 | 111 | echo -e "\033[1;34mUploading attachments to release\033[0m" 112 | 113 | gh release upload --repo wix/AppleSimulatorUtils "$VERSION" "build/${SRC_TGZ_FILE}#$(basename ${SRC_TGZ_FILE})" 114 | 115 | for BOTTLE in "${BOTTLES[@]}" 116 | do 117 | BOTTLE_TGZ_FILE="applesimutils-${VERSION}.${BOTTLE}.bottle.tar.gz" 118 | gh release upload --repo wix/AppleSimulatorUtils "$VERSION" "bottle/${BOTTLE_TGZ_FILE}#$(basename ${BOTTLE_TGZ_FILE})" 119 | done 120 | 121 | rm -fr build 122 | rm -fr bottle 123 | rm -f "${RELEASE_NOTES_FILE}" -------------------------------------------------------------------------------- /sample/PermissionsTest/PermissionsTest.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 39BEA8F922B7946000B2F24E /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 39BEA8F822B7946000B2F24E /* HealthKit.framework */; }; 11 | 39DABBA921BD1784002653EE /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 39DABBA821BD1784002653EE /* AppDelegate.m */; }; 12 | 39DABBAC21BD1784002653EE /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 39DABBAB21BD1784002653EE /* ViewController.m */; }; 13 | 39DABBAF21BD1784002653EE /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 39DABBAD21BD1784002653EE /* Main.storyboard */; }; 14 | 39DABBB121BD1786002653EE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 39DABBB021BD1786002653EE /* Assets.xcassets */; }; 15 | 39DABBB421BD1786002653EE /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 39DABBB221BD1786002653EE /* LaunchScreen.storyboard */; }; 16 | 39DABBB721BD1786002653EE /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 39DABBB621BD1786002653EE /* main.m */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | 39BEA8F622B7946000B2F24E /* PermissionsTest.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = PermissionsTest.entitlements; sourceTree = ""; }; 21 | 39BEA8F822B7946000B2F24E /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = System/Library/Frameworks/HealthKit.framework; sourceTree = SDKROOT; }; 22 | 39DABBA421BD1784002653EE /* PermissionsTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PermissionsTest.app; sourceTree = BUILT_PRODUCTS_DIR; }; 23 | 39DABBA721BD1784002653EE /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 24 | 39DABBA821BD1784002653EE /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 25 | 39DABBAA21BD1784002653EE /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 26 | 39DABBAB21BD1784002653EE /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 27 | 39DABBAE21BD1784002653EE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 28 | 39DABBB021BD1786002653EE /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 29 | 39DABBB321BD1786002653EE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 30 | 39DABBB521BD1786002653EE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 31 | 39DABBB621BD1786002653EE /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 32 | /* End PBXFileReference section */ 33 | 34 | /* Begin PBXFrameworksBuildPhase section */ 35 | 39DABBA121BD1784002653EE /* Frameworks */ = { 36 | isa = PBXFrameworksBuildPhase; 37 | buildActionMask = 2147483647; 38 | files = ( 39 | 39BEA8F922B7946000B2F24E /* HealthKit.framework in Frameworks */, 40 | ); 41 | runOnlyForDeploymentPostprocessing = 0; 42 | }; 43 | /* End PBXFrameworksBuildPhase section */ 44 | 45 | /* Begin PBXGroup section */ 46 | 39BEA8F722B7946000B2F24E /* Frameworks */ = { 47 | isa = PBXGroup; 48 | children = ( 49 | 39BEA8F822B7946000B2F24E /* HealthKit.framework */, 50 | ); 51 | name = Frameworks; 52 | sourceTree = ""; 53 | }; 54 | 39DABB9B21BD1784002653EE = { 55 | isa = PBXGroup; 56 | children = ( 57 | 39DABBA621BD1784002653EE /* PermissionsTest */, 58 | 39DABBA521BD1784002653EE /* Products */, 59 | 39BEA8F722B7946000B2F24E /* Frameworks */, 60 | ); 61 | sourceTree = ""; 62 | }; 63 | 39DABBA521BD1784002653EE /* Products */ = { 64 | isa = PBXGroup; 65 | children = ( 66 | 39DABBA421BD1784002653EE /* PermissionsTest.app */, 67 | ); 68 | name = Products; 69 | sourceTree = ""; 70 | }; 71 | 39DABBA621BD1784002653EE /* PermissionsTest */ = { 72 | isa = PBXGroup; 73 | children = ( 74 | 39BEA8F622B7946000B2F24E /* PermissionsTest.entitlements */, 75 | 39DABBA721BD1784002653EE /* AppDelegate.h */, 76 | 39DABBA821BD1784002653EE /* AppDelegate.m */, 77 | 39DABBAA21BD1784002653EE /* ViewController.h */, 78 | 39DABBAB21BD1784002653EE /* ViewController.m */, 79 | 39DABBAD21BD1784002653EE /* Main.storyboard */, 80 | 39DABBB021BD1786002653EE /* Assets.xcassets */, 81 | 39DABBB221BD1786002653EE /* LaunchScreen.storyboard */, 82 | 39DABBB521BD1786002653EE /* Info.plist */, 83 | 39DABBB621BD1786002653EE /* main.m */, 84 | ); 85 | path = PermissionsTest; 86 | sourceTree = ""; 87 | }; 88 | /* End PBXGroup section */ 89 | 90 | /* Begin PBXNativeTarget section */ 91 | 39DABBA321BD1784002653EE /* PermissionsTest */ = { 92 | isa = PBXNativeTarget; 93 | buildConfigurationList = 39DABBBA21BD1786002653EE /* Build configuration list for PBXNativeTarget "PermissionsTest" */; 94 | buildPhases = ( 95 | 39DABBA021BD1784002653EE /* Sources */, 96 | 39DABBA121BD1784002653EE /* Frameworks */, 97 | 39DABBA221BD1784002653EE /* Resources */, 98 | 39DABBBD21BD1D71002653EE /* AppleSimUtils */, 99 | ); 100 | buildRules = ( 101 | ); 102 | dependencies = ( 103 | ); 104 | name = PermissionsTest; 105 | productName = PermissionsTest; 106 | productReference = 39DABBA421BD1784002653EE /* PermissionsTest.app */; 107 | productType = "com.apple.product-type.application"; 108 | }; 109 | /* End PBXNativeTarget section */ 110 | 111 | /* Begin PBXProject section */ 112 | 39DABB9C21BD1784002653EE /* Project object */ = { 113 | isa = PBXProject; 114 | attributes = { 115 | LastUpgradeCheck = 1010; 116 | ORGANIZATIONNAME = "Leo Natan"; 117 | TargetAttributes = { 118 | 39DABBA321BD1784002653EE = { 119 | CreatedOnToolsVersion = 10.1; 120 | }; 121 | }; 122 | }; 123 | buildConfigurationList = 39DABB9F21BD1784002653EE /* Build configuration list for PBXProject "PermissionsTest" */; 124 | compatibilityVersion = "Xcode 9.3"; 125 | developmentRegion = en; 126 | hasScannedForEncodings = 0; 127 | knownRegions = ( 128 | en, 129 | Base, 130 | ); 131 | mainGroup = 39DABB9B21BD1784002653EE; 132 | productRefGroup = 39DABBA521BD1784002653EE /* Products */; 133 | projectDirPath = ""; 134 | projectRoot = ""; 135 | targets = ( 136 | 39DABBA321BD1784002653EE /* PermissionsTest */, 137 | ); 138 | }; 139 | /* End PBXProject section */ 140 | 141 | /* Begin PBXResourcesBuildPhase section */ 142 | 39DABBA221BD1784002653EE /* Resources */ = { 143 | isa = PBXResourcesBuildPhase; 144 | buildActionMask = 2147483647; 145 | files = ( 146 | 39DABBB421BD1786002653EE /* LaunchScreen.storyboard in Resources */, 147 | 39DABBB121BD1786002653EE /* Assets.xcassets in Resources */, 148 | 39DABBAF21BD1784002653EE /* Main.storyboard in Resources */, 149 | ); 150 | runOnlyForDeploymentPostprocessing = 0; 151 | }; 152 | /* End PBXResourcesBuildPhase section */ 153 | 154 | /* Begin PBXShellScriptBuildPhase section */ 155 | 39DABBBD21BD1D71002653EE /* AppleSimUtils */ = { 156 | isa = PBXShellScriptBuildPhase; 157 | buildActionMask = 2147483647; 158 | files = ( 159 | ); 160 | inputFileListPaths = ( 161 | ); 162 | inputPaths = ( 163 | ); 164 | name = AppleSimUtils; 165 | outputFileListPaths = ( 166 | ); 167 | outputPaths = ( 168 | ); 169 | runOnlyForDeploymentPostprocessing = 0; 170 | shellPath = "/bin/sh -e"; 171 | shellScript = "# applesimutils --byId \"${TARGET_DEVICE_IDENTIFIER}\" --setPermissions \"calendar=YES,location=inuse,notifications=YES\" --bundle com.wix.PermissionsTest\n"; 172 | }; 173 | /* End PBXShellScriptBuildPhase section */ 174 | 175 | /* Begin PBXSourcesBuildPhase section */ 176 | 39DABBA021BD1784002653EE /* Sources */ = { 177 | isa = PBXSourcesBuildPhase; 178 | buildActionMask = 2147483647; 179 | files = ( 180 | 39DABBAC21BD1784002653EE /* ViewController.m in Sources */, 181 | 39DABBB721BD1786002653EE /* main.m in Sources */, 182 | 39DABBA921BD1784002653EE /* AppDelegate.m in Sources */, 183 | ); 184 | runOnlyForDeploymentPostprocessing = 0; 185 | }; 186 | /* End PBXSourcesBuildPhase section */ 187 | 188 | /* Begin PBXVariantGroup section */ 189 | 39DABBAD21BD1784002653EE /* Main.storyboard */ = { 190 | isa = PBXVariantGroup; 191 | children = ( 192 | 39DABBAE21BD1784002653EE /* Base */, 193 | ); 194 | name = Main.storyboard; 195 | sourceTree = ""; 196 | }; 197 | 39DABBB221BD1786002653EE /* LaunchScreen.storyboard */ = { 198 | isa = PBXVariantGroup; 199 | children = ( 200 | 39DABBB321BD1786002653EE /* Base */, 201 | ); 202 | name = LaunchScreen.storyboard; 203 | sourceTree = ""; 204 | }; 205 | /* End PBXVariantGroup section */ 206 | 207 | /* Begin XCBuildConfiguration section */ 208 | 39DABBB821BD1786002653EE /* Debug */ = { 209 | isa = XCBuildConfiguration; 210 | buildSettings = { 211 | ALWAYS_SEARCH_USER_PATHS = NO; 212 | CLANG_ANALYZER_NONNULL = YES; 213 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 214 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 215 | CLANG_CXX_LIBRARY = "libc++"; 216 | CLANG_ENABLE_MODULES = YES; 217 | CLANG_ENABLE_OBJC_ARC = YES; 218 | CLANG_ENABLE_OBJC_WEAK = YES; 219 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 220 | CLANG_WARN_BOOL_CONVERSION = YES; 221 | CLANG_WARN_COMMA = YES; 222 | CLANG_WARN_CONSTANT_CONVERSION = YES; 223 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 224 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 225 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 226 | CLANG_WARN_EMPTY_BODY = YES; 227 | CLANG_WARN_ENUM_CONVERSION = YES; 228 | CLANG_WARN_INFINITE_RECURSION = YES; 229 | CLANG_WARN_INT_CONVERSION = YES; 230 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 231 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 232 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 233 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 234 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 235 | CLANG_WARN_STRICT_PROTOTYPES = YES; 236 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 237 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 238 | CLANG_WARN_UNREACHABLE_CODE = YES; 239 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 240 | CODE_SIGN_IDENTITY = "iPhone Developer"; 241 | COPY_PHASE_STRIP = NO; 242 | DEBUG_INFORMATION_FORMAT = dwarf; 243 | ENABLE_STRICT_OBJC_MSGSEND = YES; 244 | ENABLE_TESTABILITY = YES; 245 | GCC_C_LANGUAGE_STANDARD = gnu11; 246 | GCC_DYNAMIC_NO_PIC = NO; 247 | GCC_NO_COMMON_BLOCKS = YES; 248 | GCC_OPTIMIZATION_LEVEL = 0; 249 | GCC_PREPROCESSOR_DEFINITIONS = ( 250 | "DEBUG=1", 251 | "$(inherited)", 252 | ); 253 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 254 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 255 | GCC_WARN_UNDECLARED_SELECTOR = YES; 256 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 257 | GCC_WARN_UNUSED_FUNCTION = YES; 258 | GCC_WARN_UNUSED_VARIABLE = YES; 259 | IPHONEOS_DEPLOYMENT_TARGET = 12.1; 260 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 261 | MTL_FAST_MATH = YES; 262 | ONLY_ACTIVE_ARCH = YES; 263 | SDKROOT = iphoneos; 264 | }; 265 | name = Debug; 266 | }; 267 | 39DABBB921BD1786002653EE /* Release */ = { 268 | isa = XCBuildConfiguration; 269 | buildSettings = { 270 | ALWAYS_SEARCH_USER_PATHS = NO; 271 | CLANG_ANALYZER_NONNULL = YES; 272 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 273 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 274 | CLANG_CXX_LIBRARY = "libc++"; 275 | CLANG_ENABLE_MODULES = YES; 276 | CLANG_ENABLE_OBJC_ARC = YES; 277 | CLANG_ENABLE_OBJC_WEAK = YES; 278 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 279 | CLANG_WARN_BOOL_CONVERSION = YES; 280 | CLANG_WARN_COMMA = YES; 281 | CLANG_WARN_CONSTANT_CONVERSION = YES; 282 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 283 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 284 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 285 | CLANG_WARN_EMPTY_BODY = YES; 286 | CLANG_WARN_ENUM_CONVERSION = YES; 287 | CLANG_WARN_INFINITE_RECURSION = YES; 288 | CLANG_WARN_INT_CONVERSION = YES; 289 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 290 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 291 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 292 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 293 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 294 | CLANG_WARN_STRICT_PROTOTYPES = YES; 295 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 296 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 297 | CLANG_WARN_UNREACHABLE_CODE = YES; 298 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 299 | CODE_SIGN_IDENTITY = "iPhone Developer"; 300 | COPY_PHASE_STRIP = NO; 301 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 302 | ENABLE_NS_ASSERTIONS = NO; 303 | ENABLE_STRICT_OBJC_MSGSEND = YES; 304 | GCC_C_LANGUAGE_STANDARD = gnu11; 305 | GCC_NO_COMMON_BLOCKS = YES; 306 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 307 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 308 | GCC_WARN_UNDECLARED_SELECTOR = YES; 309 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 310 | GCC_WARN_UNUSED_FUNCTION = YES; 311 | GCC_WARN_UNUSED_VARIABLE = YES; 312 | IPHONEOS_DEPLOYMENT_TARGET = 12.1; 313 | MTL_ENABLE_DEBUG_INFO = NO; 314 | MTL_FAST_MATH = YES; 315 | SDKROOT = iphoneos; 316 | VALIDATE_PRODUCT = YES; 317 | }; 318 | name = Release; 319 | }; 320 | 39DABBBB21BD1786002653EE /* Debug */ = { 321 | isa = XCBuildConfiguration; 322 | buildSettings = { 323 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 324 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = NO; 325 | CODE_SIGN_ENTITLEMENTS = PermissionsTest/PermissionsTest.entitlements; 326 | CODE_SIGN_STYLE = Manual; 327 | DEVELOPMENT_TEAM = ""; 328 | INFOPLIST_FILE = PermissionsTest/Info.plist; 329 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 330 | LD_RUNPATH_SEARCH_PATHS = ( 331 | "$(inherited)", 332 | "@executable_path/Frameworks", 333 | ); 334 | PRODUCT_BUNDLE_IDENTIFIER = com.wix.PermissionsTest; 335 | PRODUCT_NAME = "$(TARGET_NAME)"; 336 | PROVISIONING_PROFILE_SPECIFIER = ""; 337 | TARGETED_DEVICE_FAMILY = "1,2"; 338 | }; 339 | name = Debug; 340 | }; 341 | 39DABBBC21BD1786002653EE /* Release */ = { 342 | isa = XCBuildConfiguration; 343 | buildSettings = { 344 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 345 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = NO; 346 | CODE_SIGN_ENTITLEMENTS = PermissionsTest/PermissionsTest.entitlements; 347 | CODE_SIGN_STYLE = Manual; 348 | DEVELOPMENT_TEAM = ""; 349 | INFOPLIST_FILE = PermissionsTest/Info.plist; 350 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 351 | LD_RUNPATH_SEARCH_PATHS = ( 352 | "$(inherited)", 353 | "@executable_path/Frameworks", 354 | ); 355 | PRODUCT_BUNDLE_IDENTIFIER = com.wix.PermissionsTest; 356 | PRODUCT_NAME = "$(TARGET_NAME)"; 357 | PROVISIONING_PROFILE_SPECIFIER = ""; 358 | TARGETED_DEVICE_FAMILY = "1,2"; 359 | }; 360 | name = Release; 361 | }; 362 | /* End XCBuildConfiguration section */ 363 | 364 | /* Begin XCConfigurationList section */ 365 | 39DABB9F21BD1784002653EE /* Build configuration list for PBXProject "PermissionsTest" */ = { 366 | isa = XCConfigurationList; 367 | buildConfigurations = ( 368 | 39DABBB821BD1786002653EE /* Debug */, 369 | 39DABBB921BD1786002653EE /* Release */, 370 | ); 371 | defaultConfigurationIsVisible = 0; 372 | defaultConfigurationName = Release; 373 | }; 374 | 39DABBBA21BD1786002653EE /* Build configuration list for PBXNativeTarget "PermissionsTest" */ = { 375 | isa = XCConfigurationList; 376 | buildConfigurations = ( 377 | 39DABBBB21BD1786002653EE /* Debug */, 378 | 39DABBBC21BD1786002653EE /* Release */, 379 | ); 380 | defaultConfigurationIsVisible = 0; 381 | defaultConfigurationName = Release; 382 | }; 383 | /* End XCConfigurationList section */ 384 | }; 385 | rootObject = 39DABB9C21BD1784002653EE /* Project object */; 386 | } 387 | -------------------------------------------------------------------------------- /sample/PermissionsTest/PermissionsTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /sample/PermissionsTest/PermissionsTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /sample/PermissionsTest/PermissionsTest.xcodeproj/xcshareddata/xcschemes/PermissionsTest.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /sample/PermissionsTest/PermissionsTest/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // PermissionsTest 4 | // 5 | // Created by Leo Natan (Wix) on 12/9/18. 6 | // Copyright © 2018 Leo Natan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /sample/PermissionsTest/PermissionsTest/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // PermissionsTest 4 | // 5 | // Created by Leo Natan (Wix) on 12/9/18. 6 | // Copyright © 2018 Leo Natan. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /sample/PermissionsTest/PermissionsTest/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /sample/PermissionsTest/PermissionsTest/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /sample/PermissionsTest/PermissionsTest/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 | -------------------------------------------------------------------------------- /sample/PermissionsTest/PermissionsTest/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 30 | 37 | 44 | 51 | 58 | 66 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /sample/PermissionsTest/PermissionsTest/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 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | NSCalendarsUsageDescription 24 | Davai 25 | NSPhotoLibraryUsageDescription 26 | Zan-Gief 27 | NSHealthShareUsageDescription 28 | My heart will go on... 29 | NSHealthUpdateUsageDescription 30 | My heart will go on... 31 | NSLocationAlwaysAndWhenInUseUsageDescription 32 | Gief 33 | NSLocationWhenInUseUsageDescription 34 | Come on 35 | NSUserTrackingUsageDescription 36 | Stalker Alert!!1 37 | UILaunchStoryboardName 38 | LaunchScreen 39 | UIMainStoryboardFile 40 | Main 41 | UIRequiredDeviceCapabilities 42 | 43 | armv7 44 | 45 | UISupportedInterfaceOrientations 46 | 47 | UIInterfaceOrientationPortrait 48 | UIInterfaceOrientationLandscapeLeft 49 | UIInterfaceOrientationLandscapeRight 50 | 51 | UISupportedInterfaceOrientations~ipad 52 | 53 | UIInterfaceOrientationPortrait 54 | UIInterfaceOrientationPortraitUpsideDown 55 | UIInterfaceOrientationLandscapeLeft 56 | UIInterfaceOrientationLandscapeRight 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /sample/PermissionsTest/PermissionsTest/PermissionsTest.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.developer.healthkit 6 | 7 | com.apple.developer.usernotifications.critical-alerts 8 | 9 | com.apple.developer.healthkit.access 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /sample/PermissionsTest/PermissionsTest/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // PermissionsTest 4 | // 5 | // Created by Leo Natan (Wix) on 12/9/18. 6 | // Copyright © 2018 Leo Natan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /sample/PermissionsTest/PermissionsTest/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // PermissionsTest 4 | // 5 | // Created by Leo Natan (Wix) on 12/9/18. 6 | // Copyright © 2018 Leo Natan. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | @import EventKit; 11 | @import CoreLocation; 12 | @import UserNotifications; 13 | @import HealthKit; 14 | @import Photos; 15 | @import PhotosUI; 16 | @import AppTrackingTransparency; 17 | 18 | @interface ViewController () 19 | { 20 | EKEventStore* _eventStore; 21 | CLLocationManager* _locationManager; 22 | HKHealthStore* _healthStore; 23 | } 24 | 25 | @end 26 | 27 | @implementation ViewController 28 | 29 | - (void)viewDidLoad 30 | { 31 | [super viewDidLoad]; 32 | 33 | _eventStore = [EKEventStore new]; 34 | 35 | _healthStore = [HKHealthStore new]; 36 | 37 | NSLog(@"%@", NSBundle.mainBundle.bundleURL.path); 38 | } 39 | 40 | - (IBAction)_photos:(id)sender 41 | { 42 | id handler = ^(PHAuthorizationStatus status) { 43 | if(@available(iOS 14, *)) 44 | { 45 | if(status == PHAuthorizationStatusLimited) 46 | { 47 | NSLog(@"Photos: "); 48 | 49 | return; 50 | } 51 | } 52 | 53 | NSLog(@"Photos: %@", status == PHAuthorizationStatusRestricted ? @"" : status == PHAuthorizationStatusDenied ? @"" : status == PHAuthorizationStatusAuthorized ? @"" : @""); 54 | }; 55 | 56 | if(@available(iOS 14, *)) 57 | { 58 | [PHPhotoLibrary requestAuthorizationForAccessLevel:PHAccessLevelReadWrite handler:handler]; 59 | } 60 | else 61 | { 62 | [PHPhotoLibrary requestAuthorization:handler]; 63 | } 64 | } 65 | 66 | - (IBAction)_photosOS14:(id)sender 67 | { 68 | if(@available(iOS 14, *)) 69 | { 70 | PHPickerConfiguration* config = [PHPickerConfiguration new]; 71 | config.selectionLimit = 0; 72 | 73 | PHPickerViewController* picker = [[PHPickerViewController alloc] initWithConfiguration:config]; 74 | picker.delegate = self; 75 | [self presentViewController:picker animated:YES completion:nil]; 76 | } 77 | else 78 | { 79 | UIImagePickerController* picker = [UIImagePickerController new]; 80 | picker.delegate = self; 81 | [self presentViewController:picker animated:YES completion:nil]; 82 | } 83 | } 84 | 85 | - (IBAction)_location:(id)sender 86 | { 87 | _locationManager = [CLLocationManager new]; 88 | _locationManager.delegate = self; 89 | [_locationManager requestAlwaysAuthorization]; 90 | } 91 | 92 | - (IBAction)_calendar:(id)sender 93 | { 94 | [_eventStore requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError * _Nullable error) { 95 | NSLog(@"Calendar: %@", granted ? @"" : @""); 96 | }]; 97 | } 98 | 99 | - (IBAction)_notifications:(id)sender 100 | { 101 | UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter]; 102 | [center requestAuthorizationWithOptions: UNAuthorizationOptionAlert | UNAuthorizationOptionSound completionHandler:^(BOOL granted, NSError * _Nullable error) { 103 | NSLog(@"Notifications: %@", granted ? @"" : @""); 104 | }]; 105 | } 106 | 107 | - (IBAction)_health:(id)sender 108 | { 109 | NSSet* readableTypes = [NSSet setWithObjects:[HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierPeakExpiratoryFlowRate], [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBloodPressureSystolic], nil]; 110 | 111 | [_healthStore requestAuthorizationToShareTypes:readableTypes readTypes:readableTypes completion:^(BOOL success, NSError * _Nullable error) { 112 | NSLog(@"Health: %@", success ? ([_healthStore authorizationStatusForType:[HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierPeakExpiratoryFlowRate]] == HKAuthorizationStatusSharingAuthorized ? @"" : @"") : [NSString stringWithFormat:@"", error]); 113 | }]; 114 | } 115 | 116 | - (IBAction)_trackOS14:(id)sender 117 | { 118 | if(@available(iOS 14, *)) { 119 | [ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) { 120 | 121 | NSLog(@"Track: %@", status == ATTrackingManagerAuthorizationStatusRestricted ? @"" : status == ATTrackingManagerAuthorizationStatusDenied ? @"" : status == ATTrackingManagerAuthorizationStatusAuthorized ? @"" : @""); 122 | }]; 123 | } 124 | else 125 | { 126 | NSLog(@"Track: "); 127 | } 128 | } 129 | 130 | - (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status 131 | { 132 | NSLog(@"Location: %@", status == kCLAuthorizationStatusAuthorizedAlways ? @"" : status == kCLAuthorizationStatusAuthorizedWhenInUse ? @"" : @""); 133 | } 134 | 135 | - (void)picker:(PHPickerViewController *)picker didFinishPicking:(NSArray *)results API_AVAILABLE(ios(14)) 136 | { 137 | [self dismissViewControllerAnimated:YES completion:nil]; 138 | } 139 | 140 | @end 141 | -------------------------------------------------------------------------------- /sample/PermissionsTest/PermissionsTest/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // PermissionsTest 4 | // 5 | // Created by Leo Natan (Wix) on 12/9/18. 6 | // Copyright © 2018 Leo Natan. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | --------------------------------------------------------------------------------