Repository: XcodesOrg/XcodesApp Branch: main Commit: 1a0d3353b9a1 Files: 213 Total size: 1.4 MB Directory structure: gitextract_lwe5ux5j/ ├── .github/ │ ├── CODEOWNERS │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ └── feature_request.md │ ├── dependabot.yml │ ├── release-drafter.yml │ └── workflows/ │ ├── appcast.yml │ ├── ci.yml │ ├── release-drafter.yml │ └── xcstrings.yml ├── .gitignore ├── AppCast/ │ ├── .gitignore │ ├── Gemfile │ ├── _config.yml │ ├── _includes/ │ │ └── appcast.inc │ ├── _plugins/ │ │ └── signature_filter.rb │ ├── appcast.xml │ └── appcast_pre.xml ├── CONTRIBUTING.md ├── DECISIONS.md ├── HelperXPCShared/ │ └── HelperXPCShared.swift ├── LICENSE ├── README.md ├── Scripts/ │ ├── export_options.plist │ ├── fix_libfido2_framework.sh │ ├── increment_build_number.sh │ ├── notarize.sh │ ├── package_release.sh │ ├── sign_update │ └── uninstall_privileged_helper.sh ├── Xcodes/ │ ├── AcknowledgementsGenerator/ │ │ ├── .gitignore │ │ ├── .swiftpm/ │ │ │ └── xcode/ │ │ │ └── package.xcworkspace/ │ │ │ └── contents.xcworkspacedata │ │ ├── Package.swift │ │ ├── README.md │ │ ├── Sources/ │ │ │ └── AcknowledgementsGenerator/ │ │ │ ├── Extensions/ │ │ │ │ ├── CollectionExtensions.swift │ │ │ │ └── StringExtensions.swift │ │ │ ├── Tools/ │ │ │ │ └── Xcode.swift │ │ │ └── main.swift │ │ └── spm-licenses.LICENSE │ ├── AppleAPI/ │ │ ├── .gitignore │ │ ├── Package.swift │ │ ├── README.md │ │ ├── Sources/ │ │ │ └── AppleAPI/ │ │ │ ├── Client.swift │ │ │ ├── Environment.swift │ │ │ ├── Hashcash.swift │ │ │ └── URLRequest+Apple.swift │ │ └── Tests/ │ │ ├── AppleAPITests/ │ │ │ ├── AppleAPITests.swift │ │ │ └── XCTestManifests.swift │ │ └── LinuxMain.swift │ ├── Backend/ │ │ ├── AppState+Install.swift │ │ ├── AppState+Runtimes.swift │ │ ├── AppState+Update.swift │ │ ├── AppState.swift │ │ ├── Aria2CError.swift │ │ ├── AvailableXcode.swift │ │ ├── Bundle+InfoPlistValues.swift │ │ ├── Collection+.swift │ │ ├── Configure.swift │ │ ├── DataSource.swift │ │ ├── DateFormatter+.swift │ │ ├── Downloader.swift │ │ ├── Downloads.swift │ │ ├── Entry+.swift │ │ ├── Environment.swift │ │ ├── FileError.swift │ │ ├── FileManager+.swift │ │ ├── FocusedValues.swift │ │ ├── Foundation.swift │ │ ├── Hardware.swift │ │ ├── HelperClient.swift │ │ ├── HelperInstallState.swift │ │ ├── InstalledXcode.swift │ │ ├── IsTesting.swift │ │ ├── NotificationManager.swift │ │ ├── Optional+IsNotNil.swift │ │ ├── Path+.swift │ │ ├── Process.swift │ │ ├── Progress+.swift │ │ ├── Publisher+Resumable.swift │ │ ├── SDKs+Xcode.swift │ │ ├── SelectedActionType.swift │ │ ├── SelectedXcode.swift │ │ ├── URLRequest+Apple.swift │ │ ├── URLSession+DownloadTaskPublisher.swift │ │ ├── Version+.swift │ │ ├── Version+Xcode.swift │ │ ├── Version+XcodeReleases.swift │ │ ├── Xcode.swift │ │ ├── XcodeCommands.swift │ │ └── XcodeInstallState.swift │ ├── Frontend/ │ │ ├── About/ │ │ │ ├── AboutView.swift │ │ │ ├── AcknowledgementsView.swift │ │ │ └── ScrollingTextView.swift │ │ ├── Common/ │ │ │ ├── NavigationSplitViewWrapper.swift │ │ │ ├── ObservingProgressIndicator.swift │ │ │ ├── ProgressButton.swift │ │ │ ├── ProgressIndicator.swift │ │ │ ├── TagView.swift │ │ │ ├── TrailingIconLabelStyle.swift │ │ │ ├── XcodesAlert.swift │ │ │ └── XcodesSheet.swift │ │ ├── InfoPane/ │ │ │ ├── CompatibilityView.swift │ │ │ ├── CompilersView.swift │ │ │ ├── CornerRadiusModifier.swift │ │ │ ├── IconView.swift │ │ │ ├── IdenticalBuildView.swift │ │ │ ├── InfoPane.swift │ │ │ ├── InfoPaneControls.swift │ │ │ ├── InstallationStepDetailView.swift │ │ │ ├── InstalledStateButtons.swift │ │ │ ├── NotInstalledStateButtons.swift │ │ │ ├── PlatformsView.swift │ │ │ ├── ReleaseDateView.swift │ │ │ ├── ReleaseNotesView.swift │ │ │ ├── RuntimeInstallationStepDetailView.swift │ │ │ ├── SDKsView.swift │ │ │ └── UnselectedView.swift │ │ ├── MainWindow.swift │ │ ├── Preferences/ │ │ │ ├── AdvancedPreferencePane.swift │ │ │ ├── DownloadPreferencePane.swift │ │ │ ├── ExperiementsPreferencePane.swift │ │ │ ├── GeneralPreferencePane.swift │ │ │ ├── NotificationsView.swift │ │ │ ├── PlatformsListView.swift │ │ │ ├── PreferencesView.swift │ │ │ └── UpdatesPreferencePane.swift │ │ ├── SignIn/ │ │ │ ├── AttributedText.swift │ │ │ ├── NSAttributedString+.swift │ │ │ ├── PinCodeTextView.swift │ │ │ ├── SignIn2FAView.swift │ │ │ ├── SignInCredentialsView.swift │ │ │ ├── SignInPhoneListView.swift │ │ │ ├── SignInSMSView.swift │ │ │ ├── SignInSecurityKeyPinView.swift │ │ │ ├── SignInSecurityKeyTouchView.swift │ │ │ └── SignedInView.swift │ │ ├── View+Conditional.swift │ │ ├── View+IsHidden.swift │ │ └── XcodeList/ │ │ ├── AppStoreButtonStyle.swift │ │ ├── BottomStatusBar.swift │ │ ├── InstallationStepRowView.swift │ │ ├── MainToolbar.swift │ │ ├── Tag.swift │ │ ├── XcodeListCategory.swift │ │ ├── XcodeListView.swift │ │ └── XcodeListViewRow.swift │ ├── Preview Content/ │ │ └── Preview Assets.xcassets/ │ │ └── Contents.json │ ├── Resources/ │ │ ├── Assets.xcassets/ │ │ │ ├── AppIcon.appiconset/ │ │ │ │ └── Contents.json │ │ │ ├── Contents.json │ │ │ ├── Icons/ │ │ │ │ └── Contents.json │ │ │ ├── install.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── xcode-beta.imageset/ │ │ │ │ └── Contents.json │ │ │ └── xcode.imageset/ │ │ │ └── Contents.json │ │ ├── Info.plist │ │ ├── Licenses.rtf │ │ ├── Localizable.xcstrings │ │ ├── Xcodes.entitlements │ │ ├── XcodesIcon.icon/ │ │ │ └── icon.json │ │ ├── XcodesTest.entitlements │ │ ├── aria2c │ │ ├── aria2c.LICENSE │ │ └── unxip │ ├── XcodesApp.swift │ └── XcodesKit/ │ ├── .gitignore │ ├── Package.swift │ ├── README.md │ ├── Sources/ │ │ └── XcodesKit/ │ │ ├── Extensions/ │ │ │ ├── Foundation.swift │ │ │ └── Logger.swift │ │ ├── Models/ │ │ │ ├── Runtimes/ │ │ │ │ ├── CoreSimulatorImage.swift │ │ │ │ ├── RuntimeInstallState.swift │ │ │ │ ├── RuntimeInstallationStep.swift │ │ │ │ └── Runtimes.swift │ │ │ ├── XcodeInstallState.swift │ │ │ ├── XcodeInstallationStep.swift │ │ │ └── XcodeReleases/ │ │ │ ├── Architecture.swift │ │ │ ├── Checksums.swift │ │ │ ├── Compilers.swift │ │ │ ├── Link.swift │ │ │ ├── Release.swift │ │ │ ├── SDKs.swift │ │ │ ├── XcodeRelease.swift │ │ │ ├── XcodeVersion.swift │ │ │ └── YMD.swift │ │ ├── Services/ │ │ │ └── RuntimeService.swift │ │ ├── Shell/ │ │ │ ├── Process.swift │ │ │ └── XcodesShell.swift │ │ └── XcodesKitEnvironment.swift │ └── Tests/ │ └── XcodesKitTests/ │ └── XcodesKitTests.swift ├── Xcodes.xcodeproj/ │ ├── ._project.xcworkspace │ ├── project.pbxproj │ ├── project.xcworkspace/ │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata/ │ │ ├── IDEWorkspaceChecks.plist │ │ └── swiftpm/ │ │ └── Package.resolved │ └── xcshareddata/ │ └── xcschemes/ │ ├── Xcodes.xcscheme │ └── com.robotsandpencils.XcodesApp.Helper.xcscheme ├── XcodesTests/ │ ├── AppStateTests.swift │ ├── AppStateUpdateTests.swift │ ├── Bundle+XcodesTests.swift │ ├── Environment+Mock.swift │ ├── Fixtures/ │ │ ├── Stub-0.0.0.Info.plist │ │ └── Stub-version.plist │ └── Info.plist ├── com.xcodesorg.xcodesapp.Helper/ │ ├── AuditTokenHack.h │ ├── AuditTokenHack.m │ ├── ConnectionVerifier.swift │ ├── Info.plist │ ├── Logger.swift │ ├── SimpleXPCApp.LICENSE │ ├── XPCDelegate.swift │ ├── com.xcodesorg.xcodesapp.Helper-Bridging-Header.h │ ├── com.xcodesorg.xcodesapp.HelperTest.entitlements │ ├── launchd.plist │ └── main.swift └── nextstep.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/CODEOWNERS ================================================ * @RobotsAndPencils/xcodes ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: Bug report about: Create a report to help us improve title: '' labels: bug assignees: '' --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: **Expected behavior** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. **Version** - OS: - Xcodes: ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md ================================================ --- name: Feature request about: Suggest an idea for this project title: '' labels: enhancement assignees: '' --- **Tell us how we can improve Xcodes** **Is your feature request related to a problem? Please describe.** **What would you like to see? How would you like it to work?** ================================================ FILE: .github/dependabot.yml ================================================ version: 2 updates: # Maintain dependencies for GitHub Actions - package-ecosystem: "github-actions" directory: "/" schedule: interval: "daily" ================================================ FILE: .github/release-drafter.yml ================================================ categories: - title: '🚀 Enhancements' labels: - 'enhancement' - title: '🐛 Bug Fixes' labels: - 'bugfix' - title: '🌎 Localization' labels: - 'localization' - title: '🧰 Maintenance' label: - 'chore' - 'documentation' - 'dependencies' template: | Install Xcodes using one of the methods listed [here](https://github.com/RobotsAndPencils/XcodesApp#installation). Update Xcodes by selecting Check for Updates... in the Xcodes menu in the menu bar. ## Changes $CHANGES ================================================ FILE: .github/workflows/appcast.yml ================================================ name: Build and publish a new appcast file on: workflow_dispatch: release: jobs: jekyll: runs-on: ubuntu-latest steps: - name: Checkout 🛎 uses: actions/checkout@v4 with: # If you're using actions/checkout@v4 you must set persist-credentials to false in most cases for the deployment to work correctly. persist-credentials: false # - name: Cache 📦 # uses: actions/cache@v4.1.1 # with: # path: AppCast/vendor/bundle # key: ${{ runner.os }}-gems-v1.0-${{ hashFiles('AppCast/Gemfile') }} # restore-keys: | # ${{ runner.os }}-gems- - name: Setup Ruby, JRuby and TruffleRuby uses: ruby/setup-ruby@v1.197.0 with: ruby-version: '3.0' - name: Bundler 💎 working-directory: AppCast env: BUNDLE_PATH: vendor/bundle run: | gem install bundler bundle install - name: Build 🛠 working-directory: AppCast env: BUNDLE_PATH: vendor/bundle JEKYLL_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: bundle exec jekyll build - name: Publish 🚀 uses: JamesIves/github-pages-deploy-action@releases/v4 with: token: ${{ secrets.GITHUB_TOKEN }} branch: gh-pages folder: AppCast/_site ================================================ FILE: .github/workflows/ci.yml ================================================ name: CI on: push: branches: - main pull_request: jobs: test: runs-on: macos-15 steps: - uses: actions/checkout@v4 - name: Run tests env: DEVELOPER_DIR: /Applications/Xcode_16.4.app run: xcodebuild test -scheme Xcodes ================================================ FILE: .github/workflows/release-drafter.yml ================================================ name: Release Drafter on: # Allow running it manually in case we forget to label a PR before merging workflow_dispatch: push: branches: - main jobs: update_release_draft: runs-on: ubuntu-latest steps: - uses: release-drafter/release-drafter@v6 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ================================================ FILE: .github/workflows/xcstrings.yml ================================================ name: XCStrings Validation on: workflow_dispatch: push: branches: - main pull_request: jobs: test: runs-on: macos-13 steps: - uses: actions/checkout@v4 - name: Clone SwiftPolyglot run: git clone --branch 0.3.1 -- https://github.com/appdecostudio/SwiftPolyglot.git ../SwiftPolyglot - name: validate translations run: | swift build --package-path ../SwiftPolyglot --configuration release swift run --package-path ../SwiftPolyglot swiftpolyglot "ca,de,el,es,fi,fr,hi,it,ja,ko,nl,pl,pt-BR,ru,tr,uk,zh-Hans,zh-Hant" --errorOnMissing ================================================ FILE: .gitignore ================================================ # Xcode # # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore ## User settings xcuserdata/ ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) *.xcscmblueprint *.xccheckout ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) build/ DerivedData/ *.moved-aside *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 ## Obj-C/Swift specific *.hmap ## App packaging *.ipa *.dSYM.zip *.dSYM Archive/* Product/* ## Playgrounds timeline.xctimeline playground.xcworkspace # Jetbrains IDEA .idea # Swift Package Manager # # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. # Packages/ # Package.pins # Package.resolved # *.xcodeproj # # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata # hence it is not needed unless you have added a package configuration file to your project # .swiftpm .build/ # CocoaPods # # We recommend against adding the Pods directory to your .gitignore. However # you should judge for yourself, the pros and cons are mentioned at: # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control # # Pods/ # # Add this line if you want to avoid checking in source code from the Xcode workspace # *.xcworkspace # Carthage # # Add this line if you want to avoid checking in source code from Carthage dependencies. # Carthage/Checkouts Carthage/Build/ # Accio dependency management Dependencies/ .accio/ # fastlane # # It is recommended to not store the screenshots in the git repo. # Instead, use fastlane to re-generate the screenshots whenever they are needed. # For more information about the recommended setup visit: # https://docs.fastlane.tools/best-practices/source-control/#source-control fastlane/report.xml fastlane/Preview.html fastlane/screenshots/**/*.png fastlane/test_output # Code Injection # # After new code Injection tools there's a generated folder /iOSInjectionProject # https://github.com/johnno1962/injectionforxcode iOSInjectionProject/ .DS_Store ================================================ FILE: AppCast/.gitignore ================================================ _site .sass-cache .jekyll-metadata ================================================ FILE: AppCast/Gemfile ================================================ source "https://rubygems.org" # Hello! This is where you manage which Jekyll version is used to run. # When you want to use a different version, change it below, save the # file and run `bundle install`. Run Jekyll with `bundle exec`, like so: # # bundle exec jekyll serve # # This will help ensure the proper Jekyll version is running. # Happy Jekylling! gem "jekyll", "~> 4.4.1" gem "jekyll-github-metadata", group: :jekyll_plugins # Windows does not include zoneinfo files, so bundle the tzinfo-data gem # and associated library. install_if -> { RUBY_PLATFORM =~ %r!mingw|mswin|java! } do gem "tzinfo", "~> 1.2" gem "tzinfo-data" end # Performance-booster for watching directories on Windows gem "wdm", "~> 0.1.0", :install_if => Gem.win_platform? # kramdown v2 ships without the gfm parser by default. If you're using # kramdown v1, comment out this line. gem "kramdown-parser-gfm" ================================================ FILE: AppCast/_config.yml ================================================ # Welcome to Jekyll! # # This config file is meant for settings that affect your whole blog, values # which you are expected to set up once and rarely edit after that. If you find # yourself editing this file very often, consider using Jekyll's data files # feature for the data you need to update frequently. # # For technical reasons, this file is *NOT* reloaded automatically when you use # 'bundle exec jekyll serve'. If you change this file, please restart the server process. # Site settings # These are used to personalize your new site. If you look in the HTML files, # you will see them accessed via {{ site.title }}, {{ site.email }}, and so on. # You can create any custom variable you would like, and they will be accessible # in the templates via {{ site.myvariable }}. title: Xcodes.app description: >- # this means to ignore newlines until "baseurl:" baseurl: "" # the subpath of your site, e.g. /blog url: "" # the base hostname & protocol for your site, e.g. http://example.com # Build settings markdown: kramdown plugins: - "jekyll-github-metadata" # Exclude from processing. # The following items will not be processed, by default. Create a custom list # to override the default setting. # exclude: # - Gemfile # - Gemfile.lock # - node_modules # - vendor/bundle/ # - vendor/cache/ # - vendor/gems/ # - vendor/ruby/ ================================================ FILE: AppCast/_includes/appcast.inc ================================================ {{ site.github.project_title }} Most recent changes with links to updates. en {% for release in site.github.releases %} {% unless release.draft %} {% unless release.prerelease and page.release_only %} {{ release.name }} {{ release.published_at | date_to_rfc822 }} {% for asset in release.assets limit:1 %} {% assign signature = release.body | sparkle_signature %} {% assign build_nums = release.tag_name | replace_first:'v','' | replace_first:'b',',' | split:',' %} {% if build_nums.size == 2 %} {% assign version_number = build_nums[0] %} {% assign build_number = build_nums[1] %} {% else %} {% assign version = release.tag_name | remove_first:'v' %} {% endif %} {% endfor %} {% endunless %} {% endunless %} {% endfor %} ================================================ FILE: AppCast/_plugins/signature_filter.rb ================================================ module Jekyll module SignatureFilter def sparkle_signature(release_body) regex = //m signature = release_body.match(regex).named_captures["signature"] raise "Didn't find a signature in the release body." if signature.empty? signature end end end Liquid::Template.register_filter(Jekyll::SignatureFilter) ================================================ FILE: AppCast/appcast.xml ================================================ --- release_only: true --- {%include appcast.inc %} ================================================ FILE: AppCast/appcast_pre.xml ================================================ --- release_only: false --- {%include appcast.inc %} ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing to Xcodes We love your input! We want to make contributing to this project as easy and transparent as possible, whether it's: - Reporting a bug - Discussing the current state of the code - Submitting a fix - Proposing new features - Becoming a maintainer ## We Develop with GitHub We use GitHub to host code, to track issues and feature requests, as well as accept pull requests. ## All Code Changes Happen Through Pull Requests Pull requests are the best way to propose changes to the codebase We actively welcome your pull requests: 1. Fork the repo and create your branch from `main`. 2. If you've added code that should be tested, add tests. 3. If you've added new functionality, add documentation 4. Ensure the test suite passes. 5. Make sure your code lints. 6. Issue that pull request! ## Any contributions you make will be under the MIT Software License In short, when you submit code changes, your submissions are understood to be under the same [MIT License](http://choosealicense.com/licenses/mit/) that covers the project. Feel free to contact the maintainers if that's a concern. ## Report bugs using GitHub [issues](https://github.com/robotsandpencils/xcodesapp/issues) We use GitHub issues to track public bugs. Report a bug by [opening a new issue](); it's that easy! ## Write bug reports with detail, background, and sample code **Great Bug Reports** tend to have: - A quick summary and/or background - Steps to reproduce - Be specific! - What you expected would happen - What actually happens - Notes (possibly including why you think this might be happening, or stuff you tried that didn't work) People *love* thorough bug reports. ## License By contributing, you agree that your contributions will be licensed under its MIT License. ================================================ FILE: DECISIONS.md ================================================ # Decisions This file exists to provide a historical record of the motivation for important technical decisions in the project. It's inspired by Architectural Decision Records, but the implementation is intentionally simpler than usual. When a new decision is made, append it to the end of the file with a header. Decisions can be changed later. This is a reflection of real life, not a contract that has to be followed. ## Why Make Xcodes.app? [xcodes](https://github.com/RobotsAndPencils/xcodes) has been well-received within and outside of Robots and Pencils as an easy way to manage Xcode versions. A command line tool can have a familiar interface for developers, and is also easier to automate than most GUI apps. Not everyone wants to use a command line tool though, and there's an opportunity to create an even better developer experience with an app. This is also an opportunity for contributors to get more familiar with SwiftUI and Combine on macOS. ## Code Organization To begin, we will intentionally not attempt to share code between xcodes and Xcodes.app. In the future, once we have a better idea of the two tools' functionality, we can revisit this decision. An example of code that could be shared are the two AppleAPI libraries which will likely be very similar. While the intent of xcodes' XcodesKit library was to potentially reuse it in a GUI context, it still makes a lot of assumptions about how the UI works that would prevent that happening immediately. As we reuse that code (by copying and pasting) and tweak it to work in Xcodes.app, we may end up with something that can work in both contexts. ## Asynchrony Xcodes.app uses Combine to model asynchronous work. This is different than xcodes, which uses PromiseKit because it began prior to Combine's existence. This means that there is a migration of the existing code that has to happen, but the result is easier to use with a SwiftUI app. ## Dependency Injection xcodes used Point Free's Environment type, and I'm happy with how that turned out. It looks a lot simpler to implement and grow with a codebase, but still allows setting up test double for tests. - https://www.pointfree.co/episodes/ep16-dependency-injection-made-easy - https://www.pointfree.co/episodes/ep18-dependency-injection-made-comfortable - https://vimeo.com/291588126 ## State Management While I'm curious and eager to try Point Free's [Composable Architecture](https://github.com/pointfreeco/swift-composable-architecture), I'm going to avoid it at first in favour of a simpler AppState ObservableObject. My motivation for this is to try to have something more familiar to a contributor that was also new to SwiftUI, so that the codebase doesn't have too many new or unfamiliar things. If we run into performance or correctness issues in the future I think TCA should be a candidate to reconsider. ## Privilege Escalation Unlike [xcodes](https://github.com/RobotsAndPencils/xcodes/blob/master/DECISIONS.md#privilege-escalation), there is a better option than running sudo in a Process when we need to escalate privileges in Xcodes.app, namely a privileged helper. A separate, bundle executable is installed as a privileged helper using SMJobBless and communicates with the main app (the client) over XPC. This helper performs the post-install and xcode-select tasks that would require sudo from the command line. The helper and main app validate each other's bundle ID, version and code signing certificate chain. Validation of the connection is done using the private audit token API. An alternative is to validate the code signature of the client based on the PID from a first "handshake" message. DTS [seems to say](https://developer.apple.com/forums/thread/72881#420409022) that this would also be safe against an attacker PID-wrapping. Because the SMJobBless + XPC examples I found online all use the audit token instead, I decided to go with that. The tradeoff is that this is private API. Uninstallation is not provided yet. I had this partially implemented (one attempt was based on [DoNotDisturb's approach](https://github.com/objective-see/DoNotDisturb/blob/237b19800fa356f830d1c02715a9a75be08b8924/configure/Helper/HelperInterface.m#L123)) but an issue that I kept hitting was that despite the helper not being installed or running I was able to get a remote object proxy over the connection. Adding a timeout to getVersion might be sufficient as a workaround, as it should return the string immediately. - [Apple Developer: Creating XPC Services](https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingXPCServices.html) - [Objective Development: The Story Behind CVE-2019-13013](https://blog.obdev.at/what-we-have-learned-from-a-vulnerability/) - [Apple Developer Forums: How to and When to uninstall a privileged helper](https://developer.apple.com/forums/thread/66821) - [Apple Developer Forums: XPC restricted to processes with the same code signing?](https://developer.apple.com/forums/thread/72881#419817) - [Wojciech Reguła: Learn XPC exploitation - Part 1: Broken cryptography](https://wojciechregula.blog/post/learn-xpc-exploitation-part-1-broken-cryptography/) - [Wojciech Reguła: Learn XPC exploitation - Part 2: Say no to the PID!](https://wojciechregula.blog/post/learn-xpc-exploitation-part-2-say-no-to-the-pid/) - [Wojciech Reguła: Learn XPC exploitation - Part 3: Code injections](https://wojciechregula.blog/post/learn-xpc-exploitation-part-3-code-injections/) - [Apple Developer: EvenBetterAuthorizationSample](https://developer.apple.com/library/archive/samplecode/EvenBetterAuthorizationSample/Introduction/Intro.html) - [erikberglund/SwiftPrivilegedHelper](https://github.com/erikberglund/SwiftPrivilegedHelper) - [aronskaya/smjobbless](https://github.com/aronskaya/smjobbless) - [securing/SimpleXPCApp](https://github.com/securing/SimpleXPCApp) ## Selecting the active version of Xcode This isn't a technical decision, but we spent enough time talking about this that it's probably worth sharing. When a user has more than one version of Xcode installed, a specific version of the developer tools can be selected with the `xcode-select` tool. The selected version of tools like xcodebuild or xcrun will be used unless the DEVELOPER_DIR environment variable has been set to a different path. You can read more about this in the `xcode-select` man pages. Notably, the man pages and [some notarization documentation](https://developer.apple.com/documentation/xcode/notarizing_macos_software_before_distribution) use the term "active" to indicate the Xcode version that's been selected. [This](https://developer.apple.com/library/archive/technotes/tn2339/_index.html#//apple_ref/doc/uid/DTS40014588-CH1-HOW_DO_I_SELECT_THE_DEFAULT_VERSION_OF_XCODE_TO_USE_FOR_MY_COMMAND_LINE_TOOLS_) older tech note uses the term "default". And of course, the `xcode-select` tool has the term "select" in its name. xcodes used the terms "select" and "selected" for this functionality, intending to match the xcode-select tool. Here are the descriptions of these terms from [Apple's Style Guide](https://books.apple.com/ca/book/apple-style-guide/id1161855204): > active: Use to refer to the app or window currently being used. Preferred to in front. > default: OK to use to describe the state of settings before the user changes them. See also preset. > preset: Use to refer to a group of customized settings an app provides or the user saves for reuse. > select: Use select, not choose, to refer to the action users perform when they select among multiple objects. Xcodes.app has this same functionality as xcodes, which still uses `xcode-select` under the hood, but because the main UI is a list of selectable rows, there _may_ be some ambiguity about the meaning of "selected". "Default" has a less clear connection to `xcode-select`'s name, but does accurately describe the behaviour that results. In Xcode 11 Launch Services also uses the selected Xcode version when opening a (GUI) developer tool bundled with Xcode, like Instruments. We could also try to follow Apple's lead by using the term "active" from the `xcode-select` man pages and notarization documentation. According to the style guide "active" already has a clear meaning in a GUI context. Ultimately, we've decided to align with Apple's usage of "active" and "make active" in this specific context, despite possible confusion with the definition in the style guide. ## Software Updates We're familiar with using GitHub releases to distribute pre-built, code signed and notarized versions of `xcodes` via direct download and Homebrew. Ideally we could use GitHub releases here too with an update mechanism more suitable for an app bundle. For distribution outside the Mac App Store, the most popular choice for updates is [Sparkle](https://sparkle-project.org). The v2 branch has been in beta for a long time, but since Xcodes.app isn't (currently) sandboxed, we can use the production-ready v1 releases. Based on [this blog post](https://yiqiu.me/2015/11/19/sparkle-update-on-github/), we can use GitHub Pages to generate the appcast for Sparkle to point at releases in our repo. We've made a few changes, like putting the source for the Jekyll site on the main branch, and including the EdDSA signature in the appcast. Generating the appcast file manually would be more straightforward, but we can always edit the files on the gh_pages branch manually if we need to, and it's one less step for a release manager to perform when they're already creating the release in the repo. We're deliberately not capturing system profile data with Sparkle right now, because we don't want it and because it would require additional infrastructure. We also considered https://github.com/mxcl/AppUpdater, but decided against it because it seemed less battle-tested than Sparkle and currently lacks an open source license. ================================================ FILE: HelperXPCShared/HelperXPCShared.swift ================================================ import Foundation let machServiceName = "com.xcodesorg.xcodesapp.Helper" let clientBundleID = "com.xcodesorg.xcodesapp" let subjectOrganizationalUnit = Bundle.main.infoDictionary!["CODE_SIGNING_SUBJECT_ORGANIZATIONAL_UNIT"] as! String @objc(HelperXPCProtocol) protocol HelperXPCProtocol { func getVersion(completion: @escaping (String) -> Void) func xcodeSelect(absolutePath: String, completion: @escaping (Error?) -> Void) func devToolsSecurityEnable(completion: @escaping (Error?) -> Void) func addStaffToDevelopersGroup(completion: @escaping (Error?) -> Void) func acceptXcodeLicense(absoluteXcodePath: String, completion: @escaping (Error?) -> Void) func runFirstLaunch(absoluteXcodePath: String, completion: @escaping (Error?) -> Void) } ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2019-2021 Robots and Pencils Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================

Xcodes.app

The easiest way to install and switch between multiple versions of Xcode. _If you're looking for a command-line version of Xcodes.app, try [`xcodes`](https://github.com/XcodesOrg/xcodes)._ ![CI](https://github.com/XcodesOrg/XcodesApp/workflows/CI/badge.svg) ![](screenshot_light.png#gh-light-mode-only) ![](screenshot_dark.png#gh-dark-mode-only) ### :tada: Announcement XcodesApp is now part of the `XcodesOrg` - [read more here](nextstep.md) ## Features - List all available Xcode versions from [Xcode Releases'](https://xcodereleases.com) data or the Apple Developer website. - Install any Xcode version, **fully automated** from start to finish. Xcodes uses [`aria2`](https://aria2.github.io), which uses up to 16 connections to download 3-5x faster than URLSession. - Automatically resumes installs if network errors. - Apple ID required to download Xcode versions. - Just click a button to make a version active with `xcode-select`. - View release notes, OS compatibility, included SDKs and compilers from [Xcode Releases](https://xcodereleases.com). - Dark/Light Mode supported - Security Key Authentication supported - Support installing Platforms/Runtimes - Support installing Apple Silicon variants ## Platforms/Runtimes - Xcodes supports downloading the Apple runtimes via the app. Simply click on the Platform, and Xcodes will install automatically for you. **Note: iOS 18+, tvOS 18+, watchOS 11+, visionOS 2+ requires that Xcode 16.1 Beta 3+ be installed and active.** ## Apple Silicon Variants As of Xcode 26, Apple provides Apple Silicon as well as Universal variants for Xcode versions as well as each runtime. Simply tap on which variant you want installed. To install the Apple Silicon runtime variant Xcode 26 is required to be active. ## Experiments - Thanks to the wonderful work of [https://github.com/saagarjha/unxip](https://github.com/saagarjha/unxip), turn on the experiment to increase your unxipping time by up to 70%! More can be found on his repo, but bugs, high memory may occur if used. ![](experiment_light.png#gh-light-mode-only) ![](experiment_dark.png#gh-dark-mode-only) ## Localization Xcodes supports localization in several languages. The following languages are supported because of the following community users! ||||| |-|-|-|-| |French 🇫🇷 |[@dompepin](https://github.com/dompepin)|Italian 🇮🇹 |[gualtierofrigerio](https://github.com/gualtierofrigerio)| |Spanish 🇪🇸🇲 |[@cesartru88](https://github.com/cesartru88)|Korean 🇰🇷 |[@ryan-son](https://github.com/ryan-son)| |Russian 🇷🇺 |[@alexmazlov](https://github.com/alexmazlov)|Turkish 🇹🇷 |[@egesucu](https://github.com/egesucu)| |Hindi 🇮🇳 |[@KGurpreet](https://github.com/KGurpreet)|Chinese-Simplified 🇨🇳|[@megabitsenmzq](https://github.com/megabitsenmzq)| |Finnish 🇫🇮 |[@marcusziade](https://github.com/marcusziade)|Chinese-Traditional 🇹🇼|[@itszero](https://github.com/itszero)| |Ukranian 🇺🇦 |[@gelosi](https://github.com/gelosi)|Japanese 🇯🇵|[@tatsuz0u](https://github.com/tatsuz0u)| |German 🇩🇪|[@drct](https://github.com/drct)|Dutch 🇳🇱|[@jfversluis](https://github/com/jfversluis)| |Brazilian Portuguese 🇧🇷|[@brunomunizaf](https://github.com/brunomunizaf)|Polish 🇵🇱|[@jakex7](https://github.com/jakex7)| |Catalan|[@ferranabello](https://github.com/ferranabello)|Greek 🇬🇷|[@alladinian](https://github.com/alladinian) |Thai 🇹🇭|[@neetrath](https://github.com/neetrath)| Want to add more languages? Simply create a PR with the updated strings file. ## Installation v1.X - requires macOS 11 or newer v2.X - requires macOS 13 v3.X - requires macOS 13 - architecture variants and updated icon. ### Install with Homebrew Developer ID-signed and notarized release builds are available on Homebrew. These don't require Xcode to already be installed in order to use. ```sh brew install --cask xcodes ``` ### Manually install 1. Download the latest version [here](https://github.com/XcodesOrg/XcodesApp/releases/latest) using the **Xcodes.zip** asset. These are Developer ID-signed and notarized release builds and don't require Xcode to already be installed in order to use. 2. Move the unzipped `Xcodes.app` to your `/Applications` directory ## Support Xcodes.app and CLI is updated, maintained with contributors like yourself. Even open source libraries and tools come with expenses. If you would like to support Xcodes or donate to the development and maintenance of the tool, it would be greatly appreciated. There is absolutely no obligation! ## Development You'll need macOS 15.6 Ventura and Xcode 26 in order to build and run Xcodes.app. `Unxip` and `aria2` must be compiled as a universal binary ``` # compile for Intel swiftc -parse-as-library -O -target x86_64-apple-macos11 unxip.swift # compile for M1 swiftc -parse-as-library -O -target arm64-apple-macos11 unxip.swift # combine for universal binary lipo -create -output unxip unxip_intel unxip_m1 # check it lipo -archs unxip ``` [`xcode-install`](https://github.com/xcpretty/xcode-install) and [fastlane/spaceship](https://github.com/fastlane/fastlane/tree/master/spaceship) both deserve credit for figuring out the hard parts of what makes this possible.
Releasing a new version Follow the steps below to build and release a new version of Xcodes.app. For any of the git steps, you can use your preferred tool, but please sign the tag. ```sh # Update the version number in Xcode and commit the change, if necessary # Question: Did anything in XPCHelper change? # - com.xcodesorg.xcodesapp.Helper folder and HelperXPCShared # - if so, bump the version number in com.xcodesorg.xcodesapp.Helper target. # Note: you do not have to bump the version number if nothing has changed. # Note2: If you do bump the version, the end user, must re-install the XPCHelper and give permission again. # Increment the build number scripts/increment_build_number.sh # Commit the change git add Xcodes/Resources/Info.plist git commit -asm "Increment build number" # Tag the latest commit # Replace $VERSION and $BUILD below with the latest real values git tag -asm "v$VERSIONb$BUILD" "v$VERSIONb$BUILD" # Push to origin git push --follow-tags # Build the app # Make sure you have the Xcode Selected you want to build with scripts/package_release.sh # Notarize the app # Do this from the Product directory so the app is zipped without being nested inside Product # Create a app specific password on appleid.apple.com if you haven't already # xcrun notarytool store-credentials "AC_PASSWORD" \ # --apple-id "test@example.com" \ # --team-id "teamid" \ # --password "app specific password" pushd Product ../scripts/notarize.sh Xcodes.zip # Sign the .zip for Sparkle, note the signature in the output for later # If you're warned about the signing key not being found, see the Xcodes 1Password vault for the key and installation instructions. ../scripts/sign_update Xcodes.zip popd # Go to https://github.com/XcodesOrg/XcodesApp/releases # If there are uncategorized PRs, add the appropriate label and run the Release Drafter action manually # Edit the latest draft release # Set its tag to the tag you just pushed # Set its title to a string with the format "$VERSION ($BUILD)" # Polish the draft release notes, if necessary # Add the signature to the bottom of the release notes in a comment, like: # Attach the zip that was created in the Product directory to the release # Publish the release shasum -a 256 xcodes.zip # Update the [Homebrew Cask](https://github.com/XcodesOrg/homebrew-cask/blob/master/Casks/x/xcodes.rb). ```
## Maintainers [Matt Kiazyk](https://github.com/mattkiazyk) - [Twitter](https://www.twitter.com/mattkiazyk) [Twitter](https://twitter.com/xcodesApp) | [GitHub](https://github.com/xcodesOrg) | [Mastadon](https://iosdev.space/@XcodesApp) | ================================================ FILE: Scripts/export_options.plist ================================================ method developer-id ================================================ FILE: Scripts/fix_libfido2_framework.sh ================================================ #!/bin/sh # Fix libfido2.framework structure for macOS validation # If this script is not run, the build will fail because xcodebuild is expecting the library in a specific structure FRAMEWORK_PATH="${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/Contents/Frameworks/libfido2.framework" if [ -d "$FRAMEWORK_PATH" ] && [ -f "$FRAMEWORK_PATH/Info.plist" ] && [ ! -d "$FRAMEWORK_PATH/Versions" ]; then echo "Fixing libfido2.framework bundle structure..." # Create proper bundle structure mkdir -p "$FRAMEWORK_PATH/Versions/A/Resources" # Move files to proper locations mv "$FRAMEWORK_PATH/Info.plist" "$FRAMEWORK_PATH/Versions/A/Resources/" mv "$FRAMEWORK_PATH/libfido2" "$FRAMEWORK_PATH/Versions/A/" if [ -f "$FRAMEWORK_PATH/LICENSE" ]; then mv "$FRAMEWORK_PATH/LICENSE" "$FRAMEWORK_PATH/Versions/A/" fi # Create symbolic links ln -sf A "$FRAMEWORK_PATH/Versions/Current" ln -sf Versions/Current/libfido2 "$FRAMEWORK_PATH/libfido2" ln -sf Versions/Current/Resources "$FRAMEWORK_PATH/Resources" echo "libfido2.framework structure fixed" fi ================================================ FILE: Scripts/increment_build_number.sh ================================================ #!/bin/sh # # Increment build number # # This will get the latest build number from git tags, add 1, then set it in the Info.plist. # Assumes that build numbers are monotonically increasing positive integers, across version numbers. # Tags must be named v$version_numberb$build_number, e.g. v1.2.3b456 infoplist_file="$(pwd)/Xcodes/Resources/Info.plist" # Get latest tag hash matching pattern hash=$(git rev-list --tags="v*" --max-count=1) # Get latest tag at hash that matches the same pattern as a prefix in order to support commits with multiple tags last_tag=$(git describe --tags --match "v*" "$hash") # Get build number from last component of tag name last_build_number=$(echo "$last_tag" | grep -o "b.*" | cut -c 2-) build_number=$(($last_build_number + 1)) /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $build_number" "${infoplist_file}" ================================================ FILE: Scripts/notarize.sh ================================================ #!/bin/sh # # Notarize # # Uploads to Apple's notarization service, polls until it completes, staples the ticket to the built app, then creates a new zip. # # Requires four arguments: # - Apple ID username # - Apple ID app-specific password (store this in your Keychain and use the @keychain:$NAME syntax to prevent your password from being added to your shell history) # - App Store Connect provider name # - Path to .app to upload # # Assumes that there's a .app beside the .zip with the same name so it can be stapled and re-zipped. # # E.g. notarize.sh "test@example.com" "@keychain:altool" MyOrg Xcodes.zip # # https://developer.apple.com/documentation/xcode/notarizing_macos_software_before_distribution/customizing_the_notarization_workflow # Adapted from https://github.com/keybase/client/blob/46f5df0aa64ff19198ba7b044bbb7cd907c0be9f/packaging/desktop/package_darwin.sh file="$1" team_id="$2" echo "Uploading to notarization service" result=$(xcrun notarytool submit "$file" \ --keychain-profile "AC_PASSWORD" \ --team-id "$team_id" \ --wait) # echo "done1" echo $result # My grep/awk is bad and I can't figure out how to get the UUID out properly # uuid=$("$result" | \ # grep 'id:' | tail -n1 | \ # cut -d":" -f2-) echo "Successfully uploaded to notarization service, polling for result: $uuid" # we should check here using the info (or notarytool log) to check the results and log # # fullstatus=$(xcrun notarytool info "$uuid" \ # --keychain-profile "AC_PASSWORD" 2>&1) # status=$(echo "$fullstatus" | grep 'status\:' | awk '{ print $2 }') # if [ "$status" = "Accepted" ]; then # echo "Notarization success" # exit 0 # else # echo "Notarization failed, full status below" # echo "$fullstatus" # exit 1 # fi # Remove .zip rm $file # Staple ticket to .app app_path="$(basename -s ".zip" "$file").app" xcrun stapler staple "$app_path" # Zip the stapled app for distribution ditto -c -k --sequesterRsrc --keepParent "$app_path" "$file" ================================================ FILE: Scripts/package_release.sh ================================================ #!/bin/bash # # Package release # # This will build and archive the app and then compress it in a .zip file at Product/Xcodes.zip # You must already have all required code signing assets installed on your computer PROJECT_NAME=Xcodes PROJECT_DIR=$(pwd)/$PROJECT_NAME/Resources SCRIPTS_DIR=$(pwd)/Scripts INFOPLIST_FILE="Info.plist" # If needed ensure that the unxip binary is signed with a hardened runtime so we can notarize # codesign --force --options runtime --sign "Developer ID Application: Robots and Pencils Inc." $PROJECT_DIR/unxip # Ensure a clean build rm -rf Archive/* rm -rf Product/* xcodebuild clean -project $PROJECT_NAME.xcodeproj -configuration Release -alltargets # Archive the app and export for release distribution xcodebuild archive -project $PROJECT_NAME.xcodeproj -scheme $PROJECT_NAME -archivePath Archive/$PROJECT_NAME.xcarchive xcodebuild -archivePath Archive/$PROJECT_NAME.xcarchive -exportArchive -exportPath Product/$PROJECT_NAME -exportOptionsPlist "${SCRIPTS_DIR}/export_options.plist" cp -a "Product/$PROJECT_NAME/$PROJECT_NAME.app" "Product/$PROJECT_NAME.app" # Create a ZIP archive suitable for altool. /usr/bin/ditto -c -k --keepParent "Product/$PROJECT_NAME.app" "Product/$PROJECT_NAME.zip" ================================================ FILE: Scripts/uninstall_privileged_helper.sh ================================================ #!/bin/bash PRIVILEGED_HELPER_LABEL=com.xcodesorg.xcodesapp.Helper sudo rm /Library/PrivilegedHelperTools/$PRIVILEGED_HELPER_LABEL sudo rm /Library/LaunchDaemons/$PRIVILEGED_HELPER_LABEL.plist sudo launchctl bootout system/$PRIVILEGED_HELPER_LABEL #'Boot-out failed: 36: Operation now in progress' is OK output echo "Querying launchd..." LAUNCHD_OUTPUT=$(sudo launchctl list | grep $PRIVILEGED_HELPER_LABEL) if [ -z "$LAUNCHD_OUTPUT" ] then echo "Finished successfully." else echo "WARNING: $PRIVILEGED_HELPER_LABEL is not removed" fi ================================================ FILE: Xcodes/AcknowledgementsGenerator/.gitignore ================================================ .DS_Store /.build /Packages /*.xcodeproj xcuserdata/ ================================================ FILE: Xcodes/AcknowledgementsGenerator/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: Xcodes/AcknowledgementsGenerator/Package.swift ================================================ // swift-tools-version:5.4 import PackageDescription let package = Package( name: "AcknowledgementsGenerator", platforms: [.macOS(.v11)], products: [ .executable( name: "AcknowledgementsGenerator", targets: ["AcknowledgementsGenerator"] ), ], dependencies: [ ], targets: [ .executableTarget( name: "AcknowledgementsGenerator", dependencies: [] ), ] ) ================================================ FILE: Xcodes/AcknowledgementsGenerator/README.md ================================================ # AcknowledgementsGenerator Scans an Xcode project's checked-out SPM packages for license files, then combines them into a single RTF file. Based on https://github.com/MacPaw/spm-licenses. ================================================ FILE: Xcodes/AcknowledgementsGenerator/Sources/AcknowledgementsGenerator/Extensions/CollectionExtensions.swift ================================================ // // CollectionExtensions.swift // spm-licenses // // Created by Sergii Kryvoblotskyi on 11/11/19. // Copyright © 2019 MacPaw. All rights reserved. // import Foundation public extension Collection { /// Returns the element at the specified index iff it is within bounds, otherwise nil. subscript (safe index: Index) -> Element? { return indices.contains(index) ? self[index] : nil } } ================================================ FILE: Xcodes/AcknowledgementsGenerator/Sources/AcknowledgementsGenerator/Extensions/StringExtensions.swift ================================================ // // StringExtensions.swift // spm-licenses // // Created by Sergii Kryvoblotskyi on 11/11/19. // Copyright © 2019 MacPaw. All rights reserved. // import Foundation public extension String { var nsString: NSString { (self as NSString) } var pathExtension: String { return nsString.pathExtension } var lastPathComponent: String { return nsString.lastPathComponent } var deletingLastPathComponent: String { return nsString.deletingLastPathComponent } var stringByDeletingPathExtension: String { return nsString.deletingPathExtension } var expandingTildeInPath: String { return nsString.expandingTildeInPath } func appendingPathComponent(_ component: String) -> String { return nsString.appendingPathComponent(component) } } ================================================ FILE: Xcodes/AcknowledgementsGenerator/Sources/AcknowledgementsGenerator/Tools/Xcode.swift ================================================ // // Xcode.swift // spm-licenses // // Created by Sergii Kryvoblotskyi on 11/11/19. // Copyright © 2019 MacPaw. All rights reserved. // import Foundation struct Xcode { static var derivedDataURL: URL { if let overridenPath = readOverridenDerivedDataPath() { return URL(fileURLWithPath: overridenPath.expandingTildeInPath) } let defaultPath = "~/Library/Developer/Xcode/DerivedData/".expandingTildeInPath return URL(fileURLWithPath: defaultPath) } } //defaults read com.apple.dt.Xcode.plist IDECustomDerivedDataLocation //If the line returns // //The domain/default pair of (com.apple.dt.Xcode.plist, IDECustomDerivedDataLocation) does not exist //it's the default path ~/Library/Developer/Xcode/DerivedData/ otherwise the custom path. private extension Xcode { static func readOverridenDerivedDataPath() -> String? { let task = Process() let pipe = Pipe() task.executableURL = URL(fileURLWithPath: "/usr/bin/defaults") task.arguments = ["read","com.apple.dt.Xcode.plist", "IDECustomDerivedDataLocation"] task.standardOutput = pipe try? task.run() let handle = pipe.fileHandleForReading let data = handle.readDataToEndOfFile() let path = String(data: data, encoding: String.Encoding.utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) return (path?.isEmpty ?? true) ? nil : path } } extension Xcode { struct Project { let url: URL let info: [String: Any] var workspacePath: String? { return info["WorkspacePath"] as? String } } } extension Xcode.Project { struct License { let url: URL let name: String } } extension Xcode.Project.License { func makeRepresentation() throws -> [String: String] { let data = try Data(contentsOf: url) let text = String(data: data, encoding: .utf8) ?? "" return [ "Title": name, "Type": "PSGroupSpecifier", "FooterText": text ] } } ================================================ FILE: Xcodes/AcknowledgementsGenerator/Sources/AcknowledgementsGenerator/main.swift ================================================ // // main.swift // spm-licenses // // Created by Sergii Kryvoblotskyi on 11/11/19. // Copyright © 2019 MacPaw. All rights reserved. // import Foundation import AppKit let arguments = CommandLine.arguments guard let projectIndex = arguments.firstIndex(of: "-p"), let projectPath = arguments[safe: projectIndex + 1] else { print("Project path is missing. Specify -p.") exit(EXIT_FAILURE) } guard let outputIndex = arguments.firstIndex(of: "-o"), let outputPath = arguments[safe: outputIndex + 1] else { print("Output path is missing. Specify -o.") exit(EXIT_FAILURE) } let fileManager = FileManager.default let projectURL = URL(fileURLWithPath: projectPath.expandingTildeInPath) if !fileManager.fileExists(atPath: projectURL.path) { print("xcodeproj not found at \(projectURL)") exit(EXIT_FAILURE) } let packageURL = projectURL.appendingPathComponent("project.xcworkspace/xcshareddata/swiftpm/Package.resolved") if !fileManager.fileExists(atPath: packageURL.path) { print("Package.resolved not found at \(packageURL)") exit(EXIT_FAILURE) } let packageData = try Data(contentsOf: packageURL) let packageInfo = try JSONSerialization.jsonObject(with: packageData, options: .allowFragments) guard let package = packageInfo as? [String: Any] else { print("Invalid package format") exit(EXIT_FAILURE) } guard let object = package["object"] as? [String: Any] else { print("Invalid obejct format") exit(EXIT_FAILURE) } guard let pins = object["pins"] as? [[String: Any]] else { print("Invalid pins format") exit(EXIT_FAILURE) } let projectsURL = Xcode.derivedDataURL func projectsInfo(at url: URL) throws -> [Xcode.Project] { try fileManager .contentsOfDirectory(at: url, includingPropertiesForKeys: nil, options: .skipsHiddenFiles) .map { $0.appendingPathComponent("info.plist") } .compactMap { guard let info = NSDictionary(contentsOf: $0) as? [String: Any] else { return nil } return Xcode.Project(url: $0, info: info) } } let projects = try projectsInfo(at: projectsURL) // Despite the naming, if the project only has an xcodeproj and not an xcworkspace, the WorkspacePath value will be the path to the xcodeproj guard let currentProject = projects.first(where: ({ $0.workspacePath == projectPath.expandingTildeInPath })) else { print("Derived data missing for project") exit(EXIT_FAILURE) } let checkouts = currentProject.url.deletingLastPathComponent().appendingPathComponent("SourcePackages/checkouts") let checkedDependencies = try fileManager.contentsOfDirectory(at: checkouts, includingPropertiesForKeys: nil, options: .skipsHiddenFiles) let spmLicences: [Xcode.Project.License] = checkedDependencies.compactMap { let supportedFilenames = ["LICENSE", "LICENSE.txt", "LICENSE.md"] for filename in supportedFilenames { let licenseURL = $0.appendingPathComponent(filename) if fileManager.fileExists(atPath: licenseURL.path) { return Xcode.Project.License(url: licenseURL, name: $0.lastPathComponent) } } return nil } var manualLicenses: [Xcode.Project.License] = [] let enumerator = fileManager.enumerator(at: projectURL.deletingLastPathComponent(), includingPropertiesForKeys: [URLResourceKey.nameKey], options: .skipsHiddenFiles)! for case let url as URL in enumerator where url.lastPathComponent.hasSuffix(".LICENSE") { manualLicenses.append( Xcode.Project.License( url: url, name: url.lastPathComponent.replacingOccurrences(of: ".LICENSE", with: "") ) ) } let licences = spmLicences + manualLicenses let acknowledgementsAttributedString = NSMutableAttributedString() for licence in licences { acknowledgementsAttributedString.append(NSAttributedString(string: licence.name + "\n\n", attributes: [.font: NSFont.preferredFont(forTextStyle: .title2)])) let licenseContents = try String(contentsOf: licence.url) acknowledgementsAttributedString.append(NSAttributedString(string: licenseContents + "\n\n", attributes: [.font: NSFont.preferredFont(forTextStyle: .body)])) } guard let data = acknowledgementsAttributedString.rtf(from: NSRange(location: 0, length: acknowledgementsAttributedString.length), documentAttributes: [:]) else { print("Failed to create RTF data") exit(EXIT_FAILURE) } try data.write(to: URL(fileURLWithPath: outputPath.expandingTildeInPath)) print("Licenses have been saved to \(outputPath)") ================================================ FILE: Xcodes/AcknowledgementsGenerator/spm-licenses.LICENSE ================================================ MIT License Copyright (c) 2019 MacPaw Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Xcodes/AppleAPI/.gitignore ================================================ .DS_Store /.build /Packages /*.xcodeproj xcuserdata/ ================================================ FILE: Xcodes/AppleAPI/Package.swift ================================================ // swift-tools-version:5.7 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "AppleAPI", platforms: [.macOS(.v11)], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( name: "AppleAPI", targets: ["AppleAPI"]), ], dependencies: [ .package(url: "https://github.com/xcodesOrg/swift-srp", branch: "main") ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. // Targets can depend on other targets in this package, and on products in packages this package depends on. .target( name: "AppleAPI", dependencies: [.product(name: "SRP", package: "swift-srp")]), .testTarget( name: "AppleAPITests", dependencies: ["AppleAPI"]), ] ) ================================================ FILE: Xcodes/AppleAPI/README.md ================================================ # AppleAPI A description of this package. ================================================ FILE: Xcodes/AppleAPI/Sources/AppleAPI/Client.swift ================================================ import Foundation import Combine import SRP import Crypto import CommonCrypto public class Client { private static let authTypes = ["sa", "hsa", "non-sa", "hsa2"] public init() {} // MARK: - Login public func srpLogin(accountName: String, password: String) -> AnyPublisher { var serviceKey: String! let client = SRPClient(configuration: SRPConfiguration(.N2048)) let clientKeys = client.generateKeys() let a = clientKeys.public return Current.network.dataTask(with: URLRequest.itcServiceKey) .map(\.data) .decode(type: ServiceKeyResponse.self, decoder: JSONDecoder()) .flatMap { serviceKeyResponse -> AnyPublisher<(String, String), Swift.Error> in serviceKey = serviceKeyResponse.authServiceKey // Fixes issue https://github.com/RobotsAndPencils/XcodesApp/issues/360 // On 2023-02-23, Apple added a custom implementation of hashcash to their auth flow // Without this addition, Apple ID's would get set to locked return self.loadHashcash(accountName: accountName, serviceKey: serviceKey) .map { return (serviceKey, $0)} .eraseToAnyPublisher() } .flatMap { (serviceKey, hashcash) -> AnyPublisher<(String, String, ServerSRPInitResponse), Swift.Error> in return Current.network.dataTask(with: URLRequest.SRPInit(serviceKey: serviceKey, a: Data(a.bytes).base64EncodedString(), accountName: accountName)) .map(\.data) .decode(type: ServerSRPInitResponse.self, decoder: JSONDecoder()) .map { return (serviceKey, hashcash, $0) } .eraseToAnyPublisher() } .flatMap { (serviceKey, hashcash, srpInit) -> AnyPublisher in guard let decodedB = Data(base64Encoded: srpInit.b) else { return Fail(error: AuthenticationError.srpInvalidPublicKey) .eraseToAnyPublisher() } guard let decodedSalt = Data(base64Encoded: srpInit.salt) else { return Fail(error: AuthenticationError.srpInvalidPublicKey) .eraseToAnyPublisher() } let iterations = srpInit.iteration do { guard let encryptedPassword = self.pbkdf2(password: password, saltData: decodedSalt, keyByteCount: 32, prf: CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA256), rounds: iterations, protocol: srpInit.protocol) else { return Fail(error: AuthenticationError.srpInvalidPublicKey) .eraseToAnyPublisher() } let sharedSecret = try client.calculateSharedSecret(password: encryptedPassword, salt: [UInt8](decodedSalt), clientKeys: clientKeys, serverPublicKey: .init([UInt8](decodedB))) let m1 = client.calculateClientProof(username: accountName, salt: [UInt8](decodedSalt), clientPublicKey: a, serverPublicKey: .init([UInt8](decodedB)), sharedSecret: .init(sharedSecret.bytes)) let m2 = client.calculateServerProof(clientPublicKey: a, clientProof: m1, sharedSecret: .init([UInt8](sharedSecret.bytes))) return Current.network.dataTask(with: URLRequest.SRPComplete(serviceKey: serviceKey, hashcash: hashcash, accountName: accountName, c: srpInit.c, m1: Data(m1).base64EncodedString(), m2: Data(m2).base64EncodedString())) .mapError { $0 as Swift.Error } .eraseToAnyPublisher() } catch { return Fail(error: AuthenticationError.srpInvalidPublicKey) .eraseToAnyPublisher() } } .flatMap { result -> AnyPublisher in let (data, response) = result return Just(data) .decode(type: SignInResponse.self, decoder: JSONDecoder()) .flatMap { responseBody -> AnyPublisher in let httpResponse = response as! HTTPURLResponse switch httpResponse.statusCode { case 200: return Current.network.dataTask(with: URLRequest.olympusSession) .map { _ in AuthenticationState.authenticated } .mapError { $0 as Swift.Error } .eraseToAnyPublisher() case 401: return Fail(error: AuthenticationError.invalidUsernameOrPassword(username: accountName)) .eraseToAnyPublisher() case 403: let errorMessage = responseBody.serviceErrors?.first?.description.replacingOccurrences(of: "-20209: ", with: "") ?? "" return Fail(error: AuthenticationError.accountLocked(errorMessage)) .eraseToAnyPublisher() case 409: return self.handleTwoStepOrFactor(data: data, response: response, serviceKey: serviceKey) case 412 where Client.authTypes.contains(responseBody.authType ?? ""): return Fail(error: AuthenticationError.appleIDAndPrivacyAcknowledgementRequired) .eraseToAnyPublisher() default: return Fail(error: AuthenticationError.unexpectedSignInResponse(statusCode: httpResponse.statusCode, message: responseBody.serviceErrors?.map { $0.description }.joined(separator: ", "))) .eraseToAnyPublisher() } } .eraseToAnyPublisher() } .mapError { $0 as Swift.Error } .eraseToAnyPublisher() } func loadHashcash(accountName: String, serviceKey: String) -> AnyPublisher { Result { try URLRequest.federate(account: accountName, serviceKey: serviceKey) } .publisher .flatMap { request in Current.network.dataTask(with: request) .mapError { $0 as Error } .tryMap { (data, response) throws -> (String) in guard let urlResponse = response as? HTTPURLResponse else { throw AuthenticationError.invalidSession } switch urlResponse.statusCode { case 200..<300: let httpResponse = response as! HTTPURLResponse guard let bitsString = httpResponse.allHeaderFields["X-Apple-HC-Bits"] as? String, let bits = UInt(bitsString) else { throw AuthenticationError.invalidHashcash } guard let challenge = httpResponse.allHeaderFields["X-Apple-HC-Challenge"] as? String else { throw AuthenticationError.invalidHashcash } guard let hashcash = Hashcash().mint(resource: challenge, bits: bits) else { throw AuthenticationError.invalidHashcash } return (hashcash) case 400, 401: throw AuthenticationError.invalidHashcash case let code: throw AuthenticationError.badStatusCode(statusCode: code, data: data, response: urlResponse) } } } .eraseToAnyPublisher() } func handleTwoStepOrFactor(data: Data, response: URLResponse, serviceKey: String) -> AnyPublisher { let httpResponse = response as! HTTPURLResponse let sessionID = (httpResponse.allHeaderFields["X-Apple-ID-Session-Id"] as! String) let scnt = (httpResponse.allHeaderFields["scnt"] as! String) return Current.network.dataTask(with: URLRequest.authOptions(serviceKey: serviceKey, sessionID: sessionID, scnt: scnt)) .map(\.data) .decode(type: AuthOptionsResponse.self, decoder: JSONDecoder()) .flatMap { authOptions -> AnyPublisher in switch authOptions.kind { case .twoStep: return Fail(error: AuthenticationError.accountUsesTwoStepAuthentication) .eraseToAnyPublisher() case .twoFactor, .securityKey: return self.handleTwoFactor(serviceKey: serviceKey, sessionID: sessionID, scnt: scnt, authOptions: authOptions) .eraseToAnyPublisher() case .unknown: let possibleResponseString = String(data: data, encoding: .utf8) return Fail(error: AuthenticationError.accountUsesUnknownAuthenticationKind(possibleResponseString)) .eraseToAnyPublisher() } } .eraseToAnyPublisher() } func handleTwoFactor(serviceKey: String, sessionID: String, scnt: String, authOptions: AuthOptionsResponse) -> AnyPublisher { let option: TwoFactorOption // SMS was sent automatically if authOptions.smsAutomaticallySent { option = .smsSent(authOptions.trustedPhoneNumbers!.first!) // SMS wasn't sent automatically because user needs to choose a phone to send to } else if authOptions.canFallBackToSMS { option = .smsPendingChoice // Code is shown on trusted devices } else if authOptions.fsaChallenge != nil { option = .securityKey // User needs to use a physical security key to respond to the challenge } else { option = .codeSent } let sessionData = AppleSessionData(serviceKey: serviceKey, sessionID: sessionID, scnt: scnt) return Just(AuthenticationState.waitingForSecondFactor(option, authOptions, sessionData)) .setFailureType(to: Error.self) .eraseToAnyPublisher() } // MARK: - Continue 2FA public func requestSMSSecurityCode(to trustedPhoneNumber: AuthOptionsResponse.TrustedPhoneNumber, authOptions: AuthOptionsResponse, sessionData: AppleSessionData) -> AnyPublisher { Result { try URLRequest.requestSecurityCode(serviceKey: sessionData.serviceKey, sessionID: sessionData.sessionID, scnt: sessionData.scnt, trustedPhoneID: trustedPhoneNumber.id) } .publisher .flatMap { request in Current.network.dataTask(with: request) .mapError { $0 as Error } } .map { _ in AuthenticationState.waitingForSecondFactor(.smsSent(trustedPhoneNumber), authOptions, sessionData) } .eraseToAnyPublisher() } public func submitSecurityCode(_ code: SecurityCode, sessionData: AppleSessionData) -> AnyPublisher { Result { try URLRequest.submitSecurityCode(serviceKey: sessionData.serviceKey, sessionID: sessionData.sessionID, scnt: sessionData.scnt, code: code) } .publisher .flatMap { request in Current.network.dataTask(with: request) .mapError { $0 as Error } .tryMap { (data, response) throws -> (Data, URLResponse) in guard let urlResponse = response as? HTTPURLResponse else { return (data, response) } switch urlResponse.statusCode { case 200..<300: return (data, urlResponse) case 400, 401: throw AuthenticationError.incorrectSecurityCode case 412: throw AuthenticationError.appleIDAndPrivacyAcknowledgementRequired case let code: throw AuthenticationError.badStatusCode(statusCode: code, data: data, response: urlResponse) } } .flatMap { (data, response) -> AnyPublisher in self.updateSession(serviceKey: sessionData.serviceKey, sessionID: sessionData.sessionID, scnt: sessionData.scnt) } } .eraseToAnyPublisher() } public func submitChallenge(response: Data, sessionData: AppleSessionData) -> AnyPublisher { Result { URLRequest.respondToChallenge(serviceKey: sessionData.serviceKey, sessionID: sessionData.sessionID, scnt: sessionData.scnt, response: response) } .publisher .flatMap { request in Current.network.dataTask(with: request) .mapError { $0 as Error } .tryMap { (data, response) throws -> (Data, URLResponse) in guard let urlResponse = response as? HTTPURLResponse else { return (data, response) } switch urlResponse.statusCode { case 200..<300: return (data, urlResponse) case 400, 401: throw AuthenticationError.incorrectSecurityCode case 412: throw AuthenticationError.appleIDAndPrivacyAcknowledgementRequired case let code: throw AuthenticationError.badStatusCode(statusCode: code, data: data, response: urlResponse) } } .flatMap { (data, response) -> AnyPublisher in self.updateSession(serviceKey: sessionData.serviceKey, sessionID: sessionData.sessionID, scnt: sessionData.scnt) } }.eraseToAnyPublisher() } // MARK: - Session /// Use the olympus session endpoint to see if the existing session is still valid public func validateSession() -> AnyPublisher { return Current.network.dataTask(with: URLRequest.olympusSession) .tryMap { result -> Data in let httpResponse = result.response as! HTTPURLResponse if httpResponse.statusCode == 401 { throw AuthenticationError.notAuthorized } return result.data } .decode(type: AppleSession.self, decoder: JSONDecoder()) .tryMap { session in // A user that is a non-paid Apple Developer will have a provider == nil // Those users can still download Xcode. // Non Apple Developers will get caught in the download as invalid // if session.provider == nil { // throw AuthenticationError.notDeveloperAppleId // } } .eraseToAnyPublisher() } func updateSession(serviceKey: String, sessionID: String, scnt: String) -> AnyPublisher { return Current.network.dataTask(with: URLRequest.trust(serviceKey: serviceKey, sessionID: sessionID, scnt: scnt)) .flatMap { (data, response) in Current.network.dataTask(with: URLRequest.olympusSession) .map { _ in AuthenticationState.authenticated } } .mapError { $0 as Error } .eraseToAnyPublisher() } func sha256(data : Data) -> Data { var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH)) data.withUnsafeBytes { _ = CC_SHA256($0.baseAddress, CC_LONG(data.count), &hash) } return Data(hash) } private func pbkdf2(password: String, saltData: Data, keyByteCount: Int, prf: CCPseudoRandomAlgorithm, rounds: Int, protocol srpProtocol: SRPProtocol) -> Data? { guard let passwordData = password.data(using: .utf8) else { return nil } let hashedPasswordDataRaw = sha256(data: passwordData) let hashedPasswordData = switch srpProtocol { case .s2k: hashedPasswordDataRaw // the legacy s2k_fo protocol requires hex-encoding the digest before performing PBKDF2. case .s2k_fo: Data(hashedPasswordDataRaw.hexEncodedString().lowercased().utf8) } var derivedKeyData = Data(repeating: 0, count: keyByteCount) let derivedCount = derivedKeyData.count let derivationStatus: Int32 = derivedKeyData.withUnsafeMutableBytes { derivedKeyBytes in let keyBuffer: UnsafeMutablePointer = derivedKeyBytes.baseAddress!.assumingMemoryBound(to: UInt8.self) return saltData.withUnsafeBytes { saltBytes -> Int32 in let saltBuffer: UnsafePointer = saltBytes.baseAddress!.assumingMemoryBound(to: UInt8.self) return hashedPasswordData.withUnsafeBytes { hashedPasswordBytes -> Int32 in let passwordBuffer: UnsafePointer = hashedPasswordBytes.baseAddress!.assumingMemoryBound(to: UInt8.self) return CCKeyDerivationPBKDF( CCPBKDFAlgorithm(kCCPBKDF2), passwordBuffer, hashedPasswordData.count, saltBuffer, saltData.count, prf, UInt32(rounds), keyBuffer, derivedCount) } } } return derivationStatus == kCCSuccess ? derivedKeyData : nil } } // MARK: - Types public enum AuthenticationState: Equatable { case unauthenticated case waitingForSecondFactor(TwoFactorOption, AuthOptionsResponse, AppleSessionData) case authenticated case notAppleDeveloper } public enum AuthenticationError: Swift.Error, LocalizedError, Equatable { case invalidSession case invalidHashcash case invalidUsernameOrPassword(username: String) case incorrectSecurityCode case unexpectedSignInResponse(statusCode: Int, message: String?) case appleIDAndPrivacyAcknowledgementRequired case accountUsesTwoStepAuthentication case accountUsesUnknownAuthenticationKind(String?) case accountLocked(String) case badStatusCode(statusCode: Int, data: Data, response: HTTPURLResponse) case notDeveloperAppleId case notAuthorized case invalidResult(resultString: String?) case srpInvalidPublicKey public var errorDescription: String? { switch self { case .invalidSession: return "Your authentication session is invalid. Try signing in again." case .invalidHashcash: return "Could not create a hashcash for the session." case .invalidUsernameOrPassword: return "Invalid username and password combination." case .incorrectSecurityCode: return "The code that was entered is incorrect." case let .unexpectedSignInResponse(statusCode, message): return """ Received an unexpected sign in response. If you continue to have problems, please submit a bug report in the Help menu and include the following information: Status code: \(statusCode) \(message != nil ? ("Message: " + message!) : "") """ case .appleIDAndPrivacyAcknowledgementRequired: return "You must sign in to https://appstoreconnect.apple.com and acknowledge the Apple ID & Privacy agreement." case .accountUsesTwoStepAuthentication: return "Received a response from Apple that indicates this account has two-step authentication enabled. xcodes currently only supports the newer two-factor authentication, though. Please consider upgrading to two-factor authentication, or explain why this isn't an option for you by making a new feature request in the Help menu." case .accountUsesUnknownAuthenticationKind: return "Received a response from Apple that indicates this account has two-step or two-factor authentication enabled, but xcodes is unsure how to handle this response. If you continue to have problems, please submit a bug report in the Help menu." case let .accountLocked(message): return message case let .badStatusCode(statusCode, _, _): return "Received an unexpected status code: \(statusCode). If you continue to have problems, please submit a bug report in the Help menu." case .notDeveloperAppleId: return "You are not registered as an Apple Developer. Please visit Apple Developer Registration. https://developer.apple.com/register/" case .notAuthorized: return "You are not authorized. Please Sign in with your Apple ID first." case let .invalidResult(resultString): return resultString ?? "If you continue to have problems, please submit a bug report in the Help menu." case .srpInvalidPublicKey: return "Invalid Key" } } } public struct AppleSessionData: Equatable, Identifiable { public let serviceKey: String public let sessionID: String public let scnt: String public var id: String { sessionID } public init(serviceKey: String, sessionID: String, scnt: String) { self.serviceKey = serviceKey self.sessionID = sessionID self.scnt = scnt } } struct ServiceKeyResponse: Decodable { let authServiceKey: String } struct SignInResponse: Decodable { let authType: String? let serviceErrors: [ServiceError]? struct ServiceError: Decodable, CustomStringConvertible { let code: String let message: String var description: String { return "\(code): \(message)" } } } public enum TwoFactorOption: Equatable { case smsSent(AuthOptionsResponse.TrustedPhoneNumber) case codeSent case smsPendingChoice case securityKey } public struct FSAChallenge: Equatable, Decodable { public let challenge: String public let keyHandles: [String] public let allowedCredentials: String } public struct AuthOptionsResponse: Equatable, Decodable { public let trustedPhoneNumbers: [TrustedPhoneNumber]? public let trustedDevices: [TrustedDevice]? public let securityCode: SecurityCodeInfo? public let noTrustedDevices: Bool? public let serviceErrors: [ServiceError]? public let fsaChallenge: FSAChallenge? public init( trustedPhoneNumbers: [AuthOptionsResponse.TrustedPhoneNumber]?, trustedDevices: [AuthOptionsResponse.TrustedDevice]?, securityCode: AuthOptionsResponse.SecurityCodeInfo, noTrustedDevices: Bool? = nil, serviceErrors: [ServiceError]? = nil, fsaChallenge: FSAChallenge? = nil ) { self.trustedPhoneNumbers = trustedPhoneNumbers self.trustedDevices = trustedDevices self.securityCode = securityCode self.noTrustedDevices = noTrustedDevices self.serviceErrors = serviceErrors self.fsaChallenge = fsaChallenge } public var kind: Kind { if trustedDevices != nil { return .twoStep } else if trustedPhoneNumbers != nil { return .twoFactor } else if fsaChallenge != nil { return .securityKey } else { return .unknown } } // One time with a new testing account I had a response where noTrustedDevices was nil, but the account didn't have any trusted devices. // This should have been a situation where an SMS security code was sent automatically. // This resolved itself either after some time passed, or by signing into appleid.apple.com with the account. // Not sure if it's worth explicitly handling this case or if it'll be really rare. public var canFallBackToSMS: Bool { noTrustedDevices == true } public var smsAutomaticallySent: Bool { trustedPhoneNumbers?.count == 1 && canFallBackToSMS } public struct TrustedPhoneNumber: Equatable, Decodable, Identifiable { public let id: Int public let numberWithDialCode: String public init(id: Int, numberWithDialCode: String) { self.id = id self.numberWithDialCode = numberWithDialCode } } public struct TrustedDevice: Equatable, Decodable { public let id: String public let name: String public let modelName: String public init(id: String, name: String, modelName: String) { self.id = id self.name = name self.modelName = modelName } } public struct SecurityCodeInfo: Equatable, Decodable { public let length: Int public let tooManyCodesSent: Bool public let tooManyCodesValidated: Bool public let securityCodeLocked: Bool public let securityCodeCooldown: Bool public init( length: Int, tooManyCodesSent: Bool = false, tooManyCodesValidated: Bool = false, securityCodeLocked: Bool = false, securityCodeCooldown: Bool = false ) { self.length = length self.tooManyCodesSent = tooManyCodesSent self.tooManyCodesValidated = tooManyCodesValidated self.securityCodeLocked = securityCodeLocked self.securityCodeCooldown = securityCodeCooldown } } public enum Kind: Equatable { case twoStep, twoFactor, securityKey, unknown } } public struct ServiceError: Decodable, Equatable { let code: String let message: String } public enum SecurityCode { case device(code: String) case sms(code: String, phoneNumberId: Int) var urlPathComponent: String { switch self { case .device: return "trusteddevice" case .sms: return "phone" } } } /// Object returned from olympus/v1/session /// Used to check Provider, and show name /// If Provider is nil, we can assume the Apple User is NOT an Apple Developer and can't download Xcode. public struct AppleSession: Decodable, Equatable { public let user: AppleUser public let provider: AppleProvider? } public struct AppleProvider: Decodable, Equatable { public let providerId: Int public let name: String } public struct AppleUser: Decodable, Equatable { public let fullName: String } public struct ServerSRPInitResponse: Decodable { let iteration: Int let salt: String let b: String let c: String let `protocol`: SRPProtocol } extension String { func base64ToU8Array() -> Data { return Data(base64Encoded: self) ?? Data() } } extension Data { func hexEncodedString() -> String { return map { String(format: "%02hhx", $0) }.joined() } } ================================================ FILE: Xcodes/AppleAPI/Sources/AppleAPI/Environment.swift ================================================ import Foundation import Combine /** Lightweight dependency injection using global mutable state :P - SeeAlso: https://www.pointfree.co/episodes/ep16-dependency-injection-made-easy - SeeAlso: https://www.pointfree.co/episodes/ep18-dependency-injection-made-comfortable - SeeAlso: https://vimeo.com/291588126 */ public struct Environment { public var network = Network() } public var Current = Environment() public struct Network { public var session = URLSession.shared public var dataTask: (URLRequest) -> URLSession.DataTaskPublisher = { Current.network.session.dataTaskPublisher(for: $0) } public func dataTask(with request: URLRequest) -> URLSession.DataTaskPublisher { dataTask(request) } } ================================================ FILE: Xcodes/AppleAPI/Sources/AppleAPI/Hashcash.swift ================================================ // // Hashcash.swift // // // Created by Matt Kiazyk on 2023-02-23. // import Foundation import CryptoKit import CommonCrypto /* # This App Store Connect hashcash spec was generously donated by... # # __ _ # __ _ _ __ _ __ / _|(_) __ _ _ _ _ __ ___ ___ # / _` || '_ \ | '_ \ | |_ | | / _` || | | || '__|/ _ \/ __| # | (_| || |_) || |_) || _|| || (_| || |_| || | | __/\__ \ # \__,_|| .__/ | .__/ |_| |_| \__, | \__,_||_| \___||___/ # |_| |_| |___/ # # */ public struct Hashcash { /// A function to returned a minted hash, using a bit and resource string /// /** X-APPLE-HC: 1:11:20230223170600:4d74fb15eb23f465f1f6fcbf534e5877::6373 ^ ^ ^ ^ ^ | | | | +-- Counter | | | +-- Resource | | +-- Date YYMMDD[hhmm[ss]] | +-- Bits (number of leading zeros) +-- Version We can't use an off-the-shelf Hashcash because Apple's implementation is not quite the same as the spec/convention. 1. The spec calls for a nonce called "Rand" to be inserted between the Ext and Counter. They don't do that at all. 2. The Counter conventionally encoded as base-64 but Apple just uses the decimal number's string representation. Iterate from Counter=0 to Counter=N finding an N that makes the SHA1(X-APPLE-HC) lead with Bits leading zero bits We get the "Resource" from the X-Apple-HC-Challenge header and Bits from X-Apple-HC-Bits */ /// - Parameters: /// - resource: a string to be used for minting /// - bits: grabbed from `X-Apple-HC-Bits` header /// - date: Default uses Date() otherwise used for testing to check. /// - Returns: A String hash to use in `X-Apple-HC` header on /signin public func mint(resource: String, bits: UInt = 10, date: String? = nil) -> String? { let ver = "1" var ts: String if let date = date { ts = date } else { let formatter = DateFormatter() formatter.dateFormat = "yyMMddHHmmss" ts = formatter.string(from: Date()) } let challenge = "\(ver):\(bits):\(ts):\(resource):" var counter = 0 while true { guard let digest = ("\(challenge):\(counter)").sha1 else { print("ERROR: Can't generate SHA1 digest") return nil } if digest == bits { return "\(challenge):\(counter)" } counter += 1 } } } extension String { var sha1: Int? { let data = Data(self.utf8) var digest = [UInt8](repeating: 0, count:Int(CC_SHA1_DIGEST_LENGTH)) data.withUnsafeBytes { _ = CC_SHA1($0.baseAddress, CC_LONG(data.count), &digest) } let bigEndianValue = digest.withUnsafeBufferPointer { ($0.baseAddress!.withMemoryRebound(to: UInt32.self, capacity: 1) { $0 }) }.pointee let value = UInt32(bigEndian: bigEndianValue) return value.leadingZeroBitCount } } ================================================ FILE: Xcodes/AppleAPI/Sources/AppleAPI/URLRequest+Apple.swift ================================================ import Foundation public extension URL { static let itcServiceKey = URL(string: "https://appstoreconnect.apple.com/olympus/v1/app/config?hostname=itunesconnect.apple.com")! static let signIn = URL(string: "https://idmsa.apple.com/appleauth/auth/signin")! static let authOptions = URL(string: "https://idmsa.apple.com/appleauth/auth")! static let requestSecurityCode = URL(string: "https://idmsa.apple.com/appleauth/auth/verify/phone")! static func submitSecurityCode(_ code: SecurityCode) -> URL { URL(string: "https://idmsa.apple.com/appleauth/auth/verify/\(code.urlPathComponent)/securitycode")! } static let trust = URL(string: "https://idmsa.apple.com/appleauth/auth/2sv/trust")! static let federate = URL(string: "https://idmsa.apple.com/appleauth/auth/federate")! static let olympusSession = URL(string: "https://appstoreconnect.apple.com/olympus/v1/session")! static let keyAuth = URL(string: "https://idmsa.apple.com/appleauth/auth/verify/security/key")! static let srpInit = URL(string: "https://idmsa.apple.com/appleauth/auth/signin/init")! static let srpComplete = URL(string: "https://idmsa.apple.com/appleauth/auth/signin/complete?isRememberMeEnabled=false")! } public extension URLRequest { static var itcServiceKey: URLRequest { return URLRequest(url: .itcServiceKey) } static func signIn(serviceKey: String, accountName: String, password: String, hashcash: String) -> URLRequest { struct Body: Encodable { let accountName: String let password: String let rememberMe = true } var request = URLRequest(url: .signIn) request.allHTTPHeaderFields = request.allHTTPHeaderFields ?? [:] request.allHTTPHeaderFields?["Content-Type"] = "application/json" request.allHTTPHeaderFields?["X-Requested-With"] = "XMLHttpRequest" request.allHTTPHeaderFields?["X-Apple-Widget-Key"] = serviceKey request.allHTTPHeaderFields?["X-Apple-HC"] = hashcash request.allHTTPHeaderFields?["Accept"] = "application/json, text/javascript" request.httpMethod = "POST" request.httpBody = try! JSONEncoder().encode(Body(accountName: accountName, password: password)) return request } static func authOptions(serviceKey: String, sessionID: String, scnt: String) -> URLRequest { var request = URLRequest(url: .authOptions) request.allHTTPHeaderFields = request.allHTTPHeaderFields ?? [:] request.allHTTPHeaderFields?["X-Apple-ID-Session-Id"] = sessionID request.allHTTPHeaderFields?["X-Apple-Widget-Key"] = serviceKey request.allHTTPHeaderFields?["scnt"] = scnt request.allHTTPHeaderFields?["accept"] = "application/json" return request } static func requestSecurityCode(serviceKey: String, sessionID: String, scnt: String, trustedPhoneID: Int) throws -> URLRequest { struct Body: Encodable { let phoneNumber: PhoneNumber let mode = "sms" struct PhoneNumber: Encodable { let id: Int } } var request = URLRequest(url: .requestSecurityCode) request.allHTTPHeaderFields = request.allHTTPHeaderFields ?? [:] request.allHTTPHeaderFields?["Content-Type"] = "application/json" request.allHTTPHeaderFields?["X-Apple-ID-Session-Id"] = sessionID request.allHTTPHeaderFields?["X-Apple-Widget-Key"] = serviceKey request.allHTTPHeaderFields?["scnt"] = scnt request.allHTTPHeaderFields?["accept"] = "application/json" request.httpMethod = "PUT" request.httpBody = try JSONEncoder().encode(Body(phoneNumber: .init(id: trustedPhoneID))) return request } static func submitSecurityCode(serviceKey: String, sessionID: String, scnt: String, code: SecurityCode) throws -> URLRequest { struct DeviceSecurityCodeRequest: Encodable { let securityCode: SecurityCode struct SecurityCode: Encodable { let code: String } } struct SMSSecurityCodeRequest: Encodable { let securityCode: SecurityCode let phoneNumber: PhoneNumber let mode = "sms" struct SecurityCode: Encodable { let code: String } struct PhoneNumber: Encodable { let id: Int } } var request = URLRequest(url: .submitSecurityCode(code)) request.allHTTPHeaderFields = request.allHTTPHeaderFields ?? [:] request.allHTTPHeaderFields?["X-Apple-ID-Session-Id"] = sessionID request.allHTTPHeaderFields?["X-Apple-Widget-Key"] = serviceKey request.allHTTPHeaderFields?["scnt"] = scnt request.allHTTPHeaderFields?["Accept"] = "application/json" request.allHTTPHeaderFields?["Content-Type"] = "application/json" request.httpMethod = "POST" switch code { case .device(let code): request.httpBody = try JSONEncoder().encode(DeviceSecurityCodeRequest(securityCode: .init(code: code))) case .sms(let code, let phoneNumberId): request.httpBody = try JSONEncoder().encode(SMSSecurityCodeRequest(securityCode: .init(code: code), phoneNumber: .init(id: phoneNumberId))) } return request } static func respondToChallenge(serviceKey: String, sessionID: String, scnt: String, response: Data) -> URLRequest { var request = URLRequest(url: .keyAuth) request.allHTTPHeaderFields = request.allHTTPHeaderFields ?? [:] request.allHTTPHeaderFields?["X-Apple-ID-Session-Id"] = sessionID request.allHTTPHeaderFields?["X-Apple-Widget-Key"] = serviceKey request.allHTTPHeaderFields?["scnt"] = scnt request.allHTTPHeaderFields?["Accept"] = "application/json" request.allHTTPHeaderFields?["Content-Type"] = "application/json" request.httpMethod = "POST" request.httpBody = response return request } static func trust(serviceKey: String, sessionID: String, scnt: String) -> URLRequest { var request = URLRequest(url: .trust) request.allHTTPHeaderFields = request.allHTTPHeaderFields ?? [:] request.allHTTPHeaderFields?["X-Apple-ID-Session-Id"] = sessionID request.allHTTPHeaderFields?["X-Apple-Widget-Key"] = serviceKey request.allHTTPHeaderFields?["scnt"] = scnt request.allHTTPHeaderFields?["Accept"] = "application/json" return request } static var olympusSession: URLRequest { return URLRequest(url: .olympusSession) } static func federate(account: String, serviceKey: String) throws -> URLRequest { struct FederateRequest: Encodable { let accountName: String let rememberMe: Bool } var request = URLRequest(url: .signIn) request.allHTTPHeaderFields?["Accept"] = "application/json" request.allHTTPHeaderFields?["Content-Type"] = "application/json" request.httpMethod = "GET" // let encoder = JSONEncoder() // encoder.outputFormatting = .withoutEscapingSlashes // request.httpBody = try encoder.encode(FederateRequest(accountName: account, rememberMe: true)) return request } static func SRPInit(serviceKey: String, a: String, accountName: String) -> URLRequest { struct ServerSRPInitRequest: Encodable { public let a: String public let accountName: String public let protocols: [SRPProtocol] } var request = URLRequest(url: .srpInit) request.httpMethod = "POST" request.allHTTPHeaderFields = request.allHTTPHeaderFields ?? [:] request.allHTTPHeaderFields?["Accept"] = "application/json" request.allHTTPHeaderFields?["Content-Type"] = "application/json" request.allHTTPHeaderFields?["X-Requested-With"] = "XMLHttpRequest" request.allHTTPHeaderFields?["X-Apple-Widget-Key"] = serviceKey request.httpBody = try? JSONEncoder().encode(ServerSRPInitRequest(a: a, accountName: accountName, protocols: [.s2k, .s2k_fo])) return request } static func SRPComplete(serviceKey: String, hashcash: String, accountName: String, c: String, m1: String, m2: String) -> URLRequest { struct ServerSRPCompleteRequest: Encodable { let accountName: String let c: String let m1: String let m2: String let rememberMe: Bool } var request = URLRequest(url: .srpComplete) request.httpMethod = "POST" request.allHTTPHeaderFields = request.allHTTPHeaderFields ?? [:] request.allHTTPHeaderFields?["Accept"] = "application/json" request.allHTTPHeaderFields?["Content-Type"] = "application/json" request.allHTTPHeaderFields?["X-Requested-With"] = "XMLHttpRequest" request.allHTTPHeaderFields?["X-Apple-Widget-Key"] = serviceKey request.allHTTPHeaderFields?["X-Apple-HC"] = hashcash request.httpBody = try? JSONEncoder().encode(ServerSRPCompleteRequest(accountName: accountName, c: c, m1: m1, m2: m2, rememberMe: false)) return request } } public enum SRPProtocol: String, Codable { case s2k, s2k_fo } ================================================ FILE: Xcodes/AppleAPI/Tests/AppleAPITests/AppleAPITests.swift ================================================ import XCTest @testable import AppleAPI final class AppleAPITests: XCTestCase { func testValidHashCashMint() { let bits: UInt = 11 let resource = "4d74fb15eb23f465f1f6fcbf534e5877" let testDate = "20230223170600" let stamp = Hashcash().mint(resource: resource, bits: bits, date: testDate) XCTAssertEqual(stamp, "1:11:20230223170600:4d74fb15eb23f465f1f6fcbf534e5877::6373") } func testValidHashCashMint2() { let bits: UInt = 10 let resource = "bb63edf88d2f9c39f23eb4d6f0281158" let testDate = "20230224001754" let stamp = Hashcash().mint(resource: resource, bits: bits, date: testDate) XCTAssertEqual(stamp, "1:10:20230224001754:bb63edf88d2f9c39f23eb4d6f0281158::866") } static var allTests = [ ("testValidHashCashMint", testValidHashCashMint), ("testValidHashCashMint2", testValidHashCashMint2), ] } ================================================ FILE: Xcodes/AppleAPI/Tests/AppleAPITests/XCTestManifests.swift ================================================ import XCTest #if !canImport(ObjectiveC) public func allTests() -> [XCTestCaseEntry] { return [ testCase(AppleAPITests.allTests), ] } #endif ================================================ FILE: Xcodes/AppleAPI/Tests/LinuxMain.swift ================================================ import XCTest import AppleAPITests var tests = [XCTestCaseEntry]() tests += AppleAPITests.allTests() XCTMain(tests) ================================================ FILE: Xcodes/Backend/AppState+Install.swift ================================================ import Combine import Foundation import Path import AppleAPI import Version import LegibleError import os.log import DockProgress import XcodesKit /// Downloads and installs Xcodes extension AppState { // check to see if we should auto install for the user public func autoInstallIfNeeded() { guard let storageValue = Current.defaults.get(forKey: "autoInstallation") as? Int, let autoInstallType = AutoInstallationType(rawValue: storageValue) else { return } if autoInstallType == .none { return } // get newest xcode version guard let newestXcode = allXcodes.first, newestXcode.installState == .notInstalled else { Logger.appState.info("User has latest Xcode already installed") return } if autoInstallType == .newestBeta { Logger.appState.info("Auto installing newest Xcode Beta") // install it, as user doesn't have it installed and it's either latest beta or latest release checkMinVersionAndInstall(id: newestXcode.id) } else if autoInstallType == .newestVersion && newestXcode.version.isNotPrerelease { Logger.appState.info("Auto installing newest Xcode") checkMinVersionAndInstall(id: newestXcode.id) } else { Logger.appState.info("No new Xcodes version found to auto install") } } public func install(_ installationType: InstallationType, downloader: Downloader) -> AnyPublisher { install(installationType, downloader: downloader, attemptNumber: 0) .map { _ in Void() } .eraseToAnyPublisher() } private func install(_ installationType: InstallationType, downloader: Downloader, attemptNumber: Int) -> AnyPublisher { Logger.appState.info("Using \(downloader) downloader") setupDockProgress() return validateSession() .flatMap { _ in self.getXcodeArchive(installationType, downloader: downloader) } .flatMap { xcode, url -> AnyPublisher in self.installArchivedXcode(xcode, at: url) } .catch { error -> AnyPublisher in self.resetDockProgressTracking() switch error { case InstallationError.damagedXIP(let damagedXIPURL): guard attemptNumber < 1 else { return Fail(error: error).eraseToAnyPublisher() } switch installationType { case .version: // If the XIP was just downloaded, remove it and try to recover. do { Logger.appState.error("\(error.legibleLocalizedDescription)") Logger.appState.info("Removing damaged XIP and re-attempting installation.") try Current.files.removeItem(at: damagedXIPURL) return self.install(installationType, downloader: downloader, attemptNumber: attemptNumber + 1) .eraseToAnyPublisher() } catch { return Fail(error: error) .eraseToAnyPublisher() } } default: return Fail(error: error) .eraseToAnyPublisher() } } .handleEvents(receiveOutput: { installedXcode in DispatchQueue.main.async { guard let index = self.allXcodes.firstIndex(where: { $0.version.isEquivalent(to: installedXcode.version) }) else { return } self.allXcodes[index].installState = .installed(installedXcode.path) } }) .eraseToAnyPublisher() } private func getXcodeArchive(_ installationType: InstallationType, downloader: Downloader) -> AnyPublisher<(AvailableXcode, URL), Error> { switch installationType { case .version(let availableXcode): if let installedXcode = Current.files.installedXcodes(Path.installDirectory).first(where: { $0.version.isEquivalent(to: availableXcode.version) }) { return Fail(error: InstallationError.versionAlreadyInstalled(installedXcode)) .eraseToAnyPublisher() } return downloadXcode(availableXcode: availableXcode, downloader: downloader) } } private func downloadXcode(availableXcode: AvailableXcode, downloader: Downloader) -> AnyPublisher<(AvailableXcode, URL), Error> { self.downloadOrUseExistingArchive(for: availableXcode, downloader: downloader, progressChanged: { [unowned self] progress in DispatchQueue.main.async { self.setInstallationStep(of: availableXcode.version, to: .downloading(progress: progress)) self.overallProgress.addChild(progress, withPendingUnitCount: AppState.totalProgressUnits - AppState.unxipProgressWeight) } }) .map { return (availableXcode, $0) } .eraseToAnyPublisher() } public func downloadOrUseExistingArchive(for availableXcode: AvailableXcode, downloader: Downloader, progressChanged: @escaping (Progress) -> Void) -> AnyPublisher { // Check to see if the archive is in the expected path in case it was downloaded but failed to install let expectedArchivePath = Path.xcodesApplicationSupport/"Xcode-\(availableXcode.version).\(availableXcode.filename.suffix(fromLast: "."))" // aria2 downloads directly to the destination (instead of into /tmp first) so we need to make sure that the download isn't incomplete let aria2DownloadMetadataPath = expectedArchivePath.parent/(expectedArchivePath.basename() + ".aria2") var aria2DownloadIsIncomplete = false if case .aria2 = downloader, aria2DownloadMetadataPath.exists { aria2DownloadIsIncomplete = true } if Current.files.fileExistsAtPath(expectedArchivePath.string), aria2DownloadIsIncomplete == false { Logger.appState.info("Found existing archive that will be used for installation at \(expectedArchivePath).") return Just(expectedArchivePath.url) .setFailureType(to: Error.self) .eraseToAnyPublisher() } else { let destination = Path.xcodesApplicationSupport/"Xcode-\(availableXcode.version).\(availableXcode.filename.suffix(fromLast: "."))" switch downloader { case .aria2: let aria2Path = Path(url: Bundle.main.url(forAuxiliaryExecutable: "aria2c")!)! return downloadXcodeWithAria2( availableXcode, to: destination, aria2Path: aria2Path, progressChanged: progressChanged ) case .urlSession: return downloadXcodeWithURLSession( availableXcode, to: destination, progressChanged: progressChanged ) } } } public func downloadXcodeWithAria2(_ availableXcode: AvailableXcode, to destination: Path, aria2Path: Path, progressChanged: @escaping (Progress) -> Void) -> AnyPublisher { let cookies = AppleAPI.Current.network.session.configuration.httpCookieStorage?.cookies(for: availableXcode.url) ?? [] let (progress, publisher) = Current.shell.downloadWithAria2( aria2Path, availableXcode.url, destination, cookies ) progressChanged(progress) return publisher .map { _ in destination.url } .eraseToAnyPublisher() } public func downloadXcodeWithURLSession(_ availableXcode: AvailableXcode, to destination: Path, progressChanged: @escaping (Progress) -> Void) -> AnyPublisher { let resumeDataPath = Path.xcodesApplicationSupport/"Xcode-\(availableXcode.version).resumedata" let persistedResumeData = Current.files.contents(atPath: resumeDataPath.string) return attemptResumableTask(maximumRetryCount: 3) { resumeData -> AnyPublisher in let (progress, publisher) = Current.network.downloadTask(with: availableXcode.url, to: destination.url, resumingWith: resumeData ?? persistedResumeData) progressChanged(progress) return publisher .map { $0.saveLocation } .eraseToAnyPublisher() } .handleEvents(receiveCompletion: { completion in self.persistOrCleanUpResumeData(at: resumeDataPath, for: completion) }) .eraseToAnyPublisher() } public func installArchivedXcode(_ availableXcode: AvailableXcode, at archiveURL: URL) -> AnyPublisher { unxipProgress.completedUnitCount = 0 overallProgress.addChild(unxipProgress, withPendingUnitCount: AppState.unxipProgressWeight) do { let destinationURL = Path.installDirectory.join("Xcode-\(availableXcode.version.descriptionWithoutBuildMetadata).app").url switch archiveURL.pathExtension { case "xip": return unarchiveAndMoveXIP(availableXcode: availableXcode, at: archiveURL, to: destinationURL) .tryMap { xcodeURL throws -> InstalledXcode in guard let path = Path(url: xcodeURL), Current.files.fileExists(atPath: path.string), let installedXcode = InstalledXcode(path: path) else { throw InstallationError.failedToMoveXcodeToApplications } return installedXcode } .flatMap { installedXcode -> AnyPublisher in do { self.setInstallationStep(of: availableXcode.version, to: .trashingArchive) try Current.files.trashItem(at: archiveURL) self.setInstallationStep(of: availableXcode.version, to: .checkingSecurity) return self.verifySecurityAssessment(of: installedXcode) .combineLatest(self.verifySigningCertificate(of: installedXcode.path.url)) .map { _ in installedXcode } .eraseToAnyPublisher() } catch { return Fail(error: error) .eraseToAnyPublisher() } } .flatMap { installedXcode -> AnyPublisher in self.setInstallationStep(of: availableXcode.version, to: .finishing) return self.performPostInstallSteps(for: installedXcode) .map { installedXcode } // Show post-install errors but don't fail because of them .handleEvents(receiveCompletion: { [unowned self] completion in if case let .failure(error) = completion { self.error = error self.presentedAlert = .generic(title: localizeString("Alert.InstallArchive.Error.Title"), message: error.legibleLocalizedDescription) } resetDockProgressTracking() }) .catch { _ in Just(installedXcode) .setFailureType(to: Error.self) .eraseToAnyPublisher() } .eraseToAnyPublisher() } .eraseToAnyPublisher() case "dmg": throw InstallationError.unsupportedFileFormat(extension: "dmg") default: throw InstallationError.unsupportedFileFormat(extension: archiveURL.pathExtension) } } catch { return Fail(error: error) .eraseToAnyPublisher() } } func unarchiveAndMoveXIP(availableXcode: AvailableXcode, at source: URL, to destination: URL) -> AnyPublisher { self.setInstallationStep(of: availableXcode.version, to: .unarchiving) return unxipOrUnxipExperiment(source) .catch { error -> AnyPublisher in if let executionError = error as? ProcessExecutionError { if executionError.standardError.contains("damaged and can’t be expanded") { return Fail(error: InstallationError.damagedXIP(url: source)) .eraseToAnyPublisher() } else if executionError.standardError.contains("can’t be expanded because the selected volume doesn’t have enough free space.") { return Fail(error: InstallationError.notEnoughFreeSpaceToExpandArchive(archivePath: Path(url: source)!, version: availableXcode.version)) .eraseToAnyPublisher() } } return Fail(error: error) .eraseToAnyPublisher() } .tryMap { output -> URL in self.setInstallationStep(of: availableXcode.version, to: .moving(destination: destination.path)) let xcodeURL = source.deletingLastPathComponent().appendingPathComponent("Xcode.app") let xcodeBetaURL = source.deletingLastPathComponent().appendingPathComponent("Xcode-beta.app") if Current.files.fileExists(atPath: xcodeURL.path) { try Current.files.moveItem(at: xcodeURL, to: destination) } else if Current.files.fileExists(atPath: xcodeBetaURL.path) { try Current.files.moveItem(at: xcodeBetaURL, to: destination) } return destination } .handleEvents(receiveCancel: { if Current.files.fileExists(atPath: source.path) { try? Current.files.removeItem(source) } if Current.files.fileExists(atPath: destination.path) { try? Current.files.removeItem(destination) } }) .eraseToAnyPublisher() } func unxipOrUnxipExperiment(_ source: URL) -> AnyPublisher { if unxipExperiment { // All hard work done by https://github.com/saagarjha/unxip // Compiled to binary with `swiftc -parse-as-library -O unxip.swift` return Current.shell.unxipExperiment(source) } else { return Current.shell.unxip(source) } } public func verifySecurityAssessment(of xcode: InstalledXcode) -> AnyPublisher { return Current.shell.spctlAssess(xcode.path.url) .catch { (error: Swift.Error) -> AnyPublisher in var output = "" if let executionError = error as? ProcessExecutionError { output = [executionError.standardOutput, executionError.standardError].joined(separator: "\n") } return Fail(error: InstallationError.failedSecurityAssessment(xcode: xcode, output: output)) .eraseToAnyPublisher() } .map { _ in Void() } .eraseToAnyPublisher() } func verifySigningCertificate(of url: URL) -> AnyPublisher { return Current.shell.codesignVerify(url) .catch { error -> AnyPublisher in var output = "" if let executionError = error as? ProcessExecutionError { output = [executionError.standardOutput, executionError.standardError].joined(separator: "\n") } return Fail(error: InstallationError.codesignVerifyFailed(output: output)) .eraseToAnyPublisher() } .map { output -> CertificateInfo in // codesign prints to stderr return self.parseCertificateInfo(output.err) } .tryMap { cert in guard cert.teamIdentifier == XcodeTeamIdentifier, cert.authority == XcodeCertificateAuthority else { throw InstallationError.unexpectedCodeSigningIdentity(identifier: cert.teamIdentifier, certificateAuthority: cert.authority) } return Void() } .eraseToAnyPublisher() } public struct CertificateInfo { public var authority: [String] public var teamIdentifier: String public var bundleIdentifier: String } public func parseCertificateInfo(_ rawInfo: String) -> CertificateInfo { var info = CertificateInfo(authority: [], teamIdentifier: "", bundleIdentifier: "") for part in rawInfo.trimmingCharacters(in: .whitespacesAndNewlines).components(separatedBy: .newlines) { if part.hasPrefix("Authority") { info.authority.append(part.components(separatedBy: "=")[1]) } if part.hasPrefix("TeamIdentifier") { info.teamIdentifier = part.components(separatedBy: "=")[1] } if part.hasPrefix("Identifier") { info.bundleIdentifier = part.components(separatedBy: "=")[1] } } return info } // MARK: - Post-Install /// Attemps to install the helper once, then performs all post-install steps public func performPostInstallSteps(for xcode: InstalledXcode) { performPostInstallSteps(for: xcode) .sink( receiveCompletion: { completion in if case let .failure(error) = completion { self.error = error self.presentedAlert = .generic(title: localizeString("Alert.PostInstall.Title"), message: error.legibleLocalizedDescription) } }, receiveValue: {} ) .store(in: &cancellables) } /// Attemps to install the helper once, then performs all post-install steps public func performPostInstallSteps(for xcode: InstalledXcode) -> AnyPublisher { let postInstallPublisher: AnyPublisher = Deferred { [unowned self] in self.installHelperIfNecessary() } .flatMap { [unowned self] in self.enableDeveloperMode() } .flatMap { [unowned self] in self.approveLicense(for: xcode) } .flatMap { [unowned self] in self.installComponents(for: xcode) } .mapError { [unowned self] error in Logger.appState.error("Performing post-install steps failed: \(error.legibleLocalizedDescription)") return InstallationError.postInstallStepsNotPerformed(version: xcode.version, helperInstallState: self.helperInstallState) } .eraseToAnyPublisher() guard helperInstallState == .installed else { // If the helper isn't installed yet then we need to prepare the user for the install prompt, // and then actually perform the installation, // and the post-install steps need to wait until that is complete. // This subject, which completes upon isPreparingUserForActionRequiringHelper being invoked, is used to achieve that. // This is not the most straightforward code I've ever written... let helperInstallConsentSubject = PassthroughSubject() // Need to dispatch this to avoid duplicate alerts, // the second of which will crash when force-unwrapping isPreparingUserForActionRequiringHelper DispatchQueue.main.async { self.isPreparingUserForActionRequiringHelper = { [unowned self] userConsented in if userConsented { helperInstallConsentSubject.send() } else { Logger.appState.info("User did not consent to installing helper during post-install steps.") helperInstallConsentSubject.send( completion: .failure( InstallationError.postInstallStepsNotPerformed(version: xcode.version, helperInstallState: self.helperInstallState) ) ) } } self.presentedAlert = .privilegedHelper } unxipProgress.completedUnitCount = AppState.totalProgressUnits resetDockProgressTracking() return helperInstallConsentSubject .flatMap { postInstallPublisher } .eraseToAnyPublisher() } return postInstallPublisher } private func enableDeveloperMode() -> AnyPublisher { Current.helper.devToolsSecurityEnable() .flatMap { Current.helper.addStaffToDevelopersGroup() } .eraseToAnyPublisher() } private func approveLicense(for xcode: InstalledXcode) -> AnyPublisher { Current.helper.acceptXcodeLicense(xcode.path.string) .eraseToAnyPublisher() } private func installComponents(for xcode: InstalledXcode) -> AnyPublisher { Current.helper.runFirstLaunch(xcode.path.string) .flatMap { Current.shell.getUserCacheDir().map { $0.out } .combineLatest( Current.shell.buildVersion().map { $0.out }, Current.shell.xcodeBuildVersion(xcode).map { $0.out } ) } .flatMap { cacheDirectory, macOSBuildVersion, toolsVersion in Current.shell.touchInstallCheck(cacheDirectory, macOSBuildVersion, toolsVersion) } .map { _ in Void() } .eraseToAnyPublisher() } // MARK: - Dock Progress Tracking private func setupDockProgress() { Task { @MainActor in DockProgress.progressInstance = nil DockProgress.style = .bar let progress = Progress(totalUnitCount: AppState.totalProgressUnits) progress.kind = .file progress.fileOperationKind = .downloading overallProgress = progress DockProgress.progressInstance = overallProgress } } func resetDockProgressTracking() { Task { @MainActor in DockProgress.progress = 1 // Only way to completely remove overlay with DockProgress is setting progress to complete } } // MARK: - func setInstallationStep(of version: Version, to step: XcodeInstallationStep) { DispatchQueue.main.async { guard let index = self.allXcodes.firstIndex(where: { $0.version.isEquivalent(to: version) }) else { return } self.allXcodes[index].installState = .installing(step) let xcode = self.allXcodes[index] Current.notificationManager.scheduleNotification(title: xcode.version.major.description + "." + xcode.version.appleDescription, body: step.description, category: .normal) } } func setInstallationStep(of runtime: DownloadableRuntime, to step: RuntimeInstallationStep, postNotification: Bool = true) { DispatchQueue.main.async { guard let index = self.downloadableRuntimes.firstIndex(where: { $0.identifier == runtime.identifier }) else { return } self.downloadableRuntimes[index].installState = .installing(step) if postNotification { Current.notificationManager.scheduleNotification(title: runtime.name, body: step.description, category: .normal) } } } } extension AppState { func persistOrCleanUpResumeData(at path: Path, for completion: Subscribers.Completion) { switch completion { case .finished: try? Current.files.removeItem(at: path.url) case .failure(let error): guard let resumeData = (error as NSError).userInfo[NSURLSessionDownloadTaskResumeData] as? Data else { return } Current.files.createFile(atPath: path.string, contents: resumeData) } } } public enum InstallationError: LocalizedError, Equatable { case damagedXIP(url: URL) case notEnoughFreeSpaceToExpandArchive(archivePath: Path, version: Version) case failedToMoveXcodeToApplications case failedSecurityAssessment(xcode: InstalledXcode, output: String) case codesignVerifyFailed(output: String) case unexpectedCodeSigningIdentity(identifier: String, certificateAuthority: [String]) case unsupportedFileFormat(extension: String) case missingSudoerPassword case unavailableVersion(Version) case noNonPrereleaseVersionAvailable case noPrereleaseVersionAvailable case missingUsernameOrPassword case versionAlreadyInstalled(InstalledXcode) case invalidVersion(String) case versionNotInstalled(Version) case postInstallStepsNotPerformed(version: Version, helperInstallState: HelperInstallState) public var errorDescription: String? { switch self { case .damagedXIP(let url): return String(format: localizeString("InstallationError.DamagedXIP"), url.lastPathComponent) case let .notEnoughFreeSpaceToExpandArchive(archivePath, version): return String(format: localizeString("InstallationError.NotEnoughFreeSpaceToExpandArchive"), archivePath.basename(), version.appleDescription) case .failedToMoveXcodeToApplications: return String(format: localizeString("InstallationError.FailedToMoveXcodeToApplications"), Path.installDirectory.string) case .failedSecurityAssessment(let xcode, let output): return String(format: localizeString("InstallationError.FailedSecurityAssessment"), String(xcode.version), output, xcode.path.string) case .codesignVerifyFailed(let output): return String(format: localizeString("InstallationError.CodesignVerifyFailed"), output) case .unexpectedCodeSigningIdentity(let identity, let certificateAuthority): return String(format: localizeString("InstallationError.UnexpectedCodeSigningIdentity"), identity, certificateAuthority, XcodeTeamIdentifier, XcodeCertificateAuthority) case .unsupportedFileFormat(let fileExtension): return String(format: localizeString("InstallationError.UnsupportedFileFormat"), fileExtension) case .missingSudoerPassword: return localizeString("InstallationError.MissingSudoerPassword") case let .unavailableVersion(version): return String(format: localizeString("InstallationError.UnavailableVersion"), version.appleDescription) case .noNonPrereleaseVersionAvailable: return localizeString("InstallationError.NoNonPrereleaseVersionAvailable") case .noPrereleaseVersionAvailable: return localizeString("InstallationError.NoPrereleaseVersionAvailable") case .missingUsernameOrPassword: return localizeString("InstallationError.MissingUsernameOrPassword") case let .versionAlreadyInstalled(installedXcode): return String(format: localizeString("InstallationError.VersionAlreadyInstalled"), installedXcode.version.appleDescription, installedXcode.path.string) case let .invalidVersion(version): return String(format: localizeString("InstallationError.InvalidVersion"), version) case let .versionNotInstalled(version): return String(format: localizeString("InstallationError.VersionNotInstalled"), version.appleDescription) case let .postInstallStepsNotPerformed(version, helperInstallState): switch helperInstallState { case .installed: return String(format: localizeString("InstallationError.PostInstallStepsNotPerformed.Installed"), version.appleDescription) case .notInstalled, .unknown: return String(format: localizeString("InstallationError.PostInstallStepsNotPerformed.NotInstalled"), version.appleDescription) } } } } public enum InstallationType { case version(AvailableXcode) } public enum AutoInstallationType: Int, Identifiable { case none = 0 case newestVersion case newestBeta public var id: Self { self } public var isAutoInstalling: Bool { get { return self != .none } set { self = newValue ? .newestVersion : .none } } public var isAutoInstallingBeta: Bool { get { return self == .newestBeta } set { self = newValue ? .newestBeta : (isAutoInstalling ? .newestVersion : .none) } } } let XcodeTeamIdentifier = "59GAB85EFG" let XcodeCertificateAuthority = ["Software Signing", "Apple Code Signing Certification Authority", "Apple Root CA"] ================================================ FILE: Xcodes/Backend/AppState+Runtimes.swift ================================================ import Foundation import XcodesKit import OSLog import Combine import Path import AppleAPI import Version extension AppState { func updateDownloadableRuntimes() { Task { do { let downloadableRuntimes = try await self.runtimeService.downloadableRuntimes() let runtimes = downloadableRuntimes.downloadables.map { runtime in var updatedRuntime = runtime // This loops through and matches up the simulatorVersion to the mappings let simulatorBuildUpdate = downloadableRuntimes.sdkToSimulatorMappings.filter { SDKToSimulatorMapping in SDKToSimulatorMapping.simulatorBuildUpdate == runtime.simulatorVersion.buildUpdate } updatedRuntime.sdkBuildUpdate = simulatorBuildUpdate.map { $0.sdkBuildUpdate } return updatedRuntime } DispatchQueue.main.async { self.downloadableRuntimes = runtimes } try? cacheDownloadableRuntimes(runtimes) } catch { Logger.appState.error("Error downloading runtimes: \(error.localizedDescription)") } } } func updateInstalledRuntimes() { Task { do { Logger.appState.info("Loading Installed runtimes") let runtimes = try await self.runtimeService.localInstalledRuntimes() DispatchQueue.main.async { self.installedRuntimes = runtimes } } catch { Logger.appState.error("Error loading installed runtimes: \(error.localizedDescription)") } } } func downloadRuntime(runtime: DownloadableRuntime) { guard let selectedXcode = self.allXcodes.first(where: { $0.selected }) else { Logger.appState.error("No selected Xcode") DispatchQueue.main.async { self.presentedAlert = .generic(title: localizeString("Alert.Install.Error.Title"), message: "No selected Xcode. Please make an Xcode active") } return } // new runtimes if runtime.contentType == .cryptexDiskImage { // only selected xcodes > 16.1 beta 3 can download runtimes via a xcodebuild -downloadPlatform version // only Runtimes coming from cryptexDiskImage can be downloaded via xcodebuild if selectedXcode.version > Version(major: 16, minor: 0, patch: 0) { if runtime.architectures?.isAppleSilicon ?? false { // Need Xcode 26 but with some RC/Beta's its simpler to just to greater > 25 if selectedXcode.version > Version(major: 25, minor: 0, patch: 0) { downloadRuntimeViaXcodeBuild(runtime: runtime) } else { // not supported Logger.appState.error("Trying to download a runtime we can't download") DispatchQueue.main.async { self.presentedAlert = .generic(title: localizeString("Alert.Install.Error.Title"), message: localizeString("Alert.Install.Error.Need.Xcode26")) } return } } else { downloadRuntimeViaXcodeBuild(runtime: runtime) } } else { // not supported Logger.appState.error("Trying to download a runtime we can't download") DispatchQueue.main.async { self.presentedAlert = .generic(title: localizeString("Alert.Install.Error.Title"), message: localizeString("Alert.Install.Error.Need.Xcode16.1")) } return } } else { downloadRuntimeObseleteWay(runtime: runtime) } } func downloadRuntimeViaXcodeBuild(runtime: DownloadableRuntime) { let downloadRuntimeTask = Current.shell.downloadRuntime(runtime.platform.shortName, runtime.simulatorVersion.buildUpdate, runtime.architectures?.isAppleSilicon ?? false ? Architecture.arm64.rawValue : nil) runtimePublishers[runtime.identifier] = Task { [weak self] in guard let self = self else { return } do { for try await progress in downloadRuntimeTask { if progress.isIndeterminate { DispatchQueue.main.async { self.setInstallationStep(of: runtime, to: .installing, postNotification: false) } } else { DispatchQueue.main.async { self.setInstallationStep(of: runtime, to: .downloading(progress: progress), postNotification: false) } } } Logger.appState.debug("Done downloading runtime - \(runtime.name)") DispatchQueue.main.async { guard let index = self.downloadableRuntimes.firstIndex(where: { $0.identifier == runtime.identifier }) else { return } self.downloadableRuntimes[index].installState = .installed self.update() } } catch { Logger.appState.error("Error downloading runtime: \(error.localizedDescription)") DispatchQueue.main.async { self.error = error if let error = error as? String { self.presentedAlert = .generic(title: localizeString("Alert.Install.Error.Title"), message: error) } else { self.presentedAlert = .generic(title: localizeString("Alert.Install.Error.Title"), message: error.legibleLocalizedDescription) } } } } } func downloadRuntimeObseleteWay(runtime: DownloadableRuntime) { runtimePublishers[runtime.identifier] = Task { do { let downloadedURL = try await downloadRunTimeFull(runtime: runtime) if !Task.isCancelled { Logger.appState.debug("Installing runtime: \(runtime.name)") DispatchQueue.main.async { self.setInstallationStep(of: runtime, to: .installing) } switch runtime.contentType { case .cryptexDiskImage: // not supported yet (do we need to for old packages?) throw "Installing via cryptexDiskImage not support - please install manually from \(downloadedURL.description)" case .package: // not supported yet (do we need to for old packages?) throw "Installing via package not support - please install manually from \(downloadedURL.description)" case .diskImage: try await self.installFromImage(dmgURL: downloadedURL) DispatchQueue.main.async { self.setInstallationStep(of: runtime, to: .trashingArchive) } try Current.files.removeItem(at: downloadedURL) } DispatchQueue.main.async { guard let index = self.downloadableRuntimes.firstIndex(where: { $0.identifier == runtime.identifier }) else { return } self.downloadableRuntimes[index].installState = .installed } updateInstalledRuntimes() } } catch { Logger.appState.error("Error downloading runtime: \(error.localizedDescription)") DispatchQueue.main.async { self.error = error if let error = error as? String { self.presentedAlert = .generic(title: localizeString("Alert.Install.Error.Title"), message: error) } else { self.presentedAlert = .generic(title: localizeString("Alert.Install.Error.Title"), message: error.legibleLocalizedDescription) } } } } } func downloadRunTimeFull(runtime: DownloadableRuntime) async throws -> URL { guard let source = runtime.source else { throw "Invalid runtime source" } guard let downloadPath = runtime.downloadPath else { throw "Invalid runtime downloadPath" } // sets a proper cookie for runtimes try await validateADCSession(path: downloadPath) let downloader = Downloader(rawValue: Current.defaults.string(forKey: "downloader") ?? "aria2") ?? .aria2 let url = URL(string: source)! let expectedRuntimePath = Path.xcodesApplicationSupport/"\(url.lastPathComponent)" // aria2 downloads directly to the destination (instead of into /tmp first) so we need to make sure that the download isn't incomplete let aria2DownloadMetadataPath = expectedRuntimePath.parent/(expectedRuntimePath.basename() + ".aria2") var aria2DownloadIsIncomplete = false if case .aria2 = downloader, aria2DownloadMetadataPath.exists { aria2DownloadIsIncomplete = true } if Current.files.fileExistsAtPath(expectedRuntimePath.string), aria2DownloadIsIncomplete == false { Logger.appState.info("Found existing runtime that will be used for installation at \(expectedRuntimePath).") return expectedRuntimePath.url } Logger.appState.info("Downloading \(runtime.visibleIdentifier) with \(downloader)") switch downloader { case .aria2: let aria2Path = Path(url: Bundle.main.url(forAuxiliaryExecutable: "aria2c")!)! for try await progress in downloadRuntimeWithAria2(runtime, to: expectedRuntimePath, aria2Path: aria2Path) { DispatchQueue.main.async { self.setInstallationStep(of: runtime, to: .downloading(progress: progress), postNotification: false) } } Logger.appState.debug("Done downloading runtime") case .urlSession: throw "Downloading runtimes with URLSession is not supported. Please use aria2" } return expectedRuntimePath.url } public func downloadRuntimeWithAria2(_ runtime: DownloadableRuntime, to destination: Path, aria2Path: Path) -> AsyncThrowingStream { guard let url = runtime.url else { return AsyncThrowingStream { continuation in continuation.finish(throwing: "Invalid or non existant runtime url") } } let cookies = AppleAPI.Current.network.session.configuration.httpCookieStorage?.cookies(for: url) ?? [] return Current.shell.downloadWithAria2Async(aria2Path, url, destination, cookies) } public func installFromImage(dmgURL: URL) async throws { try await self.runtimeService.installRuntimeImage(dmgURL: dmgURL) } func cancelRuntimeInstall(runtime: DownloadableRuntime) { // Cancel the publisher runtimePublishers[runtime.identifier]?.cancel() runtimePublishers[runtime.identifier] = nil // If the download is cancelled by the user, clean up the download files that aria2 creates. guard let source = runtime.source else { return } let url = URL(string: source)! let expectedRuntimePath = Path.xcodesApplicationSupport/"\(url.lastPathComponent)" let aria2DownloadMetadataPath = expectedRuntimePath.parent/(expectedRuntimePath.basename() + ".aria2") try? Current.files.removeItem(at: expectedRuntimePath.url) try? Current.files.removeItem(at: aria2DownloadMetadataPath.url) guard let index = self.downloadableRuntimes.firstIndex(where: { $0.identifier == runtime.identifier }) else { return } self.downloadableRuntimes[index].installState = .notInstalled updateInstalledRuntimes() } func runtimeInstallPath(xcode: Xcode, runtime: DownloadableRuntime) -> Path? { if let coreSimulatorInfo = coreSimulatorInfo(runtime: runtime) { let urlString = coreSimulatorInfo.path["relative"]! // app was not allowed to open up file:// url's so remove let fileRemovedString = urlString.replacingOccurrences(of: "file://", with: "") let url = URL(fileURLWithPath: fileRemovedString) return Path(url: url)! } return nil } func coreSimulatorInfo(runtime: DownloadableRuntime) -> CoreSimulatorImage? { return installedRuntimes.filter({ $0.runtimeInfo.build == runtime.simulatorVersion.buildUpdate && ((runtime.architectures ?? []).isEmpty ? true : $0.runtimeInfo.supportedArchitectures == runtime.architectures )}).first } func deleteRuntime(runtime: DownloadableRuntime) async throws { if let info = coreSimulatorInfo(runtime: runtime) { try await runtimeService.deleteRuntime(identifier: info.uuid) // give it some time to actually finish deleting before updating DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in self?.updateInstalledRuntimes() } } else { throw "No simulator found with \(runtime.identifier)" } } } extension AnyPublisher { func async() async throws -> Output { try await withCheckedThrowingContinuation { continuation in var cancellable: AnyCancellable? cancellable = first() .sink { result in switch result { case .finished: break case let .failure(error): continuation.resume(throwing: error) } cancellable?.cancel() } receiveValue: { value in continuation.resume(with: .success(value)) } } } } extension AnyPublisher where Failure: Error { struct Subscriber { fileprivate let send: (Output) -> Void fileprivate let complete: (Subscribers.Completion) -> Void func send(_ value: Output) { self.send(value) } func send(completion: Subscribers.Completion) { self.complete(completion) } } init(_ closure: (Subscriber) -> AnyCancellable) { let subject = PassthroughSubject() let subscriber = Subscriber( send: subject.send, complete: subject.send(completion:) ) let cancel = closure(subscriber) self = subject .handleEvents(receiveCancel: cancel.cancel) .eraseToAnyPublisher() } } extension AnyPublisher where Failure == Error { init(taskPriority: TaskPriority? = nil, asyncFunc: @escaping () async throws -> Output) { self.init { subscriber in let task = Task(priority: taskPriority) { do { subscriber.send(try await asyncFunc()) subscriber.send(completion: .finished) } catch { subscriber.send(completion: .failure(error)) } } return AnyCancellable { task.cancel() } } } } ================================================ FILE: Xcodes/Backend/AppState+Update.swift ================================================ import Combine import Foundation import Path import Version import SwiftSoup import AppleAPI import XcodesKit extension AppState { var isReadyForUpdate: Bool { guard let lastUpdated = Current.defaults.date(forKey: "lastUpdated"), // This is bad date math but for this use case it doesn't need to be exact lastUpdated < Current.date().addingTimeInterval(-60 * 60 * 5) else { return false } return true } func updateIfNeeded() { guard isReadyForUpdate else { updatePublisher = updateSelectedXcodePath() .sink( receiveCompletion: { _ in self.updatePublisher = nil }, receiveValue: { _ in } ) return } update() as Void } func update() { guard !isUpdating else { return } updateDownloadableRuntimes() updateInstalledRuntimes() updatePublisher = updateSelectedXcodePath() .flatMap { _ in self.updateAvailableXcodes(from: self.dataSource) } .sink( receiveCompletion: { [unowned self] completion in switch completion { case let .failure(error): // Prevent setting the app state error if it is an invalid session, we will present the sign in view instead if error as? AuthenticationError != .invalidSession { self.error = error self.presentedAlert = .generic(title: localizeString("Alert.Update.Error.Title"), message: error.legibleLocalizedDescription) } case .finished: Current.defaults.setDate(Current.date(), forKey: "lastUpdated") } self.updatePublisher = nil }, receiveValue: { _ in } ) } func updateSelectedXcodePath() -> AnyPublisher { Current.shell.xcodeSelectPrintPath() .handleEvents(receiveOutput: { output in self.selectedXcodePath = output.out }) // Ignore xcode-select failures .map { _ in Void() } .catch { _ in Just(()) } .eraseToAnyPublisher() } private func updateAvailableXcodes(from dataSource: DataSource) -> AnyPublisher<[AvailableXcode], Error> { switch dataSource { case .apple: return signInIfNeeded() .flatMap { [unowned self] in // this will check to see if the Apple ID is a valid Apple Developer or not. // If it's not, we can't use the Apple source to get xcode info. self.validateSession() } .flatMap { [unowned self] in self.releasedXcodes().combineLatest(self.prereleaseXcodes()) } .receive(on: DispatchQueue.main) .map { releasedXcodes, prereleaseXcodes in // Starting with Xcode 11 beta 6, developer.apple.com/download and developer.apple.com/download/more both list some pre-release versions of Xcode. // Previously pre-release versions only appeared on developer.apple.com/download. // /download/more doesn't include build numbers, so we trust that if the version number and prerelease identifiers are the same that they're the same build. // If an Xcode version is listed on both sites then prefer the one on /download because the build metadata is used to compare against installed Xcodes. let xcodes = releasedXcodes.filter { releasedXcode in prereleaseXcodes.contains { $0.version.isEquivalent(to: releasedXcode.version) } == false } + prereleaseXcodes return xcodes } .handleEvents( receiveOutput: { xcodes in self.availableXcodes = xcodes try? self.cacheAvailableXcodes(xcodes) } ) .eraseToAnyPublisher() case .xcodeReleases: return xcodeReleases() .receive(on: DispatchQueue.main) .handleEvents( receiveOutput: { xcodes in self.availableXcodes = xcodes try? self.cacheAvailableXcodes(xcodes) } ) .eraseToAnyPublisher() } } } extension AppState { // MARK: - Available Xcode Cache func loadCachedAvailableXcodes() throws { guard let data = Current.files.contents(atPath: Path.cacheFile.string) else { return } let xcodes = try JSONDecoder().decode([AvailableXcode].self, from: data) self.availableXcodes = xcodes } func cacheAvailableXcodes(_ xcodes: [AvailableXcode]) throws { let data = try JSONEncoder().encode(xcodes) try FileManager.default.createDirectory(at: Path.cacheFile.url.deletingLastPathComponent(), withIntermediateDirectories: true) try data.write(to: Path.cacheFile.url) } // MARK: Runtime Cache func loadCacheDownloadableRuntimes() throws { guard let data = Current.files.contents(atPath: Path.runtimeCacheFile.string) else { return } let runtimes = try JSONDecoder().decode([DownloadableRuntime].self, from: data) self.downloadableRuntimes = runtimes } func cacheDownloadableRuntimes(_ runtimes: [DownloadableRuntime]) throws { let data = try JSONEncoder().encode(runtimes) try FileManager.default.createDirectory(at: Path.runtimeCacheFile.url.deletingLastPathComponent(), withIntermediateDirectories: true) try data.write(to: Path.runtimeCacheFile.url) } } extension AppState { // MARK: - Apple private func releasedXcodes() -> AnyPublisher<[AvailableXcode], Swift.Error> { Current.network.dataTask(with: URLRequest.downloads) .map(\.data) .decode(type: Downloads.self, decoder: configure(JSONDecoder()) { $0.dateDecodingStrategy = .formatted(.downloadsDateModified) }) .tryMap { downloads -> [AvailableXcode] in if downloads.hasError { throw AuthenticationError.invalidResult(resultString: downloads.resultsString) } guard let downloadList = downloads.downloads else { throw AuthenticationError.invalidResult(resultString: localizeString("DownloadingError")) } let xcodes = downloadList .filter { $0.name.range(of: "^Xcode [0-9]", options: .regularExpression) != nil } .compactMap { download -> AvailableXcode? in let urlPrefix = URL(string: "https://download.developer.apple.com/")! guard let xcodeFile = download.files.first(where: { $0.remotePath.hasSuffix("dmg") || $0.remotePath.hasSuffix("xip") }), let version = Version(xcodeVersion: download.name) else { return nil } let url = urlPrefix.appendingPathComponent(xcodeFile.remotePath) return AvailableXcode(version: version, url: url, filename: String(xcodeFile.remotePath.suffix(fromLast: "/")), releaseDate: download.dateModified, fileSize: xcodeFile.fileSize) } return xcodes } .eraseToAnyPublisher() } private func prereleaseXcodes() -> AnyPublisher<[AvailableXcode], Error> { Current.network.dataTask(with: URLRequest.download) .tryMap { (data, _) -> [AvailableXcode] in try self.parsePrereleaseXcodes(from: data) } .eraseToAnyPublisher() } private func parsePrereleaseXcodes(from data: Data) throws -> [AvailableXcode] { let body = String(data: data, encoding: .utf8)! let document = try SwiftSoup.parse(body) guard let xcodeHeader = try document.select("h2:containsOwn(Xcode)").first(), let productBuildVersion = try xcodeHeader.parent()?.select("li:contains(Build)").text().replacingOccurrences(of: "Build", with: ""), let releaseDateString = try xcodeHeader.parent()?.select("li:contains(Released)").text().replacingOccurrences(of: "Released", with: ""), let version = Version(xcodeVersion: try xcodeHeader.text(), buildMetadataIdentifier: productBuildVersion), let path = try document.select(".direct-download[href*=xip]").first()?.attr("href"), let url = URL(string: "https://developer.apple.com" + path) else { return [] } let filename = String(path.suffix(fromLast: "/")) return [AvailableXcode(version: version, url: url, filename: filename, releaseDate: DateFormatter.downloadsReleaseDate.date(from: releaseDateString))] } } extension AppState { // MARK: - XcodeReleases private func xcodeReleases() -> AnyPublisher<[AvailableXcode], Error> { Current.network.dataTask(with: URLRequest(url: URL(string: "https://xcodereleases.com/data.json")!)) .map(\.data) .decode(type: [XcodeRelease].self, decoder: JSONDecoder()) .map { xcReleasesXcodes in let xcodes = xcReleasesXcodes.compactMap { xcReleasesXcode -> AvailableXcode? in guard let downloadURL = xcReleasesXcode.links?.download?.url, let version = Version(xcReleasesXcode: xcReleasesXcode) else { return nil } let releaseDate = Calendar(identifier: .gregorian).date(from: DateComponents( year: xcReleasesXcode.date.year, month: xcReleasesXcode.date.month, day: xcReleasesXcode.date.day )) return AvailableXcode( version: version, url: downloadURL, filename: String(downloadURL.path.suffix(fromLast: "/")), releaseDate: releaseDate, requiredMacOSVersion: xcReleasesXcode.requires, releaseNotesURL: xcReleasesXcode.links?.notes?.url, sdks: xcReleasesXcode.sdks, compilers: xcReleasesXcode.compilers, architectures: xcReleasesXcode.architectures ) } return xcodes } .eraseToAnyPublisher() } } ================================================ FILE: Xcodes/Backend/AppState.swift ================================================ import AppKit import AppleAPI import Combine import Path import LegibleError import KeychainAccess import Path import Version import os.log import DockProgress import XcodesKit import LibFido2Swift enum PreferenceKey: String { case installPath case localPath case unxipExperiment case createSymLinkOnSelect case onSelectActionType case showOpenInRosettaOption case autoInstallation case SUEnableAutomaticChecks case includePrereleaseVersions case downloader case dataSource case xcodeListCategory case allowedMajorVersions case hideSupportXcodes case xcodeListArchitectures func isManaged() -> Bool { UserDefaults.standard.objectIsForced(forKey: self.rawValue) } } class AppState: ObservableObject { private let client = AppleAPI.Client() internal let runtimeService = RuntimeService() // MARK: - Published Properties @Published var authenticationState: AuthenticationState = .unauthenticated @Published var availableXcodes: [AvailableXcode] = [] { willSet { if newValue.count > availableXcodes.count && availableXcodes.count != 0 { Current.notificationManager.scheduleNotification(title: localizeString("Notification.NewXcodeVersion.Title"), body: localizeString("Notification.NewXcodeVersion.Body"), category: .normal) } updateAllXcodes( availableXcodes: newValue, installedXcodes: Current.files.installedXcodes(Path.installDirectory), selectedXcodePath: selectedXcodePath ) } didSet { autoInstallIfNeeded() } } @Published var allXcodes: [Xcode] = [] @Published var selectedXcodePath: String? { willSet { updateAllXcodes( availableXcodes: availableXcodes, installedXcodes: Current.files.installedXcodes(Path.installDirectory), selectedXcodePath: newValue ) } } @Published var updatePublisher: AnyCancellable? var isUpdating: Bool { updatePublisher != nil } @Published var presentedSheet: XcodesSheet? = nil @Published var isProcessingAuthRequest = false @Published var xcodeBeingConfirmedForUninstallation: Xcode? @Published var presentedAlert: XcodesAlert? @Published var presentedPreferenceAlert: XcodesPreferencesAlert? @Published var helperInstallState: HelperInstallState = .notInstalled /// Whether the user is being prepared for the helper installation alert with an explanation. /// This closure will be performed after the user chooses whether or not to proceed. @Published var isPreparingUserForActionRequiringHelper: ((Bool) -> Void)? // MARK: - Errors @Published var error: Error? @Published var authError: Error? // MARK: Advanced Preferences @Published var localPath = "" { didSet { Current.defaults.set(localPath, forKey: "localPath") } } var disableLocalPathChange: Bool { PreferenceKey.localPath.isManaged() } @Published var installPath = "" { didSet { Current.defaults.set(installPath, forKey: "installPath") } } var disableInstallPathChange: Bool { PreferenceKey.installPath.isManaged() } @Published var unxipExperiment = false { didSet { Current.defaults.set(unxipExperiment, forKey: "unxipExperiment") } } var disableUnxipExperiment: Bool { PreferenceKey.unxipExperiment.isManaged() } @Published var createSymLinkOnSelect = false { didSet { Current.defaults.set(createSymLinkOnSelect, forKey: "createSymLinkOnSelect") } } var createSymLinkOnSelectDisabled: Bool { return onSelectActionType == .rename || PreferenceKey.createSymLinkOnSelect.isManaged() } @Published var onSelectActionType = SelectedActionType.none { didSet { Current.defaults.set(onSelectActionType.rawValue, forKey: "onSelectActionType") if onSelectActionType == .rename { createSymLinkOnSelect = false } } } var onSelectActionTypeDisabled: Bool { PreferenceKey.onSelectActionType.isManaged() } @Published var showOpenInRosettaOption = false { didSet { Current.defaults.set(showOpenInRosettaOption, forKey: "showOpenInRosettaOption") } } @Published var terminateAfterLastWindowClosed = false { didSet { Current.defaults.set(terminateAfterLastWindowClosed, forKey: "terminateAfterLastWindowClosed") } } // MARK: - Runtimes @Published var downloadableRuntimes: [DownloadableRuntime] = [] @Published var installedRuntimes: [CoreSimulatorImage] = [] // MARK: - Publisher Cancellables var cancellables = Set() private var installationPublishers: [XcodeID: AnyCancellable] = [:] internal var runtimePublishers: [String: Task<(), any Error>] = [:] private var selectPublisher: AnyCancellable? private var uninstallPublisher: AnyCancellable? private var autoInstallTimer: Timer? // MARK: - Dock Progress Tracking public static let totalProgressUnits = Int64(10) public static let unxipProgressWeight = Int64(1) var overallProgress = Progress() var unxipProgress = { let progress = Progress(totalUnitCount: totalProgressUnits) progress.kind = .file progress.fileOperationKind = .copying return progress }() // MARK: - var dataSource: DataSource { Current.defaults.string(forKey: "dataSource").flatMap(DataSource.init(rawValue:)) ?? .default } var savedUsername: String? { Current.defaults.string(forKey: "username") } var hasSavedUsername: Bool { savedUsername != nil } var bottomStatusBarMessage: String { let formatter = DateFormatter() formatter.dateFormat = "dd/MM/yyyy" let finishDate = formatter.date(from: "11/06/2022") if Date().compare(finishDate!) == .orderedAscending { return String(format: localizeString("WWDC.Message"), "2022") } return "" } // MARK: - Init init() { guard !isTesting else { return } try? loadCachedAvailableXcodes() try? loadCacheDownloadableRuntimes() checkIfHelperIsInstalled() setupAutoInstallTimer() setupDefaults() } func setupDefaults() { localPath = Current.defaults.string(forKey: "localPath") ?? Path.defaultXcodesApplicationSupport.string unxipExperiment = Current.defaults.bool(forKey: "unxipExperiment") ?? false createSymLinkOnSelect = Current.defaults.bool(forKey: "createSymLinkOnSelect") ?? false onSelectActionType = SelectedActionType(rawValue: Current.defaults.string(forKey: "onSelectActionType") ?? "none") ?? .none installPath = Current.defaults.string(forKey: "installPath") ?? Path.defaultInstallDirectory.string showOpenInRosettaOption = Current.defaults.bool(forKey: "showOpenInRosettaOption") ?? false terminateAfterLastWindowClosed = Current.defaults.bool(forKey: "terminateAfterLastWindowClosed") ?? false } // MARK: Timer /// Runs a timer every 6 hours when app is open to check if it needs to auto install any xcodes func setupAutoInstallTimer() { guard let storageValue = Current.defaults.get(forKey: "autoInstallation") as? Int, let autoInstallType = AutoInstallationType(rawValue: storageValue) else { return } if autoInstallType == .none { return } autoInstallTimer = Timer.scheduledTimer(withTimeInterval: 60*60*6, repeats: true) { [weak self] _ in self?.updateIfNeeded() } } // MARK: - Authentication func validateADCSession(path: String) -> AnyPublisher { return Current.network.dataTask(with: URLRequest.downloadADCAuth(path: path)) .receive(on: DispatchQueue.main) .tryMap { result -> Void in let httpResponse = result.response as! HTTPURLResponse if httpResponse.statusCode == 401 { throw AuthenticationError.notAuthorized } } .eraseToAnyPublisher() } func validateADCSession(path: String) async throws { let result = try await Current.network.dataTaskAsync(with: URLRequest.downloadADCAuth(path: path)) let httpResponse = result.1 as! HTTPURLResponse if httpResponse.statusCode == 401 { throw AuthenticationError.notAuthorized } } func validateSession() -> AnyPublisher { return Current.network.validateSession() .receive(on: DispatchQueue.main) .handleEvents(receiveCompletion: { completion in if case .failure = completion { // this is causing some awkwardness with showing an alert with the error and also popping up the sign in view // self.authenticationState = .unauthenticated // self.presentedSheet = .signIn } }) .eraseToAnyPublisher() } func signInIfNeeded() -> AnyPublisher { validateSession() .catch { (error) -> AnyPublisher in guard let username = self.savedUsername, let password = try? Current.keychain.getString(username) else { return Fail(error: error) .eraseToAnyPublisher() } return self.signIn(username: username, password: password) .map { _ in Void() } .eraseToAnyPublisher() } .eraseToAnyPublisher() } func signIn(username: String, password: String) { authError = nil signIn(username: username.lowercased(), password: password) .sink( receiveCompletion: { _ in }, receiveValue: { _ in } ) .store(in: &cancellables) } func signIn(username: String, password: String) -> AnyPublisher { try? Current.keychain.set(password, key: username) Current.defaults.set(username, forKey: "username") isProcessingAuthRequest = true return client.srpLogin(accountName: username, password: password) .receive(on: DispatchQueue.main) .handleEvents( receiveOutput: { authenticationState in self.authenticationState = authenticationState }, receiveCompletion: { completion in self.handleAuthenticationFlowCompletion(completion) self.isProcessingAuthRequest = false } ) .eraseToAnyPublisher() } func handleTwoFactorOption(_ option: TwoFactorOption, authOptions: AuthOptionsResponse, serviceKey: String, sessionID: String, scnt: String) { let sessionData = AppleSessionData(serviceKey: serviceKey, sessionID: sessionID, scnt: scnt) if option == .securityKey, fido2DeviceIsPresent() && !fido2DeviceNeedsPin() { createAndSubmitSecurityKeyAssertationWithPinCode(nil, sessionData: sessionData, authOptions: authOptions) } else { self.presentedSheet = .twoFactor(.init( option: option, authOptions: authOptions, sessionData: sessionData )) } } func requestSMS(to trustedPhoneNumber: AuthOptionsResponse.TrustedPhoneNumber, authOptions: AuthOptionsResponse, sessionData: AppleSessionData) { isProcessingAuthRequest = true client.requestSMSSecurityCode(to: trustedPhoneNumber, authOptions: authOptions, sessionData: sessionData) .receive(on: DispatchQueue.main) .sink( receiveCompletion: { completion in self.handleAuthenticationFlowCompletion(completion) self.isProcessingAuthRequest = false }, receiveValue: { authenticationState in self.authenticationState = authenticationState if case let AuthenticationState.waitingForSecondFactor(option, authOptions, sessionData) = authenticationState { self.handleTwoFactorOption(option, authOptions: authOptions, serviceKey: sessionData.serviceKey, sessionID: sessionData.sessionID, scnt: sessionData.scnt) } } ) .store(in: &cancellables) } func choosePhoneNumberForSMS(authOptions: AuthOptionsResponse, sessionData: AppleSessionData) { self.presentedSheet = .twoFactor(.init( option: .smsPendingChoice, authOptions: authOptions, sessionData: sessionData )) } func submitSecurityCode(_ code: SecurityCode, sessionData: AppleSessionData) { isProcessingAuthRequest = true client.submitSecurityCode(code, sessionData: sessionData) .receive(on: DispatchQueue.main) .sink( receiveCompletion: { completion in self.handleAuthenticationFlowCompletion(completion) self.isProcessingAuthRequest = false }, receiveValue: { authenticationState in self.authenticationState = authenticationState } ) .store(in: &cancellables) } private lazy var fido2 = FIDO2() func createAndSubmitSecurityKeyAssertationWithPinCode(_ pinCode: String?, sessionData: AppleSessionData, authOptions: AuthOptionsResponse) { self.presentedSheet = .securityKeyTouchToConfirm guard let fsaChallenge = authOptions.fsaChallenge else { // This shouldn't happen // we shouldn't have called this method without setting the fsaChallenge // so this is an assertionFailure assertionFailure() self.authError = "Something went wrong. Please file a bug report" return } // The challenge is encoded in Base64URL encoding let challengeUrl = fsaChallenge.challenge let challenge = FIDO2.base64urlToBase64(base64url: challengeUrl) let origin = "https://idmsa.apple.com" let rpId = "apple.com" // Allowed creds is sent as a comma separated string let validCreds = fsaChallenge.allowedCredentials.split(separator: ",").map(String.init) Task { do { let response = try fido2.respondToChallenge(args: ChallengeArgs(rpId: rpId, validCredentials: validCreds, devPin: pinCode, challenge: challenge, origin: origin)) Task { @MainActor in self.isProcessingAuthRequest = true } let respData = try JSONEncoder().encode(response) client.submitChallenge(response: respData, sessionData: AppleSessionData(serviceKey: sessionData.serviceKey, sessionID: sessionData.sessionID, scnt: sessionData.scnt)) .receive(on: DispatchQueue.main) .handleEvents( receiveOutput: { authenticationState in self.authenticationState = authenticationState }, receiveCompletion: { completion in self.handleAuthenticationFlowCompletion(completion) self.isProcessingAuthRequest = false } ).sink( receiveCompletion: { _ in }, receiveValue: { _ in } ).store(in: &cancellables) } catch FIDO2Error.canceledByUser { // User cancelled the auth flow // we don't have to show an error // because the sheet will already be dismissed } catch { Task { @MainActor in authError = error } } } } func fido2DeviceIsPresent() -> Bool { fido2.hasDeviceAttached() } func fido2DeviceNeedsPin() -> Bool { do { return try fido2.deviceHasPin() } catch { Task { @MainActor in authError = error } return true } } func cancelSecurityKeyAssertationRequest() { self.fido2.cancel() } private func handleAuthenticationFlowCompletion(_ completion: Subscribers.Completion) { switch completion { case let .failure(error): // remove saved username and any stored keychain password if authentication fails so it doesn't try again. clearLoginCredentials() Logger.appState.error("Authentication error: \(error.legibleDescription)") self.authError = error case .finished: switch self.authenticationState { case .authenticated, .unauthenticated, .notAppleDeveloper: self.presentedSheet = nil case let .waitingForSecondFactor(option, authOptions, sessionData): self.handleTwoFactorOption(option, authOptions: authOptions, serviceKey: sessionData.serviceKey, sessionID: sessionData.sessionID, scnt: sessionData.scnt) } } } func signOut() { clearLoginCredentials() AppleAPI.Current.network.session.configuration.httpCookieStorage?.removeCookies(since: .distantPast) authenticationState = .unauthenticated } // MARK: - Helper /// Install the privileged helper if it isn't already installed. /// /// The way this is done is a little roundabout, because it requires user interaction in an alert before installation should be attempted. /// The first time this method is invoked should be with `shouldPrepareUserForHelperInstallation` set to true. /// If the helper is already installed, then nothing will happen. /// If the helper is not already installed, the user will be prepared for installation and this method will return early. /// If they consent to installing the helper then this method will be invoked again with `shouldPrepareUserForHelperInstallation` set to false. /// This will install the helper. /// /// - Parameter shouldPrepareUserForHelperInstallation: Whether the user should be presented with an alert preparing them for helper installation. func installHelperIfNecessary(shouldPrepareUserForHelperInstallation: Bool = true) { guard helperInstallState == .installed || shouldPrepareUserForHelperInstallation == false else { isPreparingUserForActionRequiringHelper = { [unowned self] userConsented in guard userConsented else { return } self.installHelperIfNecessary(shouldPrepareUserForHelperInstallation: false) } presentedAlert = .privilegedHelper return } installHelperIfNecessary() .sink( receiveCompletion: { [unowned self] completion in if case let .failure(error) = completion { self.error = error self.presentedAlert = .generic(title: localizeString("Alert.PrivilegedHelper.Error.Title"), message: error.legibleLocalizedDescription) } }, receiveValue: {} ) .store(in: &cancellables) } func installHelperIfNecessary() -> AnyPublisher { Result { if helperInstallState == .notInstalled { try Current.helper.install() checkIfHelperIsInstalled() } } .publisher .subscribe(on: DispatchQueue.main) .eraseToAnyPublisher() } private func checkIfHelperIsInstalled() { helperInstallState = .unknown Current.helper.checkIfLatestHelperIsInstalled() .receive(on: DispatchQueue.main) .sink( receiveValue: { installed in self.helperInstallState = installed ? .installed : .notInstalled } ) .store(in: &cancellables) } // MARK: - Install func checkMinVersionAndInstall(id: XcodeID) { guard let availableXcode = availableXcodes.first(where: { $0.xcodeID == id }) else { return } // Check to see if users macOS is supported if let requiredMacOSVersion = availableXcode.requiredMacOSVersion { if hasMinSupportedOS(requiredMacOSVersion: requiredMacOSVersion) { // prompt self.presentedAlert = .checkMinSupportedVersion(xcode: availableXcode, macOS: ProcessInfo.processInfo.operatingSystemVersion.versionString()) return } } switch self.dataSource { case .apple: install(id: id) case .xcodeReleases: install(id: id) } } func hasMinSupportedOS(requiredMacOSVersion: String) -> Bool { let split = requiredMacOSVersion.components(separatedBy: ".").compactMap { Int($0) } let xcodeMinimumMacOSVersion = OperatingSystemVersion(majorVersion: split[safe: 0] ?? 0, minorVersion: split[safe: 1] ?? 0, patchVersion: split[safe: 2] ?? 0) return !ProcessInfo.processInfo.isOperatingSystemAtLeast(xcodeMinimumMacOSVersion) } func install(id: XcodeID) { guard let availableXcode = availableXcodes.first(where: { $0.xcodeID == id }) else { return } installationPublishers[id] = signInIfNeeded() .handleEvents( receiveSubscription: { [unowned self] _ in self.setInstallationStep(of: availableXcode.version, to: .authenticating) } ) .flatMap { [unowned self] in // signInIfNeeded might finish before the user actually authenticates if UI is involved. // This publisher will wait for the @Published authentication state to change to authenticated or unauthenticated before finishing, // indicating that the user finished what they were doing in the UI. self.$authenticationState .filter { state in switch state { case .authenticated, .unauthenticated, .notAppleDeveloper: return true case .waitingForSecondFactor: return false } } .prefix(1) .tryMap { state in if state == .unauthenticated { throw AuthenticationError.invalidSession } if state == .notAppleDeveloper { throw AuthenticationError.notDeveloperAppleId } return Void() } } .flatMap { // This request would've already been made if the Apple data source were being used. // That's not the case for the Xcode Releases data source. // We need the cookies from its response in order to download Xcodes though, // so perform it here first just to be sure. Current.network.dataTask(with: URLRequest.downloads) .map(\.data) .decode(type: Downloads.self, decoder: configure(JSONDecoder()) { $0.dateDecodingStrategy = .formatted(.downloadsDateModified) }) .tryMap { downloads -> Void in if downloads.hasError { throw AuthenticationError.invalidResult(resultString: downloads.resultsString) } if downloads.downloads == nil { throw AuthenticationError.invalidResult(resultString: localizeString("DownloadingError")) } } .mapError { $0 as Error } } .flatMap { [unowned self] in self.install(.version(availableXcode), downloader: Downloader(rawValue: Current.defaults.string(forKey: "downloader") ?? "aria2") ?? .aria2) } .receive(on: DispatchQueue.main) .sink( receiveCompletion: { [unowned self] completion in self.installationPublishers[id] = nil if case let .failure(error) = completion { // Prevent setting the app state error if it is an invalid session, we will present the sign in view instead if let error = error as? AuthenticationError, case .notAuthorized = error { self.error = error self.presentedAlert = .unauthenticated } else if error as? AuthenticationError != .invalidSession { self.error = error self.presentedAlert = .generic(title: localizeString("Alert.Install.Error.Title"), message: error.legibleLocalizedDescription) } if let index = self.allXcodes.firstIndex(where: { $0.id == id }) { self.allXcodes[index].installState = .notInstalled } } }, receiveValue: { _ in } ) } /// Skips using the username/password to log in to Apple, and simply gets a Auth Cookie used in downloading /// As of Nov 2022 this was returning a 403 forbidden func installWithoutLogin(id: Xcode.ID) { guard let availableXcode = availableXcodes.first(where: { $0.xcodeID == id }) else { return } installationPublishers[id] = self.install(.version(availableXcode), downloader: Downloader(rawValue: Current.defaults.string(forKey: "downloader") ?? "aria2") ?? .aria2) .receive(on: DispatchQueue.main) .sink( receiveCompletion: { [unowned self] completion in self.installationPublishers[id] = nil if case let .failure(error) = completion { // Prevent setting the app state error if it is an invalid session, we will present the sign in view instead if error as? AuthenticationError != .invalidSession { self.error = error self.presentedAlert = .generic(title: localizeString("Alert.Install.Error.Title"), message: error.legibleLocalizedDescription) } if let index = self.allXcodes.firstIndex(where: { $0.id == id }) { self.allXcodes[index].installState = .notInstalled } } }, receiveValue: { _ in } ) } func cancelInstall(id: Xcode.ID) { guard let availableXcode = availableXcodes.first(where: { $0.xcodeID == id }) else { return } // Cancel the publisher installationPublishers[id] = nil resetDockProgressTracking() // If the download is cancelled by the user, clean up the download files that aria2 creates. // This isn't done as part of the publisher with handleEvents(receiveCancel:) because it shouldn't happen when e.g. the app quits. let expectedArchivePath = Path.xcodesApplicationSupport/"Xcode-\(availableXcode.version).\(availableXcode.filename.suffix(fromLast: "."))" let aria2DownloadMetadataPath = expectedArchivePath.parent/(expectedArchivePath.basename() + ".aria2") try? Current.files.removeItem(at: expectedArchivePath.url) try? Current.files.removeItem(at: aria2DownloadMetadataPath.url) if let index = allXcodes.firstIndex(where: { $0.id == id }) { allXcodes[index].installState = .notInstalled } } // MARK: - Uninstall func uninstall(xcode: Xcode) { guard let installedXcodePath = xcode.installedPath, uninstallPublisher == nil else { return } uninstallPublisher = uninstallXcode(path: installedXcodePath) .flatMap { [unowned self] _ in self.updateSelectedXcodePath() } .sink( receiveCompletion: { [unowned self] completion in if case let .failure(error) = completion { self.error = error self.presentedAlert = .generic(title: localizeString("Alert.Uninstall.Error.Title"), message: error.legibleLocalizedDescription) } self.uninstallPublisher = nil }, receiveValue: { _ in } ) } func reveal(_ path: Path?) { // TODO: show error if not guard let path = path else { return } NSWorkspace.shared.activateFileViewerSelecting([path.url]) } func reveal(path: String) { let url = URL(fileURLWithPath: path) NSWorkspace.shared.activateFileViewerSelecting([url]) } /// Make an Xcode active, a.k.a select it, in the `xcode-select` sense. /// /// The underlying work is done by the privileged helper, so we need to make sure that it's installed first. /// The way this is done is a little roundabout, because it requires user interaction in an alert before the `selectPublisher` is subscribed to. /// The first time this method is invoked should be with `shouldPrepareUserForHelperInstallation` set to true. /// If the helper is already installed, the Xcode will be made active immediately. /// If the helper is not already installed, the user will be prepared for installation and this method will return early. /// If they consent to installing the helper then this method will be invoked again with `shouldPrepareUserForHelperInstallation` set to false. /// This will install the helper and make the Xcode active. /// /// - Parameter xcode: The Xcode to make active. /// - Parameter shouldPrepareUserForHelperInstallation: Whether the user should be presented with an alert preparing them for helper installation before making the Xcode version active. func select(xcode: Xcode, shouldPrepareUserForHelperInstallation: Bool = true) { guard helperInstallState == .installed || shouldPrepareUserForHelperInstallation == false else { isPreparingUserForActionRequiringHelper = { [unowned self] userConsented in guard userConsented else { return } self.select(xcode: xcode, shouldPrepareUserForHelperInstallation: false) } presentedAlert = .privilegedHelper return } guard var installedXcodePath = xcode.installedPath, selectPublisher == nil else { return } if onSelectActionType == .rename { guard let newDestinationXcodePath = renameToXcode(xcode: xcode) else { return } installedXcodePath = newDestinationXcodePath } selectPublisher = installHelperIfNecessary() .flatMap { Current.helper.switchXcodePath(installedXcodePath.string) } .flatMap { [unowned self] _ in self.updateSelectedXcodePath() } .sink( receiveCompletion: { [unowned self] completion in if case let .failure(error) = completion { self.error = error self.presentedAlert = .generic(title: localizeString("Alert.Select.Error.Title"), message: error.legibleLocalizedDescription) } else { if self.createSymLinkOnSelect { createSymbolicLink(xcode: xcode) } } self.selectPublisher = nil }, receiveValue: { _ in } ) } func open(xcode: Xcode, openInRosetta: Bool? = false) { switch xcode.installState { case let .installed(path): let config = NSWorkspace.OpenConfiguration.init() if (openInRosetta ?? false) { config.architecture = CPU_TYPE_X86_64 } config.allowsRunningApplicationSubstitution = false NSWorkspace.shared.openApplication(at: path.url, configuration: config) default: Logger.appState.error("\(xcode.id.version) is not installed") return } } func copyPath(xcode: Xcode) { guard let installedXcodePath = xcode.installedPath else { return } NSPasteboard.general.declareTypes([.URL, .string], owner: nil) NSPasteboard.general.writeObjects([installedXcodePath.url as NSURL]) NSPasteboard.general.setString(installedXcodePath.string, forType: .string) } func copyReleaseNote(from url: URL?) { guard let url = url else { return } NSPasteboard.general.declareTypes([.URL, .string], owner: nil) NSPasteboard.general.writeObjects([url as NSURL]) NSPasteboard.general.setString(url.absoluteString, forType: .string) } func createSymbolicLink(xcode: Xcode, isBeta: Bool = false) { guard let installedXcodePath = xcode.installedPath else { return } let destinationPath: Path = Path.installDirectory/"Xcode\(isBeta ? "-Beta" : "").app" // does an Xcode.app file exist? if FileManager.default.fileExists(atPath: destinationPath.string) { do { // if it's not a symlink, error because we don't want to delete an actual xcode.app file let attributes: [FileAttributeKey : Any]? = try? FileManager.default.attributesOfItem(atPath: destinationPath.string) if attributes?[.type] as? FileAttributeType == FileAttributeType.typeSymbolicLink { try FileManager.default.removeItem(atPath: destinationPath.string) Logger.appState.info("Successfully deleted old symlink") } else { self.presentedAlert = .generic(title: localizeString("Alert.SymLink.Title"), message: localizeString("Alert.SymLink.Message")) return } } catch { self.presentedAlert = .generic(title: localizeString("Alert.SymLink.Title"), message: error.localizedDescription) } } do { try FileManager.default.createSymbolicLink(atPath: destinationPath.string, withDestinationPath: installedXcodePath.string) Logger.appState.info("Successfully created symbolic link with Xcode\(isBeta ? "-Beta": "").app") } catch { Logger.appState.error("Unable to create symbolic Link") self.error = error self.presentedAlert = .generic(title: localizeString("Alert.SymLink.Title"), message: error.legibleLocalizedDescription) } } func renameToXcode(xcode: Xcode) -> Path? { guard let installedXcodePath = xcode.installedPath else { return nil } let destinationPath: Path = Path.installDirectory/"Xcode.app" // rename any old named `Xcode.app` to the Xcodes versioned named files if FileManager.default.fileExists(atPath: destinationPath.string) { if let originalXcode = Current.files.installedXcode(destination: destinationPath) { let newName = "Xcode-\(originalXcode.version.descriptionWithoutBuildMetadata).app" Logger.appState.debug("Found Xcode.app - renaming back to \(newName)") do { try destinationPath.rename(to: newName) } catch { Logger.appState.error("Unable to create rename Xcode.app back to original") self.error = error // TODO UPDATE MY ERROR STRING self.presentedAlert = .generic(title: localizeString("Alert.SymLink.Title"), message: error.legibleLocalizedDescription) } } } // rename passed in xcode to xcode.app Logger.appState.debug("Found Xcode.app - renaming back to Xcode.app") do { return try installedXcodePath.rename(to: "Xcode.app") } catch { Logger.appState.error("Unable to create rename Xcode.app back to original") self.error = error // TODO UPDATE MY ERROR STRING self.presentedAlert = .generic(title: localizeString("Alert.SymLink.Title"), message: error.legibleLocalizedDescription) } return nil } func updateAllXcodes(availableXcodes: [AvailableXcode], installedXcodes: [InstalledXcode], selectedXcodePath: String?) { var adjustedAvailableXcodes = availableXcodes // First, adjust all of the available Xcodes so that available and installed versions line up and the second part of this function works properly. if dataSource == .apple { for installedXcode in installedXcodes { // We can trust that build metadata identifiers are unique for each version of Xcode, so if we have it then it's all we need. // If build metadata matches exactly, replace the available version with the installed version. // This should handle Apple versions from /downloads/more which don't have build metadata identifiers. if let index = adjustedAvailableXcodes.map(\.version).firstIndex(where: { $0.buildMetadataIdentifiers == installedXcode.version.buildMetadataIdentifiers }) { adjustedAvailableXcodes[index].xcodeID = installedXcode.xcodeID } // If an installed version is the same as one that's listed online which doesn't have build metadata, replace it with the installed version // Not all prerelease Apple versions available online include build metadata else if let index = adjustedAvailableXcodes.firstIndex(where: { availableXcode in availableXcode.version.isEquivalent(to: installedXcode.version) && availableXcode.version.buildMetadataIdentifiers.isEmpty }) { adjustedAvailableXcodes[index].xcodeID = installedXcode.xcodeID } } } // Map all of the available versions into Xcode values that join available and installed Xcode data for display. var newAllXcodes = adjustedAvailableXcodes .filter { availableXcode in // If we don't have the build identifier, don't attempt to filter prerelease versions with identical build identifiers guard !availableXcode.version.buildMetadataIdentifiers.isEmpty else { return true } let availableXcodesWithIdenticalBuildIdentifiers = availableXcodes .filter({ $0.version.buildMetadataIdentifiers == availableXcode.version.buildMetadataIdentifiers }) // Include this version if there's only one with this build identifier return availableXcodesWithIdenticalBuildIdentifiers.count == 1 || // Or if there's more than one with this build identifier and this is the release version availableXcodesWithIdenticalBuildIdentifiers.count > 1 && (availableXcode.version.prereleaseIdentifiers.isEmpty || availableXcode.architectures?.count ?? 0 != 0) } .map { availableXcode -> Xcode in let installedXcode = installedXcodes.first(where: { installedXcode in // if we want to have only specific Xcodes as selected instead of the Architecture Equivalent. // if availableXcode.architectures == nil { // return availableXcode.version.isEquivalent(to: installedXcode.version) // } else { // return availableXcode.xcodeID == installedXcode.xcodeID // } return availableXcode.version.isEquivalent(to: installedXcode.version) }) let identicalBuilds: [XcodeID] let prereleaseAvailableXcodesWithIdenticalBuildIdentifiers = availableXcodes .filter { return $0.version.buildMetadataIdentifiers == availableXcode.version.buildMetadataIdentifiers && !$0.version.prereleaseIdentifiers.isEmpty && // If we don't have the build identifier, don't consider this as a potential identical build !$0.version.buildMetadataIdentifiers.isEmpty } // If this is the release version, add the identical builds to it if !prereleaseAvailableXcodesWithIdenticalBuildIdentifiers.isEmpty, availableXcode.version.prereleaseIdentifiers.isEmpty { identicalBuilds = [availableXcode.xcodeID] + prereleaseAvailableXcodesWithIdenticalBuildIdentifiers.map(\.xcodeID) } else { identicalBuilds = [] } // If the existing install state is "installing", keep it let existingXcodeInstallState = allXcodes.first { $0.id == availableXcode.xcodeID && $0.installState.installing }?.installState // Otherwise, determine it from whether there's an installed Xcode let defaultXcodeInstallState: XcodeInstallState = installedXcode.map { .installed($0.path) } ?? .notInstalled return Xcode( version: availableXcode.version, identicalBuilds: identicalBuilds, installState: existingXcodeInstallState ?? defaultXcodeInstallState, selected: installedXcode != nil && selectedXcodePath?.hasPrefix(installedXcode!.path.string) == true, icon: (installedXcode?.path.string).map(NSWorkspace.shared.icon(forFile:)), requiredMacOSVersion: availableXcode.requiredMacOSVersion, releaseNotesURL: availableXcode.releaseNotesURL, releaseDate: availableXcode.releaseDate, sdks: availableXcode.sdks, compilers: availableXcode.compilers, downloadFileSize: availableXcode.fileSize, architectures: availableXcode.architectures ) } // If an installed version isn't listed in the available versions, add the installed version // Xcode Releases should have all versions // Apple didn't used to keep all prerelease versions around but has started to recently for installedXcode in installedXcodes { if !newAllXcodes.contains(where: { xcode in xcode.version.isEquivalent(to: installedXcode.version) }) { newAllXcodes.append( Xcode( version: installedXcode.version, installState: .installed(installedXcode.path), selected: selectedXcodePath?.hasPrefix(installedXcode.path.string) == true, icon: NSWorkspace.shared.icon(forFile: installedXcode.path.string) ) ) } } self.allXcodes = newAllXcodes.sorted { $0.version > $1.version } } // MARK: - Private private func uninstallXcode(path: Path) -> AnyPublisher { return Deferred { Future { promise in do { try Current.files.trashItem(at: path.url) promise(.success(())) } catch { promise(.failure(error)) } } } .eraseToAnyPublisher() } /// removes saved username and credentials stored in keychain private func clearLoginCredentials() { if let username = savedUsername { try? Current.keychain.remove(username) } Current.defaults.removeObject(forKey: "username") } // MARK: - Nested Types struct AlertContent: Identifiable { var title: String var message: String var id: String { title + message } } } extension OperatingSystemVersion { func versionString() -> String { return String(majorVersion) + "." + String(minorVersion) + "." + String(patchVersion) } } ================================================ FILE: Xcodes/Backend/Aria2CError.swift ================================================ import Foundation /// A LocalizedError that represents a non-zero exit code from running aria2c. struct Aria2CError: LocalizedError { var code: Code init?(exitStatus: Int32) { guard let code = Code(rawValue: exitStatus) else { return nil } self.code = code } var errorDescription: String? { "aria2c error: \(code.description)" } // https://github.com/aria2/aria2/blob/master/src/error_code.h enum Code: Int32, CustomStringConvertible { case undefined = -1 // Ignoring, not an error // case finished = 0 case unknownError = 1 case timeOut case resourceNotFound case maxFileNotFound case tooSlowDownloadSpeed case networkProblem case inProgress case cannotResume case notEnoughDiskSpace case pieceLengthChanged case duplicateDownload case duplicateInfoHash case fileAlreadyExists case fileRenamingFailed case fileOpenError case fileCreateError case fileIoError case dirCreateError case nameResolveError case metalinkParseError case ftpProtocolError case httpProtocolError case httpTooManyRedirects case httpAuthFailed case bencodeParseError case bittorrentParseError case magnetParseError case optionError case httpServiceUnavailable case jsonParseError case removed case checksumError var description: String { switch self { case .undefined: return "Undefined" case .unknownError: return "Unknown error" case .timeOut: return "Timed out" case .resourceNotFound: return "Resource not found" case .maxFileNotFound: return "Maximum number of file not found errors reached" case .tooSlowDownloadSpeed: return "Download speed too slow" case .networkProblem: return "Network problem" case .inProgress: return "Unfinished downloads in progress" case .cannotResume: return "Remote server did not support resume when resume was required to complete download" case .notEnoughDiskSpace: return "Not enough disk space available" case .pieceLengthChanged: return "Piece length was different from one in .aria2 control file" case .duplicateDownload: return "Duplicate download" case .duplicateInfoHash: return "Duplicate info hash torrent" case .fileAlreadyExists: return "File already exists" case .fileRenamingFailed: return "Renaming file failed" case .fileOpenError: return "Could not open existing file" case .fileCreateError: return "Could not create new file or truncate existing file" case .fileIoError: return "File I/O error" case .dirCreateError: return "Could not create directory" case .nameResolveError: return "Name resolution failed" case .metalinkParseError: return "Could not parse Metalink document" case .ftpProtocolError: return "FTP command failed" case .httpProtocolError: return "HTTP response header was bad or unexpected" case .httpTooManyRedirects: return "Too many redirects occurred" case .httpAuthFailed: return "HTTP authorization failed" case .bencodeParseError: return "Could not parse bencoded file (usually \".torrent\" file)" case .bittorrentParseError: return "\".torrent\" file was corrupted or missing information" case .magnetParseError: return "Magnet URI was bad" case .optionError: return "Bad/unrecognized option was given or unexpected option argument was given" case .httpServiceUnavailable: return "HTTP service unavailable" case .jsonParseError: return "Could not parse JSON-RPC request" case .removed: return "Reserved. Not used." case .checksumError: return "Checksum validation failed" } } } } ================================================ FILE: Xcodes/Backend/AvailableXcode.swift ================================================ import Foundation import Version import XcodesKit /// A version of Xcode that's available for installation public struct AvailableXcode: Codable { public var version: Version { return xcodeID.version } public let url: URL public let filename: String public let releaseDate: Date? public let requiredMacOSVersion: String? public let releaseNotesURL: URL? public let sdks: SDKs? public let compilers: Compilers? public let fileSize: Int64? public let architectures: [Architecture]? public var downloadPath: String { return url.path } public var xcodeID: XcodeID public init( version: Version, url: URL, filename: String, releaseDate: Date?, requiredMacOSVersion: String? = nil, releaseNotesURL: URL? = nil, sdks: SDKs? = nil, compilers: Compilers? = nil, fileSize: Int64? = nil, architectures: [Architecture]? = nil ) { self.url = url self.filename = filename self.releaseDate = releaseDate self.requiredMacOSVersion = requiredMacOSVersion self.releaseNotesURL = releaseNotesURL self.sdks = sdks self.compilers = compilers self.fileSize = fileSize self.architectures = architectures self.xcodeID = XcodeID(version: version, architectures: architectures) } } ================================================ FILE: Xcodes/Backend/Bundle+InfoPlistValues.swift ================================================ import Foundation extension Bundle { var bundleName: String? { infoDictionary?["CFBundleName"] as? String } var shortVersion: String? { infoDictionary?["CFBundleShortVersionString"] as? String } var version: String? { infoDictionary?["CFBundleVersion"] as? String } var humanReadableCopyright: String? { infoDictionary?["NSHumanReadableCopyright"] as? String } } ================================================ FILE: Xcodes/Backend/Collection+.swift ================================================ // // Collection+.swift // Xcodes // // Created by Matt Kiazyk on 2022-04-11. // Copyright © 2022 Robots and Pencils. All rights reserved. // import Foundation public extension Collection { /// Returns the element at the specified index iff it is within bounds, otherwise nil. subscript (safe index: Index) -> Element? { return indices.contains(index) ? self[index] : nil } } ================================================ FILE: Xcodes/Backend/Configure.swift ================================================ public func configure(_ subject: Subject, configuration: (inout Subject) -> Void) -> Subject { var copy = subject configuration(©) return copy } ================================================ FILE: Xcodes/Backend/DataSource.swift ================================================ import Foundation public enum DataSource: String, CaseIterable, Identifiable, CustomStringConvertible { case apple case xcodeReleases public var id: Self { self } public static var `default` = DataSource.xcodeReleases public var description: String { switch self { case .apple: return "Apple" case .xcodeReleases: return "Xcode Releases" } } var isManaged: Bool { PreferenceKey.dataSource.isManaged() } } ================================================ FILE: Xcodes/Backend/DateFormatter+.swift ================================================ import Foundation extension DateFormatter { /// Date format used in JSON returned from `URL.downloads` static let downloadsDateModified: DateFormatter = { let formatter = DateFormatter() formatter.dateFormat = "MM/dd/yy HH:mm" formatter.locale = Locale(identifier: "en_US_POSIX") formatter.calendar = .init(identifier: .iso8601) return formatter }() /// Date format used in HTML returned from `URL.download` static let downloadsReleaseDate: DateFormatter = { let formatter = DateFormatter() formatter.dateFormat = "MMMM d, yyyy" return formatter }() } ================================================ FILE: Xcodes/Backend/Downloader.swift ================================================ import Foundation import Path public enum Downloader: String, CaseIterable, Identifiable, CustomStringConvertible { case aria2 case urlSession public var id: Self { self } public var description: String { switch self { case .urlSession: return "URLSession" case .aria2: return "aria2" } } var isManaged: Bool { PreferenceKey.downloader.isManaged() } } ================================================ FILE: Xcodes/Backend/Downloads.swift ================================================ import Foundation import Path import Version struct Downloads: Codable { let resultCode: Int let resultsString: String? let downloads: [Download]? var hasError: Bool { return resultCode != 0 } } // Set to Int64 as ByteCountFormatter uses it. public typealias ByteCount = Int64 public struct Download: Codable { public let name: String public let files: [File] public let dateModified: Date public struct File: Codable { public let remotePath: String public let fileSize: ByteCount } } ================================================ FILE: Xcodes/Backend/Entry+.swift ================================================ import Foundation import Path extension Path { static func isAppBundle(path: Path) -> Bool { path.isDirectory && path.extension == "app" && !path.isSymlink } static func infoPlist(path: Path) -> InfoPlist? { let infoPlistPath = path.join("Contents").join("Info.plist") guard let infoPlistData = try? Data(contentsOf: infoPlistPath.url), let infoPlist = try? PropertyListDecoder().decode(InfoPlist.self, from: infoPlistData) else { return nil } return infoPlist } var isAppBundle: Bool { Path.isAppBundle(path: self) } var infoPlist: InfoPlist? { Path.infoPlist(path: self) } } ================================================ FILE: Xcodes/Backend/Environment.swift ================================================ import Combine import Foundation import Path import AppleAPI import KeychainAccess import XcodesKit /** Lightweight dependency injection using global mutable state :P - SeeAlso: https://www.pointfree.co/episodes/ep16-dependency-injection-made-easy - SeeAlso: https://www.pointfree.co/episodes/ep18-dependency-injection-made-comfortable - SeeAlso: https://vimeo.com/291588126 */ public struct Environment { public var shell = Shell() public var files = Files() public var network = Network() public var keychain = Keychain() public var defaults = Defaults() public var date: () -> Date = Date.init public var helper = Helper() public var notificationManager = NotificationManager() } public var Current = Environment() public struct Shell { public var unxip: (URL) -> AnyPublisher = { Process.run(Path.root.usr.bin.xip, workingDirectory: $0.deletingLastPathComponent(), "--expand", "\($0.path)") } public var spctlAssess: (URL) -> AnyPublisher = { Process.run(Path.root.usr.sbin.spctl, "--assess", "--verbose", "--type", "execute", "\($0.path)") } public var codesignVerify: (URL) -> AnyPublisher = { Process.run(Path.root.usr.bin.codesign, "-vv", "-d", "\($0.path)") } public var buildVersion: () -> AnyPublisher = { Process.run(Path.root.usr.bin.sw_vers, "-buildVersion") } public var xcodeBuildVersion: (InstalledXcode) -> AnyPublisher = { Process.run(Path.root.usr.libexec.PlistBuddy, "-c", "Print :ProductBuildVersion", "\($0.path.string)/Contents/version.plist") } public var getUserCacheDir: () -> AnyPublisher = { Process.run(Path.root.usr.bin.getconf, "DARWIN_USER_CACHE_DIR") } public var touchInstallCheck: (String, String, String) -> AnyPublisher = { Process.run(Path.root.usr.bin/"touch", "\($0)com.apple.dt.Xcode.InstallCheckCache_\($1)_\($2)") } public var xcodeSelectPrintPath: () -> AnyPublisher = { Process.run(Path.root.usr.bin.join("xcode-select"), "-p") } public var downloadWithAria2: (Path, URL, Path, [HTTPCookie]) -> (Progress, AnyPublisher) = { aria2Path, url, destination, cookies in let process = Process() process.executableURL = aria2Path.url process.arguments = [ "--header=Cookie: \(cookies.map { "\($0.name)=\($0.value)" }.joined(separator: "; "))", "--max-connection-per-server=16", "--split=16", "--summary-interval=1", "--stop-with-process=\(ProcessInfo.processInfo.processIdentifier)", // if xcodes quits, stop aria2 process "--dir=\(destination.parent.string)", "--out=\(destination.basename())", "--human-readable=false", // sets the output to use bytes instead of formatting url.absoluteString, ] let stdOutPipe = Pipe() process.standardOutput = stdOutPipe let stdErrPipe = Pipe() process.standardError = stdErrPipe var progress = Progress() progress.kind = .file progress.fileOperationKind = .downloading let observer = NotificationCenter.default.addObserver( forName: .NSFileHandleDataAvailable, object: nil, queue: OperationQueue.main ) { note in guard // This should always be the case for Notification.Name.NSFileHandleDataAvailable let handle = note.object as? FileHandle, handle === stdOutPipe.fileHandleForReading || handle === stdErrPipe.fileHandleForReading else { return } defer { handle.waitForDataInBackgroundAndNotify() } let string = String(decoding: handle.availableData, as: UTF8.self) progress.updateFromAria2(string: string) } stdOutPipe.fileHandleForReading.waitForDataInBackgroundAndNotify() stdErrPipe.fileHandleForReading.waitForDataInBackgroundAndNotify() do { try process.run() } catch { return (progress, Fail(error: error).eraseToAnyPublisher()) } let publisher = Deferred { Future { promise in DispatchQueue.global(qos: .default).async { process.waitUntilExit() NotificationCenter.default.removeObserver(observer, name: .NSFileHandleDataAvailable, object: nil) guard process.terminationReason == .exit, process.terminationStatus == 0 else { if let aria2cError = Aria2CError(exitStatus: process.terminationStatus) { return promise(.failure(aria2cError)) } else { return promise(.failure(ProcessExecutionError(process: process, standardOutput: "", standardError: ""))) } } promise(.success(())) } } } .handleEvents(receiveCancel: { process.terminate() NotificationCenter.default.removeObserver(observer, name: .NSFileHandleDataAvailable, object: nil) }) .eraseToAnyPublisher() return (progress, publisher) } public var downloadWithAria2Async: (Path, URL, Path, [HTTPCookie]) -> AsyncThrowingStream = { aria2Path, url, destination, cookies in return AsyncThrowingStream { continuation in Task { // Assume progress will not have data races, so we manually opt-out isolation checks. nonisolated(unsafe) var progress = Progress() progress.kind = .file progress.fileOperationKind = .downloading let process = Process() process.executableURL = aria2Path.url process.arguments = [ "--header=Cookie: \(cookies.map { "\($0.name)=\($0.value)" }.joined(separator: "; "))", "--max-connection-per-server=16", "--split=16", "--summary-interval=1", "--stop-with-process=\(ProcessInfo.processInfo.processIdentifier)", // if xcodes quits, stop aria2 process "--dir=\(destination.parent.string)", "--out=\(destination.basename())", "--human-readable=false", // sets the output to use bytes instead of formatting url.absoluteString, ] let stdOutPipe = Pipe() process.standardOutput = stdOutPipe let stdErrPipe = Pipe() process.standardError = stdErrPipe let observer = NotificationCenter.default.addObserver( forName: .NSFileHandleDataAvailable, object: nil, queue: OperationQueue.main ) { note in guard // This should always be the case for Notification.Name.NSFileHandleDataAvailable let handle = note.object as? FileHandle, handle === stdOutPipe.fileHandleForReading || handle === stdErrPipe.fileHandleForReading else { return } defer { handle.waitForDataInBackgroundAndNotify() } let string = String(decoding: handle.availableData, as: UTF8.self) // TODO: fix warning. ObservingProgressView is currently tied to an updating progress progress.updateFromAria2(string: string) continuation.yield(progress) } stdOutPipe.fileHandleForReading.waitForDataInBackgroundAndNotify() stdErrPipe.fileHandleForReading.waitForDataInBackgroundAndNotify() continuation.onTermination = { @Sendable _ in process.terminate() NotificationCenter.default.removeObserver(observer, name: .NSFileHandleDataAvailable, object: nil) } do { try process.run() } catch { continuation.finish(throwing: error) } process.waitUntilExit() NotificationCenter.default.removeObserver(observer, name: .NSFileHandleDataAvailable, object: nil) guard process.terminationReason == .exit, process.terminationStatus == 0 else { if let aria2cError = Aria2CError(exitStatus: process.terminationStatus) { continuation.finish(throwing: aria2cError) } else { continuation.finish(throwing: ProcessExecutionError(process: process, standardOutput: "", standardError: "")) } return } continuation.finish() } } } public var unxipExperiment: (URL) -> AnyPublisher = { url in let unxipPath = Path(url: Bundle.main.url(forAuxiliaryExecutable: "unxip")!)! return Process.run(unxipPath.url, workingDirectory: url.deletingLastPathComponent(), ["\(url.path)"]) } public var downloadRuntime: (String, String, String?) -> AsyncThrowingStream = { platform, version, architecture in return AsyncThrowingStream { continuation in Task { // Assume progress will not have data races, so we manually opt-out isolation checks. nonisolated(unsafe) var progress = Progress() progress.kind = .file progress.fileOperationKind = .downloading var process = Process() let xcodeBuildPath = Path.root.usr.bin.join("xcodebuild").url process.executableURL = xcodeBuildPath process.arguments = [ "-downloadPlatform", "\(platform)", "-buildVersion", "\(version)" ] if let architecture { process.arguments?.append(contentsOf: [ "-architectureVariant", "\(architecture)" ]) } let stdOutPipe = Pipe() process.standardOutput = stdOutPipe let stdErrPipe = Pipe() process.standardError = stdErrPipe let observer = NotificationCenter.default.addObserver( forName: .NSFileHandleDataAvailable, object: nil, queue: OperationQueue.main ) { note in guard // This should always be the case for Notification.Name.NSFileHandleDataAvailable let handle = note.object as? FileHandle, handle === stdOutPipe.fileHandleForReading || handle === stdErrPipe.fileHandleForReading else { return } defer { handle.waitForDataInBackgroundAndNotify() } let string = String(decoding: handle.availableData, as: UTF8.self) // TODO: fix warning. ObservingProgressView is currently tied to an updating progress progress.updateFromXcodebuild(text: string) continuation.yield(progress) } stdOutPipe.fileHandleForReading.waitForDataInBackgroundAndNotify() stdErrPipe.fileHandleForReading.waitForDataInBackgroundAndNotify() continuation.onTermination = { @Sendable _ in process.terminate() NotificationCenter.default.removeObserver(observer, name: .NSFileHandleDataAvailable, object: nil) } do { try process.run() } catch { continuation.finish(throwing: error) } process.waitUntilExit() NotificationCenter.default.removeObserver(observer, name: .NSFileHandleDataAvailable, object: nil) guard process.terminationReason == .exit, process.terminationStatus == 0 else { continuation.finish(throwing: ProcessExecutionError(process: process, standardOutput: "", standardError: "")) return } continuation.finish() } } } } public struct Files { public var fileExistsAtPath: (String) -> Bool = { FileManager.default.fileExists(atPath: $0) } public func fileExists(atPath path: String) -> Bool { return fileExistsAtPath(path) } public var moveItem: (URL, URL) throws -> Void = { try FileManager.default.moveItem(at: $0, to: $1) } public func moveItem(at srcURL: URL, to dstURL: URL) throws { try moveItem(srcURL, dstURL) } public var contentsAtPath: (String) -> Data? = { FileManager.default.contents(atPath: $0) } public func contents(atPath path: String) -> Data? { return contentsAtPath(path) } public var removeItem: (URL) throws -> Void = { try FileManager.default.removeItem(at: $0) } public func removeItem(at URL: URL) throws { try removeItem(URL) } public var trashItem: (URL) throws -> URL = { try FileManager.default.trashItem(at: $0) } @discardableResult public func trashItem(at URL: URL) throws -> URL { return try trashItem(URL) } public var createFile: (String, Data?, [FileAttributeKey: Any]?) -> Bool = { FileManager.default.createFile(atPath: $0, contents: $1, attributes: $2) } @discardableResult public func createFile(atPath path: String, contents data: Data?, attributes attr: [FileAttributeKey : Any]? = nil) -> Bool { return createFile(path, data, attr) } public var createDirectory: (URL, Bool, [FileAttributeKey : Any]?) throws -> Void = FileManager.default.createDirectory(at:withIntermediateDirectories:attributes:) public func createDirectory(at url: URL, withIntermediateDirectories createIntermediates: Bool, attributes: [FileAttributeKey : Any]? = nil) throws { try createDirectory(url, createIntermediates, attributes) } public var installedXcodes = _installedXcodes public func installedXcode(destination: Path) -> InstalledXcode? { if Path.isAppBundle(path: destination) && Path.infoPlist(path: destination)?.bundleID == "com.apple.dt.Xcode" { return InstalledXcode.init(path: destination) } else { return nil } } public var write: (Data, URL) throws -> Void = { try $0.write(to: $1) } public func write(_ data: Data, to url: URL) throws { try write(data, url) } } private func _installedXcodes(destination: Path) -> [InstalledXcode] { destination.ls() .filter { $0.isAppBundle && $0.infoPlist?.bundleID == "com.apple.dt.Xcode" } .map { $0 } .compactMap(InstalledXcode.init) } public struct Network { private static let client = AppleAPI.Client() public var dataTask: (URLRequest) -> AnyPublisher = { AppleAPI.Current.network.session.dataTaskPublisher(for: $0) .mapError { $0 as Error } .eraseToAnyPublisher() } public func dataTask(with request: URLRequest) -> AnyPublisher { dataTask(request) } public func dataTaskAsync(with request: URLRequest) async throws -> (Data, URLResponse) { return try await AppleAPI.Current.network.session.data(for: request) } public var downloadTask: (URL, URL, Data?) -> (Progress, AnyPublisher<(saveLocation: URL, response: URLResponse), Error>) = { AppleAPI.Current.network.session.downloadTask(with: $0, to: $1, resumingWith: $2) } public func downloadTask(with url: URL, to saveLocation: URL, resumingWith resumeData: Data?) -> (progress: Progress, publisher: AnyPublisher<(saveLocation: URL, response: URLResponse), Error>) { return downloadTask(url, saveLocation, resumeData) } public var validateSession: () -> AnyPublisher = { return client.validateSession() } } public struct Keychain { private static let keychain = KeychainAccess.Keychain(service: "com.robotsandpencils.XcodesApp") public var getString: (String) throws -> String? = keychain.getString(_:) public func getString(_ key: String) throws -> String? { try getString(key) } public var set: (String, String) throws -> Void = keychain.set(_:key:) public func set(_ value: String, key: String) throws { try set(value, key) } public var remove: (String) throws -> Void = keychain.remove(_:) public func remove(_ key: String) throws -> Void { try remove(key) } } public struct Defaults { public var string: (String) -> String? = { UserDefaults.standard.string(forKey: $0) } public func string(forKey key: String) -> String? { string(key) } public var date: (String) -> Date? = { Date(timeIntervalSince1970: UserDefaults.standard.double(forKey: $0)) } public func date(forKey key: String) -> Date? { date(key) } public var setDate: (Date?, String) -> Void = { UserDefaults.standard.set($0?.timeIntervalSince1970, forKey: $1) } public func setDate(_ value: Date?, forKey key: String) { setDate(value, key) } public var set: (Any?, String) -> Void = { UserDefaults.standard.set($0, forKey: $1) } public func set(_ value: Any?, forKey key: String) { set(value, key) } public var removeObject: (String) -> Void = { UserDefaults.standard.removeObject(forKey: $0) } public func removeObject(forKey key: String) { removeObject(key) } public var get: (String) -> Any? = { UserDefaults.standard.value(forKey: $0) } public func get(forKey key: String) -> Any? { get(key) } public var bool: (String) -> Bool? = { UserDefaults.standard.bool(forKey: $0) } public func bool(forKey key: String) -> Bool? { bool(key) } } private let helperClient = HelperClient() public struct Helper { var install: () throws -> Void = helperClient.install var checkIfLatestHelperIsInstalled: () -> AnyPublisher = helperClient.checkIfLatestHelperIsInstalled var getVersion: () -> AnyPublisher = helperClient.getVersion var switchXcodePath: (_ absolutePath: String) -> AnyPublisher = helperClient.switchXcodePath var devToolsSecurityEnable: () -> AnyPublisher = helperClient.devToolsSecurityEnable var addStaffToDevelopersGroup: () -> AnyPublisher = helperClient.addStaffToDevelopersGroup var acceptXcodeLicense: (_ absoluteXcodePath: String) -> AnyPublisher = helperClient.acceptXcodeLicense var runFirstLaunch: (_ absoluteXcodePath: String) -> AnyPublisher = helperClient.runFirstLaunch } ================================================ FILE: Xcodes/Backend/FileError.swift ================================================ // // FileError.swift // Xcodes // // Created by Leon Wolf on 06.10.22. // Copyright © 2022 Robots and Pencils. All rights reserved. // import Foundation import LegibleError enum FileError: LocalizedError{ case fileNotFound(_ fileName: String) } extension FileError { var errorDescription: String? { switch self { case .fileNotFound(let fileName): return String(format: localizeString("Alert.Uninstall.Error.Message.FileNotFound"), fileName) } } } ================================================ FILE: Xcodes/Backend/FileManager+.swift ================================================ import Foundation extension FileManager { /** Moves an item to the trash. This implementation exists only to make the existing method more idiomatic by returning the resulting URL instead of setting the value on an inout argument. FB6735133: FileManager.trashItem(at:resultingItemURL:) is not an idiomatic Swift API */ @discardableResult func trashItem(at url: URL) throws -> URL { var resultingItemURL: NSURL! if fileExists(atPath: url.path) { try trashItem(at: url, resultingItemURL: &resultingItemURL) } else { throw FileError.fileNotFound(url.lastPathComponent) } return resultingItemURL as URL } } ================================================ FILE: Xcodes/Backend/FocusedValues.swift ================================================ import SwiftUI // MARK: - FocusedXcodeKey struct FocusedXcodeKey : FocusedValueKey { typealias Value = SelectedXcode } // MARK: - FocusedValues extension FocusedValues { var selectedXcode: FocusedXcodeKey.Value? { get { self[FocusedXcodeKey.self] } set { self[FocusedXcodeKey.self] = newValue } } } ================================================ FILE: Xcodes/Backend/Foundation.swift ================================================ import Foundation public extension BidirectionalCollection where Element: Equatable { func suffix(fromLast delimiter: Element) -> Self.SubSequence { guard let lastIndex = lastIndex(of: delimiter), index(after: lastIndex) < endIndex else { return suffix(0) } return suffix(from: index(after: lastIndex)) } } public extension NumberFormatter { convenience init(numberStyle: NumberFormatter.Style) { self.init() self.numberStyle = numberStyle } func string(from number: N) -> String? { return string(from: number as! NSNumber) } } extension Sequence { func sorted(_ keyPath: KeyPath) -> [Element] { sorted(by: { $0[keyPath: keyPath] < $1[keyPath: keyPath] }) } } ================================================ FILE: Xcodes/Backend/Hardware.swift ================================================ import Foundation struct Hardware { /// /// Determines the architecture of the Mac on which we're running. Returns `arm64` for Apple Silicon /// and `x86_64` for Intel-based Macs or `nil` if the system call fails. static func getMachineHardwareName() -> String? { var sysInfo = utsname() let retVal = uname(&sysInfo) var finalString: String? = nil if retVal == EXIT_SUCCESS { let bytes = Data(bytes: &sysInfo.machine, count: Int(_SYS_NAMELEN)) finalString = String(data: bytes, encoding: .utf8) } // _SYS_NAMELEN will include a billion null-terminators. Clear those out so string comparisons work as you expect. return finalString?.trimmingCharacters(in: CharacterSet(charactersIn: "\0")) } static func isAppleSilicon() -> Bool { return Hardware.getMachineHardwareName() == "arm64" } } ================================================ FILE: Xcodes/Backend/HelperClient.swift ================================================ import Combine import Foundation import os.log import ServiceManagement final class HelperClient { private var connection: NSXPCConnection? private func currentConnection() -> NSXPCConnection? { guard self.connection == nil else { return self.connection } let connection = NSXPCConnection(machServiceName: machServiceName, options: .privileged) connection.remoteObjectInterface = NSXPCInterface(with: HelperXPCProtocol.self) connection.invalidationHandler = { self.connection?.invalidationHandler = nil DispatchQueue.main.async { self.connection = nil } } self.connection = connection connection.resume() return self.connection } private func helper(errorSubject: PassthroughSubject) -> HelperXPCProtocol? { guard let helper = self.currentConnection()?.remoteObjectProxyWithErrorHandler({ error in errorSubject.send(completion: .failure(error)) }) as? HelperXPCProtocol else { return nil } return helper } func checkIfLatestHelperIsInstalled() -> AnyPublisher { Logger.helperClient.info(#function) let helperURL = Bundle.main.bundleURL.appendingPathComponent("Contents/Library/LaunchServices/" + machServiceName) guard let helperBundleInfo = CFBundleCopyInfoDictionaryForURL(helperURL as CFURL) as? [String: Any], let bundledHelperVersion = helperBundleInfo["CFBundleShortVersionString"] as? String else { return Just(false) .handleEvents(receiveOutput: { Logger.helperClient.info("\(#function): \(String(describing: $0))") }) .eraseToAnyPublisher() } return getVersion() .map { installedHelperVersion in installedHelperVersion == bundledHelperVersion } .catch { _ in Just(false) } // Failure is Never, so don't bother logging completion .handleEvents(receiveOutput: { Logger.helperClient.info("\(#function): \(String(describing: $0), privacy: .public)") }) .eraseToAnyPublisher() } func getVersion() -> AnyPublisher { Logger.helperClient.info(#function) let connectionErrorSubject = PassthroughSubject() guard let helper = self.helper(errorSubject: connectionErrorSubject) else { return Fail(error: HelperClientError.failedToCreateRemoteObjectProxy) .handleEvents(receiveCompletion: { Logger.helperClient.error("\(#function): \(String(describing: $0))") }) .eraseToAnyPublisher() } return Deferred { Future { promise in helper.getVersion { version in promise(.success(version)) } } } // Take values, but fail when connectionErrorSubject fails .zip( connectionErrorSubject .prepend("") .map { _ in Void() } ) .map { $0.0 } .handleEvents(receiveOutput: { Logger.helperClient.info("\(#function): \(String(describing: $0), privacy: .public)") }, receiveCompletion: { completion in switch completion { case .finished: Logger.helperClient.info("\(#function): finished") case let .failure(error): Logger.helperClient.error("\(#function): \(String(describing: error))") } }) .eraseToAnyPublisher() } func switchXcodePath(_ absolutePath: String) -> AnyPublisher { Logger.helperClient.info("\(#function): \(absolutePath, privacy: .private(mask: .hash))") let connectionErrorSubject = PassthroughSubject() guard let helper = self.helper(errorSubject: connectionErrorSubject) else { return Fail(error: HelperClientError.failedToCreateRemoteObjectProxy) .handleEvents(receiveCompletion: { Logger.helperClient.error("\(#function): \(String(describing: $0))") }) .eraseToAnyPublisher() } return Deferred { Future { promise in helper.xcodeSelect(absolutePath: absolutePath, completion: { (possibleError) in if let error = possibleError { promise(.failure(error)) } else { promise(.success(())) } }) } } // Take values, but fail when connectionErrorSubject fails .zip( connectionErrorSubject .prepend("") .map { _ in Void() } ) .map { $0.0 } .handleEvents(receiveOutput: { Logger.helperClient.info("\(#function): \(String(describing: $0))") }, receiveCompletion: { completion in switch completion { case .finished: Logger.helperClient.info("\(#function): finished") case let .failure(error): Logger.helperClient.error("\(#function): \(String(describing: error))") } }) .eraseToAnyPublisher() } func devToolsSecurityEnable() -> AnyPublisher { Logger.helperClient.info(#function) let connectionErrorSubject = PassthroughSubject() guard let helper = self.helper(errorSubject: connectionErrorSubject) else { return Fail(error: HelperClientError.failedToCreateRemoteObjectProxy) .handleEvents(receiveCompletion: { Logger.helperClient.error("\(#function): \(String(describing: $0))") }) .eraseToAnyPublisher() } return Deferred { Future { promise in helper.devToolsSecurityEnable(completion: { (possibleError) in if let error = possibleError { promise(.failure(error)) } else { promise(.success(())) } }) } } // Take values, but fail when connectionErrorSubject fails .zip( connectionErrorSubject .prepend("") .map { _ in Void() } ) .map { $0.0 } .handleEvents(receiveOutput: { Logger.helperClient.info("\(#function): \(String(describing: $0))") }, receiveCompletion: { completion in switch completion { case .finished: Logger.helperClient.info("\(#function): finished") case let .failure(error): Logger.helperClient.error("\(#function): \(String(describing: error))") } }) .eraseToAnyPublisher() } func addStaffToDevelopersGroup() -> AnyPublisher { Logger.helperClient.info(#function) let connectionErrorSubject = PassthroughSubject() guard let helper = self.helper(errorSubject: connectionErrorSubject) else { return Fail(error: HelperClientError.failedToCreateRemoteObjectProxy) .handleEvents(receiveCompletion: { Logger.helperClient.error("\(#function): \(String(describing: $0))") }) .eraseToAnyPublisher() } return Deferred { Future { promise in helper.addStaffToDevelopersGroup(completion: { (possibleError) in if let error = possibleError { promise(.failure(error)) } else { promise(.success(())) } }) } } // Take values, but fail when connectionErrorSubject fails .zip( connectionErrorSubject .prepend("") .map { _ in Void() } ) .map { $0.0 } .handleEvents(receiveOutput: { Logger.helperClient.info("\(#function): \(String(describing: $0))") }, receiveCompletion: { completion in switch completion { case .finished: Logger.helperClient.info("\(#function): finished") case let .failure(error): Logger.helperClient.error("\(#function): \(String(describing: error))") } }) .eraseToAnyPublisher() } func acceptXcodeLicense(absoluteXcodePath: String) -> AnyPublisher { Logger.helperClient.info("\(#function): \(absoluteXcodePath, privacy: .private(mask: .hash))") let connectionErrorSubject = PassthroughSubject() guard let helper = self.helper(errorSubject: connectionErrorSubject) else { return Fail(error: HelperClientError.failedToCreateRemoteObjectProxy) .handleEvents(receiveCompletion: { Logger.helperClient.error("\(#function): \(String(describing: $0))") }) .eraseToAnyPublisher() } return Deferred { Future { promise in helper.acceptXcodeLicense(absoluteXcodePath: absoluteXcodePath, completion: { (possibleError) in if let error = possibleError { promise(.failure(error)) } else { promise(.success(())) } }) } } // Take values, but fail when connectionErrorSubject fails .zip( connectionErrorSubject .prepend("") .map { _ in Void() } ) .map { $0.0 } .handleEvents(receiveOutput: { Logger.helperClient.info("\(#function): \(String(describing: $0))") }, receiveCompletion: { completion in switch completion { case .finished: Logger.helperClient.info("\(#function): finished") case let .failure(error): Logger.helperClient.error("\(#function): \(String(describing: error))") } }) .eraseToAnyPublisher() } func runFirstLaunch(absoluteXcodePath: String) -> AnyPublisher { Logger.helperClient.info("\(#function): \(absoluteXcodePath, privacy: .private(mask: .hash))") let connectionErrorSubject = PassthroughSubject() guard let helper = self.helper(errorSubject: connectionErrorSubject) else { return Fail(error: HelperClientError.failedToCreateRemoteObjectProxy) .handleEvents(receiveCompletion: { Logger.helperClient.error("\(#function): \(String(describing: $0))") }) .eraseToAnyPublisher() } return Deferred { Future { promise in helper.runFirstLaunch(absoluteXcodePath: absoluteXcodePath, completion: { (possibleError) in if let error = possibleError { promise(.failure(error)) } else { promise(.success(())) } }) } } // Take values, but fail when connectionErrorSubject fails .zip( connectionErrorSubject .prepend("") .map { _ in Void() } ) .map { $0.0 } .handleEvents(receiveOutput: { Logger.helperClient.info("\(#function): \(String(describing: $0))") }, receiveCompletion: { completion in switch completion { case .finished: Logger.helperClient.info("\(#function): finished") case let .failure(error): Logger.helperClient.error("\(#function): \(String(describing: error))") } }) .eraseToAnyPublisher() } // MARK: - Install // From https://github.com/securing/SimpleXPCApp/ func install() throws { Logger.helperClient.info(#function) var authItem = kSMRightBlessPrivilegedHelper.withCString { name in AuthorizationItem(name: name, valueLength: 0, value:UnsafeMutableRawPointer(bitPattern: 0), flags: 0) } var authRights = withUnsafeMutablePointer(to: &authItem) { authItem in AuthorizationRights(count: 1, items: authItem) } do { let authRef = try authorizationRef(&authRights, nil, [.interactionAllowed, .extendRights, .preAuthorize]) var cfError: Unmanaged? SMJobBless(kSMDomainSystemLaunchd, machServiceName as CFString, authRef, &cfError) if let error = cfError?.takeRetainedValue() { throw error } self.connection?.invalidate() self.connection = nil Logger.helperClient.info("\(#function): Finished installation") } catch { Logger.helperClient.error("\(#function): \(error.localizedDescription)") throw error } } private func executeAuthorizationFunction(_ authorizationFunction: () -> (OSStatus) ) throws { let osStatus = authorizationFunction() guard osStatus == errAuthorizationSuccess else { if let message = SecCopyErrorMessageString(osStatus, nil) { throw HelperClientError.message(String(message as NSString)) } else { throw HelperClientError.message("Unknown error") } } } func authorizationRef(_ rights: UnsafePointer?, _ environment: UnsafePointer?, _ flags: AuthorizationFlags) throws -> AuthorizationRef? { var authRef: AuthorizationRef? try executeAuthorizationFunction { AuthorizationCreate(rights, environment, flags, &authRef) } return authRef } } enum HelperClientError: LocalizedError { case failedToCreateRemoteObjectProxy case message(String) var errorDescription: String? { switch self { case .failedToCreateRemoteObjectProxy: return localizeString("HelperClient.error") case let .message(message): return message } } } ================================================ FILE: Xcodes/Backend/HelperInstallState.swift ================================================ import Foundation public enum HelperInstallState: Equatable { case unknown case notInstalled case installed } ================================================ FILE: Xcodes/Backend/InstalledXcode.swift ================================================ import Foundation import Version import Path import XcodesKit /// A version of Xcode that's already installed public struct InstalledXcode: Equatable { public let path: Path public let xcodeID: XcodeID /// Composed of the bundle short version from Info.plist and the product build version from version.plist public var version: Version { return xcodeID.version } public init?(path: Path) { self.path = path let infoPlistPath = path.join("Contents").join("Info.plist") let versionPlistPath = path.join("Contents").join("version.plist") guard let infoPlistData = Current.files.contents(atPath: infoPlistPath.string), let infoPlist = try? PropertyListDecoder().decode(InfoPlist.self, from: infoPlistData), let bundleShortVersion = infoPlist.bundleShortVersion, let bundleVersion = Version(tolerant: bundleShortVersion), let versionPlistData = Current.files.contents(atPath: versionPlistPath.string), let versionPlist = try? PropertyListDecoder().decode(VersionPlist.self, from: versionPlistData) else { return nil } // Installed betas don't include the beta number anywhere, so try to parse it from the filename or fall back to simply "beta" var prereleaseIdentifiers = bundleVersion.prereleaseIdentifiers if let filenameVersion = Version(path.basename(dropExtension: true).replacingOccurrences(of: "Xcode-", with: "")) { prereleaseIdentifiers = filenameVersion.prereleaseIdentifiers } else if infoPlist.bundleIconName == "XcodeBeta", !prereleaseIdentifiers.contains("beta") { prereleaseIdentifiers = ["beta"] } let archsString = try? XcodesKit.Current.shell.archs(path.url.appending(path: "Contents/MacOS/Xcode")).out let architectures = archsString? .trimmingCharacters(in: .whitespacesAndNewlines) .split(separator: " ") .compactMap { Architecture(rawValue: String($0)) } let version = Version(major: bundleVersion.major, minor: bundleVersion.minor, patch: bundleVersion.patch, prereleaseIdentifiers: prereleaseIdentifiers, buildMetadataIdentifiers: [versionPlist.productBuildVersion].compactMap { $0 }) self.xcodeID = XcodeID(version: version, architectures: architectures) } } public struct InfoPlist: Decodable { public let bundleID: String? public let bundleShortVersion: String? public let bundleIconName: String? public enum CodingKeys: String, CodingKey { case bundleID = "CFBundleIdentifier" case bundleShortVersion = "CFBundleShortVersionString" case bundleIconName = "CFBundleIconName" } } public struct VersionPlist: Decodable { public let productBuildVersion: String public enum CodingKeys: String, CodingKey { case productBuildVersion = "ProductBuildVersion" } } ================================================ FILE: Xcodes/Backend/IsTesting.swift ================================================ import Foundation let isTesting = NSClassFromString("XCTestCase") != nil ================================================ FILE: Xcodes/Backend/NotificationManager.swift ================================================ import Foundation import os.log import UserNotifications /// Representation of the 3 states of the Notifications permission prompt which may either have not been shown, or was shown and denied or accepted /// Unknown is value to indicate that we have not yet determined the status and should not be used other than as a default value before determining the actual status public enum NotificationPermissionPromptStatus: Int { case unknown, notShown, shownAndDenied, shownAndAccepted } public enum XcodesNotificationCategory: String { case normal case error } public enum XcodesNotificationType: String, Identifiable, CaseIterable, CustomStringConvertible { case newVersionAvailable case finishedInstalling public var id: Self { self } public var description: String { switch self { case .newVersionAvailable: return localizeString("Notification.NewVersionAvailable") case .finishedInstalling: return localizeString("Notification.FinishedInstalling") } } } public class NotificationManager: NSObject, UNUserNotificationCenterDelegate, ObservableObject { private let notificationCenter = UNUserNotificationCenter.current() @Published var notificationStatus = NotificationPermissionPromptStatus.unknown public override init() { super.init() loadNotificationStatus() notificationCenter.delegate = self } public func loadNotificationStatus() { UNUserNotificationCenter.current().getNotificationSettings(completionHandler: { [weak self] (settings) in let status = NotificationManager.systemPromptStatusFromSettings(settings) self?.notificationStatus = status }) } private class func systemPromptStatusFromSettings(_ settings: UNNotificationSettings) -> NotificationPermissionPromptStatus { switch settings.authorizationStatus { case .notDetermined: return .notShown case .authorized, .provisional: return .shownAndAccepted case .denied: return .shownAndDenied @unknown default: return .unknown } } public func requestAccess() { notificationCenter.requestAuthorization(options: [.alert, .sound, .badge]) { [weak self] granted, error in DispatchQueue.main.async { if let error = error { // Handle the error here. Logger.appState.error("Error requesting notification accesss: \(error.legibleLocalizedDescription)") } else { Logger.appState.log("User has \(granted ? "Granted" : "NOT GRANTED") notification permission") } self?.loadNotificationStatus() } } } func scheduleNotification(title: String?, body: String, category: XcodesNotificationCategory) { let content = UNMutableNotificationContent() if let title = title { content.title = title } content.body = body content.sound = .default content.categoryIdentifier = category.rawValue let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 0.3, repeats: false) let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger) notificationCenter.add(request) } // MARK: UNUserNotificationCenterDelegate public func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { completionHandler( [.banner, .badge, .sound]) } } ================================================ FILE: Xcodes/Backend/Optional+IsNotNil.swift ================================================ import Foundation extension Optional { /// Note that this is lossy when setting, so you can really only set it to nil, but this is sufficient for mapping `Binding` to `Binding` for Alerts, Popovers, etc. var isNotNil: Bool { get { self != nil } set { self = newValue ? self : nil } } } ================================================ FILE: Xcodes/Backend/Path+.swift ================================================ import Path import Foundation extension Path { static let defaultXcodesApplicationSupport = Path.applicationSupport/"com.robotsandpencils.XcodesApp" static var xcodesApplicationSupport: Path { guard let savedApplicationSupport = Current.defaults.string(forKey: "localPath") else { return defaultXcodesApplicationSupport } guard let path = Path(savedApplicationSupport) else { return defaultXcodesApplicationSupport } return path } static var cacheFile: Path { return xcodesApplicationSupport/"available-xcodes.json" } static let defaultInstallDirectory = Path.root/"Applications" static var installDirectory: Path { guard let savedInstallDirectory = Current.defaults.string(forKey: "installPath") else { return defaultInstallDirectory } guard let path = Path(savedInstallDirectory) else { return defaultInstallDirectory } return path } static var runtimeCacheFile: Path { return xcodesApplicationSupport/"downloadable-runtimes.json" } static var xcodesCaches: Path { return caches/"com.xcodesorg.xcodesapp" } @discardableResult func setCurrentUserAsOwner() -> Path { let user = ProcessInfo.processInfo.environment["SUDO_USER"] ?? NSUserName() try? FileManager.default.setAttributes([.ownerAccountName: user], ofItemAtPath: string) return self } } ================================================ FILE: Xcodes/Backend/Process.swift ================================================ import Combine import Foundation import os.log import Path import XcodesKit public typealias ProcessOutput = (status: Int32, out: String, err: String) extension Process { @discardableResult static func run(_ executable: any Pathish, workingDirectory: URL? = nil, input: String? = nil, _ arguments: String...) -> AnyPublisher { return run(executable.url, workingDirectory: workingDirectory, input: input, arguments) } @discardableResult static func run(_ executable: URL, workingDirectory: URL? = nil, input: String? = nil, _ arguments: [String]) -> AnyPublisher { Deferred { Future { promise in DispatchQueue.global().async { let process = Process() process.currentDirectoryURL = workingDirectory ?? executable.deletingLastPathComponent() process.executableURL = executable process.arguments = arguments let (stdout, stderr) = (Pipe(), Pipe()) process.standardOutput = stdout process.standardError = stderr if let input = input { let inputPipe = Pipe() process.standardInput = inputPipe.fileHandleForReading inputPipe.fileHandleForWriting.write(Data(input.utf8)) inputPipe.fileHandleForWriting.closeFile() } do { Logger.subprocess.info("Process.run executable: \(executable), input: \(input ?? ""), arguments: \(arguments.joined(separator: ", "))") try process.run() process.waitUntilExit() let output = String(data: stdout.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) ?? "" let error = String(data: stderr.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) ?? "" Logger.subprocess.info("Process.run output: \(output)") if !error.isEmpty { Logger.subprocess.error("Process.run error: \(error)") } guard process.terminationReason == .exit, process.terminationStatus == 0 else { DispatchQueue.main.async { promise(.failure(ProcessExecutionError(process: process, standardOutput: output, standardError: error))) } return } DispatchQueue.main.async { promise(.success((process.terminationStatus, output, error))) } } catch { DispatchQueue.main.async { promise(.failure(error)) } } } } } .eraseToAnyPublisher() } } ================================================ FILE: Xcodes/Backend/Progress+.swift ================================================ import os.log import Foundation extension Progress { var xcodesLocalizedDescription: String { return localizedAdditionalDescription.replacingOccurrences(of: " — ", with: "\n") } func updateFromAria2(string: String) { let range = NSRange(location: 0, length: string.utf16.count) // MARK: Total Downloaded let regexTotalDownloaded = try! NSRegularExpression(pattern: #"(?<= )(.*)(?=\/)"#) if let match = regexTotalDownloaded.firstMatch(in: string, options: [], range: range), let matchRange = Range(match.range(at: 0), in: string), let totalDownloaded = Int(string[matchRange].replacingOccurrences(of: "B", with: "")) { self.completedUnitCount = Int64(totalDownloaded) } // MARK: Filesize let regexTotalFileSize = try! NSRegularExpression(pattern: #"(?<=/)(.*)(?=\()"#) if let match = regexTotalFileSize.firstMatch(in: string, options: [], range: range), let matchRange = Range(match.range(at: 0), in: string), let totalFileSize = Int(string[matchRange].replacingOccurrences(of: "B", with: "")) { if totalFileSize > 0 { self.totalUnitCount = Int64(totalFileSize) } } // MARK: PERCENT DOWNLOADED // Since we get fractionCompleted from completedUnitCount + totalUnitCount, no need to process // let regexPercent = try! NSRegularExpression(pattern: #"((?\d+)%\))"#) // MARK: Speed let regexSpeed = try! NSRegularExpression(pattern: #"(?<=DL:)(.*)(?= )"#) if let match = regexSpeed.firstMatch(in: string, options: [], range: range), let matchRange = Range(match.range(at: 0), in: string), let speed = Int(string[matchRange].replacingOccurrences(of: "B", with: "")) { self.throughput = speed } else { Logger.appState.debug("Could not parse throughput from aria2 download output") } // MARK: Estimated Time Remaining let regexETA = try! NSRegularExpression(pattern: #"(?<=ETA:)(?\d*h)?(?\d*m)?(?\d*s)?"#) if let match = regexETA.firstMatch(in: string, options: [], range: range) { var seconds: Int = 0 if let matchRange = Range(match.range(withName: "hours"), in: string), let hours = Int(string[matchRange].replacingOccurrences(of: "h", with: "")) { seconds += (hours * 60 * 60) } if let matchRange = Range(match.range(withName: "minutes"), in: string), let minutes = Int(string[matchRange].replacingOccurrences(of: "m", with: "")) { seconds += (minutes * 60) } if let matchRange = Range(match.range(withName: "seconds"), in: string), let second = Int(string[matchRange].replacingOccurrences(of: "s", with: "")) { seconds += (second) } self.estimatedTimeRemaining = TimeInterval(seconds) } } func updateFromXcodebuild(text: String) { self.totalUnitCount = 100 self.completedUnitCount = 0 self.localizedAdditionalDescription = "" // to not show the addtional do { let downloadPattern = #"(\d+\.\d+)% \(([\d.]+ (?:MB|GB)) of ([\d.]+ GB)\)"# let downloadRegex = try NSRegularExpression(pattern: downloadPattern) // Search for matches in the text if let match = downloadRegex.firstMatch(in: text, range: NSRange(text.startIndex..., in: text)) { // Extract the percentage - simpler then trying to extract size MB/GB and convert to bytes. if let percentRange = Range(match.range(at: 1), in: text), let percentDouble = Double(text[percentRange]) { let percent = Int64(percentDouble.rounded()) self.completedUnitCount = percent } } // "Downloading tvOS 18.1 Simulator (22J5567a): Installing..." or // "Downloading tvOS 18.1 Simulator (22J5567a): Installing (registering download)..." if text.range(of: "Installing") != nil { // sets the progress to indeterminite to show animating progress self.totalUnitCount = 0 self.completedUnitCount = 0 } } catch { Logger.appState.error("Invalid regular expression") } } } ================================================ FILE: Xcodes/Backend/Publisher+Resumable.swift ================================================ import Combine import Foundation /// Attempt and retry a task that fails with resume data up to `maximumRetryCount` times func attemptResumableTask( maximumRetryCount: Int = 3, delayBeforeRetry: TimeInterval = 2, _ body: @escaping (Data?) -> AnyPublisher ) -> AnyPublisher { var attempts = 0 func attempt(with resumeData: Data? = nil) -> AnyPublisher { attempts += 1 return body(resumeData) .catch { error -> AnyPublisher in guard attempts < maximumRetryCount, let resumeData = (error as NSError).userInfo[NSURLSessionDownloadTaskResumeData] as? Data else { return Fail(error: error).eraseToAnyPublisher() } return attempt(with: resumeData) .delay(for: .seconds(delayBeforeRetry), scheduler: DispatchQueue.main) .eraseToAnyPublisher() } .eraseToAnyPublisher() } return attempt() } ///// Attempt and retry a task up to `maximumRetryCount` times //func attemptRetryableTask( // maximumRetryCount: Int = 3, // delayBeforeRetry: DispatchTimeInterval = .seconds(2), // _ body: @escaping () -> AnyPublisher //) -> AnyPublisher { // var attempts = 0 // func attempt() -> Promise { // attempts += 1 // return body().recover { error -> Promise in // guard attempts < maximumRetryCount else { throw error } // return after(delayBeforeRetry).then(on: nil) { attempt() } // } // } // return attempt() //} ================================================ FILE: Xcodes/Backend/SDKs+Xcode.swift ================================================ // // SDKs+Xcode.swift // Xcodes // // Created by Matt Kiazyk on 2023-06-05. // Copyright © 2023 Robots and Pencils. All rights reserved. // import Foundation import XcodesKit import SwiftUI extension SDKs { /// Loops through all SDK's and returns an array of buildNumbers (to be used to correlate runtimes) func allBuilds() -> [String] { var buildNumbers: [String] = [] if let iOS = self.iOS?.compactMap({ $0.build }) { buildNumbers += iOS } if let tvOS = self.tvOS?.compactMap({ $0.build }) { buildNumbers += tvOS } if let macOS = self.macOS?.compactMap({ $0.build }) { buildNumbers += macOS } if let watchOS = self.watchOS?.compactMap({ $0.build }) { buildNumbers += watchOS } if let visionOS = self.visionOS?.compactMap({ $0.build }) { buildNumbers += visionOS } return buildNumbers } } extension DownloadableRuntime { func icon() -> Image { switch self.platform { case .iOS: return Image(systemName: "iphone") case .macOS: return Image(systemName: "macwindow") case .watchOS: return Image(systemName: "applewatch") case .tvOS: return Image(systemName: "appletv") case .visionOS: if #available(macOS 14, *) { return Image(systemName: "visionpro") } else { return Image(systemName: "eyeglasses") } } } } ================================================ FILE: Xcodes/Backend/SelectedActionType.swift ================================================ // // SelectedActionType.swift // Xcodes // // Created by Matt Kiazyk on 2022-07-24. // Copyright © 2022 Robots and Pencils. All rights reserved. // import Foundation public enum SelectedActionType: String, CaseIterable, Identifiable, CustomStringConvertible { case none case rename public var id: Self { self } public static var `default` = SelectedActionType.none public var description: String { switch self { case .none: return localizeString("OnSelectDoNothing") case .rename: return localizeString("OnSelectRenameXcode") } } public var detailedDescription: String { switch self { case .none: return localizeString("OnSelectDoNothingDescription") case .rename: return localizeString("OnSelectRenameXcodeDescription") } } } ================================================ FILE: Xcodes/Backend/SelectedXcode.swift ================================================ import Foundation /// As part of the unexpected way we have to use focusedValue in XcodesApp, we need to provide an `Optional` because there isn't always a selected Xcode in the focused window. /// But FocusedValueKey.Value is already optional, because there might not be a focused UI element to begin with, so the type ends up being `Optional>`. /// This is weird enough, but I wasn't able to find a way to have FocusedXcodeKey.Value be `Optional>` and still compile. /// There was always an error somewhere in either the use of @FocusedValue or FocusedValues.xcode or .focusedValue, as if it is only ever expecting a single level of optionality. /// But! If we make our own Optional replica like SelectedXcode, it _does_ compile, and there's some more noise required to turn it back into an `Optional`. /// All this to say, maybe one day we don't need to have this type at all. enum SelectedXcode { case none case some(Xcode) init(_ optional: Optional) { switch optional { case .none: self = .none case let .some(xcode): self = .some(xcode) } } var asOptional: Xcode? { switch self { case .none: return .none case let .some(xcode): return .some(xcode) } } } extension Optional where Wrapped == SelectedXcode { var unwrapped: Xcode? { switch self { case Optional.none: return Optional.none case let .some(selectedXcode): return selectedXcode.asOptional } } } ================================================ FILE: Xcodes/Backend/URLRequest+Apple.swift ================================================ import Foundation extension URL { static let download = URL(string: "https://developer.apple.com/download")! static let downloads = URL(string: "https://developer.apple.com/services-account/QH65B2/downloadws/listDownloads.action")! static let downloadXcode = URL(string: "https://developer.apple.com/devcenter/download.action")! static let downloadADCAuth = URL(string: "https://developerservices2.apple.com/services/download")! } extension URLRequest { static var download: URLRequest { return URLRequest(url: .download) } static var downloads: URLRequest { var request = URLRequest(url: .downloads) request.httpMethod = "POST" return request } static func downloadXcode(path: String) -> URLRequest { var components = URLComponents(url: .downloadXcode, resolvingAgainstBaseURL: false)! components.queryItems = [URLQueryItem(name: "path", value: path)] var request = URLRequest(url: components.url!) request.allHTTPHeaderFields = request.allHTTPHeaderFields ?? [:] request.allHTTPHeaderFields?["Accept"] = "*/*" return request } static func downloadADCAuth(path: String) -> URLRequest { var components = URLComponents(url: .downloadADCAuth, resolvingAgainstBaseURL: false)! components.queryItems = [URLQueryItem(name: "path", value: path)] var request = URLRequest(url: components.url!) request.allHTTPHeaderFields = request.allHTTPHeaderFields ?? [:] request.allHTTPHeaderFields?["Accept"] = "*/*" return request } } ================================================ FILE: Xcodes/Backend/URLSession+DownloadTaskPublisher.swift ================================================ import Combine import Foundation extension URLSession { /** - Parameter convertible: A URL or URLRequest. - Parameter saveLocation: A URL to move the downloaded file to after it completes. Apple deletes the temporary file immediately after the underyling completion handler returns. - Parameter resumeData: Data describing the state of a previously cancelled or failed download task. See the Discussion section for `downloadTask(withResumeData:completionHandler:)` https://developer.apple.com/documentation/foundation/urlsession/1411598-downloadtask# - Returns: Tuple containing a Progress object for the task and a publisher of the save location and response. - Note: We do not create the destination directory for you, because we move the file with FileManager.moveItem which changes its behavior depending on the directory status of the URL you provide. So create your own directory first! */ public func downloadTask( with url: URL, to saveLocation: URL, resumingWith resumeData: Data? ) -> (progress: Progress, publisher: AnyPublisher<(saveLocation: URL, response: URLResponse), Error>) { var progress: Progress! var task: URLSessionDownloadTask! // Intentionally not wrapping in Deferred because we need to return the Progress and URLSessionDownloadTask immediately. // Probably a sign that this should be implemented differently... let promise = Future<(saveLocation: URL, response: URLResponse), Error> { promise in let completionHandler = { (temporaryURL: URL?, response: URLResponse?, error: Error?) in if let error = error { promise(.failure(error)) } else if let response = response, let temporaryURL = temporaryURL { do { try FileManager.default.moveItem(at: temporaryURL, to: saveLocation) promise(.success((saveLocation, response))) } catch { promise(.failure(error)) } } else { fatalError("Expecting either a temporary URL and a response, or an error, but got neither.") } } if let resumeData = resumeData { task = self.downloadTask(withResumeData: resumeData, completionHandler: completionHandler) } else { task = self.downloadTask(with: url, completionHandler: completionHandler) } progress = task.progress task.resume() } .handleEvents(receiveCancel: task.cancel) .eraseToAnyPublisher() return (progress, promise) } } ================================================ FILE: Xcodes/Backend/Version+.swift ================================================ import Version public extension Version { /// Determines if two Xcode versions should be treated equivalently. This is not the same as equality. /// /// We need a way to determine if two Xcode versions are the same without always having full information, and supporting different data sources. /// For example, the Apple data source often doesn't have build metadata identifiers. func isEquivalent(to other: Version) -> Bool { // If we don't have build metadata identifiers for both Versions, compare major, minor, patch and prerelease identifiers. if buildMetadataIdentifiers.isEmpty || other.buildMetadataIdentifiers.isEmpty { return major == other.major && minor == other.minor && patch == other.patch && prereleaseIdentifiers.map { $0.lowercased() } == other.prereleaseIdentifiers.map { $0.lowercased() } // If we have build metadata identifiers for both, we can ignore the prerelease identifiers. } else { return major == other.major && minor == other.minor && patch == other.patch && buildMetadataIdentifiers.map { $0.lowercased() } == other.buildMetadataIdentifiers.map { $0.lowercased() } } } var descriptionWithoutBuildMetadata: String { var base = "\(major).\(minor).\(patch)" if !prereleaseIdentifiers.isEmpty { base += "-" + prereleaseIdentifiers.joined(separator: ".") } return base } var isPrerelease: Bool { prereleaseIdentifiers.isEmpty == false } var isNotPrerelease: Bool { prereleaseIdentifiers.isEmpty == true } } ================================================ FILE: Xcodes/Backend/Version+Xcode.swift ================================================ import Foundation import Version public extension Version { /** E.g.: Xcode 10.2 Beta 4 Xcode 10.2 GM Xcode 10.2 GM seed 2 Xcode 10.2 Xcode 10.2.1 10.2 Beta 4 10.2 GM 10.2 10.2.1 */ init?(xcodeVersion: String, buildMetadataIdentifier: String? = nil) { let nsrange = NSRange(xcodeVersion.startIndex.. String? { let nsrange = range(withName: name) guard let range = Range(nsrange, in: string) else { return nil } return String(string[range]) } } ================================================ FILE: Xcodes/Backend/Version+XcodeReleases.swift ================================================ import Version import XcodesKit extension Version { /// Initialize a Version from an XcodeReleases' XCModel.Xcode /// /// This is kinda quick-and-dirty, and it would probably be better for us to adopt something closer to XCModel.Xcode under the hood and map the scraped data to it instead. init?(xcReleasesXcode: XcodeRelease) { var versionString = xcReleasesXcode.version.number ?? "" // Append trailing ".0" in order to get a fully-specified version string let components = versionString.components(separatedBy: ".") versionString += Array(repeating: ".0", count: 3 - components.count).joined() // Append prerelease identifier switch xcReleasesXcode.version.release { case let .beta(beta): versionString += "-Beta" if beta > 1 { versionString += ".\(beta)" } case let .dp(dp): versionString += "-DP" if dp > 1 { versionString += ".\(dp)" } case .gm: break case let .gmSeed(gmSeed): versionString += "-GM.Seed" if gmSeed > 1 { versionString += ".\(gmSeed)" } case let .rc(rc): versionString += "-Release.Candidate" if rc > 1 { versionString += ".\(rc)" } case .release: break } // Append build identifier if let buildNumber = xcReleasesXcode.version.build { versionString += "+\(buildNumber)" } self.init(versionString) } var buildMetadataIdentifiersDisplay: String { return !buildMetadataIdentifiers.isEmpty ? "(\(buildMetadataIdentifiers.joined(separator: " ")))" : "" } } ================================================ FILE: Xcodes/Backend/Xcode.swift ================================================ import AppKit import Foundation import Version import Path import XcodesKit public struct XcodeID: Codable, Hashable, Identifiable { public let version: Version public let architectures: [Architecture]? public var id: String { let architectures = architectures?.map { $0.rawValue}.joined() ?? "" return version.description + architectures } public init(version: Version, architectures: [Architecture]? = nil) { self.version = version self.architectures = architectures } } struct Xcode: Identifiable, CustomStringConvertible { var version: Version { return id.version } /// Other Xcode versions that have the same build identifier let identicalBuilds: [XcodeID] var installState: XcodeInstallState let selected: Bool let icon: NSImage? let requiredMacOSVersion: String? let releaseNotesURL: URL? let releaseDate: Date? let sdks: SDKs? let compilers: Compilers? let downloadFileSize: Int64? let architectures: [Architecture]? let id: XcodeID init( version: Version, identicalBuilds: [XcodeID] = [], installState: XcodeInstallState, selected: Bool, icon: NSImage?, requiredMacOSVersion: String? = nil, releaseNotesURL: URL? = nil, releaseDate: Date? = nil, sdks: SDKs? = nil, compilers: Compilers? = nil, downloadFileSize: Int64? = nil, architectures: [Architecture]? = nil ) { self.identicalBuilds = identicalBuilds self.installState = installState self.selected = selected self.icon = icon self.requiredMacOSVersion = requiredMacOSVersion self.releaseNotesURL = releaseNotesURL self.releaseDate = releaseDate self.sdks = sdks self.compilers = compilers self.downloadFileSize = downloadFileSize self.architectures = architectures self.id = XcodeID(version: version, architectures: architectures) } var description: String { version.appleDescription } var downloadFileSizeString: String? { if let downloadFileSize = downloadFileSize { return ByteCountFormatter.string(fromByteCount: downloadFileSize, countStyle: .file) } else { return nil } } var installedPath: Path? { switch installState { case .installed(let path): return path default: return nil } } } ================================================ FILE: Xcodes/Backend/XcodeCommands.swift ================================================ import SwiftUI import XcodesKit // MARK: - CommandMenu struct XcodeCommands: Commands { // CommandMenus don't participate in the environment hierarchy, so we need to shuffle AppState along to the individual Commands manually. let appState: AppState var body: some Commands { CommandMenu("Xcode") { Group { InstallCommand() Divider() SelectCommand() OpenCommand() RevealCommand() CopyPathCommand() CreateSymbolicLinkCommand() Divider() UninstallCommand() } .environmentObject(appState) } } } // MARK: - Buttons // These are used for both context menus and commands struct InstallButton: View { @EnvironmentObject var appState: AppState let xcode: Xcode? var body: some View { Button { install() } label: { Text("Install") .help("InstallDescription") } } private func install() { guard let xcode = xcode else { return } appState.checkMinVersionAndInstall(id: xcode.id) } } struct CancelInstallButton: View { @EnvironmentObject var appState: AppState let xcode: Xcode? var body: some View { Button(action: cancelInstall) { Label("Cancel", systemImage: "xmark") } .help(localizeString("StopInstallation")) .buttonStyle(.plain) } private func cancelInstall() { guard let xcode = xcode else { return } appState.presentedAlert = .cancelInstall(xcode: xcode) } } struct CancelRuntimeInstallButton: View { @EnvironmentObject var appState: AppState let runtime: DownloadableRuntime? var body: some View { Button(action: cancelInstall) { Image(systemName: "xmark.circle.fill") }.help(localizeString("StopInstallation")) .buttonStyle(.plain) } private func cancelInstall() { guard let runtime = runtime else { return } appState.presentedAlert = .cancelRuntimeInstall(runtime: runtime) } } struct SelectButton: View { @EnvironmentObject var appState: AppState let xcode: Xcode? var body: some View { Button(action: select) { if xcode?.selected == true { Text("Active") } else { Text("MakeActive") } } .disabled(xcode?.selected != false) .help("Select") } private func select() { guard let xcode = xcode else { return } appState.select(xcode: xcode) } } struct OpenButton: View { @EnvironmentObject var appState: AppState let xcode: Xcode? var openInRosetta: Bool { appState.showOpenInRosettaOption && Hardware.isAppleSilicon() } var body: some View { if openInRosetta { Menu("Open") { Button(action: open) { Text("Open") } .help("Open") Button(action: open) { Text("Open In Rosetta") } .help("Open In Rosetta") } } else { Button(action: open) { Text("Open") } .help("Open") } } private func open() { guard let xcode = xcode else { return } appState.open(xcode: xcode, openInRosetta: openInRosetta) } } struct UninstallButton: View { @EnvironmentObject var appState: AppState let xcode: Xcode? var body: some View { Button(action: { appState.xcodeBeingConfirmedForUninstallation = xcode }) { Text("Uninstall") } .foregroundColor(.red) .help("Uninstall") } } struct RevealButton: View { @EnvironmentObject var appState: AppState let xcode: Xcode? var body: some View { Button(action: reveal) { Text("RevealInFinder") } .help("RevealInFinder") } private func reveal() { guard let xcode = xcode else { return } appState.reveal(xcode.installedPath) } } struct CopyPathButton: View { @EnvironmentObject var appState: AppState let xcode: Xcode? var body: some View { Button(action: copyPath) { Text("CopyPath") } .help("CopyPath") } private func copyPath() { guard let xcode = xcode else { return } appState.copyPath(xcode: xcode) } } struct CopyReleaseNoteButton: View { let url: URL? @EnvironmentObject var appState: AppState var body: some View { Button(action: copyReleaseNote) { Text("CopyReleaseNoteURL") } .help("CopyReleaseNoteURL") } private func copyReleaseNote() { guard let url = url else { return } appState.copyReleaseNote(from: url) } } struct CreateSymbolicLinkButton: View { @EnvironmentObject var appState: AppState let xcode: Xcode? var body: some View { Button(action: createSymbolicLink) { Text("CreateSymLink") } .help("CreateSymLink") } private func createSymbolicLink() { guard let xcode = xcode else { return } appState.createSymbolicLink(xcode: xcode) } } struct DownloadRuntimeButton: View { @EnvironmentObject var appState: AppState let runtime: DownloadableRuntime? var body: some View { Button(action: install) { Text("Install") .help("Install") } } private func install() { guard let runtime = runtime else { return } appState.downloadRuntime(runtime: runtime) } } struct CreateSymbolicBetaLinkButton: View { @EnvironmentObject var appState: AppState let xcode: Xcode? var body: some View { Button(action: createSymbolicBetaLink) { Text("CreateSymLinkBeta") } .help("CreateSymLinkBeta") } private func createSymbolicBetaLink() { guard let xcode = xcode else { return } appState.createSymbolicLink(xcode: xcode, isBeta: true) } } // MARK: - Commands struct InstallCommand: View { @EnvironmentObject var appState: AppState @FocusedValue(\.selectedXcode) private var selectedXcode: SelectedXcode? var body: some View { if selectedXcode.unwrapped?.installState.installing == true { CancelInstallButton(xcode: selectedXcode.unwrapped) .keyboardShortcut(".", modifiers: [.command]) } else { InstallButton(xcode: selectedXcode.unwrapped) .keyboardShortcut("i", modifiers: [.command, .option]) .disabled(selectedXcode.unwrapped?.installState != .notInstalled) } } } struct SelectCommand: View { @EnvironmentObject var appState: AppState @FocusedValue(\.selectedXcode) private var selectedXcode: SelectedXcode? var body: some View { SelectButton(xcode: selectedXcode.unwrapped) .keyboardShortcut("s", modifiers: [.command, .option]) .disabled(selectedXcode.unwrapped?.installState.installed != true) } } struct OpenCommand: View { @EnvironmentObject var appState: AppState @FocusedValue(\.selectedXcode) private var selectedXcode: SelectedXcode? var body: some View { OpenButton(xcode: selectedXcode.unwrapped) .keyboardShortcut(KeyboardShortcut(.downArrow, modifiers: .command)) .disabled(selectedXcode.unwrapped?.installState.installed != true) } } struct RevealCommand: View { @EnvironmentObject var appState: AppState @FocusedValue(\.selectedXcode) private var selectedXcode: SelectedXcode? var body: some View { RevealButton(xcode: selectedXcode.unwrapped) .keyboardShortcut("r", modifiers: [.command, .option]) .disabled(selectedXcode.unwrapped?.installState.installed != true) } } struct CopyPathCommand: View { @EnvironmentObject var appState: AppState @FocusedValue(\.selectedXcode) private var selectedXcode: SelectedXcode? var body: some View { CopyPathButton(xcode: selectedXcode.unwrapped) .keyboardShortcut("c", modifiers: [.command, .option]) .disabled(selectedXcode.unwrapped?.installState.installed != true) } } struct UninstallCommand: View { @EnvironmentObject var appState: AppState @FocusedValue(\.selectedXcode) private var selectedXcode: SelectedXcode? var body: some View { UninstallButton(xcode: selectedXcode.unwrapped) .keyboardShortcut("u", modifiers: [.command, .option]) .disabled(selectedXcode.unwrapped?.installState.installed != true) } } struct CreateSymbolicLinkCommand: View { @EnvironmentObject var appState: AppState @FocusedValue(\.selectedXcode) private var selectedXcode: SelectedXcode? var body: some View { CreateSymbolicLinkButton(xcode: selectedXcode.unwrapped) .keyboardShortcut("s", modifiers: [.command, .option]) .disabled(selectedXcode.unwrapped?.installState.installed != true) } } ================================================ FILE: Xcodes/Backend/XcodeInstallState.swift ================================================ import Foundation import Path import XcodesKit enum XcodeInstallState: Equatable { case notInstalled case installing(XcodeInstallationStep) case installed(Path) var notInstalled: Bool { switch self { case .notInstalled: return true default: return false } } var installing: Bool { switch self { case .installing: return true default: return false } } var installed: Bool { switch self { case .installed: return true default: return false } } } ================================================ FILE: Xcodes/Frontend/About/AboutView.swift ================================================ import SwiftUI struct AboutView: View { let showAcknowledgementsWindow: () -> Void @SwiftUI.Environment(\.openURL) var openURL: OpenURLAction var body: some View { HStack { Image(nsImage: NSApp.applicationIconImage) VStack(alignment: .leading) { Text(Bundle.main.bundleName!) .font(.largeTitle) Text(String(format: localizeString("VersionWithBuild"), Bundle.main.shortVersion!, Bundle.main.version!)) HStack(spacing: 32) { Button(action: { openURL(URL(string: "https://github.com/RobotsAndPencils/XcodesApp/")!) }) { Label("GithubRepo", systemImage: "link") } .buttonStyle(LinkButtonStyle()) Button(action: showAcknowledgementsWindow) { Label("Acknowledgements", systemImage: "doc") } .buttonStyle(LinkButtonStyle()) } Color.clear .frame(width: 300, height: 0) Label("UnxipExperiment", systemImage: "lightbulb") HStack(spacing: 32) { Button(action: { openURL(URL(string: "https://github.com/saagarjha/unxip/")!) }) { Label("GithubRepo", systemImage: "link") } .buttonStyle(LinkButtonStyle()) Button(action: { openURL(URL(string: "https://github.com/saagarjha/unxip/blob/main/LICENSE")!) }) { Label("License", systemImage: "link") } .buttonStyle(LinkButtonStyle()) } HStack { Text(Bundle.main.humanReadableCopyright!) .font(.footnote) Button(action: { openURL(URL(string: "https://opencollective.com/xcodesapp")!) }) { HStack { Image(systemName: "heart.circle") Text("Support.Xcodes") } } } } } .padding() } } struct AboutView_Previews: PreviewProvider { static var previews: some View { AboutView(showAcknowledgementsWindow: {}) } } ================================================ FILE: Xcodes/Frontend/About/AcknowledgementsView.swift ================================================ import SwiftUI struct AcknowledgmentsView: View { var body: some View { ScrollingTextView( attributedString: NSAttributedString( rtf: try! Data(contentsOf: Bundle.main.url(forResource: "Licenses", withExtension: "rtf")!), documentAttributes: nil )! .addingAttribute(.foregroundColor, value: NSColor.labelColor) ) .frame(minWidth: 600, minHeight: 500) } } struct AcknowledgementsView_Previews: PreviewProvider { static var previews: some View { AcknowledgmentsView() } } ================================================ FILE: Xcodes/Frontend/About/ScrollingTextView.swift ================================================ import SwiftUI struct ScrollingTextView: NSViewRepresentable { typealias NSViewType = NSScrollView let attributedString: NSAttributedString func makeNSView(context: Context) -> NSViewType { let view = NSTextView.scrollableTextView() let textView = view.documentView as? NSTextView textView?.isEditable = false return view } func updateNSView(_ nsView: NSViewType, context: Context) { (nsView.documentView as? NSTextView)?.textStorage?.setAttributedString(attributedString) } } struct ScrollingTextView_Previews: PreviewProvider { static var previews: some View { ScrollingTextView(attributedString: NSAttributedString(string: "Some sample text")) } } ================================================ FILE: Xcodes/Frontend/Common/NavigationSplitViewWrapper.swift ================================================ // // NavigationSplitViewWrapper.swift // Xcodes // // Created by Matt Kiazyk on 2023-12-12. // import SwiftUI struct NavigationSplitViewWrapper: View where Sidebar: View, Detail: View { private var sidebar: Sidebar private var detail: Detail init( @ViewBuilder sidebar: () -> Sidebar, @ViewBuilder detail: () -> Detail ) { self.sidebar = sidebar() self.detail = detail() } var body: some View { NavigationSplitView { if #available(macOS 14, *) { sidebar .navigationSplitViewColumnWidth(min: 290, ideal: 290) } else { sidebar } } detail: { detail } .navigationSplitViewStyle(.balanced) } } ================================================ FILE: Xcodes/Frontend/Common/ObservingProgressIndicator.swift ================================================ import Combine import SwiftUI /// A ProgressIndicator that reflects the state of a Progress object. /// This functionality is already built in to ProgressView, /// but this implementation ensures that changes are received on the main thread. @available(iOS 14.0, macOS 11.0, *) public struct ObservingProgressIndicator: View { let controlSize: NSControl.ControlSize let style: NSProgressIndicator.Style let showsAdditionalDescription: Bool @StateObject private var progress: ProgressWrapper public init( _ progress: Progress, controlSize: NSControl.ControlSize, style: NSProgressIndicator.Style, showsAdditionalDescription: Bool = false ) { _progress = StateObject(wrappedValue: ProgressWrapper(progress: progress)) self.controlSize = controlSize self.style = style self.showsAdditionalDescription = showsAdditionalDescription } class ProgressWrapper: ObservableObject { var progress: Progress var cancellable: AnyCancellable! init(progress: Progress) { self.progress = progress cancellable = progress.publisher(for: \.fractionCompleted) .combineLatest(progress.publisher(for: \.localizedAdditionalDescription)) .combineLatest(progress.publisher(for: \.isIndeterminate)) .throttle(for: 1.0, scheduler: DispatchQueue.main, latest: true) .sink { [weak self] _ in self?.objectWillChange.send() } } } public var body: some View { VStack(alignment: .leading, spacing: 0) { ProgressIndicator( minValue: 0.0, maxValue: 1.0, doubleValue: progress.progress.fractionCompleted, controlSize: controlSize, isIndeterminate: progress.progress.isIndeterminate, style: style ) .help(String(format: localizeString("DownloadingPercentDescription"), Int((progress.progress.fractionCompleted * 100)))) if showsAdditionalDescription, progress.progress.xcodesLocalizedDescription.isEmpty == false { Text(progress.progress.xcodesLocalizedDescription) .font(.subheadline.monospacedDigit()) .foregroundColor(.secondary) } } } } @available(iOS 14.0, macOS 11.0, *) struct ObservingProgressBar_Previews: PreviewProvider { static var previews: some View { Group { ObservingProgressIndicator( configure(Progress(totalUnitCount: 100)) { $0.completedUnitCount = 40 }, controlSize: .small, style: .spinning ) ObservingProgressIndicator( configure(Progress()) { $0.kind = .file $0.fileOperationKind = .downloading $0.estimatedTimeRemaining = 123 $0.totalUnitCount = 11944848484 $0.completedUnitCount = 848444920 $0.throughput = 9211681 }, controlSize: .regular, style: .bar, showsAdditionalDescription: true ) ObservingProgressIndicator( configure(Progress()) { $0.kind = .file $0.fileOperationKind = .downloading $0.totalUnitCount = 0 $0.completedUnitCount = 0 }, controlSize: .regular, style: .bar, showsAdditionalDescription: true ) } .previewLayout(.sizeThatFits) } } ================================================ FILE: Xcodes/Frontend/Common/ProgressButton.swift ================================================ // // ProgressButton.swift // Xcodes // // Created by Chad Sykes on 2020-12-27. // Copyright © 2020 Robots and Pencils. All rights reserved. // import SwiftUI struct ProgressButton: View { let isInProgress: Bool let action: () -> Void let label: () -> Label init(isInProgress: Bool, action: @escaping () -> Void, @ViewBuilder label: @escaping () -> Label) { self.isInProgress = isInProgress self.action = action self.label = label } var body: some View { Button(action: action) { // This might look like a strange way to switch between the label and the progress view. // Doing it this way, so that the label is hidden but still has the same frame and is in the view hierarchy // makes sure that the button's frame doesn't change when isInProgress changes. label() .isHidden(isInProgress) .overlay( ProgressView() .progressViewStyle(CircularProgressViewStyle()) .scaleEffect(x: 0.5, y: 0.5, anchor: .center) .isHidden(!isInProgress) ) } .disabled(isInProgress) } } ================================================ FILE: Xcodes/Frontend/Common/ProgressIndicator.swift ================================================ import SwiftUI import AppKit /// You probably want ProgressView unless you need more of NSProgressIndicator's API, which this exposes. struct ProgressIndicator: NSViewRepresentable { typealias NSViewType = NSProgressIndicator let minValue: Double let maxValue: Double let doubleValue: Double let controlSize: NSControl.ControlSize let isIndeterminate: Bool let style: NSProgressIndicator.Style func makeNSView(context: Context) -> NSViewType { NSProgressIndicator() } func updateNSView(_ nsView: NSViewType, context: Context) { nsView.minValue = minValue nsView.maxValue = maxValue nsView.doubleValue = doubleValue nsView.controlSize = controlSize nsView.isIndeterminate = isIndeterminate nsView.usesThreadedAnimation = true nsView.style = style nsView.startAnimation(nil) } } struct ProgressIndicator_Previews: PreviewProvider { static var previews: some View { ProgressIndicator( minValue: 0, maxValue: 1, doubleValue: 0.4, controlSize: .small, isIndeterminate: false, style: .spinning ) } } ================================================ FILE: Xcodes/Frontend/Common/TagView.swift ================================================ // // TagView.swift // Xcodes // // Created by Matt Kiazyk on 2025-06-25.// import SwiftUI struct TagView: View { let text: String var body: some View { Text(text) .font(.system(size: 10)) .foregroundColor(.primary) .padding(.horizontal, 5) .padding(.vertical, 2) .background( Capsule() .fill(.quaternary) ) } } ================================================ FILE: Xcodes/Frontend/Common/TrailingIconLabelStyle.swift ================================================ // // TrailingIconLabelStyle.swift // Xcodes // // Created by Daniel Chick on 3/11/24. // Copyright © 2024 Robots and Pencils. All rights reserved. // import SwiftUI struct TrailingIconLabelStyle: LabelStyle { func makeBody(configuration: Configuration) -> some View { HStack { configuration.title configuration.icon } } } extension LabelStyle where Self == TrailingIconLabelStyle { static var trailingIcon: Self { Self() } } ================================================ FILE: Xcodes/Frontend/Common/XcodesAlert.swift ================================================ import Foundation import XcodesKit enum XcodesAlert: Identifiable { case cancelInstall(xcode: Xcode) case cancelRuntimeInstall(runtime: DownloadableRuntime) case privilegedHelper case generic(title: String, message: String) case checkMinSupportedVersion(xcode: AvailableXcode, macOS: String) case unauthenticated var id: Int { switch self { case .cancelInstall: return 1 case .privilegedHelper: return 2 case .generic: return 3 case .checkMinSupportedVersion: return 4 case .cancelRuntimeInstall: return 5 case .unauthenticated: return 6 } } } // Splitting out alerts that are shown on the preference screen as by default we are showing on the MainWindow() // and users awkwardly switch screens, sometimes losing the preference screen enum XcodesPreferencesAlert: Identifiable { case deletePlatform(runtime: DownloadableRuntime) case generic(title: String, message: String) var id: Int { switch self { case .deletePlatform: return 1 case .generic: return 2 } } } ================================================ FILE: Xcodes/Frontend/Common/XcodesSheet.swift ================================================ import Foundation import AppleAPI enum XcodesSheet: Identifiable { case signIn case twoFactor(SecondFactorData) case securityKeyTouchToConfirm var id: Int { Kind(self).hashValue } struct SecondFactorData { let option: TwoFactorOption let authOptions: AuthOptionsResponse let sessionData: AppleSessionData } } extension XcodesSheet { private enum Kind: Hashable { case signIn, twoFactor(TwoFactorOption), securityKeyTouchToConfirm enum TwoFactorOption { case smsSent case codeSent case smsPendingChoice case securityKeyPin } init(_ sheet: XcodesSheet) { switch sheet { case .signIn: self = .signIn case .twoFactor(let data): switch data.option { case .smsSent: self = .twoFactor(.smsSent) case .smsPendingChoice: self = .twoFactor(.smsPendingChoice) case .codeSent: self = .twoFactor(.codeSent) case .securityKey: self = .twoFactor(.securityKeyPin) } case .securityKeyTouchToConfirm: self = .securityKeyTouchToConfirm } } } } ================================================ FILE: Xcodes/Frontend/InfoPane/CompatibilityView.swift ================================================ // // CompatibilityView.swift // Xcodes // // Created by Duong Thai on 13/10/2023. // Copyright © 2023 Robots and Pencils. All rights reserved. // import SwiftUI struct CompatibilityView: View { @EnvironmentObject var appState: AppState let requiredMacOSVersion: String? var body: some View { if let requiredMacOSVersion = requiredMacOSVersion { HStack(alignment: .top){ VStack(alignment: .leading) { Text("Compatibility") .font(.headline) Text(String(format: localizeString("MacOSRequirement"), requiredMacOSVersion)) .font(.subheadline) .foregroundColor(appState.hasMinSupportedOS(requiredMacOSVersion: requiredMacOSVersion) ? .red : .primary) } Spacer() if appState.hasMinSupportedOS(requiredMacOSVersion: requiredMacOSVersion) { Image(systemName: "exclamationmark.triangle.fill") .font(.largeTitle) } } .xcodesBackground() } else { EmptyView() } } } #Preview { CompatibilityView(requiredMacOSVersion: "10.15.4") .padding() .environmentObject(AppState()) } ================================================ FILE: Xcodes/Frontend/InfoPane/CompilersView.swift ================================================ // // CompilersView.swift // Xcodes // // Created by Duong Thai on 13/10/2023. // Copyright © 2023 Robots and Pencils. All rights reserved. // import SwiftUI import XcodesKit struct CompilersView: View { let compilers: Compilers? var body: some View { if let compilers = compilers { VStack(alignment: .leading) { Text("Compilers").font(.headline) Text(Self.content(from: compilers)) .font(.subheadline) .textSelection(.enabled) } } else { EmptyView() } } static func content(from compilers: Compilers) -> String { [ ("Swift", compilers.swift), ("Clang", compilers.clang), ("LLVM", compilers.llvm), ("LLVM GCC", compilers.llvm_gcc), ("GCC", compilers.gcc) ].compactMap { // remove nil compiler guard $0.1 != nil, // has version array !$0.1!.isEmpty // has at least 1 version else { return nil } let numbers = $0.1!.compactMap { $0.number } // remove nil number guard !numbers.isEmpty // has at least 1 number else { return nil } // description for each type of compilers return "\($0.0): \(numbers.joined(separator: ", "))" }.joined(separator: "\n") } } #Preview { let compilers = Compilers( gcc: .init(number: "4"), llvm_gcc: .init(number: "213"), llvm: .init(number: "2.3"), clang: .init(number: "7.3"), swift: .init(number: "5.3.2") ) return CompilersView(compilers: compilers) .padding() } ================================================ FILE: Xcodes/Frontend/InfoPane/CornerRadiusModifier.swift ================================================ // // CornerRadiusModifier.swift // Xcodes // // Created by Matt Kiazyk on 2023-12-19. // import Foundation import SwiftUI struct CornerRadiusModifier: ViewModifier { func body(content: Content) -> some View { content .frame(maxWidth: .infinity, alignment: .leading) .padding() .background(.background) .clipShape(RoundedRectangle(cornerRadius: 5, style: .continuous)) } } extension View { func xcodesBackground() -> some View { self.modifier( CornerRadiusModifier() ) } } struct Previews_CornerRadius_Previews: PreviewProvider { static var previews: some View { HStack { Text(verbatim: "XCODES RULES!") }.xcodesBackground() } } ================================================ FILE: Xcodes/Frontend/InfoPane/IconView.swift ================================================ // // IconView.swift // Xcodes // // Created by Duong Thai on 11/10/2023. // Copyright © 2023 Robots and Pencils. All rights reserved. // import Path import SwiftUI import Version struct IconView: View { let xcode: Xcode var body: some View { if case let .installed(path) = xcode.installState { Image(nsImage: NSWorkspace.shared.icon(forFile: path.string)) } else { Image(xcode.version.isPrerelease ? "xcode-beta" : "xcode") .resizable() .frame(width: 32, height: 32) .foregroundColor(.secondary) } } } #Preview("Installed") { IconView(xcode: Xcode(version: Version("12.3.0")!, installState: .installed(Path("/Applications/Xcode-12.3.0.app")!), selected: true, icon: nil)) .frame(width: 300, height: 100) .padding() } #Preview("Installed") { IconView(xcode: Xcode(version: Version("12.3.0")!, installState: .notInstalled, selected: true, icon: nil)) .frame(width: 300, height: 100) .padding() } #Preview("Not Installed") { IconView(xcode: Xcode(version: Version("12.0.0-1234A")!, installState: .notInstalled, selected: false, icon: nil)) .frame(width: 300, height: 100) .padding() } ================================================ FILE: Xcodes/Frontend/InfoPane/IdenticalBuildView.swift ================================================ // // IdenticalBuildView.swift // Xcodes // // Created by Duong Thai on 11/10/2023. // Copyright © 2023 Robots and Pencils. All rights reserved. // import SwiftUI import Version struct IdenticalBuildsView: View { let builds: [Version] private let isEmpty: Bool private let accessibilityDescription: String var body: some View { if isEmpty { EmptyView() } else { VStack(alignment: .leading) { HStack { Text("IdenticalBuilds") Image(systemName: "square.fill.on.square.fill") .foregroundColor(.secondary) .accessibility(hidden: true) .help("IdenticalBuilds.help") } .font(.headline) ForEach(builds, id: \.description) { version in Text(verbatim: "• \(version.appleDescription)") .font(.subheadline) } } .xcodesBackground() .accessibilityElement() .accessibility(label: Text("IdenticalBuilds")) .accessibility(value: Text(accessibilityDescription)) .accessibility(hint: Text("IdenticalBuilds.help")) } } init(builds: [Version]) { self.builds = builds self.isEmpty = builds.isEmpty self.accessibilityDescription = builds .map(\.appleDescription) .joined(separator: ", ") } } let builds: [Version] = [.init(xcodeVersion: "15.0")!, .init(xcodeVersion: "15.1")!] #Preview("Has Some Builds") { IdenticalBuildsView(builds: builds) .padding() } #Preview("No Build") { IdenticalBuildsView(builds: []) .padding() } ================================================ FILE: Xcodes/Frontend/InfoPane/InfoPane.swift ================================================ import AppKit import XcodesKit import Path import SwiftUI import Version struct InfoPane: View { let xcode: Xcode var body: some View { if #available(macOS 14.0, *) { mainContent .contentMargins(10, for: .scrollContent) } else { mainContent .padding() } } private var mainContent: some View { ScrollView(.vertical) { HStack(alignment: .top) { VStack { VStack(spacing: 5) { HStack { IconView(xcode: xcode) Text(verbatim: "Xcode \(xcode.description) \(xcode.version.buildMetadataIdentifiersDisplay)") .font(.title) .frame(maxWidth: .infinity, alignment: .leading) .textSelection(.enabled) } InfoPaneControls(xcode: xcode) } .xcodesBackground() PlatformsView(xcode: xcode) } .frame(minWidth: 380) VStack(alignment: .leading) { ReleaseDateView(date: xcode.releaseDate, url: xcode.releaseNotesURL) CompatibilityView(requiredMacOSVersion: xcode.requiredMacOSVersion) IdenticalBuildsView(builds: xcode.identicalBuilds.map { $0.version }) SDKandCompilers } .frame(width: 200) } } } @ViewBuilder var SDKandCompilers: some View { VStack(alignment: .leading, spacing: 16) { SDKsView(sdks: xcode.sdks) CompilersView(compilers: xcode.compilers) } .frame(maxWidth: .infinity, alignment: .leading) .padding() .background(.background) .clipShape(RoundedRectangle(cornerRadius: 5, style: .continuous)) } } #Preview(XcodePreviewName.allCases[0].rawValue) { makePreviewContent(for: 0) } #Preview(XcodePreviewName.allCases[1].rawValue) { makePreviewContent(for: 1) } #Preview(XcodePreviewName.allCases[2].rawValue) { makePreviewContent(for: 2) } #Preview(XcodePreviewName.allCases[3].rawValue) { makePreviewContent(for: 3) } #Preview(XcodePreviewName.allCases[4].rawValue) { makePreviewContent(for: 4) } #Preview(XcodePreviewName.allCases[5].rawValue) { makePreviewContent(for: 5) } private func makePreviewContent(for index: Int) -> some View { let name = XcodePreviewName.allCases[index] return InfoPane(xcode: xcodeDict[name]!) .environmentObject(configure(AppState()) { $0.allXcodes = [xcodeDict[name]!] }) .frame(width: 600, height: 400) .padding() } enum XcodePreviewName: String, CaseIterable, Identifiable { case Populated_Installed_Selected case Populated_Installed_Unselected case Populated_Uninstalled case Basic_Installed case Basic_Installing case Basic_Unarchiving var id: XcodePreviewName { self } } var xcodeDict: [XcodePreviewName: Xcode] = [ .Populated_Installed_Selected: .init( version: _versionNoMeta, installState: .installed(Path(_path)!), selected: true, icon: NSWorkspace.shared.icon(forFile: _path), requiredMacOSVersion: _requiredMacOSVersion, releaseNotesURL: URL(string: "https://developer.apple.com/documentation/xcode-release-notes/xcode-12_3-release-notes/")!, releaseDate: Date(), sdks: _sdks, compilers: _compilers, downloadFileSize: _downloadFileSize ), .Populated_Installed_Unselected: .init( version: _versionNoMeta, installState: .installed(Path(_path)!), selected: false, icon: NSWorkspace.shared.icon(forFile: _path), sdks: _sdks, compilers: _compilers, downloadFileSize: _downloadFileSize ), .Populated_Uninstalled: .init( version: Version(major: 12, minor: 3, patch: 0), installState: .notInstalled, selected: false, icon: nil, sdks: _sdks, compilers: _compilers, downloadFileSize: _downloadFileSize ), .Basic_Installed: .init( version: _versionWithMeta, installState: .installed(Path(_path)!), selected: false, icon: nil, sdks: nil, compilers: nil ), .Basic_Installing: .init( version: _versionWithMeta, installState: .installing(.downloading( progress: configure(Progress()) { $0.kind = .file $0.fileOperationKind = .downloading $0.estimatedTimeRemaining = 123 $0.totalUnitCount = 11_944_848_484 $0.completedUnitCount = 848_444_920 $0.throughput = 9_211_681 } )), selected: false, icon: nil, sdks: nil, compilers: nil ), .Basic_Unarchiving: .init( version: _versionWithMeta, installState: .installing(.unarchiving), selected: false, icon: nil, sdks: nil, compilers: nil ), ] var downloadableRuntimes: [DownloadableRuntime] = { var runtimes = try! JSONDecoder().decode([DownloadableRuntime].self, from: Current.files.contents(atPath: Path.runtimeCacheFile.string)!) // set iOS to installed let iOSIndex = 0//runtimes.firstIndex { $0.sdkBuildUpdate.contains == "19E239" }! var iOSRuntime = runtimes[iOSIndex] iOSRuntime.installState = .installed runtimes[iOSIndex] = iOSRuntime let watchOSIndex = 0//runtimes.firstIndex { $0.sdkBuildUpdate.first == "20R362" }! var runtime = runtimes[watchOSIndex] runtime.installState = .installing( RuntimeInstallationStep.downloading( progress:configure(Progress()) { $0.kind = .file $0.fileOperationKind = .downloading $0.estimatedTimeRemaining = 123 $0.totalUnitCount = 11_944_848_484 $0.completedUnitCount = 848_444_920 $0.throughput = 9_211_681 } ) ) runtimes[watchOSIndex] = runtime return runtimes }() var installedRuntimes: [CoreSimulatorImage] = { [CoreSimulatorImage(uuid: "85B22F5B-048B-4331-B6E2-F4196D8B7475", path: ["relative" : "file:///Library/Developer/CoreSimulator/Images/85B22F5B-048B-4331-B6E2-F4196D8B7475.dmg"], runtimeInfo: CoreSimulatorRuntimeInfo(build: "19E240")), CoreSimulatorImage(uuid: "85B22F5B-048B-4331-B6E2-F4196D8B7473", path: ["relative" : "file:///Library/Developer/CoreSimulator/Images/85B22F5B-048B-4331-B6E2-F4196D8B7475.dmg"], runtimeInfo: CoreSimulatorRuntimeInfo(build: "21N5233f"))] }() private let _versionNoMeta = Version(major: 12, minor: 3, patch: 0) private let _versionWithMeta = Version(major: 12, minor: 3, patch: 1, buildMetadataIdentifiers: ["1234A"]) private let _path = "/Applications/Xcode-12.3.0.app" private let _requiredMacOSVersion = "10.15.4" private let _sdks = SDKs( macOS: .init(number: "11.1"), iOS: .init(number: "15.4", "19E239"), watchOS: .init(number: "7.3", "20R362"), tvOS: .init(number: "14.3", "20K67"), visionOS: .init(number: "1.0", "21N5233e") ) private let _compilers = Compilers( gcc: .init(number: "4"), llvm_gcc: .init(number: "213"), llvm: .init(number: "2.3"), clang: .init(number: "7.3"), swift: .init(number: "5.3.2") ) private let _downloadFileSize: Int64 = 242_342_424 ================================================ FILE: Xcodes/Frontend/InfoPane/InfoPaneControls.swift ================================================ // // InfoPaneControls.swift // Xcodes // // Created by Duong Thai on 14/10/2023. // Copyright © 2023 Robots and Pencils. All rights reserved. // import SwiftUI struct InfoPaneControls: View { let xcode: Xcode var body: some View { VStack (alignment: .leading) { switch xcode.installState { case .notInstalled: HStack { Spacer() NotInstalledStateButtons( downloadFileSizeString: xcode.downloadFileSizeString, id: xcode.id) } case .installing(let installationStep): HStack(alignment: .top) { InstallationStepDetailView(installationStep: installationStep) .frame(maxWidth: .infinity, alignment: .leading) CancelInstallButton(xcode: xcode) } case .installed(_): InstalledStateButtons(xcode: xcode) } } } } #Preview(XcodePreviewName.allCases[0].rawValue) { makePreviewContent(for: 0) } #Preview(XcodePreviewName.allCases[1].rawValue) { makePreviewContent(for: 1) } #Preview(XcodePreviewName.allCases[2].rawValue) { makePreviewContent(for: 2) } #Preview(XcodePreviewName.allCases[3].rawValue) { makePreviewContent(for: 3) } #Preview(XcodePreviewName.allCases[4].rawValue) { makePreviewContent(for: 4) } #Preview(XcodePreviewName.allCases[5].rawValue) { makePreviewContent(for: 5) } private func makePreviewContent(for index: Int) -> some View { let name = XcodePreviewName.allCases[index] return InfoPaneControls(xcode: xcodeDict[name]!) .environmentObject(configure(AppState()) { $0.allXcodes = [xcodeDict[name]!] }) .frame(width: 500) .padding() } ================================================ FILE: Xcodes/Frontend/InfoPane/InstallationStepDetailView.swift ================================================ import SwiftUI import XcodesKit struct InstallationStepDetailView: View { let installationStep: XcodeInstallationStep var body: some View { VStack(alignment: .leading, spacing: 0) { Text(String(format: localizeString("InstallationStepDescription"), installationStep.stepNumber, installationStep.stepCount, installationStep.message)) switch installationStep { case let .downloading(progress): ObservingProgressIndicator( progress, controlSize: .regular, style: .bar, showsAdditionalDescription: true ) case .authenticating, .unarchiving, .moving, .trashingArchive, .checkingSecurity, .finishing: ProgressView() .scaleEffect(0.5) } } } } #Preview("Downloading") { InstallationStepDetailView( installationStep: .downloading( progress: configure(Progress()) { $0.kind = .file $0.fileOperationKind = .downloading $0.estimatedTimeRemaining = 123 $0.totalUnitCount = 11944848484 $0.completedUnitCount = 848444920 $0.throughput = 9211681 } ) ) .padding() } #Preview("Unarchiving") { InstallationStepDetailView( installationStep: .unarchiving ) .padding() } ================================================ FILE: Xcodes/Frontend/InfoPane/InstalledStateButtons.swift ================================================ // // InstallingStateButtons.swift // Xcodes // // Created by Duong Thai on 13/10/2023. // Copyright © 2023 Robots and Pencils. All rights reserved. // import SwiftUI import Version import XcodesKit import Path struct InstalledStateButtons: View { let xcode: Xcode @EnvironmentObject var appState: AppState var body: some View { VStack(alignment: .leading) { HStack { Text(xcode.installedPath?.string ?? "") Button(action: { appState.reveal(xcode.installedPath) }) { Image(systemName: "arrow.right.circle.fill") } .buttonStyle(PlainButtonStyle()) .help("RevealInFinder") } HStack { SelectButton(xcode: xcode) .disabled(xcode.selected) .help("Selected") OpenButton(xcode: xcode) .help("Open") Spacer() UninstallButton(xcode: xcode) } } } } #Preview { InstalledStateButtons(xcode: xcode) .environmentObject(configure(AppState()) { $0.allXcodes = [xcode] }) .padding() .frame(width: 300) } private let xcode = Xcode( version: Version(major: 12, minor: 3, patch: 0), installState: .installed(Path("/Applications/Xcode-12.3.0.app")!), selected: true, icon: NSWorkspace.shared.icon(forFile: "/Applications/Xcode-12.3.0.app"), requiredMacOSVersion: "10.15.4", releaseNotesURL: URL(string: "https://developer.apple.com/documentation/xcode-release-notes/xcode-12_3-release-notes/")!, releaseDate: Date(), sdks: SDKs( macOS: .init(number: "11.1"), iOS: .init(number: "14.3"), watchOS: .init(number: "7.3"), tvOS: .init(number: "14.3") ), compilers: Compilers( gcc: .init(number: "4"), llvm_gcc: .init(number: "213"), llvm: .init(number: "2.3"), clang: .init(number: "7.3"), swift: .init(number: "5.3.2") ), downloadFileSize: 242342424 ) ================================================ FILE: Xcodes/Frontend/InfoPane/NotInstalledStateButtons.swift ================================================ // // NotInstalledStateButtonsView.swift // Xcodes // // Created by Duong Thai on 13/10/2023. // Copyright © 2023 Robots and Pencils. All rights reserved. // import SwiftUI import Version struct NotInstalledStateButtons: View { let downloadFileSizeString: String? let id: XcodeID @EnvironmentObject var appState: AppState var body: some View { VStack(alignment: .leading) { Button { appState.checkMinVersionAndInstall(id: id) } label: { if id.architectures?.isAppleSilicon ?? false { Text("Install Apple Silicon").help("Install") } else { Text("Install Universal").help("Install") } } if let size = downloadFileSizeString { Text("DownloadSize") .font(.headline) Text(size) .font(.subheadline) } else { EmptyView() } } } } #Preview { NotInstalledStateButtons( downloadFileSizeString: "1,19 GB", id: XcodeID(version: Version(major: 12, minor: 3, patch: 0), architectures: nil) ) .padding() } ================================================ FILE: Xcodes/Frontend/InfoPane/PlatformsView.swift ================================================ // // PlatformsView.swift // Xcodes // // Created by Matt Kiazyk on 2023-12-18. // import Foundation import SwiftUI import XcodesKit struct PlatformsView: View { @EnvironmentObject var appState: AppState @AppStorage("selectedRuntimeArchitecture") private var selectedVariant: ArchitectureVariant = .universal let xcode: Xcode var body: some View { let builds = xcode.sdks?.allBuilds() let runtimes = builds?.flatMap { sdkBuild in appState.downloadableRuntimes.filter { $0.sdkBuildUpdate?.contains(sdkBuild) ?? false && ($0.architectures?.isEmpty ?? true || ($0.architectures?.isUniversal ?? false && selectedVariant == .universal) || ($0.architectures?.isAppleSilicon ?? false && selectedVariant == .appleSilicon) ) } } let architectures = Set((runtimes ?? []).flatMap { $0.architectures ?? [] }) VStack { HStack { Text("Platforms") .font(.title3) .frame(maxWidth: .infinity, alignment: .leading) if !architectures.isEmpty { Spacer() Picker("Architecture", selection: $selectedVariant) { ForEach(ArchitectureVariant.allCases, id: \.self) { arch in Label(arch.displayString, systemImage: arch.iconName) .tag(arch) } .labelStyle(.trailingIcon) } .pickerStyle(.menu) .menuStyle(.button) .buttonStyle(.borderless) .fixedSize() .labelsHidden() } } ForEach(runtimes ?? [], id: \.identifier) { runtime in runtimeView(runtime: runtime) .frame(minWidth: 200) .padding() .background(.quinary) .clipShape(RoundedRectangle(cornerRadius: 5, style: .continuous)) } } .xcodesBackground() } @ViewBuilder func runtimeView(runtime: DownloadableRuntime) -> some View { VStack(spacing: 10) { HStack { runtime.icon() Text("\(runtime.visibleIdentifier)") .font(.headline) ForEach(runtime.architectures ?? [], id: \.self) { architecture in TagView(text: architecture.displayString) } pathIfAvailable(xcode: xcode, runtime: runtime) if runtime.installState == .notInstalled { // TODO: Update the downloadableRuntimes with the appropriate installState so we don't have to check path awkwardly if appState.runtimeInstallPath(xcode: xcode, runtime: runtime) != nil { EmptyView() } else { HStack { Spacer() DownloadRuntimeButton(runtime: runtime) } } } Spacer() Text(runtime.downloadFileSizeString) .font(.subheadline) .frame(width: 70, alignment: .trailing) } if case let .installing(installationStep) = runtime.installState { HStack(alignment: .top, spacing: 5){ RuntimeInstallationStepDetailView(installationStep: installationStep) .fixedSize(horizontal: false, vertical: true) Spacer() CancelRuntimeInstallButton(runtime: runtime) } } } } @ViewBuilder func pathIfAvailable(xcode: Xcode, runtime: DownloadableRuntime) -> some View { if let path = appState.runtimeInstallPath(xcode: xcode, runtime: runtime) { Button(action: { appState.reveal(path: path.string) }) { Image(systemName: "arrow.right.circle.fill") } .buttonStyle(PlainButtonStyle()) .help("RevealInFinder") } else { EmptyView() } } } #Preview(XcodePreviewName.allCases[0].rawValue) { makePreviewContent(for: 0) } private func makePreviewContent(for index: Int) -> some View { let name = XcodePreviewName.allCases[index] let runtimes = downloadableRuntimes return PlatformsView(xcode: xcodeDict[name]!) .environmentObject({ () -> AppState in let a = AppState() a.allXcodes = [xcodeDict[name]!] a.installedRuntimes = installedRuntimes a.downloadableRuntimes = runtimes return a }()) .frame(width: 300) .padding() } ================================================ FILE: Xcodes/Frontend/InfoPane/ReleaseDateView.swift ================================================ // // ReleaseDateView.swift // Xcodes // // Created by Duong Thai on 11/10/2023. // Copyright © 2023 Robots and Pencils. All rights reserved. // import SwiftUI struct ReleaseDateView: View { let date: Date? let url: URL? var body: some View { if let date = date { VStack(alignment: .leading) { HStack { Text("ReleaseDate") .font(.headline) Spacer() if let url { ReleaseNotesView(url: url) } } Text("\(date, style: .date)") .font(.subheadline) } .frame(maxWidth: .infinity, alignment: .leading) .padding() .background(.background) .clipShape(RoundedRectangle(cornerRadius: 5, style: .continuous)) } else { EmptyView() } } } #Preview { ReleaseDateView(date: Date(), url: URL(string: "https://www.xcodes.app")!) .padding() } ================================================ FILE: Xcodes/Frontend/InfoPane/ReleaseNotesView.swift ================================================ // // ReleaseNotesView.swift // Xcodes // // Created by Duong Thai on 13/10/2023. // Copyright © 2023 Robots and Pencils. All rights reserved. // import SwiftUI struct ReleaseNotesView: View { let url: URL? @SwiftUI.Environment(\.openURL) var openURL: OpenURLAction var body: some View { if let url = url { Button(action: { openURL(url) }) { Image(systemName: "link.circle.fill") .font(.title) } .buttonStyle(.plain) .contextMenu(menuItems: { CopyReleaseNoteButton(url: url) }) .help("ReleaseNotes.help") } else { EmptyView() } } } #Preview { let url = URL(string: "https://developer.apple.com/documentation/xcode-release-notes/xcode-12_3-release-notes/")! return ReleaseNotesView(url: url) .padding() } ================================================ FILE: Xcodes/Frontend/InfoPane/RuntimeInstallationStepDetailView.swift ================================================ // // RuntimeInstallationStepDetailView.swift // Xcodes // // Created by Matt Kiazyk on 2023-11-23. // Copyright © 2023 Robots and Pencils. All rights reserved. // import SwiftUI import XcodesKit struct RuntimeInstallationStepDetailView: View { let installationStep: RuntimeInstallationStep var body: some View { VStack(alignment: .leading, spacing: 0) { Text(String(format: localizeString("InstallationStepDescription"), installationStep.stepNumber, installationStep.stepCount, installationStep.message)) switch installationStep { case let .downloading(progress): ObservingProgressIndicator( progress, controlSize: .regular, style: .bar, showsAdditionalDescription: true ) case .installing, .trashingArchive: ObservingProgressIndicator( Progress(), controlSize: .regular, style: .bar, showsAdditionalDescription: false ) } } } } #Preview("Downloading") { RuntimeInstallationStepDetailView( installationStep: .downloading( progress: configure(Progress()) { $0.kind = .file $0.fileOperationKind = .downloading $0.estimatedTimeRemaining = 123 $0.totalUnitCount = 11944848484 $0.completedUnitCount = 848444920 $0.throughput = 9211681 } )) } #Preview("Installing") { RuntimeInstallationStepDetailView( installationStep: .installing ) } ================================================ FILE: Xcodes/Frontend/InfoPane/SDKsView.swift ================================================ // // SDKsView.swift // Xcodes // // Created by Duong Thai on 13/10/2023. // Copyright © 2023 Robots and Pencils. All rights reserved. // import SwiftUI import XcodesKit struct SDKsView: View { let content: String var body: some View { if content.isEmpty { EmptyView() } else { VStack(alignment: .leading) { Text("SDKs").font(.headline) Text(content) .font(.subheadline) .textSelection(.enabled) } } } init(sdks: SDKs?) { guard let sdks = sdks else { self.content = "" return } let content = Self.content(from: sdks) self.content = content } static private func content(from sdks: SDKs) -> String { let content: String = [ ("macOS", sdks.macOS), ("iOS", sdks.iOS), ("watchOS", sdks.watchOS), ("tvOS", sdks.tvOS) ].compactMap { // remove nil compiler guard $0.1 != nil, // has version array !$0.1!.isEmpty // has at least 1 version else { return nil } let numbers = $0.1!.compactMap { $0.number } // remove nil number guard !numbers.isEmpty // has at least 1 number else { return nil } // description for each type of compilers return "\($0.0): \(numbers.joined(separator: ", "))" }.joined(separator: "\n") .trimmingCharacters(in: .whitespaces) return content } } #Preview { let sdks = SDKs( macOS: .init(number: "11.1"), iOS: .init(number: "14.3"), watchOS: .init(number: "7.3"), tvOS: .init(number: "14.3")) return SDKsView(sdks: sdks) .padding() } ================================================ FILE: Xcodes/Frontend/InfoPane/UnselectedView.swift ================================================ // // UnselectedView.swift // Xcodes // // Created by Duong Thai on 13/10/2023. // Copyright © 2023 Robots and Pencils. All rights reserved. // import SwiftUI struct UnselectedView: View { var body: some View { VStack { Spacer() Text("NoXcodeSelected") .font(.title) .foregroundColor(.secondary) Spacer() } } } #Preview { UnselectedView() .padding() } ================================================ FILE: Xcodes/Frontend/MainWindow.swift ================================================ import ErrorHandling import SwiftUI import XcodesKit import Path import Version struct MainWindow: View { @EnvironmentObject var appState: AppState @State private var selectedXcodeID: Xcode.ID? @State private var searchText: String = "" @AppStorage("lastUpdated") private var lastUpdated: Double? // These two properties should be per-scene state managed by @SceneStorage property wrappers. // There's currently a bug with @SceneStorage on macOS, though, where quitting the app will discard the values, which removes a lot of its utility. // In the meantime, we're using @AppStorage so that persistence and state restoration works, even though it's not per-scene. // FB8979533 SceneStorage doesn't restore value after app is quit by user @AppStorage("isShowingInfoPane") private var isShowingInfoPane = false @AppStorage("xcodeListCategory") private var category: XcodeListCategory = .all @AppStorage("xcodeListArchitecture") private var architecture: XcodeListArchitecture = .universal @AppStorage("isInstalledOnly") private var isInstalledOnly = false var body: some View { NavigationSplitViewWrapper { XcodeListView(selectedXcodeID: $selectedXcodeID, searchText: searchText, category: category, isInstalledOnly: isInstalledOnly, architecture: architecture) .layoutPriority(1) .alert(item: $appState.xcodeBeingConfirmedForUninstallation) { xcode in Alert(title: Text(String(format: localizeString("Alert.Uninstall.Title"), xcode.description)), message: Text("Alert.Uninstall.Message"), primaryButton: .destructive(Text("Uninstall"), action: { self.appState.uninstall(xcode: xcode) }), secondaryButton: .cancel(Text("Cancel"))) } .searchable(text: $searchText, placement: .sidebar) .mainToolbar( category: $category, isInstalledOnly: $isInstalledOnly, isShowingInfoPane: $isShowingInfoPane, architecture: $architecture ) } detail: { Group { if let xcode = xcode { InfoPane(xcode: xcode) } else { UnselectedView() } } .toolbar { ToolbarItemGroup { Button(action: { appState.presentedSheet = .signIn }, label: { Label("Login", systemImage: "person.circle") }) .help("LoginDescription") if #available(macOS 14, *) { SettingsLink(label: { Label("Preferences", systemImage: "gearshape") }) .help("PreferencesDescription") } else { Button(action: { NSApp.sendAction(Selector(("showSettingsWindow:")), to: nil, from: nil) }, label: { Label("Preferences", systemImage: "gearshape") }) .help("PreferencesDescription") } } } } .bottomStatusBar() .padding([.top], 0) .navigationSubtitle(subtitleText) .frame(minWidth: 600, maxWidth: .infinity, minHeight: 300, maxHeight: .infinity) .emittingError($appState.error, recoveryHandler: { _ in }) .sheet(item: $appState.presentedSheet) { sheet in switch sheet { case .signIn: signInView() .environmentObject(appState) case .twoFactor(let secondFactorData): secondFactorView(secondFactorData) .environmentObject(appState) case .securityKeyTouchToConfirm: SignInSecurityKeyTouchView(isPresented: $appState.presentedSheet.isNotNil) .environmentObject(appState) } } .alert(item: $appState.presentedAlert, content: { presentedAlert in alert(for: presentedAlert) }) // I'm expecting to be able to use this modifier on a List row, but using it at the top level here is the only way that has made XcodeCommands work so far. // FB8954571 focusedValue(_:_:) on List row doesn't propagate value to @FocusedValue .focusedValue(\.selectedXcode, SelectedXcode(appState.allXcodes.first { $0.id == selectedXcodeID })) } private var xcode: Xcode? { appState.allXcodes.first(where: { $0.id == selectedXcodeID }) } private var subtitleText: Text { if let lastUpdated = lastUpdated.map(Date.init(timeIntervalSince1970:)) { return Text("\(localizeString("UpdatedAt")) \(lastUpdated, style: .date) \(lastUpdated, style: .time)") } else { return Text("") } } @ViewBuilder private func secondFactorView(_ secondFactorData: XcodesSheet.SecondFactorData) -> some View { switch secondFactorData.option { case .codeSent: SignIn2FAView(isPresented: $appState.presentedSheet.isNotNil, authOptions: secondFactorData.authOptions, sessionData: secondFactorData.sessionData) case .smsSent(let trustedPhoneNumber): SignInSMSView(isPresented: $appState.presentedSheet.isNotNil, trustedPhoneNumber: trustedPhoneNumber, authOptions: secondFactorData.authOptions, sessionData: secondFactorData.sessionData) case .smsPendingChoice: SignInPhoneListView(isPresented: $appState.presentedSheet.isNotNil, authOptions: secondFactorData.authOptions, sessionData: secondFactorData.sessionData) case .securityKey: SignInSecurityKeyPinView(isPresented: $appState.presentedSheet.isNotNil, authOptions: secondFactorData.authOptions, sessionData: secondFactorData.sessionData) } } @ViewBuilder private func signInView() -> some View { if appState.authenticationState == .authenticated { VStack { SignedInView() .padding(32) HStack { Spacer() Button("Close") { appState.presentedSheet = nil } .keyboardShortcut(.cancelAction) } } .padding() } else { SignInCredentialsView() .frame(width: 400) } } private func alert(for alertType: XcodesAlert) -> Alert { switch alertType { case let .cancelInstall(xcode): return Alert( title: Text(String(format: localizeString("Alert.CancelInstall.Title"), xcode.description)), message: Text("Alert.CancelInstall.Message"), primaryButton: .destructive( Text("Alert.CancelInstall.PrimaryButton"), action: { self.appState.cancelInstall(id: xcode.id) } ), secondaryButton: .cancel(Text("Cancel")) ) case .privilegedHelper: return Alert( title: Text("Alert.PrivilegedHelper.Title"), message: Text("Alert.PrivilegedHelper.Message"), primaryButton: .default(Text("Install"), action: { // The isPreparingUserForActionRequiringHelper closure is set to nil by the alert's binding when its dismissed. // We need to capture it to be invoked after that happens. let helperAction = appState.isPreparingUserForActionRequiringHelper DispatchQueue.main.async { // This really shouldn't be nil, but sometimes this alert is being shown twice and I don't know why. // There are some DispatchQueue.main.async's scattered around which make this better but in some situations it's still happening. // When that happens, the second time the user clicks an alert button isPreparingUserForActionRequiringHelper will be nil. // To at least not crash, we're using ? helperAction?(true) appState.presentedAlert = nil } }), secondaryButton: .cancel { // The isPreparingUserForActionRequiringHelper closure is set to nil by the alert's binding when its dismissed. // We need to capture it to be invoked after that happens. let helperAction = appState.isPreparingUserForActionRequiringHelper DispatchQueue.main.async { // This really shouldn't be nil, but sometimes this alert is being shown twice and I don't know why. // There are some DispatchQueue.main.async's scattered around which make this better but in some situations it's still happening. // When that happens, the second time the user clicks an alert button isPreparingUserForActionRequiringHelper will be nil. // To at least not crash, we're using ? helperAction?(false) appState.presentedAlert = nil } } ) case let .generic(title, message): return Alert( title: Text(title), message: Text(message), dismissButton: .default( Text("OK"), action: { appState.presentedAlert = nil } ) ) case .unauthenticated: return Alert( title: Text("Alert.Install.Error.Title"), message: Text("Alert.Install.AuthError.Message"), primaryButton: .default( Text("OK"), action: { appState.presentedSheet = .signIn } ), secondaryButton: .cancel( Text("Cancel") ) ) case let .checkMinSupportedVersion(xcode, deviceVersion): return Alert( title: Text("Alert.MinSupported.Title"), message: Text(String(format: localizeString("Alert.MinSupported.Message"), xcode.xcodeID.version.descriptionWithoutBuildMetadata, xcode.requiredMacOSVersion ?? "", deviceVersion)), primaryButton: .default( Text("Install"), action: { self.appState.install(id: xcode.xcodeID) } ), secondaryButton: .cancel(Text("Cancel")) ) case let .cancelRuntimeInstall(runtime): return Alert( title: Text(String(format: localizeString("Alert.CancelInstall.Runtimes.Title"), runtime.name)), message: Text("Alert.CancelInstall.Message"), primaryButton: .destructive( Text("Alert.CancelInstall.PrimaryButton"), action: { self.appState.cancelRuntimeInstall(runtime: runtime) } ), secondaryButton: .cancel(Text("Cancel")) ) } } } struct MainWindow_Previews: PreviewProvider { static var previews: some View { MainWindow().environmentObject({ () -> AppState in let a = AppState() a.allXcodes = [ Xcode(version: Version("12.0.0+1234A")!, identicalBuilds: [XcodeID(version: Version("12.0.0+1234A")!), XcodeID(version: Version("12.0.0-RC+1234A")!)], installState: .installed(Path("/Applications/Xcode-12.3.0.app")!), selected: false, icon: nil), Xcode(version: Version("12.3.0")!, installState: .installed(Path("/Applications/Xcode-12.3.0.app")!), selected: true, icon: nil), Xcode(version: Version("12.2.0")!, installState: .notInstalled, selected: false, icon: nil), Xcode(version: Version("12.1.0")!, installState: .installing(.downloading(progress: configure(Progress(totalUnitCount: 100)) { $0.completedUnitCount = 40 })), selected: false, icon: nil), Xcode(version: Version("12.0.0")!, installState: .installed(Path("/Applications/Xcode-12.3.0.app")!), selected: false, icon: nil), ] return a }()) } } ================================================ FILE: Xcodes/Frontend/Preferences/AdvancedPreferencePane.swift ================================================ import AppleAPI import SwiftUI import Path struct AdvancedPreferencePane: View { @EnvironmentObject var appState: AppState var body: some View { VStack(alignment: .leading, spacing: 20) { GroupBox(label: Text("InstallDirectory")) { VStack(alignment: .leading) { HStack(alignment: .top, spacing: 5) { Text(appState.installPath).font(.footnote) .fixedSize(horizontal: false, vertical: true) .lineLimit(2) Button(action: { appState.reveal(path: appState.installPath) }) { Image(systemName: "arrow.right.circle.fill") } .buttonStyle(PlainButtonStyle()) .help("RevealInFinder") .fixedSize() } Button("Change") { let panel = NSOpenPanel() panel.allowsMultipleSelection = false panel.canChooseDirectories = true panel.canChooseFiles = false panel.canCreateDirectories = true panel.allowedContentTypes = [.folder] panel.directoryURL = URL(fileURLWithPath: appState.installPath) if panel.runModal() == .OK { guard let pathURL = panel.url, let path = Path(url: pathURL) else { return } self.appState.installPath = path.string } } .disabled(appState.disableInstallPathChange) Text("InstallPathDescription") .font(.footnote) .foregroundStyle(.secondary) .fixedSize(horizontal: false, vertical: true) } } .groupBoxStyle(PreferencesGroupBoxStyle()) GroupBox(label: Text("LocalCachePath")) { VStack(alignment: .leading) { HStack(alignment: .top, spacing: 5) { Text(appState.localPath).font(.footnote) .fixedSize(horizontal: false, vertical: true) .lineLimit(2) Button(action: { appState.reveal(path: appState.localPath) }) { Image(systemName: "arrow.right.circle.fill") } .buttonStyle(PlainButtonStyle()) .help("RevealInFinder") .fixedSize() } Button("Change") { let panel = NSOpenPanel() panel.allowsMultipleSelection = false panel.canChooseDirectories = true panel.canChooseFiles = false panel.canCreateDirectories = true panel.allowedContentTypes = [.folder] panel.directoryURL = URL(fileURLWithPath: appState.localPath) if panel.runModal() == .OK { guard let pathURL = panel.url, let path = Path(url: pathURL) else { return } self.appState.localPath = path.string } } .disabled(appState.disableLocalPathChange) Text("LocalCachePathDescription") .font(.footnote) .foregroundStyle(.secondary) .fixedSize(horizontal: false, vertical: true) } } .groupBoxStyle(PreferencesGroupBoxStyle()) GroupBox(label: Text("Active/Select")) { VStack(alignment: .leading) { Picker(selection: $appState.onSelectActionType) { Text(SelectedActionType.none.description) .tag(SelectedActionType.none) Text(SelectedActionType.rename.description) .tag(SelectedActionType.rename) } label: { Text(verbatim: "OnSelect") } .labelsHidden() .pickerStyle(.inline) .disabled(appState.onSelectActionTypeDisabled) Text(appState.onSelectActionType.detailedDescription) .font(.footnote) .foregroundStyle(.secondary) .fixedSize(horizontal: false, vertical: true) Spacer() .frame(height: 20) Toggle("AutomaticallyCreateSymbolicLink", isOn: $appState.createSymLinkOnSelect) .disabled(appState.createSymLinkOnSelectDisabled) Text("AutomaticallyCreateSymbolicLinkDescription") .font(.footnote) .foregroundStyle(.secondary) .fixedSize(horizontal: false, vertical: true) } .fixedSize(horizontal: false, vertical: true) } .groupBoxStyle(PreferencesGroupBoxStyle()) if Hardware.isAppleSilicon() { GroupBox(label: Text("Apple Silicon")) { Toggle("ShowOpenInRosetta", isOn: $appState.showOpenInRosettaOption) .disabled(appState.createSymLinkOnSelectDisabled) Text("ShowOpenInRosettaDescription") .font(.footnote) .foregroundStyle(.secondary) .fixedSize(horizontal: false, vertical: true) } .groupBoxStyle(PreferencesGroupBoxStyle()) } GroupBox(label: Text("PrivilegedHelper")) { VStack(alignment: .leading, spacing: 8) { switch appState.helperInstallState { case .unknown: ProgressView() .scaleEffect(0.5, anchor: .center) case .installed: Text("HelperInstalled") case .notInstalled: VStack(alignment: .leading) { Button("InstallHelper") { appState.installHelperIfNecessary() } Text("HelperNotInstalled") .font(.footnote) } } Text("PrivilegedHelperDescription") .font(.footnote) .foregroundStyle(.secondary) .fixedSize(horizontal: false, vertical: true) Spacer() } } .groupBoxStyle(PreferencesGroupBoxStyle()) } } } struct AdvancedPreferencePane_Previews: PreviewProvider { static var previews: some View { Group { AdvancedPreferencePane() .environmentObject(AppState()) .frame(maxWidth: 600) } .frame(width: 600, height: 700, alignment: .center) } } // A group style for the preferences struct PreferencesGroupBoxStyle: GroupBoxStyle { func makeBody(configuration: Configuration) -> some View { HStack(alignment: .top, spacing: 20) { configuration.label .frame(width: 180, alignment: .trailing) VStack(alignment: .leading) { configuration.content } } } } ================================================ FILE: Xcodes/Frontend/Preferences/DownloadPreferencePane.swift ================================================ import AppleAPI import SwiftUI struct DownloadPreferencePane: View { @EnvironmentObject var appState: AppState @AppStorage("dataSource") var dataSource: DataSource = .xcodeReleases @AppStorage("downloader") var downloader: Downloader = .aria2 var body: some View { VStack(alignment: .leading) { GroupBox(label: Text("DataSource")) { VStack(alignment: .leading) { Picker("DataSource", selection: $dataSource) { ForEach(DataSource.allCases) { dataSource in Text(dataSource.description) .tag(dataSource) } } .labelsHidden() .fixedSize() Text("DataSourceDescription") .font(.footnote) .foregroundStyle(.secondary) .fixedSize(horizontal: false, vertical: true) } } .groupBoxStyle(PreferencesGroupBoxStyle()) .disabled(dataSource.isManaged) GroupBox(label: Text("Downloader")) { VStack(alignment: .leading) { Picker("Downloader", selection: $downloader) { ForEach(Downloader.allCases) { downloader in Text(downloader.description) .tag(downloader) } } .labelsHidden() .fixedSize() Text("DownloaderDescription") .font(.footnote) .foregroundStyle(.secondary) .fixedSize(horizontal: false, vertical: true) } } .groupBoxStyle(PreferencesGroupBoxStyle()) .disabled(downloader.isManaged) } } } struct DownloadPreferencePane_Previews: PreviewProvider { static var previews: some View { Group { DownloadPreferencePane() .environmentObject(AppState()) .frame(maxWidth: 600) .frame(minHeight: 300) } } } ================================================ FILE: Xcodes/Frontend/Preferences/ExperiementsPreferencePane.swift ================================================ import AppleAPI import Path import SwiftUI struct ExperimentsPreferencePane: View { @EnvironmentObject var appState: AppState var body: some View { VStack(alignment: .leading, spacing: 20) { GroupBox(label: Text("FasterUnxip")) { VStack(alignment: .leading) { Toggle( "UseUnxipExperiment", isOn: $appState.unxipExperiment ) .disabled(appState.disableUnxipExperiment) Text("FasterUnxipDescription") .font(.footnote) .foregroundStyle(.secondary) .fixedSize(horizontal: false, vertical: true) } .fixedSize(horizontal: false, vertical: true) } .groupBoxStyle(PreferencesGroupBoxStyle()) } } } struct ExperimentsPreferencePane_Previews: PreviewProvider { static var previews: some View { Group { ExperimentsPreferencePane() .environmentObject(AppState()) .frame(maxWidth: 600) } } } ================================================ FILE: Xcodes/Frontend/Preferences/GeneralPreferencePane.swift ================================================ import AppleAPI import SwiftUI struct GeneralPreferencePane: View { @EnvironmentObject var appState: AppState var body: some View { VStack(alignment: .leading) { GroupBox(label: Text("AppleID")) { if appState.authenticationState == .authenticated { SignedInView() } else { Button("SignIn", action: { self.appState.presentedSheet = .signIn }) } } .groupBoxStyle(PreferencesGroupBoxStyle()) Divider() GroupBox(label: Text("Notifications")) { NotificationsView().environmentObject(appState) } .groupBoxStyle(PreferencesGroupBoxStyle()) Divider() GroupBox(label: Text("Misc")) { Toggle("TerminateAfterLastWindowClosed", isOn: $appState.terminateAfterLastWindowClosed) } .groupBoxStyle(PreferencesGroupBoxStyle()) } } } struct GeneralPreferencePane_Previews: PreviewProvider { static var previews: some View { Group { GeneralPreferencePane() .environmentObject(AppState()) .frame(maxWidth: 600) } } } ================================================ FILE: Xcodes/Frontend/Preferences/NotificationsView.swift ================================================ import SwiftUI struct NotificationsView: View { @EnvironmentObject var appState: AppState var body: some View { VStack(alignment: .leading) { switch Current.notificationManager.notificationStatus { case .shownAndAccepted: Text("AccessGranted") .fixedSize(horizontal: false, vertical: true) case .shownAndDenied: Text("AccessDenied") .fixedSize(horizontal: false, vertical: true) Button("NotificationSettings", action: { NSWorkspace.shared.open(URL(string: "x-apple.systempreferences:com.apple.preference.notifications")!) }) default: Button("EnableNotifications", action: { Current.notificationManager.requestAccess() }) } } .frame(maxWidth: .infinity, alignment: .leading) } } struct NotificationsView_Previews: PreviewProvider { static var previews: some View { Group { NotificationsView() } } } ================================================ FILE: Xcodes/Frontend/Preferences/PlatformsListView.swift ================================================ // // PlatformsListView.swift // Xcodes // // Created by Matt Kiazyk on 2023-12-20. // import Foundation import SwiftUI import Path import XcodesKit import OrderedCollections struct PlatformsListView: View { @EnvironmentObject var appState: AppState @State private var runtimes: OrderedDictionary = [:] @State private var selectedRuntime: DownloadableRuntime? var body: some View { List(selection: $selectedRuntime) { Text("PlatformsList.Title") .font(.body) ForEach(runtimes.elements.sorted(\.key.order), id: \.key) { platform, runtimeList in Section { ForEach(runtimeList, id: \.self) { runtime in HStack { Text(runtime.name) Spacer() Text(runtime.downloadFileSizeString) Button { deleteRuntime(runtime: runtime) } label: { Image(systemName: "trash") } .foregroundStyle(.red) .buttonStyle(.plain) } .frame(height: 30) } } header: { HStack { runtimeList.first!.icon() .aspectRatio(contentMode: .fit) .frame(width: 20) Text(platform.shortName) .font(.headline) } } footer: { EmptyView() } } } .listStyle(.inset(alternatesRowBackgrounds: true)) .task { loadRuntimes() } .onChange(of: appState.installedRuntimes) { _ in loadRuntimes() } } func loadRuntimes() { let filteredRuntimes = appState.downloadableRuntimes.filter { runtime in appState.installedRuntimes.contains { $0.runtimeInfo.build == runtime.simulatorVersion.buildUpdate } } runtimes = OrderedDictionary(grouping: filteredRuntimes, by: { $0.platform }) } func deleteRuntime(runtime: DownloadableRuntime) { appState.presentedPreferenceAlert = .deletePlatform(runtime: runtime) } } #Preview { PlatformsListView() .environmentObject({ () -> AppState in let a = AppState() a.installedRuntimes = installedRuntimes a.downloadableRuntimes = downloadableRuntimes return a }()) } ================================================ FILE: Xcodes/Frontend/Preferences/PreferencesView.swift ================================================ import SwiftUI struct PreferencesView: View { private enum Tabs: Hashable { case general, updates, advanced, experiment } @EnvironmentObject var appState: AppState @EnvironmentObject var updater: ObservableUpdater var body: some View { TabView { GeneralPreferencePane() .environmentObject(appState) .tabItem { Label("General", systemImage: "gearshape") } .tag(Tabs.general) UpdatesPreferencePane() .environmentObject(updater) .tabItem { Label("Updates", systemImage: "arrow.triangle.2.circlepath.circle") } .tag(Tabs.updates) DownloadPreferencePane() .environmentObject(appState) .tabItem { Label("Downloads", systemImage: "icloud.and.arrow.down") } AdvancedPreferencePane() .environmentObject(appState) .tabItem { Label("Advanced", systemImage: "gearshape.2") } .tag(Tabs.advanced) ExperimentsPreferencePane() .tabItem { Label("Experiments", systemImage: "lightbulb") } .tag(Tabs.experiment) } .padding(20) .frame(width: 600) } } ================================================ FILE: Xcodes/Frontend/Preferences/UpdatesPreferencePane.swift ================================================ import AppleAPI import Sparkle import SwiftUI struct UpdatesPreferencePane: View { @EnvironmentObject var updater: ObservableUpdater @AppStorage("autoInstallation") var autoInstallationType: AutoInstallationType = .none var body: some View { VStack(alignment: .leading, spacing: 20) { GroupBox(label: Text("Versions")) { VStack(alignment: .leading) { Toggle( "AutomaticInstallNewVersion", isOn: $autoInstallationType.isAutoInstalling ) .disabled(updater.disableAutoInstallNewVersions) Toggle( "IncludePreRelease", isOn: $autoInstallationType.isAutoInstallingBeta ) .disabled(updater.disableIncludePrereleaseVersions) } .fixedSize(horizontal: false, vertical: true) } .groupBoxStyle(PreferencesGroupBoxStyle()) Divider() GroupBox(label: Text("AppUpdates")) { VStack(alignment: .leading) { Toggle( "CheckForAppUpdates", isOn: $updater.automaticallyChecksForUpdates ) .fixedSize(horizontal: true, vertical: false) .disabled(updater.disableAutoUpdateXcodesApp) Toggle( "IncludePreRelease", isOn: $updater.includePrereleaseVersions ) .disabled(updater.disableAutoUpdateXcodesAppPrereleaseVersions) Button("CheckNow") { updater.checkForUpdates() } .padding(.top) .disabled(updater.disableAutoUpdateXcodesApp) Text(String(format: localizeString("LastChecked"), lastUpdatedString)) .font(.footnote) .foregroundStyle(.secondary) } .frame(maxWidth: .infinity, alignment: .leading) } .groupBoxStyle(PreferencesGroupBoxStyle()) } } private var lastUpdatedString: String { if let lastUpdatedDate = updater.lastUpdateCheckDate { return Self.formatter.string(from: lastUpdatedDate) } else { return localizeString("Never") } } private static let formatter = configure(DateFormatter()) { $0.dateStyle = .medium $0.timeStyle = .medium } } class ObservableUpdater: ObservableObject { private let updater: SPUUpdater private let updaterDelegate = UpdaterDelegate() @Published var automaticallyChecksForUpdates = false { didSet { updater.automaticallyChecksForUpdates = automaticallyChecksForUpdates } } private var automaticallyChecksForUpdatesObservation: NSKeyValueObservation? @Published var lastUpdateCheckDate: Date? private var lastUpdateCheckDateObservation: NSKeyValueObservation? @Published var includePrereleaseVersions = false { didSet { Current.defaults.set(includePrereleaseVersions, forKey: "includePrereleaseVersions") updaterDelegate.includePrereleaseVersions = includePrereleaseVersions } } var disableAutoInstallNewVersions: Bool { PreferenceKey.autoInstallation.isManaged() } var disableIncludePrereleaseVersions: Bool { PreferenceKey.autoInstallation.isManaged() } var disableAutoUpdateXcodesApp: Bool { PreferenceKey.SUEnableAutomaticChecks.isManaged() } var disableAutoUpdateXcodesAppPrereleaseVersions: Bool { PreferenceKey.includePrereleaseVersions.isManaged() } init() { updater = SPUStandardUpdaterController(startingUpdater: true, updaterDelegate: updaterDelegate, userDriverDelegate: nil).updater // upgrade from an old sparkle version which set feeds via the updater // now it uses the `updaterDelegate` updater.clearFeedURLFromUserDefaults() automaticallyChecksForUpdatesObservation = updater.observe( \.automaticallyChecksForUpdates, options: [.initial, .new, .old], changeHandler: { [unowned self] updater, change in guard change.newValue != change.oldValue else { return } self.automaticallyChecksForUpdates = updater.automaticallyChecksForUpdates } ) lastUpdateCheckDateObservation = updater.observe( \.lastUpdateCheckDate, options: [.initial, .new, .old], changeHandler: { [unowned self] updater, change in self.lastUpdateCheckDate = updater.lastUpdateCheckDate } ) includePrereleaseVersions = Current.defaults.bool(forKey: "includePrereleaseVersions") ?? false } func checkForUpdates() { updater.checkForUpdates() } } class UpdaterDelegate: NSObject, SPUUpdaterDelegate { var includePrereleaseVersions: Bool = false func feedURLString(for updater: SPUUpdater) -> String? { if includePrereleaseVersions { return .prereleaseAppcast } else { return .appcast } } } extension String { static let appcast = "https://www.xcodes.app/appcast.xml" static let prereleaseAppcast = "https://www.xcodes.app/appcast_pre.xml" } struct UpdatesPreferencePane_Previews: PreviewProvider { static var previews: some View { Group { UpdatesPreferencePane() .environmentObject(AppState()) .environmentObject(ObservableUpdater()) .frame(maxWidth: 600) .frame(minHeight: 300) } } } ================================================ FILE: Xcodes/Frontend/SignIn/AttributedText.swift ================================================ import SwiftUI /// A text view that supports NSAttributedStrings, based on NSTextView. public struct AttributedText: View { private let attributedString: NSAttributedString private let linkTextAttributes: [NSAttributedString.Key: Any]? @State private var actualSize: CGSize = .zero public init(_ attributedString: NSAttributedString, linkTextAttributes: [NSAttributedString.Key: Any]? = nil) { self.attributedString = attributedString self.linkTextAttributes = linkTextAttributes } public var body: some View { InnerAttributedStringText( attributedString: self.attributedString, actualSize: $actualSize ) // Limit the height to what's needed for the text .frame(height: actualSize.height) } } // MARK: InnerAttributedStringText fileprivate struct InnerAttributedStringText: NSViewRepresentable { private let attributedString: NSAttributedString @Binding var actualSize: CGSize internal init(attributedString: NSAttributedString, actualSize: Binding) { self.attributedString = attributedString self._actualSize = actualSize } func makeNSView(context: NSViewRepresentableContext) -> NSTextView { let textView = NSTextView() textView.backgroundColor = .clear textView.textContainer?.lineFragmentPadding = 0 textView.textContainerInset = .zero textView.isEditable = false textView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) textView.isSelectable = true return textView } func updateNSView(_ label: NSTextView, context _: NSViewRepresentableContext) { // This must happen on the next run loop so that we don't update the view hierarchy while already in the middle of an update DispatchQueue.main.async { label.textStorage?.setAttributedString(attributedString) // Calculates the height based on the current frame label.layoutManager?.ensureLayout(for: label.textContainer!) actualSize = label.layoutManager!.usedRect(for: label.textContainer!).size } } } import SwiftUI struct AttributedText_Previews: PreviewProvider { static var linkExample: NSAttributedString { let string = "The next word is a link. This is some more text to test how this wraps when it's too long." let s = NSMutableAttributedString(string: string) s.addAttribute(.link, value: URL(string: "https://robotsandpencils.com")!, range: NSRange(string.range(of: "link")!, in: string)) return s } static var previews: some SwiftUI.View { Group { // Previews don't work unless they're running, because detecting and setting the size happens on the next run loop AttributedText(linkExample) } } } ================================================ FILE: Xcodes/Frontend/SignIn/NSAttributedString+.swift ================================================ import Foundation public extension NSAttributedString { func addingAttribute(_ attribute: NSAttributedString.Key, value: Any, range: NSRange) -> NSAttributedString { let copy = mutableCopy() as! NSMutableAttributedString copy.addAttribute(attribute, value: value, range: range) return copy } func addingAttribute(_ attribute: NSAttributedString.Key, value: Any) -> NSAttributedString { addingAttribute(attribute, value: value, range: NSRange(string.startIndex ..< string.endIndex, in: string)) } /// Detects URLs and adds a NSAttributedString.Key.link attribute with the URL value func convertingURLsToLinkAttributes() -> NSAttributedString { guard let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue), let copy = self.mutableCopy() as? NSMutableAttributedString else { return self } let matches = detector.matches(in: self.string, options: [], range: NSRange(string.startIndex.. Void func makeNSView(context: Context) -> NSViewType { let view = PinCodeTextView(numberOfDigits: numberOfDigits, itemSpacing: 10) view.codeDidChange = { c in code = c } view.codeDidComplete = { complete($0) } return view } func updateNSView(_ nsView: NSViewType, context: Context) { nsView.code = (0.. Void)? = nil var codeDidComplete: ((String) -> Void)? = nil private let numberOfDigits: Int private let stackView: NSStackView = .init(frame: .zero) private var characterViews: [PinCodeCharacterTextField] = [] // MARK: - Initializers init( numberOfDigits: Int, itemSpacing: CGFloat ) { self.numberOfDigits = numberOfDigits super.init(frame: .zero) stackView.translatesAutoresizingMaskIntoConstraints = false stackView.spacing = itemSpacing stackView.orientation = .horizontal stackView.distribution = .fillEqually stackView.alignment = .centerY addSubview(stackView) NSLayoutConstraint.activate([ stackView.topAnchor.constraint(equalTo: self.topAnchor), stackView.bottomAnchor.constraint(equalTo: self.bottomAnchor), stackView.leadingAnchor.constraint(greaterThanOrEqualTo: self.leadingAnchor), stackView.trailingAnchor.constraint(greaterThanOrEqualTo: self.trailingAnchor), stackView.centerXAnchor.constraint(equalTo: self.centerXAnchor), ]) self.code = (0.. Bool { if commandSelector == #selector(deleteBackward(_:)) { // If empty, move to previous or first character view if textView.string.isEmpty { if let lastFieldIndexWithCharacter = code.lastIndex(where: { $0 != nil }) { window?.makeFirstResponder(characterViews[lastFieldIndexWithCharacter]) } else { window?.makeFirstResponder(characterViews[0]) } return true } } // Perform default behaviour return false } func controlTextDidChange(_ obj: Notification) { guard let field = obj.object as? NSTextField, isEnabled, let fieldIndex = characterViews.firstIndex(where: { $0 === field }) else { return } let newFieldText = field.stringValue let lastCharacter: Character? if newFieldText.isEmpty { lastCharacter = nil } else { lastCharacter = newFieldText[newFieldText.index(before: newFieldText.endIndex)] } code[fieldIndex] = lastCharacter if lastCharacter != nil { if fieldIndex >= characterViews.count - 1 { resignFirstResponder() } else { window?.makeFirstResponder(characterViews[fieldIndex + 1]) } } else { if let lastFieldIndexWithCharacter = code.lastIndex(where: { $0 != nil }) { window?.makeFirstResponder(characterViews[lastFieldIndexWithCharacter]) } else { window?.makeFirstResponder(characterViews[0]) } } } // MARK: NSResponder override var acceptsFirstResponder: Bool { true } override func becomeFirstResponder() -> Bool { characterViews.first?.becomeFirstResponder() ?? false } } // MARK: - PinCodeCharacterTextField class PinCodeCharacterTextField: NSTextField { var character: Character? = nil { didSet { stringValue = character.map(String.init) ?? "" } } private var lastSize: NSSize? init() { super.init(frame: .zero) wantsLayer = true alignment = .center maximumNumberOfLines = 1 font = .boldSystemFont(ofSize: 48) setContentHuggingPriority(.required, for: .vertical) setContentHuggingPriority(.required, for: .horizontal) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func textDidChange(_ notification: Notification) { super.textDidChange(notification) self.invalidateIntrinsicContentSize() } // This is kinda cheating // Assuming that 0 is the widest and tallest character in 0-9 override var intrinsicContentSize: NSSize { var size = NSAttributedString( string: "0", attributes: [ .font : self.font! ] ) .size() // I guess the cell should probably be doing this sizing in order to take into account everything outside of simply the text's frame, but for some reason I can't find a way to do that which works... size.width += 16 size.height += 8 return size } } ================================================ FILE: Xcodes/Frontend/SignIn/SignIn2FAView.swift ================================================ import SwiftUI import AppleAPI struct SignIn2FAView: View { @EnvironmentObject var appState: AppState @Binding var isPresented: Bool @State private var code: String = "" let authOptions: AuthOptionsResponse let sessionData: AppleSessionData var body: some View { VStack(alignment: .leading) { Text(String(format: localizeString("DigitCodeDescription"), authOptions.securityCode!.length)) .fixedSize(horizontal: true, vertical: false) HStack { Spacer() PinCodeTextField(code: $code, numberOfDigits: authOptions.securityCode!.length) { appState.submitSecurityCode(.device(code: $0), sessionData: sessionData) } Spacer() } .padding() HStack { Button("Cancel", action: { isPresented = false }) .keyboardShortcut(.cancelAction) Button("SendSMS", action: { appState.choosePhoneNumberForSMS(authOptions: authOptions, sessionData: sessionData) }) Spacer() ProgressButton(isInProgress: appState.isProcessingAuthRequest, action: { appState.submitSecurityCode(.device(code: code), sessionData: sessionData) }) { Text("Continue") } .keyboardShortcut(.defaultAction) .disabled(code.count != authOptions.securityCode!.length) } .frame(height: 25) } .padding() .emittingError($appState.authError, recoveryHandler: { _ in }) } } struct SignIn2FAView_Previews: PreviewProvider { static var previews: some View { SignIn2FAView( isPresented: .constant(true), authOptions: AuthOptionsResponse( trustedPhoneNumbers: nil, trustedDevices: nil, securityCode: .init(length: 6) ), sessionData: AppleSessionData(serviceKey: "", sessionID: "", scnt: "") ) .environmentObject(AppState()) } } ================================================ FILE: Xcodes/Frontend/SignIn/SignInCredentialsView.swift ================================================ import SwiftUI struct SignInCredentialsView: View { private enum FocusedField { case username, password } @EnvironmentObject var appState: AppState @State private var username: String = "" @State private var password: String = "" @FocusState private var focusedField: FocusedField? var body: some View { VStack(alignment: .leading) { Text("SignInWithApple") .bold() .padding(.vertical) HStack { Text("AppleID") .frame(minWidth: 100, alignment: .trailing) TextField(text: $username) { Text(verbatim: "example@icloud.com") } .focused($focusedField, equals: .username) } HStack { Text("Password") .frame(minWidth: 100, alignment: .trailing) SecureField("Required", text: $password) .focused($focusedField, equals: .password) } if appState.authError != nil { HStack { Text("") .frame(minWidth: 100) Text(appState.authError?.legibleLocalizedDescription ?? "") .fixedSize(horizontal: false, vertical: true) .foregroundColor(.red) } } HStack { Spacer() Button("Cancel") { appState.authError = nil appState.presentedSheet = nil } .keyboardShortcut(.cancelAction) ProgressButton( isInProgress: appState.isProcessingAuthRequest, action: { appState.signIn(username: username, password: password) }, label: { Text("Next") } ) .disabled(username.isEmpty || password.isEmpty) .keyboardShortcut(.defaultAction) } .frame(height: 25) } .padding() } } struct SignInCredentialsView_Previews: PreviewProvider { static var previews: some View { SignInCredentialsView() .environmentObject(AppState()) .previewLayout(.sizeThatFits) } } ================================================ FILE: Xcodes/Frontend/SignIn/SignInPhoneListView.swift ================================================ import AppleAPI import SwiftUI struct SignInPhoneListView: View { @EnvironmentObject var appState: AppState @Binding var isPresented: Bool @State private var selectedPhoneNumberID: AuthOptionsResponse.TrustedPhoneNumber.ID? let authOptions: AuthOptionsResponse let sessionData: AppleSessionData var body: some View { VStack(alignment: .leading) { if let phoneNumbers = authOptions.trustedPhoneNumbers, !phoneNumbers.isEmpty { Text(String(format: localizeString("SelectTrustedPhone"), authOptions.securityCode!.length)) List(phoneNumbers, selection: $selectedPhoneNumberID) { Text($0.numberWithDialCode) } .onAppear { if phoneNumbers.count == 1 { selectedPhoneNumberID = phoneNumbers.first?.id } } } else { Text("NoTrustedPhones") .font(.callout) Spacer() } HStack { Button("Cancel", action: { isPresented = false }) .keyboardShortcut(.cancelAction) Spacer() ProgressButton(isInProgress: appState.isProcessingAuthRequest, action: { appState.requestSMS(to: authOptions.trustedPhoneNumbers!.first { $0.id == selectedPhoneNumberID }!, authOptions: authOptions, sessionData: sessionData) }) { Text("Continue") } .keyboardShortcut(.defaultAction) .disabled(selectedPhoneNumberID == nil) } .frame(height: 25) } .padding() .frame(width: 400, height: 200) .emittingError($appState.authError, recoveryHandler: { _ in }) } } struct SignInPhoneListView_Previews: PreviewProvider { static var previews: some View { Group { SignInPhoneListView( isPresented: .constant(true), authOptions: AuthOptionsResponse( trustedPhoneNumbers: [.init(id: 0, numberWithDialCode: "(•••) •••-••90")], trustedDevices: nil, securityCode: .init(length: 6) ), sessionData: AppleSessionData(serviceKey: "", sessionID: "", scnt: "") ) .environmentObject(AppState()) SignInPhoneListView( isPresented: .constant(true), authOptions: AuthOptionsResponse( trustedPhoneNumbers: [], trustedDevices: nil, securityCode: .init(length: 6) ), sessionData: AppleSessionData(serviceKey: "", sessionID: "", scnt: "") ) .environmentObject(AppState()) } } } ================================================ FILE: Xcodes/Frontend/SignIn/SignInSMSView.swift ================================================ import SwiftUI import AppleAPI struct SignInSMSView: View { @EnvironmentObject var appState: AppState @Binding var isPresented: Bool @State private var code: String = "" let trustedPhoneNumber: AuthOptionsResponse.TrustedPhoneNumber let authOptions: AuthOptionsResponse let sessionData: AppleSessionData var body: some View { VStack(alignment: .leading) { Text(String(format: localizeString("EnterDigitCodeDescription"), authOptions.securityCode!.length, trustedPhoneNumber.numberWithDialCode)) HStack { Spacer() PinCodeTextField(code: $code, numberOfDigits: authOptions.securityCode!.length) { appState.submitSecurityCode(.sms(code: $0, phoneNumberId: trustedPhoneNumber.id), sessionData: sessionData) } Spacer() } .padding() HStack { Button("Cancel", action: { isPresented = false }) .keyboardShortcut(.cancelAction) Spacer() ProgressButton(isInProgress: appState.isProcessingAuthRequest, action: { appState.submitSecurityCode(.sms(code: code, phoneNumberId: trustedPhoneNumber.id), sessionData: sessionData) }) { Text("Continue") } .keyboardShortcut(.defaultAction) .disabled(code.count != authOptions.securityCode!.length) } .frame(height: 25) } .padding() .emittingError($appState.authError, recoveryHandler: { _ in }) } } struct SignInSMSView_Previews: PreviewProvider { static var previews: some View { SignInSMSView( isPresented: .constant(true), trustedPhoneNumber: .init(id: 0, numberWithDialCode: "(•••) •••-••90"), authOptions: AuthOptionsResponse( trustedPhoneNumbers: nil, trustedDevices: nil, securityCode: .init(length: 6) ), sessionData: AppleSessionData(serviceKey: "", sessionID: "", scnt: "") ) .environmentObject(AppState()) } } ================================================ FILE: Xcodes/Frontend/SignIn/SignInSecurityKeyPinView.swift ================================================ // // SignInSecurityKeyPin.swift // Xcodes // // Created by Kino on 2024-09-26. // Copyright © 2024 Robots and Pencils. All rights reserved. // import SwiftUI import AppleAPI struct SignInSecurityKeyPinView: View { @EnvironmentObject var appState: AppState @Binding var isPresented: Bool @State private var pin: String = "" let authOptions: AuthOptionsResponse let sessionData: AppleSessionData var body: some View { VStack(alignment: .leading) { Text(localizeString("SecurityKeyPinDescription")) .fixedSize(horizontal: true, vertical: false) HStack { Spacer() SecureField("PIN", text: $pin) Spacer() } .padding() HStack { Button("Cancel", action: { isPresented = false }) .keyboardShortcut(.cancelAction) Spacer() Button("PIN not set", action: submitWithoutPinCode) ProgressButton(isInProgress: appState.isProcessingAuthRequest, action: submitPinCode) { Text("Continue") } .keyboardShortcut(.defaultAction) // FIDO2 device pin codes must be at least 4 code points // https://docs.yubico.com/yesdk/users-manual/application-fido2/fido2-pin.html .disabled(pin.count < 4) } .frame(height: 25) } .padding() .emittingError($appState.authError, recoveryHandler: { _ in }) } func submitPinCode() { appState.createAndSubmitSecurityKeyAssertationWithPinCode(pin, sessionData: sessionData, authOptions: authOptions) } func submitWithoutPinCode() { appState.createAndSubmitSecurityKeyAssertationWithPinCode(nil, sessionData: sessionData, authOptions: authOptions) } } #Preview { SignInSecurityKeyPinView(isPresented: .constant(true), authOptions: AuthOptionsResponse( trustedPhoneNumbers: nil, trustedDevices: nil, securityCode: .init(length: 6) ), sessionData: AppleSessionData(serviceKey: "", sessionID: "", scnt: "")) .environmentObject(AppState()) } ================================================ FILE: Xcodes/Frontend/SignIn/SignInSecurityKeyTouchView.swift ================================================ // // SignInSecurityKeyPin.swift // Xcodes // // Created by Kino on 2024-09-26. // Copyright © 2024 Robots and Pencils. All rights reserved. // import SwiftUI import AppleAPI struct SignInSecurityKeyTouchView: View { @EnvironmentObject var appState: AppState @Binding var isPresented: Bool var body: some View { VStack(alignment: .center) { Image(systemName: "key.radiowaves.forward") .font(.system(size: 32)).bold() .padding(.bottom) HStack { Spacer() Text(localizeString("SecurityKeyTouchDescription")) .fixedSize(horizontal: true, vertical: false) Spacer() } HStack { Button("Cancel", action: self.cancel) .keyboardShortcut(.cancelAction) Spacer() ProgressView() .progressViewStyle(CircularProgressViewStyle()) .scaleEffect(x: 0.5, y: 0.5, anchor: .center) .isHidden(!appState.isProcessingAuthRequest) .keyboardShortcut(.defaultAction) } .frame(height: 25) } .padding() .emittingError($appState.authError, recoveryHandler: { _ in }) } func cancel() { appState.cancelSecurityKeyAssertationRequest() isPresented = false } } #Preview { SignInSecurityKeyTouchView(isPresented: .constant(true)) .environmentObject(AppState()) } ================================================ FILE: Xcodes/Frontend/SignIn/SignedInView.swift ================================================ import SwiftUI struct SignedInView: View { @EnvironmentObject var appState: AppState private var username: String { appState.savedUsername ?? "" } var body: some View { HStack(alignment:.top, spacing: 10) { Text(username) Button("SignOut", action: appState.signOut) } .frame(maxWidth: .infinity, alignment: .leading) } } struct SignedInView_Previews: PreviewProvider { static var previews: some View { SignedInView() .previewLayout(.sizeThatFits) } } ================================================ FILE: Xcodes/Frontend/View+Conditional.swift ================================================ import SwiftUI extension View { @ViewBuilder func `if`(_ predicate: Bool, then: (Self) -> Other) -> some View { if predicate { then(self) } else { self } } } ================================================ FILE: Xcodes/Frontend/View+IsHidden.swift ================================================ import SwiftUI extension View { @ViewBuilder func isHidden(_ isHidden: Bool) -> some View { if isHidden { self.hidden() } else { self } } } struct View_IsHidden_Previews: PreviewProvider { static var previews: some View { Group { Text(verbatim: "Not Hidden") .isHidden(false) Text(verbatim: "Hidden") .isHidden(true) } } } ================================================ FILE: Xcodes/Frontend/XcodeList/AppStoreButtonStyle.swift ================================================ import SwiftUI struct AppStoreButtonStyle: ButtonStyle { var primary: Bool var highlighted: Bool private struct AppStoreButton: View { @SwiftUI.Environment(\.isEnabled) var isEnabled var configuration: ButtonStyle.Configuration var primary: Bool var highlighted: Bool var textColor: Color { if isEnabled { if primary { if highlighted { return Color.accentColor } else { return Color.white } } else { return Color.accentColor } } else { if primary { if highlighted { return Color(.disabledControlTextColor) } else { return Color.white } } else { if highlighted { return Color.white } else { return Color(.disabledControlTextColor) } } } } func background(isPressed: Bool) -> some View { Group { if isEnabled { if primary { Capsule() .fill( highlighted ? Color.white : Color.accentColor ) .brightness(isPressed ? -0.25 : 0) } else { Capsule() .fill( Color(NSColor(red: 0.95, green: 0.95, blue: 0.97, alpha: 1)) ) .brightness(isPressed ? -0.25 : 0) } } else { if primary { Capsule() .fill( highlighted ? Color.white : Color(.disabledControlTextColor) ) .brightness(isPressed ? -0.25 : 0) } else { EmptyView() } } } } var body: some View { configuration.label .font(Font.caption.weight(.bold)) .foregroundColor(textColor) .padding(EdgeInsets(top: 4, leading: 8, bottom: 4, trailing: 8)) .frame(minWidth: 65) .background(background(isPressed: configuration.isPressed)) .padding(1) } } func makeBody(configuration: ButtonStyle.Configuration) -> some View { AppStoreButton(configuration: configuration, primary: primary, highlighted: highlighted) } } struct AppStoreButtonStyle_Previews: PreviewProvider { static var previews: some View { Group { ForEach([ColorScheme.light, .dark], id: \.self) { colorScheme in Group { Button{ } label: { Text(verbatim: "OPEN") } .buttonStyle(AppStoreButtonStyle(primary: true, highlighted: false)) .padding() .background(Color(.textBackgroundColor)) .previewDisplayName("Primary") Button{ } label: { Text(verbatim: "OPEN") } .buttonStyle(AppStoreButtonStyle(primary: true, highlighted: true)) .padding() .background(Color(.controlAccentColor)) .previewDisplayName("Primary, Highlighted") Button{ } label: { Text(verbatim: "OPEN") } .buttonStyle(AppStoreButtonStyle(primary: true, highlighted: false)) .padding() .disabled(true) .background(Color(.textBackgroundColor)) .previewDisplayName("Primary, Disabled") Button{ } label: { Text(verbatim: "INSTALL") } .buttonStyle(AppStoreButtonStyle(primary: false, highlighted: false)) .padding() .background(Color(.textBackgroundColor)) .previewDisplayName("Secondary") Button{ } label: { Text(verbatim: "INSTALL") } .buttonStyle(AppStoreButtonStyle(primary: false, highlighted: true)) .padding() .background(Color(.controlAccentColor)) .previewDisplayName("Secondary, Highlighted") Button{ } label: { Text(verbatim: "INSTALL") } .buttonStyle(AppStoreButtonStyle(primary: false, highlighted: false)) .padding() .disabled(true) .background(Color(.textBackgroundColor)) .previewDisplayName("Secondary, Disabled") } .environment(\.colorScheme, colorScheme) } } } } ================================================ FILE: Xcodes/Frontend/XcodeList/BottomStatusBar.swift ================================================ // // BottomStatusBar.swift // Xcodes // // Created by Matt Kiazyk on 2022-06-03. // Copyright © 2022 Robots and Pencils. All rights reserved. // import Foundation import SwiftUI struct BottomStatusModifier: ViewModifier { @EnvironmentObject var appState: AppState @AppStorage(PreferenceKey.hideSupportXcodes.rawValue) var hideSupportXcodes = false @SwiftUI.Environment(\.openURL) var openURL: OpenURLAction func body(content: Content) -> some View { VStack(spacing: 0) { content VStack(spacing: 0) { Divider() HStack { Text(appState.bottomStatusBarMessage) .font(.subheadline) Spacer() if !hideSupportXcodes { Button(action: { openURL(URL(string: "https://opencollective.com/xcodesapp")!) }) { HStack { Image(systemName: "heart.circle") Text("Support.Xcodes") } } } Text(verbatim: "\(Bundle.main.shortVersion!) (\(Bundle.main.version!))") .font(.subheadline) } .frame(maxWidth: .infinity, maxHeight: 30, alignment: .leading) .padding([.leading, .trailing], 10) } .frame(maxWidth: .infinity, maxHeight: 30, alignment: .leading) } } } extension View { func bottomStatusBar() -> some View { self.modifier( BottomStatusModifier() ) } } struct Previews_BottomStatusBar_Previews: PreviewProvider { static var previews: some View { Group { HStack { } .bottomStatusBar() .environmentObject({ () -> AppState in let a = AppState() return a }() ) .defaultAppStorage({ () -> UserDefaults in let d = UserDefaults(suiteName: "hide_support")! d.set(true, forKey: PreferenceKey.hideSupportXcodes.rawValue) return d }()) HStack { } .bottomStatusBar() .environmentObject({ () -> AppState in let a = AppState() return a }() ) .defaultAppStorage({ () -> UserDefaults in let d = UserDefaults(suiteName: "show_support")! d.set(false, forKey: PreferenceKey.hideSupportXcodes.rawValue) return d }()) } } } ================================================ FILE: Xcodes/Frontend/XcodeList/InstallationStepRowView.swift ================================================ import SwiftUI import XcodesKit struct InstallationStepRowView: View { let installationStep: XcodeInstallationStep let highlighted: Bool let cancel: () -> Void var body: some View { HStack { switch installationStep { case let .downloading(progress): // FB8955769 ProgressView.init(_: Progress) doesn't ensure that changes from the Progress object are applied to the UI on the main thread // This Progress is vended by URLSession so I don't think we can control that. // Use our own version of ProgressView that does this instead. ObservingProgressIndicator( progress, controlSize: .small, style: .spinning ) case .authenticating, .unarchiving, .moving, .trashingArchive, .checkingSecurity, .finishing: ProgressView() .scaleEffect(0.5) } Text(String(format: localizeString("InstallationStepDescription"), installationStep.stepNumber, installationStep.stepCount, installationStep.message)) .font(.footnote) Button(action: cancel) { Label("Cancel", systemImage: "xmark.circle.fill") .labelStyle(IconOnlyLabelStyle()) } .buttonStyle(PlainButtonStyle()) .foregroundColor(highlighted ? .white : .secondary) .help("StopInstallation") } .frame(minWidth: 80) } } struct InstallView_Previews: PreviewProvider { static var previews: some View { Group { ForEach(ColorScheme.allCases, id: \.self) { colorScheme in Group { InstallationStepRowView( installationStep: .downloading( progress: configure(Progress(totalUnitCount: 100)) { $0.completedUnitCount = 40 } ), highlighted: false, cancel: {} ) InstallationStepRowView( installationStep: .unarchiving, highlighted: false, cancel: {} ) InstallationStepRowView( installationStep: .moving(destination: "/Applications"), highlighted: false, cancel: {} ) InstallationStepRowView( installationStep: .trashingArchive, highlighted: false, cancel: {} ) InstallationStepRowView( installationStep: .checkingSecurity, highlighted: false, cancel: {} ) InstallationStepRowView( installationStep: .finishing, highlighted: false, cancel: {} ) } .padding() .background(Color(.windowBackgroundColor)) .environment(\.colorScheme, colorScheme) } ForEach(ColorScheme.allCases, id: \.self) { colorScheme in Group { InstallationStepRowView( installationStep: .downloading( progress: configure(Progress(totalUnitCount: 100)) { $0.completedUnitCount = 40 } ), highlighted: true, cancel: {} ) } .padding() .background(Color(.selectedContentBackgroundColor)) .environment(\.colorScheme, colorScheme) } } } } ================================================ FILE: Xcodes/Frontend/XcodeList/MainToolbar.swift ================================================ import SwiftUI struct MainToolbarModifier: ViewModifier { @EnvironmentObject var appState: AppState @Binding var category: XcodeListCategory @Binding var isInstalledOnly: Bool @Binding var isShowingInfoPane: Bool @Binding var architectures: XcodeListArchitecture func body(content: Content) -> some View { content .toolbar { toolbar } } private var toolbar: some ToolbarContent { ToolbarItemGroup { ProgressButton( isInProgress: appState.isUpdating, action: appState.update ) { Label("Refresh", systemImage: "arrow.clockwise") } .keyboardShortcut(KeyEquivalent("r")) .help("RefreshDescription") Spacer() let isFiltering = isInstalledOnly || category != .all || architectures != .universal Menu("Filter", systemImage: "line.horizontal.3.decrease.circle") { Section { Toggle("Installed Only", systemImage: "arrow.down.app", isOn: $isInstalledOnly) .labelStyle(.titleAndIcon) } .help("FilterInstalledDescription") Section { Picker("Category", selection: $category) { Label("All", systemImage: "line.horizontal.3.decrease.circle") .tag(XcodeListCategory.all) Label("ReleaseOnly", systemImage: "line.horizontal.3.decrease.circle.fill") .tag(XcodeListCategory.release) Label("BetaOnly", systemImage: "line.horizontal.3.decrease.circle.fill") .tag(XcodeListCategory.beta) } } .help("FilterAvailableDescription") .disabled(category.isManaged) Section { Picker("Architecture", selection: $architectures) { Label("Universal", systemImage: "cpu.fill") .tag(XcodeListArchitecture.universal) Label("Apple Silicon", systemImage: "m4.button.horizontal") .foregroundColor(.accentColor) .tag(XcodeListArchitecture.appleSilicon) } .help("FilterArchitecturesDescription") .disabled(architectures.isManaged) } .labelStyle(.titleAndIcon) } .pickerStyle(.inline) .symbolVariant(isFiltering ? .fill : .none) } } } extension View { func mainToolbar( category: Binding, isInstalledOnly: Binding, isShowingInfoPane: Binding, architecture: Binding ) -> some View { modifier( MainToolbarModifier( category: category, isInstalledOnly: isInstalledOnly, isShowingInfoPane: isShowingInfoPane, architectures: architecture ) ) } } ================================================ FILE: Xcodes/Frontend/XcodeList/Tag.swift ================================================ import SwiftUI struct Tag: View { var text: String var body: some View { Text(text) .foregroundColor(.white) .background(RoundedRectangle(cornerRadius: 3).padding([.leading, .trailing], -3)) } } struct Tag_Previews: PreviewProvider { static var previews: some View { Tag(text: "SELECTED") .foregroundColor(.green) .padding() } } ================================================ FILE: Xcodes/Frontend/XcodeList/XcodeListCategory.swift ================================================ import Foundation import XcodesKit enum XcodeListCategory: String, CaseIterable, Identifiable, CustomStringConvertible { case all case release case beta var id: Self { self } var description: String { switch self { case .all: return localizeString("All") case .release: return localizeString("Release") case .beta: return localizeString("Beta") } } var isManaged: Bool { PreferenceKey.xcodeListCategory.isManaged() } } enum XcodeListArchitecture: String, CaseIterable, Identifiable, CustomStringConvertible { case universal case appleSilicon var id: Self { self } var description: String { switch self { case .universal: return localizeString("Universal") case .appleSilicon: return localizeString("Apple Silicon") } } var isManaged: Bool { PreferenceKey.xcodeListCategory.isManaged() } } ================================================ FILE: Xcodes/Frontend/XcodeList/XcodeListView.swift ================================================ import Path import SwiftUI import Version struct XcodeListView: View { @EnvironmentObject var appState: AppState @Binding var selectedXcodeID: Xcode.ID? private let searchText: String private let category: XcodeListCategory private let architecture: XcodeListArchitecture private let isInstalledOnly: Bool @AppStorage(PreferenceKey.allowedMajorVersions.rawValue) private var allowedMajorVersions = Int.max init(selectedXcodeID: Binding, searchText: String, category: XcodeListCategory, isInstalledOnly: Bool, architecture: XcodeListArchitecture) { self._selectedXcodeID = selectedXcodeID self.searchText = searchText self.category = category self.isInstalledOnly = isInstalledOnly self.architecture = architecture } var visibleXcodes: [Xcode] { var xcodes: [Xcode] switch category { case .all: xcodes = appState.allXcodes case .release: xcodes = appState.allXcodes.filter { $0.version.isNotPrerelease } case .beta: xcodes = appState.allXcodes.filter { $0.version.isPrerelease } } if architecture == .appleSilicon { xcodes = xcodes.filter { $0.architectures == [.arm64] } } let latestMajor = xcodes.sorted(\.version) .filter { $0.version.isNotPrerelease } .last? .version .major xcodes = xcodes.filter { if $0.installState.notInstalled, let latestMajor = latestMajor, $0.version.major < (latestMajor - min(latestMajor,allowedMajorVersions)) { return false } return true } if !searchText.isEmpty { xcodes = xcodes.filter { $0.description.contains(searchText) } } if isInstalledOnly { xcodes = xcodes.filter { $0.installState.installed } } return xcodes } var body: some View { List(visibleXcodes, selection: $selectedXcodeID) { xcode in XcodeListViewRow(xcode: xcode, selected: selectedXcodeID == xcode.id, appState: appState) } .listStyle(.sidebar) .safeAreaInset(edge: .bottom, spacing: 0) { PlatformsPocket() .padding(.horizontal) .padding(.vertical, 8) } } } struct PlatformsPocket: View { @SwiftUI.Environment(\.openWindow) private var openWindow var body: some View { Button(action: { openWindow(id: "platforms") } ) { if #available(macOS 26.0, *) { platformsLabel .glassEffect(in: .rect(cornerRadius: 8, style: .continuous)) } else { platformsLabel .background(.quaternary.opacity(0.75)) .clipShape(RoundedRectangle(cornerRadius: 8, style: .continuous)) } } .buttonStyle(.plain) } var platformsLabel: some View { HStack(spacing: 5) { Image(systemName: "square.3.layers.3d") .font(.title3.weight(.medium)) Text("PlatformsDescription") Spacer() } .font(.body.weight(.medium)) .padding(.horizontal) .padding(.vertical, 12) } } struct XcodeListView_Previews: PreviewProvider { static var previews: some View { Group { XcodeListView(selectedXcodeID: .constant(nil), searchText: "", category: .all, isInstalledOnly: false, architecture: .appleSilicon) .environmentObject({ () -> AppState in let a = AppState() a.allXcodes = [ Xcode(version: Version("12.0.0+1234A")!, identicalBuilds: [XcodeID(version: Version("12.0.0+1234A")!), XcodeID(version: Version("12.0.0-RC+1234A")!)], installState: .installed(Path("/Applications/Xcode-12.3.0.app")!), selected: false, icon: nil), Xcode(version: Version("12.3.0")!, installState: .installed(Path("/Applications/Xcode-12.3.0.app")!), selected: true, icon: nil), Xcode(version: Version("12.2.0")!, installState: .notInstalled, selected: false, icon: nil), Xcode(version: Version("12.1.0")!, installState: .installing(.downloading(progress: configure(Progress(totalUnitCount: 100)) { $0.completedUnitCount = 40 })), selected: false, icon: nil), Xcode(version: Version("12.0.0")!, installState: .installed(Path("/Applications/Xcode-12.3.0.app")!), selected: false, icon: nil), Xcode(version: Version("10.1.0")!, installState: .notInstalled, selected: false, icon: nil), Xcode(version: Version("10.0.0")!, installState: .installed(Path("/Applications/Xcode-10.0.0.app")!), selected: false, icon: nil), Xcode(version: Version("9.0.0")!, installState: .notInstalled, selected: false, icon: nil), ] return a }()) } .previewLayout(.sizeThatFits) } } ================================================ FILE: Xcodes/Frontend/XcodeList/XcodeListViewRow.swift ================================================ import Path import SwiftUI import Version struct XcodeListViewRow: View { let xcode: Xcode let selected: Bool let appState: AppState var body: some View { HStack { appIconView(for: xcode) VStack(alignment: .leading) { HStack { Text(verbatim: "\(xcode.description) \(xcode.version.buildMetadataIdentifiersDisplay)") .font(.body) if !xcode.identicalBuilds.isEmpty { Image(systemName: "square.fill.on.square.fill") .font(.subheadline) .foregroundColor(.secondary) .accessibility(label: Text("IdenticalBuilds")) .accessibility(value: Text(xcode.identicalBuilds.map(\.version.appleDescription).joined(separator: ", "))) .help("IdenticalBuilds.help") } if xcode.architectures?.isAppleSilicon ?? false { Image(systemName: "m4.button.horizontal") .font(.subheadline) .foregroundColor(.secondary) .accessibility(label: Text("Apple Silicon")) .help("Apple Silicon") } } if case let .installed(path) = xcode.installState { Text(verbatim: path.string) .font(.caption) .foregroundColor(.secondary) } } Spacer() selectControl(for: xcode) .padding(.trailing, 16) installControl(for: xcode) } .padding(.vertical, 4) .contextMenu { switch xcode.installState { case .notInstalled: InstallButton(xcode: xcode) case .installing: CancelInstallButton(xcode: xcode) case let .installed(path): SelectButton(xcode: xcode) OpenButton(xcode: xcode) RevealButton(xcode: xcode) CopyPathButton(xcode: xcode) CreateSymbolicLinkButton(xcode: xcode) if xcode.version.isPrerelease { CreateSymbolicBetaLinkButton(xcode: xcode) } Divider() UninstallButton(xcode: xcode) #if DEBUG Divider() Button("Perform post-install steps") { appState.performPostInstallSteps(for: InstalledXcode(path: path)!) as Void } #endif } } } @ViewBuilder func appIconView(for xcode: Xcode) -> some View { if let icon = xcode.icon { Image(nsImage: icon) } else { Image(xcode.version.isPrerelease ? "xcode-beta" : "xcode") .resizable() .frame(width: 32, height: 32) .opacity(0.2) } } @ViewBuilder private func selectControl(for xcode: Xcode) -> some View { if xcode.installState.installed { if xcode.selected { Image(systemName: "checkmark.circle.fill") .foregroundColor(.green) .help("ActiveVersionDescription") } else { Button(action: { appState.select(xcode: xcode) }) { Image(systemName: "checkmark.circle") .foregroundColor(.secondary) } .buttonStyle(PlainButtonStyle()) .help("MakeActiveVersionDescription") } } else { EmptyView() } } @ViewBuilder private func installControl(for xcode: Xcode) -> some View { switch xcode.installState { case .installed: Button("Open") { appState.open(xcode: xcode) } .textCase(.uppercase) .buttonStyle(AppStoreButtonStyle(primary: true, highlighted: selected)) .help("OpenDescription") case .notInstalled: InstallButton(xcode: xcode) .textCase(.uppercase) .buttonStyle(AppStoreButtonStyle(primary: false, highlighted: false)) case let .installing(installationStep): InstallationStepRowView( installationStep: installationStep, highlighted: selected, cancel: { appState.presentedAlert = .cancelInstall(xcode: xcode) } ) } } } struct XcodeListViewRow_Previews: PreviewProvider { static var previews: some View { Group { XcodeListViewRow( xcode: Xcode(version: Version("12.3.0")!, installState: .installed(Path("/Applications/Xcode-12.3.0.app")!), selected: true, icon: nil), selected: false, appState: AppState() ) XcodeListViewRow( xcode: Xcode(version: Version("12.2.0")!, installState: .notInstalled, selected: false, icon: nil), selected: false, appState: AppState() ) XcodeListViewRow( xcode: Xcode(version: Version("12.1.0")!, installState: .installing(.downloading(progress: configure(Progress(totalUnitCount: 100)) { $0.completedUnitCount = 40 })), selected: false, icon: nil), selected: false, appState: AppState() ) XcodeListViewRow( xcode: Xcode(version: Version("12.0.0")!, installState: .installed(Path("/Applications/Xcode-12.3.0.app")!), selected: false, icon: nil), selected: false, appState: AppState() ) XcodeListViewRow( xcode: Xcode(version: Version("12.0.0+1234A")!, installState: .installed(Path("/Applications/Xcode-12.3.0.app")!), selected: false, icon: nil), selected: false, appState: AppState() ) XcodeListViewRow( xcode: Xcode(version: Version("12.0.0+1234A")!, identicalBuilds: [XcodeID(version: Version("12.0.0-RC+1234A")!)], installState: .installed(Path("/Applications/Xcode-12.3.0.app")!), selected: false, icon: nil), selected: false, appState: AppState() ) } } } ================================================ FILE: Xcodes/Preview Content/Preview Assets.xcassets/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: Xcodes/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "filename" : "xcodes.app-icon16.png", "idiom" : "mac", "scale" : "1x", "size" : "16x16" }, { "filename" : "xcodes.app-icon32-1.png", "idiom" : "mac", "scale" : "2x", "size" : "16x16" }, { "filename" : "xcodes.app-icon32.png", "idiom" : "mac", "scale" : "1x", "size" : "32x32" }, { "filename" : "xcodes.app-icon64.png", "idiom" : "mac", "scale" : "2x", "size" : "32x32" }, { "filename" : "xcodes.app-icon128.png", "idiom" : "mac", "scale" : "1x", "size" : "128x128" }, { "filename" : "xcodes.app-icon256-1.png", "idiom" : "mac", "scale" : "2x", "size" : "128x128" }, { "filename" : "xcodes.app-icon256.png", "idiom" : "mac", "scale" : "1x", "size" : "256x256" }, { "filename" : "xcodes.app-icon512-1.png", "idiom" : "mac", "scale" : "2x", "size" : "256x256" }, { "filename" : "xcodes.app-icon512.png", "idiom" : "mac", "scale" : "1x", "size" : "512x512" }, { "filename" : "xcodes.app-icon1024.png", "idiom" : "mac", "scale" : "2x", "size" : "512x512" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: Xcodes/Resources/Assets.xcassets/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: Xcodes/Resources/Assets.xcassets/Icons/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: Xcodes/Resources/Assets.xcassets/install.imageset/Contents.json ================================================ { "images" : [ { "filename" : "install.pdf", "idiom" : "universal" } ], "info" : { "author" : "xcode", "version" : 1 }, "properties" : { "template-rendering-intent" : "template" } } ================================================ FILE: Xcodes/Resources/Assets.xcassets/xcode-beta.imageset/Contents.json ================================================ { "images" : [ { "filename" : "Image.png", "idiom" : "universal" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: Xcodes/Resources/Assets.xcassets/xcode.imageset/Contents.json ================================================ { "images" : [ { "filename" : "xcode.png", "idiom" : "universal" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: Xcodes/Resources/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIconFile CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion $(CURRENT_PROJECT_VERSION) CODE_SIGNING_SUBJECT_ORGANIZATIONAL_UNIT $(CODE_SIGNING_SUBJECT_ORGANIZATIONAL_UNIT) LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) NSHumanReadableCopyright Copyright © 2024 XcodesOrg NSPrincipalClass NSApplication NSSupportsAutomaticTermination NSSupportsSuddenTermination SMPrivilegedExecutables com.xcodesorg.xcodesapp.Helper identifier "com.xcodesorg.xcodesapp.Helper" and info [CFBundleShortVersionString] >= "1.0.0" and anchor apple generic and certificate leaf[subject.OU] = "$(CODE_SIGNING_SUBJECT_ORGANIZATIONAL_UNIT)" SUFeedURL https://www.xcodes.app/appcast.xml SUPublicEDKey SEcz0vgUSeBTOoAXYe+64zea95G6lIf5NgzFs3InYJQ= ================================================ FILE: Xcodes/Resources/Licenses.rtf ================================================ {\rtf1\ansi\ansicpg1252\cocoartf2865 \cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fnil\fcharset0 .SFNS-Regular;} {\colortbl;\red255\green255\blue255;} {\*\expandedcolortbl;;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\partightenfactor0 \f0\fs34 \cf0 AsyncHTTPNetworkService\ \ \fs26 MIT License\ \ Copyright (c) 2022 Robots and Pencils\ \ Permission is hereby granted, free of charge, to any person obtaining a copy\ of this software and associated documentation files (the "Software"), to deal\ in the Software without restriction, including without limitation the rights\ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ copies of the Software, and to permit persons to whom the Software is\ furnished to do so, subject to the following conditions:\ \ The above copyright notice and this permission notice shall be included in all\ copies or substantial portions of the Software.\ \ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\ SOFTWARE.\ \ \ \fs34 SwiftSoup\ \ \fs26 MIT License\ \ Copyright (c) 2016 Nabil Chatbi\ \ Permission is hereby granted, free of charge, to any person obtaining a copy\ of this software and associated documentation files (the "Software"), to deal\ in the Software without restriction, including without limitation the rights\ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ copies of the Software, and to permit persons to whom the Software is\ furnished to do so, subject to the following conditions:\ \ The above copyright notice and this permission notice shall be included in all\ copies or substantial portions of the Software.\ \ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\ SOFTWARE.\ \ \ \fs34 LibFido2Swift\ \ \fs26 MIT License\ \ Copyright (c) 2024 Kino Roy\ \ Permission is hereby granted, free of charge, to any person obtaining a copy\ of this software and associated documentation files (the "Software"), to deal\ in the Software without restriction, including without limitation the rights\ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ copies of the Software, and to permit persons to whom the Software is\ furnished to do so, subject to the following conditions:\ \ The above copyright notice and this permission notice shall be included in all\ copies or substantial portions of the Software.\ \ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\ SOFTWARE.\ \ \ \fs34 ErrorHandling\ \ \fs26 MIT License\ \ Copyright (c) 2020 Robots and Pencils\ Copyright (c) 2020 John Sundell\ \ Permission is hereby granted, free of charge, to any person obtaining a copy\ of this software and associated documentation files (the "Software"), to deal\ in the Software without restriction, including without limitation the rights\ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ copies of the Software, and to permit persons to whom the Software is\ furnished to do so, subject to the following conditions:\ \ The above copyright notice and this permission notice shall be included in all\ copies or substantial portions of the Software.\ \ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\ SOFTWARE.\ \ \ \fs34 big-num\ \ \fs26 MIT License\ \ Copyright (c) 2019 Adam Fowler\ \ Permission is hereby granted, free of charge, to any person obtaining a copy\ of this software and associated documentation files (the "Software"), to deal\ in the Software without restriction, including without limitation the rights\ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ copies of the Software, and to permit persons to whom the Software is\ furnished to do so, subject to the following conditions:\ \ The above copyright notice and this permission notice shall be included in all\ copies or substantial portions of the Software.\ \ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\ SOFTWARE.\ \ \ \fs34 swift-crypto\ \ \fs26 \ Apache License\ Version 2.0, January 2004\ http://www.apache.org/licenses/\ \ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\ \ 1. Definitions.\ \ "License" shall mean the terms and conditions for use, reproduction,\ and distribution as defined by Sections 1 through 9 of this document.\ \ "Licensor" shall mean the copyright owner or entity authorized by\ the copyright owner that is granting the License.\ \ "Legal Entity" shall mean the union of the acting entity and all\ other entities that control, are controlled by, or are under common\ control with that entity. For the purposes of this definition,\ "control" means (i) the power, direct or indirect, to cause the\ direction or management of such entity, whether by contract or\ otherwise, or (ii) ownership of fifty percent (50%) or more of the\ outstanding shares, or (iii) beneficial ownership of such entity.\ \ "You" (or "Your") shall mean an individual or Legal Entity\ exercising permissions granted by this License.\ \ "Source" form shall mean the preferred form for making modifications,\ including but not limited to software source code, documentation\ source, and configuration files.\ \ "Object" form shall mean any form resulting from mechanical\ transformation or translation of a Source form, including but\ not limited to compiled object code, generated documentation,\ and conversions to other media types.\ \ "Work" shall mean the work of authorship, whether in Source or\ Object form, made available under the License, as indicated by a\ copyright notice that is included in or attached to the work\ (an example is provided in the Appendix below).\ \ "Derivative Works" shall mean any work, whether in Source or Object\ form, that is based on (or derived from) the Work and for which the\ editorial revisions, annotations, elaborations, or other modifications\ represent, as a whole, an original work of authorship. For the purposes\ of this License, Derivative Works shall not include works that remain\ separable from, or merely link (or bind by name) to the interfaces of,\ the Work and Derivative Works thereof.\ \ "Contribution" shall mean any work of authorship, including\ the original version of the Work and any modifications or additions\ to that Work or Derivative Works thereof, that is intentionally\ submitted to Licensor for inclusion in the Work by the copyright owner\ or by an individual or Legal Entity authorized to submit on behalf of\ the copyright owner. For the purposes of this definition, "submitted"\ means any form of electronic, verbal, or written communication sent\ to the Licensor or its representatives, including but not limited to\ communication on electronic mailing lists, source code control systems,\ and issue tracking systems that are managed by, or on behalf of, the\ Licensor for the purpose of discussing and improving the Work, but\ excluding communication that is conspicuously marked or otherwise\ designated in writing by the copyright owner as "Not a Contribution."\ \ "Contributor" shall mean Licensor and any individual or Legal Entity\ on behalf of whom a Contribution has been received by Licensor and\ subsequently incorporated within the Work.\ \ 2. Grant of Copyright License. Subject to the terms and conditions of\ this License, each Contributor hereby grants to You a perpetual,\ worldwide, non-exclusive, no-charge, royalty-free, irrevocable\ copyright license to reproduce, prepare Derivative Works of,\ publicly display, publicly perform, sublicense, and distribute the\ Work and such Derivative Works in Source or Object form.\ \ 3. Grant of Patent License. Subject to the terms and conditions of\ this License, each Contributor hereby grants to You a perpetual,\ worldwide, non-exclusive, no-charge, royalty-free, irrevocable\ (except as stated in this section) patent license to make, have made,\ use, offer to sell, sell, import, and otherwise transfer the Work,\ where such license applies only to those patent claims licensable\ by such Contributor that are necessarily infringed by their\ Contribution(s) alone or by combination of their Contribution(s)\ with the Work to which such Contribution(s) was submitted. If You\ institute patent litigation against any entity (including a\ cross-claim or counterclaim in a lawsuit) alleging that the Work\ or a Contribution incorporated within the Work constitutes direct\ or contributory patent infringement, then any patent licenses\ granted to You under this License for that Work shall terminate\ as of the date such litigation is filed.\ \ 4. Redistribution. You may reproduce and distribute copies of the\ Work or Derivative Works thereof in any medium, with or without\ modifications, and in Source or Object form, provided that You\ meet the following conditions:\ \ (a) You must give any other recipients of the Work or\ Derivative Works a copy of this License; and\ \ (b) You must cause any modified files to carry prominent notices\ stating that You changed the files; and\ \ (c) You must retain, in the Source form of any Derivative Works\ that You distribute, all copyright, patent, trademark, and\ attribution notices from the Source form of the Work,\ excluding those notices that do not pertain to any part of\ the Derivative Works; and\ \ (d) If the Work includes a "NOTICE" text file as part of its\ distribution, then any Derivative Works that You distribute must\ include a readable copy of the attribution notices contained\ within such NOTICE file, excluding those notices that do not\ pertain to any part of the Derivative Works, in at least one\ of the following places: within a NOTICE text file distributed\ as part of the Derivative Works; within the Source form or\ documentation, if provided along with the Derivative Works; or,\ within a display generated by the Derivative Works, if and\ wherever such third-party notices normally appear. The contents\ of the NOTICE file are for informational purposes only and\ do not modify the License. You may add Your own attribution\ notices within Derivative Works that You distribute, alongside\ or as an addendum to the NOTICE text from the Work, provided\ that such additional attribution notices cannot be construed\ as modifying the License.\ \ You may add Your own copyright statement to Your modifications and\ may provide additional or different license terms and conditions\ for use, reproduction, or distribution of Your modifications, or\ for any such Derivative Works as a whole, provided Your use,\ reproduction, and distribution of the Work otherwise complies with\ the conditions stated in this License.\ \ 5. Submission of Contributions. Unless You explicitly state otherwise,\ any Contribution intentionally submitted for inclusion in the Work\ by You to the Licensor shall be under the terms and conditions of\ this License, without any additional terms or conditions.\ Notwithstanding the above, nothing herein shall supersede or modify\ the terms of any separate license agreement you may have executed\ with Licensor regarding such Contributions.\ \ 6. Trademarks. This License does not grant permission to use the trade\ names, trademarks, service marks, or product names of the Licensor,\ except as required for reasonable and customary use in describing the\ origin of the Work and reproducing the content of the NOTICE file.\ \ 7. Disclaimer of Warranty. Unless required by applicable law or\ agreed to in writing, Licensor provides the Work (and each\ Contributor provides its Contributions) on an "AS IS" BASIS,\ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\ implied, including, without limitation, any warranties or conditions\ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\ PARTICULAR PURPOSE. You are solely responsible for determining the\ appropriateness of using or redistributing the Work and assume any\ risks associated with Your exercise of permissions under this License.\ \ 8. Limitation of Liability. In no event and under no legal theory,\ whether in tort (including negligence), contract, or otherwise,\ unless required by applicable law (such as deliberate and grossly\ negligent acts) or agreed to in writing, shall any Contributor be\ liable to You for damages, including any direct, indirect, special,\ incidental, or consequential damages of any character arising as a\ result of this License or out of the use or inability to use the\ Work (including but not limited to damages for loss of goodwill,\ work stoppage, computer failure or malfunction, or any and all\ other commercial damages or losses), even if such Contributor\ has been advised of the possibility of such damages.\ \ 9. Accepting Warranty or Additional Liability. While redistributing\ the Work or Derivative Works thereof, You may choose to offer,\ and charge a fee for, acceptance of support, warranty, indemnity,\ or other liability obligations and/or rights consistent with this\ License. However, in accepting such obligations, You may act only\ on Your own behalf and on Your sole responsibility, not on behalf\ of any other Contributor, and only if You agree to indemnify,\ defend, and hold each Contributor harmless for any liability\ incurred by, or claims asserted against, such Contributor by reason\ of your accepting any such warranty or additional liability.\ \ END OF TERMS AND CONDITIONS\ \ APPENDIX: How to apply the Apache License to your work.\ \ To apply the Apache License to your work, attach the following\ boilerplate notice, with the fields enclosed by brackets "[]"\ replaced with your own identifying information. (Don't include\ the brackets!) The text should be enclosed in the appropriate\ comment syntax for the file format. We also recommend that a\ file or class name and description of purpose be included on the\ same "printed page" as the copyright notice for easier\ identification within third-party archives.\ \ Copyright [yyyy] [name of copyright owner]\ \ Licensed under the Apache License, Version 2.0 (the "License");\ you may not use this file except in compliance with the License.\ You may obtain a copy of the License at\ \ http://www.apache.org/licenses/LICENSE-2.0\ \ Unless required by applicable law or agreed to in writing, software\ distributed under the License is distributed on an "AS IS" BASIS,\ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\ See the License for the specific language governing permissions and\ limitations under the License.\ \ \ \fs34 Path.swift\ \ \fs26 Unlicense (Public Domain)\ ============================\ \ This is free and unencumbered software released into the public domain.\ \ Anyone is free to copy, modify, publish, use, compile, sell, or\ distribute this software, either in source code form or as a compiled\ binary, for any purpose, commercial or non-commercial, and by any\ means.\ \ In jurisdictions that recognize copyright laws, the author or authors\ of this software dedicate any and all copyright interest in the\ software to the public domain. We make this dedication for the benefit\ of the public at large and to the detriment of our heirs and\ successors. We intend this dedication to be an overt act of\ relinquishment in perpetuity of all present and future rights to this\ software under copyright law.\ \ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR\ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\ OTHER DEALINGS IN THE SOFTWARE.\ \ For more information, please refer to <>\ \ \ \fs34 Version\ \ \fs26 Apache License\ Version 2.0, January 2004\ http://www.apache.org/licenses/\ \ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\ \ 1. Definitions.\ \ "License" shall mean the terms and conditions for use, reproduction,\ and distribution as defined by Sections 1 through 9 of this document.\ \ "Licensor" shall mean the copyright owner or entity authorized by\ the copyright owner that is granting the License.\ \ "Legal Entity" shall mean the union of the acting entity and all\ other entities that control, are controlled by, or are under common\ control with that entity. For the purposes of this definition,\ "control" means (i) the power, direct or indirect, to cause the\ direction or management of such entity, whether by contract or\ otherwise, or (ii) ownership of fifty percent (50%) or more of the\ outstanding shares, or (iii) beneficial ownership of such entity.\ \ "You" (or "Your") shall mean an individual or Legal Entity\ exercising permissions granted by this License.\ \ "Source" form shall mean the preferred form for making modifications,\ including but not limited to software source code, documentation\ source, and configuration files.\ \ "Object" form shall mean any form resulting from mechanical\ transformation or translation of a Source form, including but\ not limited to compiled object code, generated documentation,\ and conversions to other media types.\ \ "Work" shall mean the work of authorship, whether in Source or\ Object form, made available under the License, as indicated by a\ copyright notice that is included in or attached to the work\ (an example is provided in the Appendix below).\ \ "Derivative Works" shall mean any work, whether in Source or Object\ form, that is based on (or derived from) the Work and for which the\ editorial revisions, annotations, elaborations, or other modifications\ represent, as a whole, an original work of authorship. For the purposes\ of this License, Derivative Works shall not include works that remain\ separable from, or merely link (or bind by name) to the interfaces of,\ the Work and Derivative Works thereof.\ \ "Contribution" shall mean any work of authorship, including\ the original version of the Work and any modifications or additions\ to that Work or Derivative Works thereof, that is intentionally\ submitted to Licensor for inclusion in the Work by the copyright owner\ or by an individual or Legal Entity authorized to submit on behalf of\ the copyright owner. For the purposes of this definition, "submitted"\ means any form of electronic, verbal, or written communication sent\ to the Licensor or its representatives, including but not limited to\ communication on electronic mailing lists, source code control systems,\ and issue tracking systems that are managed by, or on behalf of, the\ Licensor for the purpose of discussing and improving the Work, but\ excluding communication that is conspicuously marked or otherwise\ designated in writing by the copyright owner as "Not a Contribution."\ \ "Contributor" shall mean Licensor and any individual or Legal Entity\ on behalf of whom a Contribution has been received by Licensor and\ subsequently incorporated within the Work.\ \ 2. Grant of Copyright License. Subject to the terms and conditions of\ this License, each Contributor hereby grants to You a perpetual,\ worldwide, non-exclusive, no-charge, royalty-free, irrevocable\ copyright license to reproduce, prepare Derivative Works of,\ publicly display, publicly perform, sublicense, and distribute the\ Work and such Derivative Works in Source or Object form.\ \ 3. Grant of Patent License. Subject to the terms and conditions of\ this License, each Contributor hereby grants to You a perpetual,\ worldwide, non-exclusive, no-charge, royalty-free, irrevocable\ (except as stated in this section) patent license to make, have made,\ use, offer to sell, sell, import, and otherwise transfer the Work,\ where such license applies only to those patent claims licensable\ by such Contributor that are necessarily infringed by their\ Contribution(s) alone or by combination of their Contribution(s)\ with the Work to which such Contribution(s) was submitted. If You\ institute patent litigation against any entity (including a\ cross-claim or counterclaim in a lawsuit) alleging that the Work\ or a Contribution incorporated within the Work constitutes direct\ or contributory patent infringement, then any patent licenses\ granted to You under this License for that Work shall terminate\ as of the date such litigation is filed.\ \ 4. Redistribution. You may reproduce and distribute copies of the\ Work or Derivative Works thereof in any medium, with or without\ modifications, and in Source or Object form, provided that You\ meet the following conditions:\ \ (a) You must give any other recipients of the Work or\ Derivative Works a copy of this License; and\ \ (b) You must cause any modified files to carry prominent notices\ stating that You changed the files; and\ \ (c) You must retain, in the Source form of any Derivative Works\ that You distribute, all copyright, patent, trademark, and\ attribution notices from the Source form of the Work,\ excluding those notices that do not pertain to any part of\ the Derivative Works; and\ \ (d) If the Work includes a "NOTICE" text file as part of its\ distribution, then any Derivative Works that You distribute must\ include a readable copy of the attribution notices contained\ within such NOTICE file, excluding those notices that do not\ pertain to any part of the Derivative Works, in at least one\ of the following places: within a NOTICE text file distributed\ as part of the Derivative Works; within the Source form or\ documentation, if provided along with the Derivative Works; or,\ within a display generated by the Derivative Works, if and\ wherever such third-party notices normally appear. The contents\ of the NOTICE file are for informational purposes only and\ do not modify the License. You may add Your own attribution\ notices within Derivative Works that You distribute, alongside\ or as an addendum to the NOTICE text from the Work, provided\ that such additional attribution notices cannot be construed\ as modifying the License.\ \ You may add Your own copyright statement to Your modifications and\ may provide additional or different license terms and conditions\ for use, reproduction, or distribution of Your modifications, or\ for any such Derivative Works as a whole, provided Your use,\ reproduction, and distribution of the Work otherwise complies with\ the conditions stated in this License.\ \ 5. Submission of Contributions. Unless You explicitly state otherwise,\ any Contribution intentionally submitted for inclusion in the Work\ by You to the Licensor shall be under the terms and conditions of\ this License, without any additional terms or conditions.\ Notwithstanding the above, nothing herein shall supersede or modify\ the terms of any separate license agreement you may have executed\ with Licensor regarding such Contributions.\ \ 6. Trademarks. This License does not grant permission to use the trade\ names, trademarks, service marks, or product names of the Licensor,\ except as required for reasonable and customary use in describing the\ origin of the Work and reproducing the content of the NOTICE file.\ \ 7. Disclaimer of Warranty. Unless required by applicable law or\ agreed to in writing, Licensor provides the Work (and each\ Contributor provides its Contributions) on an "AS IS" BASIS,\ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\ implied, including, without limitation, any warranties or conditions\ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\ PARTICULAR PURPOSE. You are solely responsible for determining the\ appropriateness of using or redistributing the Work and assume any\ risks associated with Your exercise of permissions under this License.\ \ 8. Limitation of Liability. In no event and under no legal theory,\ whether in tort (including negligence), contract, or otherwise,\ unless required by applicable law (such as deliberate and grossly\ negligent acts) or agreed to in writing, shall any Contributor be\ liable to You for damages, including any direct, indirect, special,\ incidental, or consequential damages of any character arising as a\ result of this License or out of the use or inability to use the\ Work (including but not limited to damages for loss of goodwill,\ work stoppage, computer failure or malfunction, or any and all\ other commercial damages or losses), even if such Contributor\ has been advised of the possibility of such damages.\ \ 9. Accepting Warranty or Additional Liability. While redistributing\ the Work or Derivative Works thereof, You may choose to offer,\ and charge a fee for, acceptance of support, warranty, indemnity,\ or other liability obligations and/or rights consistent with this\ License. However, in accepting such obligations, You may act only\ on Your own behalf and on Your sole responsibility, not on behalf\ of any other Contributor, and only if You agree to indemnify,\ defend, and hold each Contributor harmless for any liability\ incurred by, or claims asserted against, such Contributor by reason\ of your accepting any such warranty or additional liability.\ \ END OF TERMS AND CONDITIONS\ \ APPENDIX: How to apply the Apache License to your work.\ \ To apply the Apache License to your work, attach the following\ boilerplate notice, with the fields enclosed by brackets "[]"\ replaced with your own identifying information. (Don't include\ the brackets!) The text should be enclosed in the appropriate\ comment syntax for the file format. We also recommend that a\ file or class name and description of purpose be included on the\ same "printed page" as the copyright notice for easier\ identification within third-party archives.\ \ Copyright [yyyy] [name of copyright owner]\ \ Licensed under the Apache License, Version 2.0 (the "License");\ you may not use this file except in compliance with the License.\ You may obtain a copy of the License at\ \ http://www.apache.org/licenses/LICENSE-2.0\ \ Unless required by applicable law or agreed to in writing, software\ distributed under the License is distributed on an "AS IS" BASIS,\ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\ See the License for the specific language governing permissions and\ limitations under the License.\ \ \ \ ## Runtime Library Exception to the Apache 2.0 License: ##\ \ \ As an exception, if you use this Software to compile your source code and\ portions of this Software are embedded into the binary product as a result,\ you may redistribute such product without providing attribution as would\ otherwise be required by Sections 4(a), 4(b) and 4(d) of the License.\ \ \fs34 swift-collections\ \ \fs26 Apache License\ Version 2.0, January 2004\ http://www.apache.org/licenses/\ \ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\ \ 1. Definitions.\ \ "License" shall mean the terms and conditions for use, reproduction,\ and distribution as defined by Sections 1 through 9 of this document.\ \ "Licensor" shall mean the copyright owner or entity authorized by\ the copyright owner that is granting the License.\ \ "Legal Entity" shall mean the union of the acting entity and all\ other entities that control, are controlled by, or are under common\ control with that entity. For the purposes of this definition,\ "control" means (i) the power, direct or indirect, to cause the\ direction or management of such entity, whether by contract or\ otherwise, or (ii) ownership of fifty percent (50%) or more of the\ outstanding shares, or (iii) beneficial ownership of such entity.\ \ "You" (or "Your") shall mean an individual or Legal Entity\ exercising permissions granted by this License.\ \ "Source" form shall mean the preferred form for making modifications,\ including but not limited to software source code, documentation\ source, and configuration files.\ \ "Object" form shall mean any form resulting from mechanical\ transformation or translation of a Source form, including but\ not limited to compiled object code, generated documentation,\ and conversions to other media types.\ \ "Work" shall mean the work of authorship, whether in Source or\ Object form, made available under the License, as indicated by a\ copyright notice that is included in or attached to the work\ (an example is provided in the Appendix below).\ \ "Derivative Works" shall mean any work, whether in Source or Object\ form, that is based on (or derived from) the Work and for which the\ editorial revisions, annotations, elaborations, or other modifications\ represent, as a whole, an original work of authorship. For the purposes\ of this License, Derivative Works shall not include works that remain\ separable from, or merely link (or bind by name) to the interfaces of,\ the Work and Derivative Works thereof.\ \ "Contribution" shall mean any work of authorship, including\ the original version of the Work and any modifications or additions\ to that Work or Derivative Works thereof, that is intentionally\ submitted to Licensor for inclusion in the Work by the copyright owner\ or by an individual or Legal Entity authorized to submit on behalf of\ the copyright owner. For the purposes of this definition, "submitted"\ means any form of electronic, verbal, or written communication sent\ to the Licensor or its representatives, including but not limited to\ communication on electronic mailing lists, source code control systems,\ and issue tracking systems that are managed by, or on behalf of, the\ Licensor for the purpose of discussing and improving the Work, but\ excluding communication that is conspicuously marked or otherwise\ designated in writing by the copyright owner as "Not a Contribution."\ \ "Contributor" shall mean Licensor and any individual or Legal Entity\ on behalf of whom a Contribution has been received by Licensor and\ subsequently incorporated within the Work.\ \ 2. Grant of Copyright License. Subject to the terms and conditions of\ this License, each Contributor hereby grants to You a perpetual,\ worldwide, non-exclusive, no-charge, royalty-free, irrevocable\ copyright license to reproduce, prepare Derivative Works of,\ publicly display, publicly perform, sublicense, and distribute the\ Work and such Derivative Works in Source or Object form.\ \ 3. Grant of Patent License. Subject to the terms and conditions of\ this License, each Contributor hereby grants to You a perpetual,\ worldwide, non-exclusive, no-charge, royalty-free, irrevocable\ (except as stated in this section) patent license to make, have made,\ use, offer to sell, sell, import, and otherwise transfer the Work,\ where such license applies only to those patent claims licensable\ by such Contributor that are necessarily infringed by their\ Contribution(s) alone or by combination of their Contribution(s)\ with the Work to which such Contribution(s) was submitted. If You\ institute patent litigation against any entity (including a\ cross-claim or counterclaim in a lawsuit) alleging that the Work\ or a Contribution incorporated within the Work constitutes direct\ or contributory patent infringement, then any patent licenses\ granted to You under this License for that Work shall terminate\ as of the date such litigation is filed.\ \ 4. Redistribution. You may reproduce and distribute copies of the\ Work or Derivative Works thereof in any medium, with or without\ modifications, and in Source or Object form, provided that You\ meet the following conditions:\ \ (a) You must give any other recipients of the Work or\ Derivative Works a copy of this License; and\ \ (b) You must cause any modified files to carry prominent notices\ stating that You changed the files; and\ \ (c) You must retain, in the Source form of any Derivative Works\ that You distribute, all copyright, patent, trademark, and\ attribution notices from the Source form of the Work,\ excluding those notices that do not pertain to any part of\ the Derivative Works; and\ \ (d) If the Work includes a "NOTICE" text file as part of its\ distribution, then any Derivative Works that You distribute must\ include a readable copy of the attribution notices contained\ within such NOTICE file, excluding those notices that do not\ pertain to any part of the Derivative Works, in at least one\ of the following places: within a NOTICE text file distributed\ as part of the Derivative Works; within the Source form or\ documentation, if provided along with the Derivative Works; or,\ within a display generated by the Derivative Works, if and\ wherever such third-party notices normally appear. The contents\ of the NOTICE file are for informational purposes only and\ do not modify the License. You may add Your own attribution\ notices within Derivative Works that You distribute, alongside\ or as an addendum to the NOTICE text from the Work, provided\ that such additional attribution notices cannot be construed\ as modifying the License.\ \ You may add Your own copyright statement to Your modifications and\ may provide additional or different license terms and conditions\ for use, reproduction, or distribution of Your modifications, or\ for any such Derivative Works as a whole, provided Your use,\ reproduction, and distribution of the Work otherwise complies with\ the conditions stated in this License.\ \ 5. Submission of Contributions. Unless You explicitly state otherwise,\ any Contribution intentionally submitted for inclusion in the Work\ by You to the Licensor shall be under the terms and conditions of\ this License, without any additional terms or conditions.\ Notwithstanding the above, nothing herein shall supersede or modify\ the terms of any separate license agreement you may have executed\ with Licensor regarding such Contributions.\ \ 6. Trademarks. This License does not grant permission to use the trade\ names, trademarks, service marks, or product names of the Licensor,\ except as required for reasonable and customary use in describing the\ origin of the Work and reproducing the content of the NOTICE file.\ \ 7. Disclaimer of Warranty. Unless required by applicable law or\ agreed to in writing, Licensor provides the Work (and each\ Contributor provides its Contributions) on an "AS IS" BASIS,\ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\ implied, including, without limitation, any warranties or conditions\ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\ PARTICULAR PURPOSE. You are solely responsible for determining the\ appropriateness of using or redistributing the Work and assume any\ risks associated with Your exercise of permissions under this License.\ \ 8. Limitation of Liability. In no event and under no legal theory,\ whether in tort (including negligence), contract, or otherwise,\ unless required by applicable law (such as deliberate and grossly\ negligent acts) or agreed to in writing, shall any Contributor be\ liable to You for damages, including any direct, indirect, special,\ incidental, or consequential damages of any character arising as a\ result of this License or out of the use or inability to use the\ Work (including but not limited to damages for loss of goodwill,\ work stoppage, computer failure or malfunction, or any and all\ other commercial damages or losses), even if such Contributor\ has been advised of the possibility of such damages.\ \ 9. Accepting Warranty or Additional Liability. While redistributing\ the Work or Derivative Works thereof, You may choose to offer,\ and charge a fee for, acceptance of support, warranty, indemnity,\ or other liability obligations and/or rights consistent with this\ License. However, in accepting such obligations, You may act only\ on Your own behalf and on Your sole responsibility, not on behalf\ of any other Contributor, and only if You agree to indemnify,\ defend, and hold each Contributor harmless for any liability\ incurred by, or claims asserted against, such Contributor by reason\ of your accepting any such warranty or additional liability.\ \ END OF TERMS AND CONDITIONS\ \ APPENDIX: How to apply the Apache License to your work.\ \ To apply the Apache License to your work, attach the following\ boilerplate notice, with the fields enclosed by brackets "[]"\ replaced with your own identifying information. (Don't include\ the brackets!) The text should be enclosed in the appropriate\ comment syntax for the file format. We also recommend that a\ file or class name and description of purpose be included on the\ same "printed page" as the copyright notice for easier\ identification within third-party archives.\ \ Copyright [yyyy] [name of copyright owner]\ \ Licensed under the Apache License, Version 2.0 (the "License");\ you may not use this file except in compliance with the License.\ You may obtain a copy of the License at\ \ http://www.apache.org/licenses/LICENSE-2.0\ \ Unless required by applicable law or agreed to in writing, software\ distributed under the License is distributed on an "AS IS" BASIS,\ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\ See the License for the specific language governing permissions and\ limitations under the License.\ \ \ \ ## Runtime Library Exception to the Apache 2.0 License: ##\ \ \ As an exception, if you use this Software to compile your source code and\ portions of this Software are embedded into the binary product as a result,\ you may redistribute such product without providing attribution as would\ otherwise be required by Sections 4(a), 4(b) and 4(d) of the License.\ \ \ \fs34 swift-srp\ \ \fs26 Apache License\ Version 2.0, January 2004\ http://www.apache.org/licenses/\ \ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\ \ 1. Definitions.\ \ "License" shall mean the terms and conditions for use, reproduction,\ and distribution as defined by Sections 1 through 9 of this document.\ \ "Licensor" shall mean the copyright owner or entity authorized by\ the copyright owner that is granting the License.\ \ "Legal Entity" shall mean the union of the acting entity and all\ other entities that control, are controlled by, or are under common\ control with that entity. For the purposes of this definition,\ "control" means (i) the power, direct or indirect, to cause the\ direction or management of such entity, whether by contract or\ otherwise, or (ii) ownership of fifty percent (50%) or more of the\ outstanding shares, or (iii) beneficial ownership of such entity.\ \ "You" (or "Your") shall mean an individual or Legal Entity\ exercising permissions granted by this License.\ \ "Source" form shall mean the preferred form for making modifications,\ including but not limited to software source code, documentation\ source, and configuration files.\ \ "Object" form shall mean any form resulting from mechanical\ transformation or translation of a Source form, including but\ not limited to compiled object code, generated documentation,\ and conversions to other media types.\ \ "Work" shall mean the work of authorship, whether in Source or\ Object form, made available under the License, as indicated by a\ copyright notice that is included in or attached to the work\ (an example is provided in the Appendix below).\ \ "Derivative Works" shall mean any work, whether in Source or Object\ form, that is based on (or derived from) the Work and for which the\ editorial revisions, annotations, elaborations, or other modifications\ represent, as a whole, an original work of authorship. For the purposes\ of this License, Derivative Works shall not include works that remain\ separable from, or merely link (or bind by name) to the interfaces of,\ the Work and Derivative Works thereof.\ \ "Contribution" shall mean any work of authorship, including\ the original version of the Work and any modifications or additions\ to that Work or Derivative Works thereof, that is intentionally\ submitted to Licensor for inclusion in the Work by the copyright owner\ or by an individual or Legal Entity authorized to submit on behalf of\ the copyright owner. For the purposes of this definition, "submitted"\ means any form of electronic, verbal, or written communication sent\ to the Licensor or its representatives, including but not limited to\ communication on electronic mailing lists, source code control systems,\ and issue tracking systems that are managed by, or on behalf of, the\ Licensor for the purpose of discussing and improving the Work, but\ excluding communication that is conspicuously marked or otherwise\ designated in writing by the copyright owner as "Not a Contribution."\ \ "Contributor" shall mean Licensor and any individual or Legal Entity\ on behalf of whom a Contribution has been received by Licensor and\ subsequently incorporated within the Work.\ \ 2. Grant of Copyright License. Subject to the terms and conditions of\ this License, each Contributor hereby grants to You a perpetual,\ worldwide, non-exclusive, no-charge, royalty-free, irrevocable\ copyright license to reproduce, prepare Derivative Works of,\ publicly display, publicly perform, sublicense, and distribute the\ Work and such Derivative Works in Source or Object form.\ \ 3. Grant of Patent License. Subject to the terms and conditions of\ this License, each Contributor hereby grants to You a perpetual,\ worldwide, non-exclusive, no-charge, royalty-free, irrevocable\ (except as stated in this section) patent license to make, have made,\ use, offer to sell, sell, import, and otherwise transfer the Work,\ where such license applies only to those patent claims licensable\ by such Contributor that are necessarily infringed by their\ Contribution(s) alone or by combination of their Contribution(s)\ with the Work to which such Contribution(s) was submitted. If You\ institute patent litigation against any entity (including a\ cross-claim or counterclaim in a lawsuit) alleging that the Work\ or a Contribution incorporated within the Work constitutes direct\ or contributory patent infringement, then any patent licenses\ granted to You under this License for that Work shall terminate\ as of the date such litigation is filed.\ \ 4. Redistribution. You may reproduce and distribute copies of the\ Work or Derivative Works thereof in any medium, with or without\ modifications, and in Source or Object form, provided that You\ meet the following conditions:\ \ (a) You must give any other recipients of the Work or\ Derivative Works a copy of this License; and\ \ (b) You must cause any modified files to carry prominent notices\ stating that You changed the files; and\ \ (c) You must retain, in the Source form of any Derivative Works\ that You distribute, all copyright, patent, trademark, and\ attribution notices from the Source form of the Work,\ excluding those notices that do not pertain to any part of\ the Derivative Works; and\ \ (d) If the Work includes a "NOTICE" text file as part of its\ distribution, then any Derivative Works that You distribute must\ include a readable copy of the attribution notices contained\ within such NOTICE file, excluding those notices that do not\ pertain to any part of the Derivative Works, in at least one\ of the following places: within a NOTICE text file distributed\ as part of the Derivative Works; within the Source form or\ documentation, if provided along with the Derivative Works; or,\ within a display generated by the Derivative Works, if and\ wherever such third-party notices normally appear. The contents\ of the NOTICE file are for informational purposes only and\ do not modify the License. You may add Your own attribution\ notices within Derivative Works that You distribute, alongside\ or as an addendum to the NOTICE text from the Work, provided\ that such additional attribution notices cannot be construed\ as modifying the License.\ \ You may add Your own copyright statement to Your modifications and\ may provide additional or different license terms and conditions\ for use, reproduction, or distribution of Your modifications, or\ for any such Derivative Works as a whole, provided Your use,\ reproduction, and distribution of the Work otherwise complies with\ the conditions stated in this License.\ \ 5. Submission of Contributions. Unless You explicitly state otherwise,\ any Contribution intentionally submitted for inclusion in the Work\ by You to the Licensor shall be under the terms and conditions of\ this License, without any additional terms or conditions.\ Notwithstanding the above, nothing herein shall supersede or modify\ the terms of any separate license agreement you may have executed\ with Licensor regarding such Contributions.\ \ 6. Trademarks. This License does not grant permission to use the trade\ names, trademarks, service marks, or product names of the Licensor,\ except as required for reasonable and customary use in describing the\ origin of the Work and reproducing the content of the NOTICE file.\ \ 7. Disclaimer of Warranty. Unless required by applicable law or\ agreed to in writing, Licensor provides the Work (and each\ Contributor provides its Contributions) on an "AS IS" BASIS,\ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\ implied, including, without limitation, any warranties or conditions\ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\ PARTICULAR PURPOSE. You are solely responsible for determining the\ appropriateness of using or redistributing the Work and assume any\ risks associated with Your exercise of permissions under this License.\ \ 8. Limitation of Liability. In no event and under no legal theory,\ whether in tort (including negligence), contract, or otherwise,\ unless required by applicable law (such as deliberate and grossly\ negligent acts) or agreed to in writing, shall any Contributor be\ liable to You for damages, including any direct, indirect, special,\ incidental, or consequential damages of any character arising as a\ result of this License or out of the use or inability to use the\ Work (including but not limited to damages for loss of goodwill,\ work stoppage, computer failure or malfunction, or any and all\ other commercial damages or losses), even if such Contributor\ has been advised of the possibility of such damages.\ \ 9. Accepting Warranty or Additional Liability. While redistributing\ the Work or Derivative Works thereof, You may choose to offer,\ and charge a fee for, acceptance of support, warranty, indemnity,\ or other liability obligations and/or rights consistent with this\ License. However, in accepting such obligations, You may act only\ on Your own behalf and on Your sole responsibility, not on behalf\ of any other Contributor, and only if You agree to indemnify,\ defend, and hold each Contributor harmless for any liability\ incurred by, or claims asserted against, such Contributor by reason\ of your accepting any such warranty or additional liability.\ \ END OF TERMS AND CONDITIONS\ \ APPENDIX: How to apply the Apache License to your work.\ \ To apply the Apache License to your work, attach the following\ boilerplate notice, with the fields enclosed by brackets "[]"\ replaced with your own identifying information. (Don't include\ the brackets!) The text should be enclosed in the appropriate\ comment syntax for the file format. We also recommend that a\ file or class name and description of purpose be included on the\ same "printed page" as the copyright notice for easier\ identification within third-party archives.\ \ Copyright [yyyy] [name of copyright owner]\ \ Licensed under the Apache License, Version 2.0 (the "License");\ you may not use this file except in compliance with the License.\ You may obtain a copy of the License at\ \ http://www.apache.org/licenses/LICENSE-2.0\ \ Unless required by applicable law or agreed to in writing, software\ distributed under the License is distributed on an "AS IS" BASIS,\ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\ See the License for the specific language governing permissions and\ limitations under the License.\ \ \ \fs34 DockProgress\ \ \fs26 MIT License\ \ Copyright (c) Sindre Sorhus (https://sindresorhus.com)\ \ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\ \ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\ \ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\ \ \ \fs34 KeychainAccess\ \ \fs26 The MIT License (MIT)\ \ Copyright (c) 2014 kishikawa katsumi\ \ Permission is hereby granted, free of charge, to any person obtaining a copy\ of this software and associated documentation files (the "Software"), to deal\ in the Software without restriction, including without limitation the rights\ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ copies of the Software, and to permit persons to whom the Software is\ furnished to do so, subject to the following conditions:\ \ The above copyright notice and this permission notice shall be included in all\ copies or substantial portions of the Software.\ \ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\ SOFTWARE.\ \ \ \ \fs34 data\ \ \fs26 MIT License\ \ Copyright (c) 2018 \ \ Permission is hereby granted, free of charge, to any person obtaining a copy\ of this software and associated documentation files (the "Software"), to deal\ in the Software without restriction, including without limitation the rights\ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ copies of the Software, and to permit persons to whom the Software is\ furnished to do so, subject to the following conditions:\ \ The above copyright notice and this permission notice shall be included in all\ copies or substantial portions of the Software.\ \ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\ SOFTWARE.\ \ \ \fs34 Sparkle\ \ \fs26 Copyright (c) 2006-2013 Andy Matuschak.\ Copyright (c) 2009-2013 Elgato Systems GmbH.\ Copyright (c) 2011-2014 Kornel Lesi\uc0\u324 ski.\ Copyright (c) 2015-2017 Mayur Pawashe.\ Copyright (c) 2014 C.W. Betts.\ Copyright (c) 2014 Petroules Corporation.\ Copyright (c) 2014 Big Nerd Ranch.\ All rights reserved.\ \ Permission is hereby granted, free of charge, to any person obtaining a copy of\ this software and associated documentation files (the "Software"), to deal in\ the Software without restriction, including without limitation the rights to\ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\ the Software, and to permit persons to whom the Software is furnished to do so,\ subject to the following conditions:\ \ The above copyright notice and this permission notice shall be included in all\ copies or substantial portions of the Software.\ \ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\ \ =================\ EXTERNAL LICENSES\ =================\ \ bspatch.c and bsdiff.c, from bsdiff 4.3 :\ \ Copyright 2003-2005 Colin Percival\ All rights reserved\ \ Redistribution and use in source and binary forms, with or without\ modification, are permitted providing that the following conditions \ are met:\ 1. Redistributions of source code must retain the above copyright\ notice, this list of conditions and the following disclaimer.\ 2. Redistributions in binary form must reproduce the above copyright\ notice, this list of conditions and the following disclaimer in the\ documentation and/or other materials provided with the distribution.\ \ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING\ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\ POSSIBILITY OF SUCH DAMAGE.\ \ --\ \ sais.c and sais.c, from sais-lite (2010/08/07) :\ \ The sais-lite copyright is as follows:\ \ Copyright (c) 2008-2010 Yuta Mori All Rights Reserved.\ \ Permission is hereby granted, free of charge, to any person\ obtaining a copy of this software and associated documentation\ files (the "Software"), to deal in the Software without\ restriction, including without limitation the rights to use,\ copy, modify, merge, publish, distribute, sublicense, and/or sell\ copies of the Software, and to permit persons to whom the\ Software is furnished to do so, subject to the following\ conditions:\ \ The above copyright notice and this permission notice shall be\ included in all copies or substantial portions of the Software.\ \ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\ OTHER DEALINGS IN THE SOFTWARE.\ \ --\ \ Portable C implementation of Ed25519, from https://github.com/orlp/ed25519\ \ Copyright (c) 2015 Orson Peters \ \ This software is provided 'as-is', without any express or implied warranty. In no event will the\ authors be held liable for any damages arising from the use of this software.\ \ Permission is granted to anyone to use this software for any purpose, including commercial\ applications, and to alter it and redistribute it freely, subject to the following restrictions:\ \ 1. The origin of this software must not be misrepresented; you must not claim that you wrote the\ original software. If you use this software in a product, an acknowledgment in the product\ documentation would be appreciated but is not required.\ \ 2. Altered source versions must be plainly marked as such, and must not be misrepresented as\ being the original software.\ \ 3. This notice may not be removed or altered from any source distribution.\ \ --\ \ SUSignatureVerifier.m:\ \ Copyright (c) 2011 Mark Hamlin.\ \ All rights reserved.\ \ Redistribution and use in source and binary forms, with or without\ modification, are permitted providing that the following conditions\ are met:\ 1. Redistributions of source code must retain the above copyright\ notice, this list of conditions and the following disclaimer.\ 2. Redistributions in binary form must reproduce the above copyright\ notice, this list of conditions and the following disclaimer in the\ documentation and/or other materials provided with the distribution.\ \ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING\ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\ POSSIBILITY OF SUCH DAMAGE.\ \ \ \fs34 LegibleError\ \ \fs26 Unlicense (Public Domain)\ ============================\ \ This is free and unencumbered software released into the public domain.\ \ Anyone is free to copy, modify, publish, use, compile, sell, or\ distribute this software, either in source code form or as a compiled\ binary, for any purpose, commercial or non-commercial, and by any\ means.\ \ In jurisdictions that recognize copyright laws, the author or authors\ of this software dedicate any and all copyright interest in the\ software to the public domain. We make this dedication for the benefit\ of the public at large and to the detriment of our heirs and\ successors. We intend this dedication to be an overt act of\ relinquishment in perpetuity of all present and future rights to this\ software under copyright law.\ \ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR\ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\ OTHER DEALINGS IN THE SOFTWARE.\ \ For more information, please refer to <>\ \ \ \fs34 CombineExpectations\ \ \fs26 Copyright (C) 2019 Gwendal Rou\'e9\ \ Permission is hereby granted, free of charge, to any person obtaining a\ copy of this software and associated documentation files (the\ "Software"), to deal in the Software without restriction, including\ without limitation the rights to use, copy, modify, merge, publish,\ distribute, sublicense, and/or sell copies of the Software, and to\ permit persons to whom the Software is furnished to do so, subject to\ the following conditions:\ \ The above copyright notice and this permission notice shall be included\ in all copies or substantial portions of the Software.\ \ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS\ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\ \ \ \fs34 aria2c\ \ \fs26 GNU GENERAL PUBLIC LICENSE\ Version 2, June 1991\ \ Copyright (C) 1989, 1991 Free Software Foundation, Inc.\ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\ Everyone is permitted to copy and distribute verbatim copies\ of this license document, but changing it is not allowed.\ \ Preamble\ \ The licenses for most software are designed to take away your\ freedom to share and change it. By contrast, the GNU General Public\ License is intended to guarantee your freedom to share and change free\ software--to make sure the software is free for all its users. This\ General Public License applies to most of the Free Software\ Foundation's software and to any other program whose authors commit to\ using it. (Some other Free Software Foundation software is covered by\ the GNU Library General Public License instead.) You can apply it to\ your programs, too.\ \ When we speak of free software, we are referring to freedom, not\ price. Our General Public Licenses are designed to make sure that you\ have the freedom to distribute copies of free software (and charge for\ this service if you wish), that you receive source code or can get it\ if you want it, that you can change the software or use pieces of it\ in new free programs; and that you know you can do these things.\ \ To protect your rights, we need to make restrictions that forbid\ anyone to deny you these rights or to ask you to surrender the rights.\ These restrictions translate to certain responsibilities for you if you\ distribute copies of the software, or if you modify it.\ \ For example, if you distribute copies of such a program, whether\ gratis or for a fee, you must give the recipients all the rights that\ you have. You must make sure that they, too, receive or can get the\ source code. And you must show them these terms so they know their\ rights.\ \ We protect your rights with two steps: (1) copyright the software, and\ (2) offer you this license which gives you legal permission to copy,\ distribute and/or modify the software.\ \ Also, for each author's protection and ours, we want to make certain\ that everyone understands that there is no warranty for this free\ software. If the software is modified by someone else and passed on, we\ want its recipients to know that what they have is not the original, so\ that any problems introduced by others will not reflect on the original\ authors' reputations.\ \ Finally, any free program is threatened constantly by software\ patents. We wish to avoid the danger that redistributors of a free\ program will individually obtain patent licenses, in effect making the\ program proprietary. To prevent this, we have made it clear that any\ patent must be licensed for everyone's free use or not licensed at all.\ \ The precise terms and conditions for copying, distribution and\ modification follow.\ \page \ GNU GENERAL PUBLIC LICENSE\ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\ \ 0. This License applies to any program or other work which contains\ a notice placed by the copyright holder saying it may be distributed\ under the terms of this General Public License. The "Program", below,\ refers to any such program or work, and a "work based on the Program"\ means either the Program or any derivative work under copyright law:\ that is to say, a work containing the Program or a portion of it,\ either verbatim or with modifications and/or translated into another\ language. (Hereinafter, translation is included without limitation in\ the term "modification".) Each licensee is addressed as "you".\ \ Activities other than copying, distribution and modification are not\ covered by this License; they are outside its scope. The act of\ running the Program is not restricted, and the output from the Program\ is covered only if its contents constitute a work based on the\ Program (independent of having been made by running the Program).\ Whether that is true depends on what the Program does.\ \ 1. You may copy and distribute verbatim copies of the Program's\ source code as you receive it, in any medium, provided that you\ conspicuously and appropriately publish on each copy an appropriate\ copyright notice and disclaimer of warranty; keep intact all the\ notices that refer to this License and to the absence of any warranty;\ and give any other recipients of the Program a copy of this License\ along with the Program.\ \ You may charge a fee for the physical act of transferring a copy, and\ you may at your option offer warranty protection in exchange for a fee.\ \ 2. You may modify your copy or copies of the Program or any portion\ of it, thus forming a work based on the Program, and copy and\ distribute such modifications or work under the terms of Section 1\ above, provided that you also meet all of these conditions:\ \ a) You must cause the modified files to carry prominent notices\ stating that you changed the files and the date of any change.\ \ b) You must cause any work that you distribute or publish, that in\ whole or in part contains or is derived from the Program or any\ part thereof, to be licensed as a whole at no charge to all third\ parties under the terms of this License.\ \ c) If the modified program normally reads commands interactively\ when run, you must cause it, when started running for such\ interactive use in the most ordinary way, to print or display an\ announcement including an appropriate copyright notice and a\ notice that there is no warranty (or else, saying that you provide\ a warranty) and that users may redistribute the program under\ these conditions, and telling the user how to view a copy of this\ License. (Exception: if the Program itself is interactive but\ does not normally print such an announcement, your work based on\ the Program is not required to print an announcement.)\ \page \ These requirements apply to the modified work as a whole. If\ identifiable sections of that work are not derived from the Program,\ and can be reasonably considered independent and separate works in\ themselves, then this License, and its terms, do not apply to those\ sections when you distribute them as separate works. But when you\ distribute the same sections as part of a whole which is a work based\ on the Program, the distribution of the whole must be on the terms of\ this License, whose permissions for other licensees extend to the\ entire whole, and thus to each and every part regardless of who wrote it.\ \ Thus, it is not the intent of this section to claim rights or contest\ your rights to work written entirely by you; rather, the intent is to\ exercise the right to control the distribution of derivative or\ collective works based on the Program.\ \ In addition, mere aggregation of another work not based on the Program\ with the Program (or with a work based on the Program) on a volume of\ a storage or distribution medium does not bring the other work under\ the scope of this License.\ \ 3. You may copy and distribute the Program (or a work based on it,\ under Section 2) in object code or executable form under the terms of\ Sections 1 and 2 above provided that you also do one of the following:\ \ a) Accompany it with the complete corresponding machine-readable\ source code, which must be distributed under the terms of Sections\ 1 and 2 above on a medium customarily used for software interchange; or,\ \ b) Accompany it with a written offer, valid for at least three\ years, to give any third party, for a charge no more than your\ cost of physically performing source distribution, a complete\ machine-readable copy of the corresponding source code, to be\ distributed under the terms of Sections 1 and 2 above on a medium\ customarily used for software interchange; or,\ \ c) Accompany it with the information you received as to the offer\ to distribute corresponding source code. (This alternative is\ allowed only for noncommercial distribution and only if you\ received the program in object code or executable form with such\ an offer, in accord with Subsection b above.)\ \ The source code for a work means the preferred form of the work for\ making modifications to it. For an executable work, complete source\ code means all the source code for all modules it contains, plus any\ associated interface definition files, plus the scripts used to\ control compilation and installation of the executable. However, as a\ special exception, the source code distributed need not include\ anything that is normally distributed (in either source or binary\ form) with the major components (compiler, kernel, and so on) of the\ operating system on which the executable runs, unless that component\ itself accompanies the executable.\ \ If distribution of executable or object code is made by offering\ access to copy from a designated place, then offering equivalent\ access to copy the source code from the same place counts as\ distribution of the source code, even though third parties are not\ compelled to copy the source along with the object code.\ \page \ 4. You may not copy, modify, sublicense, or distribute the Program\ except as expressly provided under this License. Any attempt\ otherwise to copy, modify, sublicense or distribute the Program is\ void, and will automatically terminate your rights under this License.\ However, parties who have received copies, or rights, from you under\ this License will not have their licenses terminated so long as such\ parties remain in full compliance.\ \ 5. You are not required to accept this License, since you have not\ signed it. However, nothing else grants you permission to modify or\ distribute the Program or its derivative works. These actions are\ prohibited by law if you do not accept this License. Therefore, by\ modifying or distributing the Program (or any work based on the\ Program), you indicate your acceptance of this License to do so, and\ all its terms and conditions for copying, distributing or modifying\ the Program or works based on it.\ \ 6. Each time you redistribute the Program (or any work based on the\ Program), the recipient automatically receives a license from the\ original licensor to copy, distribute or modify the Program subject to\ these terms and conditions. You may not impose any further\ restrictions on the recipients' exercise of the rights granted herein.\ You are not responsible for enforcing compliance by third parties to\ this License.\ \ 7. If, as a consequence of a court judgment or allegation of patent\ infringement or for any other reason (not limited to patent issues),\ conditions are imposed on you (whether by court order, agreement or\ otherwise) that contradict the conditions of this License, they do not\ excuse you from the conditions of this License. If you cannot\ distribute so as to satisfy simultaneously your obligations under this\ License and any other pertinent obligations, then as a consequence you\ may not distribute the Program at all. For example, if a patent\ license would not permit royalty-free redistribution of the Program by\ all those who receive copies directly or indirectly through you, then\ the only way you could satisfy both it and this License would be to\ refrain entirely from distribution of the Program.\ \ If any portion of this section is held invalid or unenforceable under\ any particular circumstance, the balance of the section is intended to\ apply and the section as a whole is intended to apply in other\ circumstances.\ \ It is not the purpose of this section to induce you to infringe any\ patents or other property right claims or to contest validity of any\ such claims; this section has the sole purpose of protecting the\ integrity of the free software distribution system, which is\ implemented by public license practices. Many people have made\ generous contributions to the wide range of software distributed\ through that system in reliance on consistent application of that\ system; it is up to the author/donor to decide if he or she is willing\ to distribute software through any other system and a licensee cannot\ impose that choice.\ \ This section is intended to make thoroughly clear what is believed to\ be a consequence of the rest of this License.\ \page \ 8. If the distribution and/or use of the Program is restricted in\ certain countries either by patents or by copyrighted interfaces, the\ original copyright holder who places the Program under this License\ may add an explicit geographical distribution limitation excluding\ those countries, so that distribution is permitted only in or among\ countries not thus excluded. In such case, this License incorporates\ the limitation as if written in the body of this License.\ \ 9. The Free Software Foundation may publish revised and/or new versions\ of the General Public License from time to time. Such new versions will\ be similar in spirit to the present version, but may differ in detail to\ address new problems or concerns.\ \ Each version is given a distinguishing version number. If the Program\ specifies a version number of this License which applies to it and "any\ later version", you have the option of following the terms and conditions\ either of that version or of any later version published by the Free\ Software Foundation. If the Program does not specify a version number of\ this License, you may choose any version ever published by the Free Software\ Foundation.\ \ 10. If you wish to incorporate parts of the Program into other free\ programs whose distribution conditions are different, write to the author\ to ask for permission. For software which is copyrighted by the Free\ Software Foundation, write to the Free Software Foundation; we sometimes\ make exceptions for this. Our decision will be guided by the two goals\ of preserving the free status of all derivatives of our free software and\ of promoting the sharing and reuse of software generally.\ \ NO WARRANTY\ \ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\ FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN\ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\ PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\ OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS\ TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE\ PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\ REPAIR OR CORRECTION.\ \ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\ REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\ INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\ OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\ TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\ POSSIBILITY OF SUCH DAMAGES.\ \ END OF TERMS AND CONDITIONS\ \page \ How to Apply These Terms to Your New Programs\ \ If you develop a new program, and you want it to be of the greatest\ possible use to the public, the best way to achieve this is to make it\ free software which everyone can redistribute and change under these terms.\ \ To do so, attach the following notices to the program. It is safest\ to attach them to the start of each source file to most effectively\ convey the exclusion of warranty; and each file should have at least\ the "copyright" line and a pointer to where the full notice is found.\ \ \ Copyright (C) \ \ This program is free software; you can redistribute it and/or modify\ it under the terms of the GNU General Public License as published by\ the Free Software Foundation; either version 2 of the License, or\ (at your option) any later version.\ \ This program is distributed in the hope that it will be useful,\ but WITHOUT ANY WARRANTY; without even the implied warranty of\ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\ GNU General Public License for more details.\ \ You should have received a copy of the GNU General Public License\ along with this program; if not, write to the Free Software\ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\ \ \ Also add information on how to contact you by electronic and paper mail.\ \ If the program is interactive, make it output a short notice like this\ when it starts in an interactive mode:\ \ Gnomovision version 69, Copyright (C) year name of author\ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\ This is free software, and you are welcome to redistribute it\ under certain conditions; type `show c' for details.\ \ The hypothetical commands `show w' and `show c' should show the appropriate\ parts of the General Public License. Of course, the commands you use may\ be called something other than `show w' and `show c'; they could even be\ mouse-clicks or menu items--whatever suits your program.\ \ You should also get your employer (if you work as a programmer) or your\ school, if any, to sign a "copyright disclaimer" for the program, if\ necessary. Here is a sample; alter the names:\ \ Yoyodyne, Inc., hereby disclaims all copyright interest in the program\ `Gnomovision' (which makes passes at compilers) written by James Hacker.\ \ , 1 April 1989\ Ty Coon, President of Vice\ \ This General Public License does not permit incorporating your program into\ proprietary programs. If your program is a subroutine library, you may\ consider it more useful to permit linking proprietary applications with the\ library. If this is what you want to do, use the GNU Library General\ Public License instead of this License.\ \ \ \fs34 spm-licenses\ \ \fs26 MIT License\ \ Copyright (c) 2019 MacPaw\ \ Permission is hereby granted, free of charge, to any person obtaining a copy\ of this software and associated documentation files (the "Software"), to deal\ in the Software without restriction, including without limitation the rights\ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ copies of the Software, and to permit persons to whom the Software is\ furnished to do so, subject to the following conditions:\ \ The above copyright notice and this permission notice shall be included in all\ copies or substantial portions of the Software.\ \ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\ SOFTWARE.\ \ \ \fs34 SimpleXPCApp\ \ \fs26 MIT License\ \ Copyright (c) 2020 securing\ \ Permission is hereby granted, free of charge, to any person obtaining a copy\ of this software and associated documentation files (the "Software"), to deal\ in the Software without restriction, including without limitation the rights\ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ copies of the Software, and to permit persons to whom the Software is\ furnished to do so, subject to the following conditions:\ \ The above copyright notice and this permission notice shall be included in all\ copies or substantial portions of the Software.\ \ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\ SOFTWARE.\ \ \ } ================================================ FILE: Xcodes/Resources/Localizable.xcstrings ================================================ { "sourceLanguage" : "en", "strings" : { "" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : " " } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "" } } } }, "%@" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "%@" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "%@" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "%@" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "%@" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "%@" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "%@" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "%@" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "%@" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "%@" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "%@" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "%@" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "%@" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "%@" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "%@" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "%@" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "%@" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "%@" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "%@" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "%@" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "%@" } } } }, "%@ %@ %@" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "%1$@%2$@%3$@" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "%1$@ %2$@ %3$@" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "%1$@ %2$@ %3$@" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "%1$@ %2$@ %3$@" } }, "en" : { "stringUnit" : { "state" : "new", "value" : "%1$@ %2$@ %3$@" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "%1$@ %2$@ %3$@" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "%1$@ %2$@ %3$@" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "%1$@ %2$@ %3$@" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "%1$@ %2$@ %3$@" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "%1$@ %2$@ %3$@" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "%1$@ %2$@ %3$@" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "%1$@ %2$@ %3$@" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "%1$@ %2$@ %3$@" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "%1$@ %2$@ %3$@" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "%1$@ %2$@ %3$@" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "%1$@ %2$@ %3$@" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "%1$@ %2$@ %3$@" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "%1$@ %2$@ %3$@" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "%1$@ %2$@ %3$@" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "%1$@ %2$@ %3$@" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "%1$@ %2$@ %3$@" } } } }, "AccessDenied" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "⚠️ تم الرفض ⚠️

فضلا قوم بفتح اعدادات الاشعارات
واختار تحديث الXcode الذي تريده
ثم اعطي اذن بالسماح" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "⚠️ Accés denegat ⚠️\n\nSi us plau, obre les preferències de les notificacions i selecciona Xcodes si vols garantir-li l'accés." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "⚠️ Zugriff verweigert ⚠️\n\nBitte öffne Deine Benachrichtigungs-Einstellungen und wähle Xcodes aus, wenn Du den Zugriff erlauben möchtest." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "⚠️ Άρνηση Πρόσβασης ⚠️\n\nΑνοίξτε τις Ρυθμίσεις Γνωστοποιήσεων και επιλέξτε το Xcodes αν επιθυμείτε να επιτρέψετε την πρόσβαση." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "⚠️ Access Denied ⚠️\n\nPlease open your Notification Settings and select Xcodes if you wish to allow access." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "⚠️ Acceso Denegado ⚠️\n\nPor favor abra su Configuración de notificaciones y seleccione Xcodes si desea permitir el acceso." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "⚠️ Pääsy kielletty ⚠️\n\nAvaa ilmoitusasetukset ja valitse Xcodes, jos haluat sallia pääsyn." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "⚠️ Accès refusé ⚠️\n\nVeuillez ouvrir vos paramètres de notifications et sélectionnez Xcodes si vous souhaitez autoriser l'accès." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "⚠️ प्रवेश निषेध ⚠️\n\nकृपया अपनी सूचना सेटिंग्स खोलें और यदि आप प्रवेश की अनुमति देना चाहते हैं तो Xcodes का चयन करें." } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "⚠️ Accesso Negato ⚠️\n\nApri le Impostazioni delle Notifiche e seleziona Xcodes se desideri garantirgli accesso." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "⚠️ アクセス拒否 ⚠️\n\nアクセス許可する場合は、通知設定を開き、Xcodesを選択してください。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "⚠️ 접근 권한이 부여되지 않았습니다 ⚠️\n\n 접근을 허용하려면 알림 설정을 열고 Xcodes를 선택하여 알림을 허용해주세요." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "⚠️ Toegang Geweigerd ⚠️\n\nOpen je Notificatie Instellingen en selecteer Xcodes om toegang te verlenen." } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "⚠️ Dostęp zabroniony ⚠️\n\nOtwórz ustawienia powiadomień i wybierz Xcodes, jeśli chcesz zezwolić na dostęp." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "⚠️ Acesso negado ⚠️\n\nPor favor, abra suas configurações de notificação e selecione Xcodes se você deseja autorizar o acesso." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "⚠️ Отказано в доступе ⚠️\n\nПожалуйста, откройте настройки уведомлений и выберите Xcodes, чтобы разрешить доступ." } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "⚠️ การเข้าถึงถูกปฏิเสธ ⚠️\n\nโปรดเปิดการตั้งค่าการแจ้งเตือนและเลือก Xcodes หากคุณต้องการอนุญาตการเข้าถึง" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "⚠️ Erişim Reddedildi ⚠️\n\nEğer bildirimler için erişim vermek istiyorsanız lütfen bildirim ayarlarını açın ve Xcodes uygulamasını seçin." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "⚠️ Сповіщення Заборонено ⚠️\n\nВідкрийте будь ласка Налаштування Сповіщень та надайте дозвіл для Xcodes, якщо ви бажаєте отримувати сповіщення." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "⚠️ 没有权限 ⚠️\n\n请打开您的通知偏好设置并允许Xcodes发送通知。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "⚠️ 權限不足⚠️\n\n如果你想要啟用通知,請打開系統的通知設定並選擇 Xcodes。" } } } }, "AccessGranted" : { "comment" : "Notifications", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "لقد تم منح الوصول. سوف تتلقى إشعارات من Xcodes." } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Accés garantit. Rebràs notificacions d'Xcodes." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Zugriff erlaubt. Du empfängst jetzt Benachrichtigungen von Xcodes." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Η πρόσβαση εκχωρήθηκε. Θα λαμβάνετε γνωστοποιήσεις από το Xcode's." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Access Granted. You will receive notifications from Xcodes." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Acceso Permitido. Recibirás notificaciones de Xcodes." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Pääsy myönnetty. Saat ilmoituksia Xcodesilta." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Accès autorisé. Vous recevrez des notifications de Xcodes." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "प्रवेश करने की अनुमति है। आपको Xcodes से सूचनाएं प्राप्त होंगी।" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Accesso Garantito. Riceverai notifiche da Xcodes." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "アクセスが許可されました。Xcodesから通知が届きます。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "접근 권한이 부여되었습니다. Xcodes에서 알림을 받게 됩니다." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Toegang Verleend. Je krijgt notificaties van Xcodes." } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Dostęp przyznany. Będziesz otrzymywać powiadomienia z Xcodes." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Acesso autorizado. Você receberá notificações de Xcodes." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Доступ разрешен. Вы будете получать уведомления от Xcodes." } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ได้รับอนุญาตการเข้าถึงแล้ว คุณจะได้รับการแจ้งเตือนจาก Xcodes" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Erişim Tanımlandı. Xcodes'dan bildirimler alacaksınız." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Сповіщення Дозволено.\nВи будете отримувати сповіщення від Xcodes." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "已授权。您将会收到Xcodes的通知。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "已核准權限。你可以收到來自 Xcodes 的通知。" } } } }, "Acknowledgements" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "شكر وتقدير" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Agraïments" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Anerkennungen" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Αναγνωρίσεις" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Agradecimientos" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Kunnianosoitukset" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Remerciements" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "स्वीकृति" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Ringraziamenti" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "謝辞" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "법적 고지" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Dankbetuigingen" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Podziękowania" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Menções" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Юридическая информация" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "รับทราบ" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Teşekkür" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Подяки" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "版权声明" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "版權宣告" } } } }, "Active" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "نشط" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Activa" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Aktiv" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Ενεργή" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Activo" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Aktiivinen" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Version Active" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सक्रिय" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Attiva" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "アクティブ" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "기본 버전으로 사용 중" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Actief" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Aktywny" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Ativo" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Активно" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ใช้งาน" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Aktif" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Активний" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "已激活" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "已啟用" } } } }, "Active/Select" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "نشط/اختر" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Activar/Seleccionar" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Aktiv/Auswählen" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Ενεργή/Επιλεγμένη" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Activar/Seleccionar" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Aktiivinen/Valitse" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Activer/Sélectionner" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सक्रिय/चयन" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Attivo/Seleziona" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "アクティブ" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "기본 버전/선택" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Active/Select" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Aktywny/wybrany" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Ativo/Selecionar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Активировать/Выбрать" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ใช้งาน/เลือกใช้งาน" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Aktif/Seç" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Активний / Вибраний" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "激活/选定" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "啟用/選取" } } } }, "ActiveVersionDescription" : { "comment" : "List", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "هذا التحديث النشط" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Aquesta és la versió activa" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Dies ist die aktive Version" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Αυτή είναι η ενεργή έκδοση" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "This is the active version" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Esta es la versión activa." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Tämä on aktiivinen versio" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Ceci est la version active" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "यह सक्रिय संस्करण है" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Questa è la versione attiva" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "アクティブなバージョン" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "기본 버전으로 사용 중" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Dit is de actieve versie" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "To jest aktywna wersja" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Essa é a versão ativa" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Это активная версия" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "นี่คือเวอร์ชันที่ใช้งานอยู่" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Bu aktif sürümdür" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Це активна версія" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "这是一个已激活的版本" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "這個版本已經啟用" } } } }, "Advanced" : { "comment" : "Advanced Preference Pane", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "متقدم" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Avançat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Erweitert" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Προηγμένες" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Avanzado" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Lisäasetukset" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Avancé" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "उन्नत" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Avanzate" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "上級者向け" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "고급" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Geavanceerd" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Zaawansowane" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Avançado" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Дополнительно" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ขั้นสูง" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Gelişmiş" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Розширені" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "高级" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "進階" } } } }, "Alert.CancelInstall.Message" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "سيتم تجاهل أي تقدم." } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Tot el progrés serà descartat." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Jeglicher Fortschritt wird verworfen." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Όποια πρόοδος θα απορριφθεί." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Any progress will be discarded." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Cualquier progreso será descartado." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Kaikki edistyminen hylätään." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Toute progression sera supprimée." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "किसी भी प्रगति को छोड़ दिया जाएगा।" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Perderai quanto fatto finora." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "インストール前の状態に戻します。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "진행된 모든 사항은 삭제됩니다" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Elke voortgang zal worden weggegooid." } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Postęp zostanie utracony." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Todo progresso será descartado." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Прогресс будет сброшен." } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ความคืบหน้าใด ๆ จะถูกยกเลิก" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Herhangi bir ilerleme göz ardı edilecektir." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Будь-який прогрес буде втрачено." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "所有进度都将会丢失。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "將放棄既有進度。" } } } }, "Alert.CancelInstall.PrimaryButton" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "إيقاف التثبيت" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Aturar l'instal·lació" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Installation anhalten" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Διακοπή εγκατάστασης" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Stop Installation" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Detener Instalación" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Lopeta asennus" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Arrêter l'installation" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "स्थापना रोकें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Ferma Installazione" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "インストールを中止" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "설치 중지" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Stop Installatie" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Przerwij instalację" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Interromper instalação" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Остановить установку" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "หยุดการติดตั้ง" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yüklemeyi Durdur" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Скасувати інсталяцію" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "终止安装" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "停止安裝" } } } }, "Alert.CancelInstall.Runtimes.Title" : { "comment" : "Cancel Runtime Install", "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "هل أنت متأكد أنك تريد إيقاف تثبيت Xcode %@؟" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Are you sure you want to stop the installation of Xcode %@?" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Bist du sicher, dass Du die Installation von Xcode %@ anhalten möchtest?" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Είστε βέβαιοι ότι θέλετε να διακόψετε την εγκατάσταση του Xcode %@;" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Are you sure you want to stop the installation of Xcode %@?" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Are you sure you want to stop the installation of Xcode %@?" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Are you sure you want to stop the installation of Xcode %@?" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Êtes-vous sûr de vouloir arrêter l'installation de Xcode %@ ?" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Are you sure you want to stop the installation of Xcode %@?" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Are you sure you want to stop the installation of Xcode %@?" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Are you sure you want to stop the installation of Xcode %@?" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ 설치를 중지하시겠습니까?" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Are you sure you want to stop the installation of Xcode %@?" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Are you sure you want to stop the installation of Xcode %@?" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Tem certeza que deseja interromper a instalação do Xcode %@?" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Вы уверены, что хотите остановить установку Xcode %@?" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "คุณแน่ใจหรือไม่ว่าต้องการหยุดการติดตั้ง Xcode %@?" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ sürümünü yüklemeyi durdurmak istediğine emin misin?" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Чи дійсно ви хочете скасувати інсталяцію Xcode %@?" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "你确定要停止安装Xcode %@吗?" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "你確定要停止安裝 Xcode %@ 嗎?" } } } }, "Alert.CancelInstall.Title" : { "comment" : "Cancel Install", "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "هل أنت متأكد أنك تريد إيقاف تثبيت Xcode %@؟" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Estàs segur que vols aturar l'instal·lació de l'Xcode %@?" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Bist du sicher, dass Du die Installation von Xcode %@ anhalten möchtest?" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Είστε βέβαιοι ότι θέλετε να διακόψετε την εγκατάσταση του Xcode %@;" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Are you sure you want to stop the installation of Xcode %@?" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "¿Está seguro de que desea detener la instalación de Xcode %@?" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Haluatko varmasti lopettaa Xcode versio %@ asennuksen?" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Êtes-vous sûr de vouloir arrêter l'installation de Xcode %@ ?" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "क्या आप वाकई Xcode %@ की स्थापना को रोकना चाहते हैं?" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Sei sicuro di voler interrompere l'installazione di Xcode %@?" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ のインストールを中止してもよろしいですか?" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ 설치를 중지하시겠습니까?" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Weet je zeker dat je de installatie van Xcode %@ wilt stoppen?" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Czy na pewno chcesz przerwać instalację Xcode %@?" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Tem certeza que deseja interromper a instalação do Xcode %@?" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Вы уверены, что хотите остановить установку Xcode %@?" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "คุณแน่ใจหรือไม่ว่าต้องการหยุดการติดตั้ง Xcode %@?" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ sürümünün yüklenmesini durdurmak istediğinize emin misiniz?" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Чи дійсно ви хочете скасувати інсталяцію Xcode %@?" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "您确定要终止Xcode %@的安装吗?" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "你確定要停止安裝 Xcode %@ 嗎?" } } } }, "Alert.DeletePlatform.PrimaryButton" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "مسح" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Delete" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Löschen" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Διαγραφή" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Delete" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Delete" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Delete" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Supprimer" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Delete" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Delete" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Delete" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "삭제" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Delete" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Delete" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Remover" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Удалить" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ลบ" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Delete" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Видалити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "删除" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "刪除" } } } }, "Alert.DeletePlatform.Title" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "هل أنت متأكد أنك تريد حذف ٪@؟" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Are you sure you want to delete %@?" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Bist du sicher, dass du %@ löschen möchtest?" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Θέλετε σίγουρα να διαγράψετε το %@;" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Are you sure you want to delete %@?" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Are you sure you want to delete %@?" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Are you sure you want to delete %@?" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Êtes-vous sûr de vouloir supprimer %@ ?" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Are you sure you want to delete %@?" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Are you sure you want to delete %@?" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Are you sure you want to delete %@?" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "%@을(를) 삭제하시겠습니까?" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Are you sure you want to delete %@?" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Are you sure you want to delete %@?" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Tem certeza que deseja remover %@?" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Вы уверены, что хотите удалить %@?" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "คุณแน่ใจว่าต้องการลบ %@ หรือไม่?" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Are you sure you want to delete %@?" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Чи дійсно ви хочете видалити %@?" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "你确定要删除%@吗?" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "你確定要刪除 %@ 嗎?" } } } }, "Alert.Install.AuthError.Message" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "You are not authorized. Please Sign in with your Apple ID first." } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "You are not authorized. Please Sign in with your Apple ID first." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "You are not authorized. Please Sign in with your Apple ID first." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "You are not authorized. Please Sign in with your Apple ID first." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "You are not authorized. Please Sign in with your Apple ID first." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "You are not authorized. Please Sign in with your Apple ID first." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "You are not authorized. Please Sign in with your Apple ID first." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "You are not authorized. Please Sign in with your Apple ID first." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "You are not authorized. Please Sign in with your Apple ID first." } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "You are not authorized. Please Sign in with your Apple ID first." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "You are not authorized. Please Sign in with your Apple ID first." } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "You are not authorized. Please Sign in with your Apple ID first." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "You are not authorized. Please Sign in with your Apple ID first." } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "You are not authorized. Please Sign in with your Apple ID first." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Você não está autorizado. Por favor, entre com o seu ID Apple primeiro." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "You are not authorized. Please Sign in with your Apple ID first." } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "คุณไม่ได้รับอนุญาต กรุณาลงชื่อเข้าใช้ด้วย Apple ID ของคุณก่อน" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "You are not authorized. Please Sign in with your Apple ID first." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "You are not authorized. Please Sign in with your Apple ID first." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "You are not authorized. Please Sign in with your Apple ID first." } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "You are not authorized. Please Sign in with your Apple ID first." } } } }, "Alert.Install.Error.Need.Xcode16.1" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "A Apple oferece suporte para download somente nos sistemas operacionais iOS 16.0+, watchOS 9.0+, tvOS 16+ e visionOS 1.0+ com Xcode 16.1+. Baixe, ative e tente novamente." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "Apple รองรับการดาวน์โหลดรันไทม์ iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ ด้วย Xcode 16.1+ เท่านั้น กรุณาดาวน์โหลด เปิดใช้งาน แล้วลองอีกครั้ง" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." } } } }, "Alert.Install.Error.Need.Xcode26" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "Apple supports downloading Apple Silicon runtimes only when Xcode 26+ is selected. Please Select and try downloading again or download the universal build." } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Apple supports downloading Apple Silicon runtimes only when Xcode 26+ is selected. Please Select and try downloading again or download the universal build." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Apple supports downloading Apple Silicon runtimes only when Xcode 26+ is selected. Please Select and try downloading again or download the universal build." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Apple supports downloading Apple Silicon runtimes only when Xcode 26+ is selected. Please Select and try downloading again or download the universal build." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Apple supports downloading Apple Silicon runtimes only when Xcode 26+ is selected. Please Select and try downloading again or download the universal build." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Apple supports downloading Apple Silicon runtimes only when Xcode 26+ is selected. Please Select and try downloading again or download the universal build." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Apple supports downloading Apple Silicon runtimes only when Xcode 26+ is selected. Please Select and try downloading again or download the universal build." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Apple supports downloading Apple Silicon runtimes only when Xcode 26+ is selected. Please Select and try downloading again or download the universal build." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Apple supports downloading Apple Silicon runtimes only when Xcode 26+ is selected. Please Select and try downloading again or download the universal build." } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Apple supports downloading Apple Silicon runtimes only when Xcode 26+ is selected. Please Select and try downloading again or download the universal build." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Apple supports downloading Apple Silicon runtimes only when Xcode 26+ is selected. Please Select and try downloading again or download the universal build." } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Apple supports downloading Apple Silicon runtimes only when Xcode 26+ is selected. Please Select and try downloading again or download the universal build." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Apple supports downloading Apple Silicon runtimes only when Xcode 26+ is selected. Please Select and try downloading again or download the universal build." } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Apple supports downloading Apple Silicon runtimes only when Xcode 26+ is selected. Please Select and try downloading again or download the universal build." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "A Apple oferece suporte ao download de runtimes do Apple Silicon somente quando o Xcode 26+ estiver selecionado. Selecione e tente baixar novamente ou baixe a compilação universal." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Apple supports downloading Apple Silicon runtimes only when Xcode 26+ is selected. Please Select and try downloading again or download the universal build." } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "Apple รองรับการดาวน์โหลดรันไทม์ Apple Silicon เฉพาะเมื่อเลือก Xcode 26 ขึ้นไปเท่านั้น โปรดเลือกและลองดาวน์โหลดอีกครั้ง หรือดาวน์โหลดเวอร์ชัน Universal" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Apple supports downloading Apple Silicon runtimes only when Xcode 26+ is selected. Please Select and try downloading again or download the universal build." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Apple supports downloading Apple Silicon runtimes only when Xcode 26+ is selected. Please Select and try downloading again or download the universal build." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Apple supports downloading Apple Silicon runtimes only when Xcode 26+ is selected. Please Select and try downloading again or download the universal build." } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Apple supports downloading Apple Silicon runtimes only when Xcode 26+ is selected. Please Select and try downloading again or download the universal build." } } } }, "Alert.Install.Error.Title" : { "comment" : "Install", "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "غير قادر على تثبيت Xcode" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Incapaç d'instal·lar l'Xcode" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Installation von Xcode nicht möglich" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Δεν είναι δυνατή η εγκατάσταση του Xcode" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Unable to install Xcode" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "No se puede instalar Xcode" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Xcoden asentaminen ei onnistu" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Impossible d'installer Xcode" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "एक्सकोड स्थापित करने में असमर्थ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Non riesco a installare Xcode" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Xcode をインストールできませんでした" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Xcode를 설치할 수 없음" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Kan Xcode niet installeren" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Nie można zainstalować Xcode" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Não foi possível instalar o Xcode" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Невозможно установить Xcode" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ไม่สามารถติดตั้ง Xcode ได้" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Xcode yüklenemedi" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Не вдалося інсталювати Xcode" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "无法安装Xcode" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "無法安裝 Xcode" } } } }, "Alert.InstallArchive.Error.Title" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "غير قادر على تثبيت Xcode المؤرشفة" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Incapaç d'instal·lar l'Xcode comprimit" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Installation des archivierten Xcodes nicht möglich" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Αδυναμία εγκατάστασης αρχειοθετημένου Xcode" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Unable to install archived Xcode" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "No se puede instalar el Xcode archivado" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Arkistoitua Xcodea ei voi asentaa" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Impossible d'installer l'archive de Xcode" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "संग्रहीत Xcode स्थापित करने में असमर्थ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Non riesco a installare Xcode archiviato" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Xcode アーカイブをインストールできませんでした" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "아카이브된 Xcode를 설치할 수 없음" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Kan gearchiveerde Xcode niet installeren" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Nie można zainstalować zarchiwizowanego Xcode" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Não foi possível instalar o Xcode arquivado" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Невозможно установить заархивированный Xcode" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ไม่สามารถติดตั้ง Archive ของ Xcode ได้" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Arşivlenmiş Xcode yüklenemedi." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Помилка з архівом Xcode" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "无法安装Xcode压缩文件" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "無法安裝已封存的 Xcode" } } } }, "Alert.MinSupported.Message" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "يتطلب Xcode %@ نظام التشغيل macOS %@، ولكنك تستخدم نظام التشغيل macOS %@، فهل مازلت ترغب في تثبيته؟" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "L'Xcode %@ necessita macOS %@, però tu estàs utilitzant macOS %@, vols instal·lar-lo igualment?" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ erfordert macOS %@ aber es läuft nur macOS %@. Möchtest Du es trotzdem installieren?" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Το Xcode %@ απαιτεί macOS %@, όμως το λειτουργικό σας σύστημα είναι macOS %@, εξακολουθείτε να θέλετε να το εγκαταστήσετε;" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ requires macOS %@, but you are running macOS %@, do you still want to install it?" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ requiere macOS %@, pero está ejecutando macOS %@, ¿aún desea instalarlo?" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ vaatii macOS version %@, mutta käytät macOS versiota %@, haluatko silti asentaa sen?" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ nécessite macOS %@, mais vous utilisez macOS %@, voulez-vous toujours l'installer ?" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ को macOS %@ की आवश्यकता है, लेकिन आप macOS %@ चला रहे हैं, क्या आप अभी भी इसे स्थापित करना चाहते हैं?" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ richiede macOS %@, ma la tua versione di macOS è %@, vuoi installarlo comunque?" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ を使うには macOS %@ 以降が必要です。このシステムは macOS %@ ですが、インストールを続行しますか?" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@는 macOS %@이(가) 필요하지만 macOS %@을(를) 실행 중입니다. 그래도 설치하시겠습니까?" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ vereist macOS %@, maar jouw macOS versie is %@, wil je doorgaan met installeren?" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ wymaga macOS %@, a ty masz macOS %@. Czy nadal chcesz go zainstalować?" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ requere macOS %@, mas você está rodando macOS %@, você ainda quer instalá-lo?" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ требует macOS %@, но вы используете macOS %@, вы все еще хотите установить?" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ ต้องใช้ macOS %@ แต่คุณกำลังใช้งาน macOS %@ คุณยังคงต้องการติดตั้งหรือไม่" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ macOS %@ sürümünü gerektirir, fakat siz macOS %@ sürümünü kullanıyorsunuz, yine de indirmek istediğinize emin misiniz?" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ вимагає щонайменше macOS %@, але у вас macOS %@, всеодно продовжити?" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@需要macOS %@及以上,但您运行的是macOS %@,仍然要安装吗?" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ 需要 macOS %@,但你正在使用 macOS %@,你確定還要繼續安裝嗎?" } } } }, "Alert.MinSupported.Title" : { "comment" : "Min macOS Supported", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "لم يتم استيفاء الحد الأدنى من المتطلبات" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "No es compleixen els requeriments mínims" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Minimalanforderungen nicht erfüllt" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Δεν ικανοποιούνται οι ελάχιστες απαιτήσεις συστήματος" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Minimum requirements not met" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Requisitos mínimos no cumplidos" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Vähimmäisvaatimukset eivät täyty" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Pré-requis minimum non satisfaits" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "न्यूनतम आवश्यकताएं पूरी नहीं हुई" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Requisiti minimi non presenti" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "システム要件を満たしていません" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "최소 요건 충족되지 않음" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Minimale vereisten niet voldaan" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Niezgodność z minimalnymi wymaganiami" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Requerimentos mínimos não satisfeitos." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Текущая macOS не соответствует минимальным требованиям" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ความต้องการขั้นต่ำยังไม่เพียงพอ" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Minimum gereksinimler karşılanamadı" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Мінімальні вимоги не виконані" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "没有达到安装所需的最低要求" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "未達最低版本要求" } } } }, "Alert.PostInstall.Title" : { "comment" : "Post install", "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "غير قادر على تنفيذ خطوات ما بعد التثبيت" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Incapaç de realitzar les tasques post instal·lació" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ausführung von Post-Installationsschritten nicht möglich" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Αδυναμία εκτέλεσης μετά-εγκαταστατικών βημάτων" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Unable to perform post install steps" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "No se pueden realizar los pasos posteriores a la instalación" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Asennuksen jälkeisiä vaiheita ei voi suorittaa" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Impossible d'effectuer les étapes de post-installation" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "स्थापना के बाद के चरण निष्पादित करने में असमर्थ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Non riesco ad eseguire i comandi post installazione" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "インストール後の作業を実行できませんでした" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "설치 후 단계를 수행할 수 없음" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Kan post-installatie stappen niet uitvoeren" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Nie można wykonać czynności po-instalacyjnych" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Não foi possível realizar os comandos de pós-instalação" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Невозможно выполнить дополнительные шаги после установки" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ไม่สามารถดำเนินการตามขั้นตอนหลังการติดตั้งได้" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yükleme aşamaları çalıştırılamadı" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Не можливо виконати пост-інсталяційні дії" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "无法执行安装后准备步骤" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "無法進行安裝後步驟" } } } }, "Alert.PrivilegedHelper.Error.Title" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "غير قادر على تثبيت المساعد" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "No ha sigut possible instal·lar el servei" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Installation des Helfers nicht möglich" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Αδυναμία εγκατάστασης του βοηθητικού εργαλείου" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Unable to install helper" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "No se puede instalar el asistente" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Apuohjelmaa ei voi asentaa" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Impossible d'installer l'assistant" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "हेल्पर स्थापित करने में असमर्थ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Non riesco a installare aiutante" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ヘルパーをインストールできませんでした" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "도우미를 설치할 수 없음" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Kan helper niet installeren" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Nie można zainstalować helpera" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Não foi possível instalar o ajudante" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Невозможно установить помощник" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ไม่สามารถติดตั้งตัวช่วยได้" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yardımcı yüklenemedi" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Не вдалося інсталювати \"Privileged Helper\"" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "无法安装帮助程序" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "無法安裝輔助程式" } } } }, "Alert.PrivilegedHelper.Message" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "يستخدم Xcodes مساعدًا متميزًا منفصلاً لأداء المهام كجذر. هذه هي الأشياء التي تتطلب استخدام Sudo في سطر الأوامر، بما في ذلك خطوات ما بعد التثبيت وتبديل إصدارات Xcode باستخدام xcode-select.\n\nسيُطلب منك كلمة مرور حساب macOS لتثبيته." } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "L'Xcodes utilitza, a part, un servei que executa tasques com a root. Aquestes tasques son coses que requereixin la comanda sudo a la línia de comandes, incloent-hi les tasques al finalitzar l'instal·lació i canviar les versions d'Xcode seleccionades utilitzant xcode-select.\n\nEl sistema operatiu et demanarà la teva contrassenya per poder-lo instal·lar." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes verwendet einen separaten privilegierten Helfer, um Aufgaben als root zu erledigen. Das sind Dinge, die sudo in der Kommandozeile erfordern würden, einschließlich Post-Installationsschritte sowie das Umstellen von Xcode-Versionen mit xcode-select.\n\nUm ihn zu installieren, erfolgt eine Aufforderung zur Eingabe des Passworts für Dein macOS-Benutzerkonto." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Το Xcodes χρησιμοποιεί ένα ξεχωριστό εργαλείο με δικαιώματα ώστε να εκτελέσει εργασίες ως root. Αυτές είναι εργασίες που θα απαιτούσαν sudo στη γραμμή εντολών, συμπεριλαμβανομένων μετά-εγκαταστατικών βημάτων και αλλαγής μεταξύ εκδόσεων του Xcode με το xcode-select.\n\nΘα σας ζητηθεί το συνθηματικό του λογαριασμού σας στο macOS για την εγκατάσταση του." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes uses a separate privileged helper to perform tasks as root. These are things that would require sudo on the command line, including post-install steps and switching Xcode versions with xcode-select.\n\nYou'll be prompted for your macOS account password to install it." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes utiliza un Asistente Privilegiado independiente para realizar tareas como root. Estas son cosas que requerirían sudo en la línea de comando, incluidos los pasos posteriores a la instalación y el cambio de versiones de Xcode con xcode-select.\n\nSe le pedirá la contraseña de su cuenta de macOS para instalarlo." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes käyttää erillistä etuoikeutettua avustajaa tehtävien suorittamiseen pääkäyttäjänä. Nämä ovat asioita, jotka edellyttävät sudo komentoa komentorivillä, mukaan lukien asennuksen jälkeiset vaiheet ja Xcode-versioiden vaihtaminen xcode-selectillä.\n\nSinua pyydetään antamaan macOS-tilisi salasana sen asentamista varten." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes utilise un assistant privilégié distinct pour effectuer des tâches en tant que root. Ce sont des tâches qui nécessiteraient sudo sur la ligne de commande, y compris les étapes de post-installation et le changement de version de Xcode avec xcode-select.\n\nVous serez invité à saisir le mot de passe de votre compte macOS pour l'installer." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes कार्यों को रूट के रूप में करने के लिए एक अलग विशेषाधिकार प्राप्त सहायक का उपयोग करता है। ये ऐसी चीजें हैं जिनके लिए कमांड लाइन पर sudo की आवश्यकता होगी, जिसमें पोस्ट-इंस्टॉल चरण और xcode-select के साथ Xcode संस्करण स्विच करना शामिल है।\n\nआपको इसे स्थापित करने के लिए अपने macOS खाता पासवर्ड के लिए संकेत दिया जाएगा।" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes usa un aiutante privilegiato per svolgere dei compiti come root. Si tratta di comandi che normalmente richiederebbero sudo da linea di comando, incluse fasi di post-install e modificare la versione di Xcode con xcode-select.\n\nTi verrà richiesta la password del tuo account di macOS per installarlo." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Xcodesは、Rootとしてタスクを実行するために、別の権限のあるヘルパーを使用します。インストール後の手順や xcode-select による Xcode のバージョン切り替えなど、コマンドラインで sudo を必要とするものです。\n\nインストールには、macOS アカウントのパスワードの入力が必要です。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes는 별도의 권한을 가진 도우미를 사용하여 작업을 루트로서 수행합니다. 설치 후 단계와 xcode-select로 Xcode 버전을 전환하는 것과 같이 커맨드 라인에서 sudo가 필요한 작업이 이에 해당합니다.\n\n설치하려면 macOS 계정 암호를 입력하라는 메시지가 표시됩니다." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes gebruikt een separate privilged helper om taken uit te voeren als root. Dit zijn operaties die een sudo vereisen op de command line, inclusief post-installatie stappen en het wijzigen van Xcode versies met xcode-select.\n\nJe zult worden gevraagd om je macOS account wachtwoord om deze te installeren." } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes używa oddzielnego helpera z uprawnieniami, aby wykonywać zadania jako root. Są to rzeczy, które wymagałyby sudo w wierszu poleceń, w tym kroki po instalacji i przełączanie wersji Xcode z xcode-select.\n\nBędziesz proszony o podanie hasła do konta macOS, aby go zainstalować." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes usa um ajudante privilegiado separado para realizar tarefas como root (raíz). São tarefas onde seria necessário permissão de super usuário (sudo) na linha de comando, incluindo comandos de pós-instalação e trocar versões de Xcode com xcode-select.\n\nVocê deverá inserir sua senha do macOS para instalá-lo." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes использует отдельный привилегированный помощник для выполнения задач от имени root-пользователя. Это команды, которые потребуют sudo в командной строке, включая шаги после установки и переключение версий Xcode с помощью xcode-select.\n\nВам будет предложено указать пароль от вашей учетной записи macOS для его установки." } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes ใช้ตัวช่วยที่มีสิทธิพิเศษแยกต่างหากเพื่อดำเนินการต่าง ๆ ในฐานะ root สิ่งเหล่านี้จำเป็นต้องใช้คำสั่ง sudo บน Command Line รวมถึงขั้นตอนหลังการติดตั้งและการสลับเวอร์ชัน Xcode ด้วยคำสั่ง xcode-select\n\nระบบจะขอให้คุณป้อนรหัสผ่านของบัญชี macOS ของคุณเพื่อทำการติดตั้ง" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes, root görevlerini yerine getirmek için bir Ayrıcalıklı yardımcı aracı kullanır. Bunlar komut satırındaki sudo gerektiren, yükleme sonrası adımlarını sağlayan ve Xcode sürümü değiştiren xcode-select gibi komutlardan ibarettir.\n\nBunu yüklemek için macOS hesap şifrenizi girmeniz istenecektir." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes використовує спеціальний \"Privileged Helper\" для запуску задач від імені root. Це включає, наприклад, sudo в терміналі, та кроки після інсталяції або перемикання версії Xcode за допомогою xcode-select.\n\nБуде запит на ваш пароль від Mac щоб інсталювати цей помічник." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes使用一个独立的提权帮助程序来以root身份执行任务。就是那些需要在命令行中用sudo执行的命令。包括安装后准备步骤以及用xcode-select切换Xcode版本。\n\n您需要提供当前用户的密码来安装它。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes 會利用一個分開的具有權限的輔助程式以 root 身份執行特定工作,例如那些在命令列中通常需使用 sudo 的命令,包含安裝程序後的步驟以及使用 xcode-select 選擇 Xcode 版本。\n\n安裝此程式時,你將會需要輸入 macOS 帳號的密碼。" } } } }, "Alert.PrivilegedHelper.Title" : { "comment" : "Privileged Helper", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "مساعد مميز" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Servei privilegiat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Privilegierter Helfer" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Βοηθητικό Εργαλείο με δικαιώματα" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Privileged Helper" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Asistente Privilegiado" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Etuoikeutettu auttaja" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Assistant Privilégié" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "विशेषाधिकार प्राप्त सहायक" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Aiutante Privilegiato" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "権限のあるヘルパー" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "권한을 가진 도우미 (Privileged Helper)" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Privileged Helper" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Helper z uprawnieniami" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Ajudante privilegiado" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Привилегированный помощник" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ตัวช่วยที่มีสิทธิพิเศษ" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Ayrıcalıklı Yardımcı" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Privileged Helper" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "提权帮助程序" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "具有權限的輔助程式" } } } }, "Alert.Select.Error.Title" : { "comment" : "Active/Select", "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "غير قادر على تحديد Xcode" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Incapaç de seleccionar l'Xcode" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Auswahl von Xcode nicht möglich" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Αδυναμία επιλογής του Xcode" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Unable to select Xcode" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "No se puede seleccionar el Xcode" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Xcodea ei voi valita" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Impossible de sélectionner Xcode" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "एक्सकोड का चयन करने में असमर्थ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Non riesco a selezionare Xcode" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Xcode をアクティブにすることができませんでした" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Xcode를 선택할 수 없음" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Kan Xcode niet selecteren" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Nie można wybrać Xcode" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Não foi possível selecionar Xcode" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Невозможно выбрать Xcode" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ไม่สามารถเลือก Xcode ได้" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Xcode seçilemedi" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Не вдалося вибрати Xcode" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "无法选定Xcode版本" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "無法選擇 Xcode" } } } }, "Alert.SymLink.Message" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "Xcode.app موجود وليس رابطًا رمزيًا" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "L'Xcode.app existeix i no és un enllaç simbòlic" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Xcode.app existiert und ist kein symbolischer Link" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Το Xcode.app υπάρχει και δεν είναι συμβολικός σύνδεσμος" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Xcode.app exists and is not a symbolic link" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Xcode.app existe y no es un enlace simbólico" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Xcode.app on olemassa, eikä se ole symbolinen linkki" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Xcode.app existe et n'est pas un lien symbolique" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Xcode.app मौजूद है और एक प्रतीकात्मक लिंक नहीं है" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Xcode.app esiste e non è un link simbolico" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Xcode.appが存在し、シンボリックリンクではありません。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Xcode.app이 존재하며 심볼릭 링크가 아닙니다." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Xcode.app bestaat en is geen symbolic link" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Xcode.app istnieje i nie jest linkiem symbolicznym" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Xcode.app existe e não é um link simbólico" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Xcode.app существует и не является символической ссылкой" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "Xcode.app มีอยู่และไม่ใช่ Symbolic Link" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Xcode.app zaten var ve bir sembolik link değil" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Xcode.app вже існує, і це не символічне посилання" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Xcode.app已存在且不是软连接" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Xcode.app 已經存在,而且不是符號連結" } } } }, "Alert.SymLink.Title" : { "comment" : "Symbolic Links", "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "غير قادر على إنشاء رابط رمزي" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Incapaç de crear un enllaç simbòlic" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Erstellung des symbolischen Links nicht möglich" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Αδυναμία δημιουργίας συμβολικού συνδέσμου" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Unable to create symbolic Link" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "No se puede crear un enlace simbólico" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Symbolista linkkiä ei voi luoda" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Impossible de créer un lien symbolique" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "प्रतीकात्मक लिंक बनाने में असमर्थ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Non riesco a creare il link simbolico" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "シンボリックリンクを作成できませんでした" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "심볼릭 링크를 생성할 수 없음" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Kan geen symbolic link maken" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Nie można utworzyć linku symbolicznego" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Não foi possível criar link simbólico" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Невозможно создать символическую ссылку" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ไม่สามารถสร้าง Symbolic Link ได้" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Uygulama sembolik linki oluşturulamadı" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Не вдалося створити символічне посилання" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "无法创建软链接" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "無法建立符號連結" } } } }, "Alert.Uninstall.Error.Message.FileNotFound" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "لا يمكن ايجاد الملف \"%@\"." } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Could not find file \"%@\"." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Datei \"%@\" konnte nicht gefunden werden." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Το αρχείο «%@» δεν βρέθηκε." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Could not find file \"%@\"." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Could not find file \"%@\"." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Could not find file \"%@\"." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Impossible de trouver le fichier \"%@\"." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Could not find file \"%@\"." } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Could not find file \"%@\"." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ファイル \"%@\" が見つかりませんでした。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "\"%@\" 파일을 찾을 수 없습니다." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Could not find file \"%@\"." } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Nie znaleziono pliku \"%@\"." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Não foi possível encontrar o arquivo \"%@\"." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Не удалось найти файл «%@»." } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ไม่พบไฟล์ \"%@\"" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "\"%@\" dosyası bulunamadı." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Не вдалося знайти файл \"%@\"." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "无法找到文件\"%@\"." } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "找不到「%@」檔案。" } } } }, "Alert.Uninstall.Error.Title" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "غير قادر على إلغاء تثبيت Xcode" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Incapaç de desinstal·lar l'Xcode" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Die Deinstallation von Xcode ist nicht möglich" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Αδυναμία απεγκατάστασης του Xcode" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Unable to uninstall Xcode" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "No se puede desinstalar Xcode" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Xcode-asennuksen poistaminen ei onnistu" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Impossible de désinstaller Xcode" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Xcode की स्थापना रद्द करने में असमर्थ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Non riesco a disinstallare Xcode" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Xcode をアンインストールできませんでした" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Xcode를 제거할 수 없음" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Kan Xcode niet deinstalleren" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Nie można odinstalować Xcode" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Não foi possível desinstalar o Xcode" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Невозможно удалить Xcode" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ไม่สามารถถอนการติดตั้ง Xcode ได้" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Xcode Kaldırılamadı" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Не можливо видалити Xcode" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "无法卸载Xcode" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "無法解除安裝 Xcode" } } } }, "Alert.Uninstall.Message" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "سيتم نقله إلى سلة المهملات، ولكن لن يتم إفراغه." } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Es mourà a la paperera, però no es buidarà." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Die Anwendung wird in den Papierkorb verschoben, dieser wird aber nicht geleert." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Θα μετακινηθεί στον Κάδο, αλλά δεν θα γίνει άδειασμα." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "It will be moved to the Trash, but won't be emptied." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Se moverá a la Papelera, pero no se vaciará." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Se siirretään roskakoriin, mutta sitä ei tyhjennetä." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes sera déplacé vers la corbeille, mais la corbeille ne sera pas vidée." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "इसे ट्रैश में ले जाया जाएगा, लेकिन खाली नहीं किया जाएगा।" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Sarà spostato nel cestino, ma non svuotato." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ゴミ箱に移動されますが、ゴミ箱を自動的に空にすることはありません。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "휴지통으로 이동되지만 비워지지는 않습니다." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Het zal worden verplaatst naar de Prullenbak, maar deze zal niet geleegd worden." } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Zostanie przeniesiony do kosza, ale nie zostanie opróżniony." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Será movido para a lixeira, mas não será esvaziada." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Файлы будут перемещены в корзину, но не будут очищены из неё автоматически." } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "มันจะถูกย้ายไปที่ถังขยะ แต่จะไม่ถูกลบอย่างถาวร" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Uygulama Çöp Kutusuna taşınacaktır, fakat silinmeyecektir." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Xcode буде просто переміщено до Кошика, без повного видалення." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "它会被移动到废纸篓,但废纸篓不会清空。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "將移到垃圾桶,但垃圾桶不會清空。" } } } }, "Alert.Uninstall.Title" : { "comment" : "Uninstall", "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "إلغاء تثبيت Xcode %@؟" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Desinstal·lar l'Xcode %@?" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ deinstallieren?" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Απεγκατάσταση του Xcode %@;" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Uninstall Xcode %@?" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Desinstalar Xcode %@?" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Poista Xcode %@?" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Désinstaller Xcode %@ ?" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ की स्थापना रद्द करें?" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Disinstalla Xcode %@?" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ をアンインストールしてもよろしいですか?" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@을 제거하시겠습니까?" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ deinstalleren?" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Odinstalować Xcode %@?" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Desinstalar Xcode %@?" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Удалить Xcode %@?" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ถอนการติดตั้ง Xcode %@?" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ sürümünü Kaldır?" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Видалити Xcode %@?" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "要卸载Xcode %@吗?" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "解除安裝 Xcode %@?" } } } }, "Alert.Update.Error.Title" : { "comment" : "Update", "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "غير قادر على تحديث Xcode المحدد" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Incapaç d'actualitzar l'Xcode seleccionat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Update des ausgewählten Xcodes nicht möglich" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Αδυναμία ενημέρωσης του επιλεγμένου Xcode" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Unable to update selected Xcode" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "No se puede actualizar el Xcode seleccionado" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Valittua Xcodea ei voi päivittää" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Impossible de mettre à jour la version de Xcode sélectionnée" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "चयनित Xcode को अपडेट करने में असमर्थ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Non riesco ad aggiornare Xcode selezionato" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Xcode の一覧をアップデートできませんでした" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "선택된 Xcode를 업데이트할 수 없음" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Kan geselecteerde Xcode niet updaten" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Nie można zaktualizować wybranej wersji Xcode" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Não foi possível atualizar o Xcode selecionado" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Невозможно обновить выбранный Xcode" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ไม่สามารถอัปเดต Xcode ที่เลือกได้" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Seçili Xcode güncellenemedi" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Не вдалося оновити обраний Xcode" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "无法更新选定的Xcode版本" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "無法更新已選擇的 Xcode" } } } }, "All" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "الجميع" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Tot" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Alle" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Όλα" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Todos" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Kaikki" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Tout" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सभी" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Tutti" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "すべて" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "전체" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Alles" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Wszystkie" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Todos" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Все" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ทั้งหมด" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Tümü" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Всі" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "全部" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "全部" } } } }, "Apple Silicon" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "أبل السيليكون" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Apple Silicon" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Apple Silicon" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Apple Silicon" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Apple Silicon" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Apple Silicon" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Apple Silicon" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Apple Silicon" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Apple Silicon" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Apple Silicon" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Apple Silicon" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Apple Silicon" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Apple Silicon" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Apple Silicon" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Apple Silicon" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "Apple Silicon" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Apple Silikon" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Apple Silicon" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Apple Silicon" } } } }, "AppleID" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "ايميل ابل :" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "AppleID:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Apple-ID:" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "AppleID:" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "AppleID:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "AppleID:" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "AppleID:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Identifiant Apple :" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "AppleID:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "AppleID:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Apple ID:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "AppleID:" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "AppleID:" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "AppleID:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "AppleID:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Apple ID:" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "AppleID:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "AppleID:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Apple ID:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Apple ID:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Apple ID:" } } } }, "AppUpdates" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "تحديثات Xcodes.app" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Actualitzacions d'Xcodes.app" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes.app-Updates" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Ενημερώσεις του Xcodes.app" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes.app Updates" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes.app Actualizaciones" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes.app päivitykset" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Mises à Jour de Xcodes.app" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes.app अपडेट" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes.app Aggiornamenti" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes.appの更新" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes.app 업데이트" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes.app Updates" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Aktualizacje Xcodes.app" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Atualizações de Xcodes.app" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Обновления Xcodes.app" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "อัปเดต Xcodes.app" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes.app Güncellemeleri" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Оновлення Xcodes.app" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes.app更新" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes.app 更新" } } } }, "Architecture" : { }, "Authenticating" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "Authenticating" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Authenticating" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Authenticating" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Authenticating" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Authenticating" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Authenticating" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Authenticating" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Authenticating" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Authenticating" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Authenticating" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "認証中" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Authenticating" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Authenticating" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Authenticating" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Autenticando" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Authenticating" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "กำลังตรวจสอบความถูกต้อง" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Authenticating" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Authenticating" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Authenticating" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Authenticating" } } } }, "AutomaticallyCreateSymbolicLink" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "إنشاء رابط رمزي إلى Xcode.app تلقائيًا" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Crea automàticament un link simbòlic a Xcode.app" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Symbolischen Link zur Xcode.app automatisch erstellen" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Αυτόματη δημιουργία συμβολικού συνδέσμου προς το Xcode.app" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Automatically create symbolic link to Xcode.app" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Crear automáticamente enlace simbólico a Xcode.app" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Luo automaattisesti symbolinen linkki Xcode.appiin" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Créer automatiquement un lien symbolique vers Xcode.app" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Xcode.app के लिए स्वचालित रूप से प्रतीकात्मक लिंक बनाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Crea Automaticamente link simbolico a Xcodes.app" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Xcode.appへのシンボリックリンクの自動生成" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes.app에 대한 심볼릭 링크 자동 생성" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Creëer automatisch een symbolic link naar Xcode.app" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Automatycznie twórz symboliczne połączenie do Xcode.app" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Criar link simbólico para o Xcode.app automaticamente" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Автоматически создавать символическую ссылку к Xcode.app" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "สร้าง Symbolic Link ไปยัง Xcode.app โดยอัตโนมัติ" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes.app'in sembolik linkini otomatik oluştur." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Автоматично створювати символічне посилання Xcode.app" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "自动创建名为Xcode.app的软连接" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "自動建立連結至 Xcode.app 的符號連結" } } } }, "AutomaticallyCreateSymbolicLinkDescription" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "عند جعل إصدار Xcode نشطًا/محددًا، حاول إنشاء رابط رمزي باسم Xcode.app في دليل التثبيت" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Quan s'activa/selecciona una versió d'Xcode, prova de crear un enllaç simbòlic dins el directori d'instal·lació" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Beim Umstellen einer Xcode-Version auf Aktiv/Ausgewählt versuchen einen symbolischen Link namens Xcode.app im Installationsverzeichnis zu erstellen." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Όταν ορίζετε μια έκδοση του Xcode ως Ενεργή/Επιλεγμένη, θα γίνεται προσπάθεια δημιουργίας ενός συμβολικού συνδέσμου με το όνομα Xcode.app στον φάκελο της εγκατάστασης" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "When making an Xcode version Active/Selected, try and create a symbolic link named Xcode.app in the installation directory" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Al activar/seleccionar una versión de Xcode, intentará crear un enlace simbólico llamado Xcode.app en el directorio de instalación." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Kun teet Xcode-versiosta aktiivisen/valitun, yritä luoda symbolinen linkki nimeltä Xcode.app asennushakemistoon" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Lorsque vous activez/sélectionnez une version de Xcode, essayez de créer un lien symbolique nommé Xcode.app dans le répertoire d'installation" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Xcode संस्करण को सक्रिय/चयनित बनाते समय, स्थापना निर्देशिका में Xcode.app नामक एक प्रतीकात्मक लिंक बनाने का प्रयास करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Quando rendi una versione di Xcode Attiva/Selezionata, prova a fare un link simbolico chiamato nella directory di installazione." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Xcodeのバージョンをアクティブにする時、インストール先でXcode.appのシンボリックリンクの作成を試みます。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Xcode 버전을 기본 버전(활성)/선택됨 상태로 만들 때 설치 디렉토리에 Xcode.app이라는 심볼릭 링크를 만들어 보세요." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Bij het actief maken/selecteren van een Xcode versie, probeer een symbolic link genaamd Xcode.app te maken in de installatie map" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Przy ustawianiu Xcode jako aktywny/wybrany, próbuj automatycznie stworzyć symboliczne połączenie o nazwie Xcode.app w katalogu instalacji." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Quando ativar/selecionar uma versão do Xcode, tentar criar um link simbólico chamado Xcode.app no diretório de instalação" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Когда вы делаете версию Xcode активной/выбранной, будет выполнена попытка создать символическую ссылку с именем Xcode.app в директории установки" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "เมื่อทำให้เวอร์ชัน Xcode เป็น ใช้งาน/ถูกเลือก ให้ลองสร้าง Symbolic link ชื่อ Xcode.app ในไดเร็กทอรีของการติดตั้ง" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Bir Xcode sürümünü Aktif/Seç yaparken Xcode.app ismindeki uygulamanın sembolik linkini yükleme klasörüne otomatik oluşturmayı dene" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Створювати символічне посилання Xcode.app що вказує на обрану версію при зміні активного Xcode. \nСимволічне посилання буде розміщено у папці інсталяції Xcode" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "当激活/选定某个Xcode版本时,尝试在安装目录创建一个名为Xcode.app的软链接。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "選擇或啟用一個 Xcode 版本時,會自動建立一個名為 Xcode.app 的符號連結,並指向該版本的安裝目錄" } } } }, "AutomaticInstallNewVersion" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "تثبيت الإصدارات الجديدة من Xcode تلقائيًا" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Instal·lar automàticament noves versions d'Xcode" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Neue Versionen von Xcode automatisch installieren" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Αυτόματη εγκατάσταση νέων εκδόσεων του Xcode" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Automatically install new versions of Xcode" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Instalar automáticamente versions de Xcode" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Asenna uudet Xcode versiot automaattisesti" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Installer automatiquement les nouvelles versions de Xcode" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Xcode के नए संस्करण स्वचालित रूप से स्थापित करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Installa automaticamente nuove versioni di Xcode" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "新しい Xcode バージョンを自動的にインストールする" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "새 Xcode 버전 자동으로 설치" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Installeer nieuwe versies van Xcode automatisch" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Automatycznie instaluj nowe wersje Xcode" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Instalar novas versões do Xcode automaticamente" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Автоматически устанавливать новые версии Xcode" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ติดตั้ง Xcode เวอร์ชันล่าสุดโดยอัตโนมัติ" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Xcode'un yeni versiyonlarını otomatik yükle" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Автоматично інсталювати нові версії Xcode" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "自动安装最新的Xcode版本" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "自動安裝新版本的 Xcode" } } } }, "Beta" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "تجربية" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Beta" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Beta" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Beta" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Beta" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Beta" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Beta" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Beta" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "बीटा" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Beta" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ベータ" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "베타" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Beta" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Beta" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Beta" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Бета" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "เบต้า" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Beta" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Beta" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "测试版" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "測試版" } } } }, "BetaOnly" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "تجربية فقط" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Només Beta" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Nur Beta" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Μόνο Beta" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Beta only" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Solo Beta" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Vain beta versiot" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Seulement les versions bêtas" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "केवल बीटा" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Solo Beta" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ベータ版のみ" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "베타만" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Alleen beta" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Tylko beta" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Somente beta" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Только бета" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "เบต้าเท่านั้น" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yalnızca Beta" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "лише Beta" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "只显示测试版" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "只顯示測試版" } } } }, "Cancel" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "الغاء" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Cancelar" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Abbrechen" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Ακύρωση" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Cancelar" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Peruuta" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Annuler" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "रद्द करे" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Annulla" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "キャンセル" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "취소" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Annuleren" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Anuluj" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Cancelar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Отмена" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ยกเลิก" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "İptal Et" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Скасувати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "取消" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "取消" } } } }, "Category" : { }, "Change" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "تغير" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Canviar" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ändern" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Αλλαγή" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Cambiar" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Vaihda" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Changer" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "परिवर्तन" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Cambia" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "変更" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "변경" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Wijzig" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Zmień" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Alterar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Изменить" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "เปลี่ยน" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Değiştir" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Змінити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "变更" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "更改" } } } }, "CheckForAppUpdates" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "التحقق تلقائيًا من تحديثات التطبيق" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Cercar actualitzacions automàticament" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Automatisch auf App-Updates prüfen" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Αυτόματος έλεγχος για ενημερώσεις" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Automatically check for app updates" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Buscar automáticamente actualizaciones" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Tarkista päivitykset automaattisesti" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Vérifier automatiquement les mises à jour de l'application" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "ऐप अपडेट के लिए स्वचालित रूप से जांचें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Controlla automaticamente per aggiornamenti" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "アプリのアップデートを自動で確認する" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "업데이트 자동 확인" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Automatisch controleren op app updates" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Automatycznie sprawdzaj aktualizacje aplikacji" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Verificar atualizações do app automaticamente" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Автоматически проверять наличие обновлений" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ตรวจสอบเวอร์ชันล่าสุดของ Xcodes โดยอัตโนมัติ" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Uygulama güncellemelerini otomatik kontrol et" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Автоматично перевіряти наявність оновлень" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "自动检查更新" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "自動檢查更新項目" } } } }, "CheckingSecurity" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "تصريح امني" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Verificació de seguretat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Sicherheitsprüfung" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Επαλήθευση ασφαλείας" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Security verification" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Verificación de seguridad" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Turvallisuustarkastus" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Vérification de sécurité" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सुरक्षा सत्यापन" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Verifica di sicurezza" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "セキュリティを検証しています" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "보안 확인" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Beveiliging verificatie" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Weryfikacja zabezpieczeń" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Verificação de segurança" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Проверка безопасности" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "การตรวจสอบความปลอดภัย" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Güvenlik doğrulaması" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перевірка безпеки" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "正在检查安全性" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "安全驗證中" } } } }, "CheckNow" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "افحص الان" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Revisar ara" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Jetzt prüfen" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Έλεγχος Τώρα" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Check Now" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Revisarlo Ahora" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Tarkista nyt" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Vérifier Maintenant" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "अभी जाँचें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Controlla Ora" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "今すぐ確認" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "지금 확인하기" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Nu Controleren" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Sprawdź teraz" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Verificar agora" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Проверить сейчас" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ตรวจสอบตอนนี้" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kontrol Et" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перевірити зараз" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "现在检查" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "立即檢查" } } } }, "Close" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "اغلق" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Tancar" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Schließen" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Κλείσιμο" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Cerrar" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Sulje" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Fermer" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "बंद करे" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Chiudi" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "閉じる" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "닫기" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Sluiten" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Zamknij" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Fechar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Закрыть" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ปิด" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kapat" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Закрити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "关闭" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "關閉" } } } }, "Compatibility" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "التوافق" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Compatibilitat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Kompatibilität" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Συμβατότητα" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Compatibilidad" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Yhteensopivuus" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Compatibilité" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "अनुकूलता" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Compatibilità" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "互換性" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "호환성" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Compatibiliteit" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Kompatybilność" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Compatibilidade" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Совместимость" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ความเข้ากันได้" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Uyumluluk" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Сумісність" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "兼容性" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "相容性" } } } }, "Compilers" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "المحرر" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Compiladors" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Compiler" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Compilers" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Compiladores" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Kääntäjät" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Compilateurs" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "संकलनकर्ता" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Compilatore" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "コンパイラ" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "컴파일러" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Compilers" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Kompilatory" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Compiladores" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Компиляторы" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "คอมไพเลอร์" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Derleyiciler" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Компілятори" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "编译器" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "編譯器" } } } }, "Continue" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "استمرار" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Continuar" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Fortfahren" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Συνέχεια" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Continuar" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Jatka" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Continuer" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "जारी रखें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Continua" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "続ける" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "계속하기" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Doorgaan" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Kontynuuj" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Continuar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Продолжить" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ดำเนินการต่อ" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Devam Et" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Продовжити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "继续" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "繼續" } } } }, "CopyPath" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "نسخ المسار" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Copiar la ruta" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Pfad kopieren" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Αντιγραφή διαδρομής" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Copy Path" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Copiar Ruta" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Kopioi polku" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Copier le chemin d'accès" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पथ की कॉपी करे" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Copia Percorso" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "パスをコピー" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "경로 복사하기" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Kopieer Pad" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Kopiuj ścieżkę" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Copiar caminho" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Копировать путь" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "คัดลอก Path" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yolu Kopyala" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Скопіювати шлях" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "复制文件位置" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "拷貝路徑" } } } }, "CopyReleaseNoteURL" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "نسخ الرابط" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Copiar l'URL" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "URL kopieren" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Αντιγραφή διεύθυνσης URL" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Copy URL" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Copiar URL" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Copy URL" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Copier l'URL" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "URL कॉपी करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Copia URL" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "URLをコピー" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "URL 복사" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Kopieer URL" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Kopiuj adres URL" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Copiar URL" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Копировать URL" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "คัดลอก URL" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "URL'yi kopyala" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Скопіювати URL" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "复制 URL" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "拷貝 URL" } } } }, "CreateSymLink" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "قم بإنشاء رابط رمزي كـ Xcode.app" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Crear un Symlink com a Xcode.app" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Symlink als Xcode.app erstellen" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Δημιουργία συμβολικού συνδέσμου ως Xcode.app" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Create Symlink as Xcode.app" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Crear Symlink como Xcode.app" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Luo Symlink nimellä Xcode.app" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Créer un Symlink pour Xcode-Beta.app" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Xcode.app के रूप में सिमलिंक बनाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Crea Symlink come Xcode.app" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Xcode.appとしてシンボリックリンクを作成" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Xcode.app과 같은 Symlink 만들기" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Creëer Symlink als Xcode.app" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Utwórz Symlink jako Xcode.app" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Criar link simbólico como Xcode.app" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Создать символическую ссылку к Xcode.app" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "สร้าง Symlink เป็น Xcode.app" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Xcode.app olarak sembolik link yarat" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Створити символічне посилання як Xcode.app" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "以Xcode.app创建软链接" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "製作 Xcode.app 符號連結" } } } }, "CreateSymLinkBeta" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "أنشئ رابطًا رمزيًا باسم Xcode-Beta.app" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Create Symlink as Xcode-Beta.app" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Symlink als Xcode-Beta.app erstellen" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Δημιουργία συμβολικού συνδέσμου ως Xcode-Beta.app" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Create Symlink as Xcode-Beta.app" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Crear Symlink como Xcode-Beta.app" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Luo Symlink nimellä Xcode-Beta.app" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Créer un lien symbolique Xcode-Beta.app" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Xcode-Beta.app के रूप में सिमलिंक बनाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Crea Symlink come Xcode-Beta.app" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Xcode-Beta.appとしてシンボリックリンクを作成" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Xcode-Beta.app과 같은 Symlink 만들기" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Create Symlink as Xcode-Beta.app" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Utwórz Symlink jako Xcode-Beta.app" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Criar link simbólico como Xcode-Beta.app" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Создать символическую ссылку к Xcode-Beta.app" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "สร้าง Symlink เป็น Xcode-Beta.app" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Xcode-Beta.app olarak sembolik link yarat" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Створити символічне посилання як Xcode-Beta.app" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "以Xcode-Beta.app创建软链接" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "製作 Xcode-Beta.app 符號連結" } } } }, "DataSource" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "مصدر البيانات" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Font de dades" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Datenquelle" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Πηγή δεδομένων" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Data Source" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Fuente de datos" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Tietolähde" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Source de Données" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "डेटा स्रोत" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Sorgente Dati" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "データソース" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "데이터 소스" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Databron" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Źródło danych" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Fonte de dados" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Источник данных" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "แหล่งข้อมูล" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Veri Kaynağı" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Джерело" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "数据源" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "資料來源" } } } }, "DataSourceDescription" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "يقوم مصدر بيانات Apple بإلغاء موقع Apple Developer. سيُظهر دائمًا أحدث الإصدارات المتوفرة، ولكنها أكثر هشاشة.\n\n[إصدارات Xcode](https://xcodereleases.com) هي قائمة غير رسمية لـ [إصدارات Xcode](https://xcodereleases.com). يتم تقديمها كبيانات جيدة الإعداد، وتحتوي على معلومات إضافية لا تتوفر بسهولة من Apple، ومن غير المرجح أن تتعطل إذا أعادت Apple تصميم موقع الويب الخاص بالمطورين." } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "La font de dades d'Apple rebusca dins la pàgina web de desenvolupadors d'Apple. Això farà que sempre mostri les últimes versions disponibles, però també és una opció més inestable.\n\n[Xcode Releases](https://xcodereleases.com) es una llista no oficial de les versions d'Xcode. Està formada per dades ben composades, conté informació extra que no està disponible llegint les dades oficials d'Apple i és menys probable que es trenqui si Apple decideix redissenyar el seu portal de desenvolupadors." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Die Apple-Datenquelle liest die Apple Developer-Website aus. Sie zeigt immer die neuesten Releases an, die verfügbar sind, ist allerdings etwas instabiler.\n\n[Xcode Releases](https://xcodereleases.com) ist eine inoffizielle Liste von Xcode-Veröffentlichungen. Sie wird als formatierte Daten bereitgestellt, enthält Extrainformationen die nicht ohne weiteres von Apple erhältlich sind und ist mit höherer Wahrscheinlichkeit weiter verfügbar, sollte Apple seine Entwickler-Website neu gestalten." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Η πηγή δεδομένων «Apple» αντλεί δεδομένα από τον ιστότοπο «Apple Developer». Εμφανίζει πάντα τις πιο πρόσφατες διαθέσιμες εκδόσεις, αλλά είναι πιο εύθραστη ως πηγή.\n\nΗ επιλογή «[Xcode Releases](https://xcodereleases.com)» είναι μια ανεπίσημη λίστα απο εκδόσεις του Xcode. Παρέχεται ως μια καλοδιατηρημένη λίστα δεδομένων, περιέχει επιπλέον πληροφορίες οι οποίες δεν είναι άμεσα διαθέσιμες απο την Apple και έχει λιγότερες πιθανότητες να υπολειτουργήσει σε περίπτωση που η Apple επανασχεδιάσει τον ιστότοπο της." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "The Apple data source scrapes the Apple Developer website. It will always show the latest releases that are available, but is more fragile.\n\n[Xcode Releases](https://xcodereleases.com) is an unofficial list of [Xcode Releases](https://xcodereleases.com). It's provided as well-formed data, contains extra information that is not readily available from Apple, and is less likely to break if Apple redesigns their developer website." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "La fuente de datos de Apple la extrae de el sitio web de Apple Developer. Siempre mostrará los últimos lanzamientos disponibles, pero es más frágil.\n\n[Xcode Releases](https://xcodereleases.com) es una lista no oficial de lanzamientos de Xcode. Se proporciona como datos bien estructurados, contiene información adicional que no está disponible fácilmente en Apple y es menos probable que se rompa si Apple rediseña su sitio web para desarrolladores." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Applen tietolähde kaappaa Apple Developer -sivuston. Se näyttää aina uusimmat saatavilla olevat julkaisut, mutta se on herkempi hajoamiselle.\n\n[Xcode Releases](https://xcodereleases.com) on epävirallinen luettelo Xcode-julkaisuista. Se toimitetaan hyvin muotoiltuina tietoina, sisältää lisätietoa, jota ei ole helposti saatavilla Applelta, ja se ei todennäköisesti hajoa, jos Apple suunnittelee uudelleen kehittäjäsivustonsa." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "La source de données Apple analyse le site Web de développeurs d'Apple. Elle contient les dernières versions disponibles, mais est plus fragile.\n\n[Xcode Releases](https://xcodereleases.com) est une liste non officielle des versions de Xcode. Elle contient des informations supplémentaires qui ne sont pas facilement disponibles auprès d'Apple et est moins susceptible de se briser si Apple refait son site Web de développeurs." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Apple डेटा स्रोत Apple डेवलपर वेबसाइट को स्क्रैप करता है। यह हमेशा नवीनतम रिलीज दिखाएगा जो उपलब्ध हैं, लेकिन अधिक नाजुक है।\n\n[Xcode Releases](https://xcodereleases.com) Xcode रिलीज़ की एक अनौपचारिक सूची है। यह सुव्यवस्थित डेटा के रूप में प्रदान किया जाता है, इसमें अतिरिक्त जानकारी होती है जो Apple से आसानी से उपलब्ध नहीं होती है, और यदि Apple अपनी डेवलपर वेबसाइट को फिर से डिज़ाइन करता है तो इसके टूटने की संभावना कम होती है।" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "La sorgente dati Apple controlla il sito sviluppatori Apple. Mostra sempre le ultime versioni disponibili, ma è più fragile. [Xcode Releases](https://xcodereleases.com) è una lista non ufficiale di versioni di Xcode. E' disponibile con un formato ben strutturato, contiene più in formazioni che non vengono rilasciate da Apple ed è più difficile che smetta di funzionare se Apple ristruttura il suo sito sviluppatori." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Appleのデータソースは、Apple Developerのウェブサイトをスクレイピングしています。常に最新のリリースが表示されますが、比較的壊れやすくなっています。\n\n[Xcode Releases](https://xcodereleases.com)は、非公式なXcodeのリリース一覧です。この一覧は整形されたデータとして提供されます。Appleからは簡単に入手できない特別な情報を含んでおり、AppleがDeveloper ウェブサイトを再設計しても壊れにくくなっています。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Apple 데이터 소스는 Apple Developer 웹사이트를 스크랩합니다. Apple Developer에는 항상 사용 가능한 최신 출시 버전이 표시되지만 취약한 면이 있습니다.\n\n[Xcode Releases](https://xcodereleases.com)는 비공식적인 Xcode 출시 버전 목록입니다. [Xcode Releases](https://xcodereleases.com)에서는 정리된 데이터를 제공하며, Apple에서 쉽게 구할 수 없는 정보를 포함하며 Apple이 Apple Developer를 재설계할 경우에도 안전할 수 있습니다." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "De Apple databron scraped de Apple Developer website. Dit zal altijd de laatste nieuwe versies tonen die beschikbaar zijn, maar is meer foutgevoelig.\n\n[Xcode Releases](https://xcodereleases.com) is een onofficiële lijst van [Xcode Releases](https://xcodereleases.com). Deze lijst wordt gepresenteerd als gevalideerde data en bevat extra informatie die niet beschikbaar is vanuit Apple. Deze databron is minder foutgevoelig en niet afhankelijk van wanneer Apple de developer website aanpast." } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Źródło danych Apple przeszukuje stronę internetową Apple Developer. Zawsze wyświetla najnowsze dostępne wydania, ale jest bardziej podatne na problemy.\n\n[Xcode Releases](https://xcodereleases.com) to nieoficjalna lista wydań Xcode. Udostępnia ona dobrze sformatowane dane, zawiera dodatkowe informacje, które nie są łatwo dostępne w Apple, i jest mniej podatna na problemy, gdy Apple zmienia swoją stronę dla deweloperów." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "A fonte de dados da Apple copia o site Apple Developer. Sempre mostrará os lançamentos mais recentes que estão disponíveis, porém é mais frágil.\n\nLançamentos do Xcode ([Xcode Releases](https://xcodereleases.com)) é uma lista de lançamentos do Xcode não-oficial. É provido como dado formatado, contem informação extra que não está prontamente disponível pela Apple, e é menos provável que quebre se a Apple redesenhar o seu site de desenvolvedores." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Источник данных Apple применяет технологию \"веб-скрейпинга\" к веб-сайту Apple для разработчиков. Он всегда показывает последние доступные выпуски, но является менее стабильным источником данных.\n\n[Xcode Releases](https://xcodereleases.com) — это неофициальный список выпусков Xcode. Он предоставляется в виде удобно структурированных данных, содержит дополнительную информацию, которую не всегда можно получить от Apple и который с меньшей вероятностью перестанет работать, если Apple изменит дизайн своего веб-сайта для разработчиков." } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "แหล่งข้อมูล Apple จะรวบรวมข้อมูลจากเว็บไซต์นักพัฒนาของ Apple ซึ่งจะแสดงเวอร์ชันล่าสุดที่พร้อมใช้งานอยู่เสมอ แต่มีความเปราะบางมากกว่า\n\n[Xcode Releases](https://xcodereleases.com) คือรายชื่อของ [Xcode Releases](https://xcodereleases.com) อย่างไม่เป็นทางการ มีข้อมูลเพิ่มเติมที่ไม่สามารถหาได้จาก Apple และมีโอกาสเสียหายน้อยกว่าเมื่อ Apple ออกแบบเว็บไซต์นักพัฒนาใหม่" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Apple veri kaynağı Apple'ın geliştirici sitesini inceliyor. Uygun olan en son sürümleri gösterir, ama biraz daha hassastır.\n\n[Xcode Releases](https://xcodereleases.com), Xcode sürümlerinin bulunduğu resmi olmayan bir listedir. Data düzenli bir formata sahip bilgileri barındırır, Apple tarafında olmayan ek bilgileri içerir ve Apple'ın ileride sitesini değiştireceği için bozabileceği bir kaynak değildir." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Apple — сканування Apple Developer Portal у пошуку доступних версій Xcode. Надає список усіх нових релізів, але є менш надійним.\n\n[Xcode Releases](https://xcodereleases.com) — це неофіційний список релізів Xcode. Він є більш структурованим та надає додаткову інформацію (що не завжди доступна напряму від Apple), і менш ймовірно зламається якщо Apple оновить Developer Portal." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Apple数据源是从Apple开发者网站爬取的。它会及时反应最新版本,但较为脆弱。\n\n[Xcode Releases](https://xcodereleases.com)是一个非官方的Xcode版本列表。它提供了良好格式化的数据,包含了不便从Apple直接获得的附加信息,且更不容易在Apple开发者网站无法访问时出现问题。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "「Apple 資料來源」會爬取 Apple 開發者網站。此資料來源較容易出錯,且只會顯示最新的可用版本。\n\n「[Xcode Releases](https://xcodereleases.com)」是一個非官方的 Xcode 發行版本清單。此資料來源提供彙整好的資料,包含了 Apple 開發者網站上未列出的額外資訊,且不容易因 Apple 網站設計變更而出錯。" } } } }, "DigitCodeDescription" : { "comment" : "SMS/2FA", "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "أدخل الرمز المكون من %d من أحد أجهزتك الموثوقة:" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Introdueix el codi de %d dígits d'un dels teus dispositius de confiança:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Gib den %d-stelligen Code von einem Deiner Vertrauensgeräte ein:" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Εισάγετε τον %d-ψήφιο κωδικό από μία από τις έμπιστες συσκευές σας:" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Enter the %d digit code from one of your trusted devices:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ingrese el código de %d dígitos de uno de sus dispositivos de confianza:" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Anna %d numeroinen koodi jostakin luotetusta laitteesta:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Saisissez le code à %d chiffres de l'un de vos appareils de confiance :" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "अपने किसी विश्वसनीय डिवाइस से %d अंक कोड दर्ज करें:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Inserisci il codice di %d cifre in uno dei tuo dispositivi registrati:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "信頼できるデバイスから、%d桁のコードを入力してください。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "신뢰할 수 있는 장치 중 하나에서 %d 자리 코드를 입력하세요." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Voer de %d code in van een van je vertrouwde apparaten:" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Wprowadź %d-cyfrowy kod z jednego ze swoich zaufanych urządzeń:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Insira o código de %d dígitos de um de seus dispositivos confiáveis:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Введите %d цифровой код с одного из ваших доверенных устройств:" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ป้อนรหัส %d หลักจากหนึ่งในอุปกรณ์ที่เชื่อถือได้ของคุณ:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Güvenilir cihazından aldığın %d rakamlı kodu gir:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Введіть %d-значний код з одного з довірених пристроїв:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "请输入从信任的设备接收到的%d位代码:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "請輸入顯示在你其中一個信任裝置中的 %d 位數密碼:" } } } }, "Downloader" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "أداة التنزيل" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Descàrrega" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Downloader" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Downloader" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Descargador" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Downloader" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Téléchargeur" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "डाउनलोडर" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Scaricatore" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ダウンローダ" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "다운로더" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Downloader" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Pobieranie" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Baixador" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Загрузчик" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "เครื่องมือในการดาวน์โหลด" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yükleyici" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Завантажувач" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "下载器" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "下載器" } } } }, "DownloaderDescription" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "يستخدم [aria2](https://github.com/aria2/aria2) ما يصل إلى 16 اتصالًا لتنزيل Xcode بسرعة أكبر بمعدل 3 إلى 5 مرات من URLSession. تم تجميعه كملف قابل للتنفيذ مع كود المصدر الخاص به داخل Xcodes ليتوافق مع ترخيص GPLv2 الخاص به.\n\nURLSession هو واجهة برمجة تطبيقات Apple الافتراضية لتقديم طلبات URL." } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "[aria2](https://github.com/aria2/aria2) utilitza fins a 16 connexions simultànies per a descarregar l'Xcode 3-5x vegades més de pressa que URLSesson. A més de l'executable, està empaquestat amb el seu codi font dins Xcodes per complir amb la llicència GPLv2.\n\nURLSession is la opció per defecte de l'API d'Apple per a fer crides URL." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "[aria2](https://github.com/aria2/aria2) verwendet bis zu 16 Verbindungen, um Xcode 3-5x schneller als URLSession herunterzuladen. Es ist zusammen mit seinem Quellcode in Xcode enthalten, um seiner GPLv2-Lizenz nachzukommen.\n\nURLSession ist Apples Standard-API für URL-Requests." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Το [aria2](https://github.com/aria2/aria2) χρησσιμοποιεί έως και 16 συνδέσεις για να κατεβάσει το Xcode 3-5 φορές ταχύτερα απο το URLSession. Συμπεριλαμβάνεται ως εκτελέσιμο μαζί τον πηγαίο του κώδικα στο Xcodes ώστε να συμμορφώνεται με την άδεια χρήσης του GPLv2.\n\nΤο URLSession είναι το προεπιλεγμένο API για την εκτέλεση των URL requests." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "[aria2](https://github.com/aria2/aria2) uses up to 16 connections to download Xcode 3-5x faster than URLSession. It's bundled as an executable along with its source code within Xcodes to comply with its GPLv2 license.\n\nURLSession is the default Apple API for making URL requests." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "[aria2](https://github.com/aria2/aria2) usa hasta 16 conexiones para descargar Xcode de 3 a 5 veces más rápido que URLSession. Se incluye como un ejecutable junto con su código fuente dentro de Xcodes para cumplir con su licencia GPLv2.\n\nURLSession es la API predeterminada de Apple para realizar solicitudes de URL." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "[aria2](https://github.com/aria2/aria2) käyttää jopa 16 yhteyttä ladatakseen Xcoden 3–5 kertaa nopeammin kuin URLSession. Se on niputettu suoritettavaksi tiedostoksi ja sen lähdekoodiin Xcodesissa GPLv2-lisenssin noudattamiseksi.\n\nURLSession on Applen oletussovellusliittymä URL-pyyntöjen tekemiseen.." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "[aria2](https://github.com/aria2/aria2) utilise jusqu'à 16 connexions pour télécharger Xcode de 3 à 5 fois plus rapidement que URLSession. [aria2](https://github.com/aria2/aria2) est fourni sous forme d'exécutable avec son code source dans Xcodes pour se conformer à sa licence GPLv2.\n\nURLSession est l'API d'Apple utilisée par défaut pour effectuer des requêtes d'URL." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "[aria2](https://github.com/aria2/aria2) URLSession की तुलना में Xcode 3-5x तेजी से डाउनलोड करने के लिए 16 कनेक्शन तक का उपयोग करता है। इसे अपने GPLv2 लाइसेंस का अनुपालन करने के लिए Xcodes के भीतर अपने स्रोत कोड के साथ एक निष्पादन योग्य के रूप में बंडल किया गया है।\n\nURL अनुरोध करने के लिए URLSession डिफ़ॉल्ट Apple API है।" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "[aria2](https://github.com/aria2/aria2) usa fino a 16 connessioni per scaricar Xcode da 3 a 5 volte più veloce di URLSession. E' incluso come eseguibile assieme ai suoi sogenti dentro Xcodes per rispettare la sua licenza GPLv2. \n\nURLSession è l'API di default di Apple per fare richieste a URL remote." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "[aria2](https://github.com/aria2/aria2) は、最大16個の接続を使用して、Xcode を URLSession の3-5 倍のスピードでダウンロードします。GPLv2 ライセンスに準拠するため、Xcodes 内にソースコードと一緒に実行ファイルとしてバンドルされています。\n\nURLSession は、URLリクエストを行うための Apple のデフォルト API です。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "[aria2](https://github.com/aria2/aria2)는 최대 16 개의 연결을 사용하여 URLSession보다 3~5배 더 빠르게 Xcode를 다운로드합니다. GPLv2 라이센스를 준수하기 위해 Xcodes 내 소스 코드와 함께 실행 파일 번들로 제공됩니다.\n\nURLSession은 URL 요청을 만들기 위한 기본 Apple API입니다." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "[aria2](https://github.com/aria2/aria2) gebruikt tot 16 verbindingen om Xcode 3-5x sneller te downloaden dan met URLSession. Het is gebundeld als een executable samen met de broncode binnen Xcodes om te voldoen aan de GPLv2 licentie.\n\nURLSession is de standaard Apple API voor het maken van URL verzoeken." } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "[aria2](https://github.com/aria2/aria2) używa do pobierania do 16 połączeń, co pozwala na pobieranie Xcode 3-5x szybciej niż URLSession. Jest on dostarczany jako plik wykonywalny wraz ze swoim kodem źródłowym w ramach Xcodes w celu przestrzegania jego licencji GPLv2.\n\nURLSession to domyślne API Apple do tworzenia żądań URL." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "[aria2](https://github.com/aria2/aria2) usa até 16 conexões para baixar o Xcode 3-5x mais rápido que URLSession. Está empacotado como um executável junto com o seu código fonte dentro do Xcodes para conformar com a licensa GPLv2.\n\nURLSession é a API padrão da Apple para performar requisições URL." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "[aria2](https://github.com/aria2/aria2) использует до 16 подключений для загрузки Xcode в 3-5 раз быстрее, чем URLSession. Он поставляется в виде исполняемого файла вместе с исходным кодом в Xcodes, чтобы соответствовать лицензии GPLv2.\n\nURLSession — это API Apple по умолчанию для выполнения запросов по сети." } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "[aria2](https://github.com/aria2/aria2) ใช้การเชื่อมต่อสูงสุด 16 ทาง เพื่อดาวน์โหลด Xcode ที่เร็วกว่า URLSession 3-5 เท่า มันถูกรวมเป็นไฟล์ปฏิบัติการพร้อมกับซอร์สโค้ดภายใน Xcodes เพื่อให้สอดคล้องกับใบอนุญาต GPLv2\n\nURLSession คือ API ค่าเริ่มต้นของ Apple สำหรับการส่ง URL requests" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "[aria2](https://github.com/aria2/aria2) 16ya kadar bağlantı kullanarak Xcode'u URLSession'a göre 3-5 kat daha hızlı indirir. Çalıştırılabilir bir dosya olarak Bundle'a dahildir ve kaynak dosyaları GPLv2 lisansıyla uyumlu olması açısından Xcode içerisinde yer alır.\n\nURLSession Apple'ın URL istekleri oluşturmak için sunduğu varsayılan API'dır." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "[aria2](https://github.com/aria2/aria2) може використовувати до 16 з'єднань, завантажуючи Xcode у 3-5 разів швидше ніж URLSession. Вона поставляється у вигляді бінарника та коду, відповідно до вимог її GPLv2 ліцензії.\n\nURLSession — це завантажувач за-умовчанням від Apple" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "[aria2](https://github.com/aria2/aria2)使用最高16线程,能提供3至5倍的下载速度。它与它的源代码被一同打包在Xcodes中以符合GPLv2授权。\n\nURLSession是用于发起URL请求的默认Apple API。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "[aria2](https://github.com/aria2/aria2) 可利用多達 16 條連線來下載 Xcode,相較使用 URLSession 快達 3~5 倍。Xcodes 的套件內容中包含了 [aria2](https://github.com/aria2/aria2) 執行檔及其原始碼,以遵循其使用的 GPLv2 授權條款。\n\nURLSession 是系統內建用於發起 URL 連線請求的 Apple API。" } } } }, "Downloading" : { "comment" : "Installation Steps", "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "Downloading" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Descarregant" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Herunterladen" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Λήψη" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Downloading" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Descargando" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Ladataan" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Téléchargement" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "डाउनलोड" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Scaricando" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ダウンロードしています" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "다운로드 중" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Downloaden" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Pobieranie" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Baixando" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Скачивание" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "กำลังดาวน์โหลด" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "İndiriliyor" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Викачування" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "正在下载" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "下載中" } } } }, "DownloadingError" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "لم يتم العثور على معلومات التنزيل" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Cap informació de la descàrrega trobada" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Keine Download-Informationen gefunden" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Δεν βρέθηκαν πληροφορίες λήψης" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "No download information found" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "No se encontró información de descarga" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Lataustietoja ei löytynyt" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Aucune information de téléchargement trouvée" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कोई डाउनलोड जानकारी नहीं मिली" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Non trovo informazioni sul download" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ダウンロードに関する情報が見つかりませんでした。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "다운로드 정보를 찾을 수 없음" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Geen download informatie gevonden" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Nie znaleziono informacji o pobieraniu" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Nenhuma informação de download encontrada" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Информация о загрузке не найдена" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ไม่พบข้อมูลของการดาวน์โหลด" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "İndirme bilgisi bulunamadı" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Не знайдено інформації для викачування" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "没有找到下载信息" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "找不到下載相關資訊" } } } }, "DownloadingPercentDescription" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "التنزيل: اكتمل %d%%" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Descarregant: %d%% completat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Download: %d%% vollständig" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Λήψη: %d%% ολοκλήρωση" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Downloading: %d%% complete" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Descargando: %d%% completado" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Lataa: %d%% valmis" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Téléchargement : %d%% téléchargé" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "डाउनलोड: %d%% पूर्ण" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Download: %d%% completato" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ダウンロード中: %d%%" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "다운로드 중: %d%% 완료" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Downloaden: %d%% compleet" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Pobieranie: %d%% ukończone" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Baixando: %d%% finalizado" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Скачивание: %d% завершено" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "กำลังดาวน์โหลด: %d%% เสร็จแล้ว" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "İndirme: %d%% tamamlandı" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Викачано: %d%%" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "正在下载:已完成%d%%" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "下載中:已完成 %d%%" } } } }, "Downloads" : { "comment" : "Download Preference Pane", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "التحميلات" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Downloads" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Downloads" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Λήψεις" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Descargas" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Lataukset" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Téléchargements" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "डाउनलोड" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Downloads" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ダウンロード" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "다운로드" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Downloads" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Pobieranie" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Downloads" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Загрузки" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ดาวน์โหลด" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "İndirilenler" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Завантаження" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "下载" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "下載" } } } }, "DownloadSize" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "حجم التنزيل" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Tamany de descàrrega" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Download-Größe" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Μέγεθος λήψης" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Download Size" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Tamaño de descarga" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Latauskoko" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Taille de téléchargement" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "डाउनलोड आकार" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Dimensione Download" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "サイズ" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "다운로드 크기" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Download Grootte" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Wielkość pobierania" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Tamanho do download" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Размер загрузки" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ขนาดที่ต้องดาวน์โหลด" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "İndirme Boyutu" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Розмір завантаження" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "下载文件大小" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "下載大小" } } } }, "EnableNotifications" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "تمكين الإخطارات" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Activar les notificacions" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Benachrichtigungen einschalten" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Ενεργοποίηση Γνωστοποιήσεων" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Enable Notifications" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Permitir notificaciones" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Salli ilmoitukset" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Activer les Notifications" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सूचनाएँ सक्षम करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Abilita Notifiche" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "通知を有効にする" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "알림 활성화" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Notificaties Inschakelen" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Włącz powiadomienia" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Ativar notificações" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Включить уведомления" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "เปิดการแจ้งเตือน" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Bildirimleri Etkinleştir" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Увімкнути Сповіщення" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "启用通知" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "啟用通知" } } } }, "EnterDigitCodeDescription" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "أدخل الرمز المكون من %1$d الذي تم إرساله إلى %2$@:" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Introdueix el codi de %d dígits enviat a %@: " } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Gib den %1$d-stelligen Code ein der an '%2$@' gesendet wurde." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Εισάγετε τον %1$d-ψήφιο κωδικού που εστάλη στο %2$@:" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Enter the %1$d digit code sent to %2$@: " } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ingrese el código de dígito %1$d enviado a %2$@: " } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Anna %1$d numeroinen koodi, joka lähetettiin osoitteeseen %2$@: " } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Entrez le code à %1$d chiffres envoyé à %2$@ : " } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "%2$@ को भेजा गया %1$d अंक कोड दर्ज करें: " } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Inserisci il codice di %d cifre inviato a %@: " } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "%1$d桁のコードを%2$@に送信したので入力してください。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "%2$@(으)로 전송된 %1$d 자리 코드를 입력하세요." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Voer de %1$d code in die is verstuurd naar %2$@: " } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Wprowadź %1$d-cyfrowy kod wysłany do %2$@: " } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Insira o código de %1$d dígitos enviado para %2$@: " } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Введите %1$d цифровой код, отправленный на %2$@: " } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ป้อนรหัส %1$d หลักที่ส่งไปที่ %2$@:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "%2$@ kaynağından gönderilen %1$d rakamlı kodu gir: " } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Введіть %1$d-значний код відправлений на %2$@:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "请输入%1$d位代码,已发送到%2$@:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "請輸入 %1$d 位數密碼,已傳送至 %2$@: " } } } }, "Error" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "خطأ" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Error" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Error" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Σφάλμα" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Error" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Error" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Error" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Erreur" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Error" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Error" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Error" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "오류" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Error" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Error" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Erro" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Ошибка" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ข้อผิดพลาด" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Error" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Помилка" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "错误" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "錯誤" } } } }, "Experiments" : { "comment" : "Experiment Preference Pane", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "التجارب" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Experiments" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Experimente" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Πειράματα" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Experimentos" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Kokeilut" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Expérimentations" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "परीक्षण" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Sperimentale" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "試験的な機能" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "실험실" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Experimenten" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Eksperymenty" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Experimentos" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Эксперименты" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "การทดลอง" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Deneyler" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Експерименти" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "实验性功能" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "實驗性功能" } } } }, "FasterUnxip" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "فك الضغط بشكل أسرع" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Unxip més ràpid" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Schnellerer Unxip" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Ταχύτερο Unxip" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Faster Unxip" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Unxip más rápido" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Nopeampi Unxip" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Unxip Plus Rapide" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "तेज़ अनज़िप" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Unxip veloce" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "高速な Unxip" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "더 빠른 Unxip" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Snellere Unxip" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Szybsze Unxip" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Unxip mais rápido" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Быстрый Unxip" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "Unxip เร็วขึ้น" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Daha Hızlı Unxip" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Швидкий Unxip" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "更快的xip解压" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "更快的 Unxip" } } } }, "FasterUnxipDescription" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "بفضل [@_saagarjha](https://twitter.com/_saagarjha)، يمكن لهذه التجربة زيادة سرعة فك الضغط بنسبة تصل إلى 70% لبعض الأنظمة.\n\nيمكن الاطلاع على مزيد من المعلومات حول كيفية تحقيق ذلك في unxip repo - https://github.com/saagarjha/unxip" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Gràcies a [@_saagarjha](https://twitter.com/_saagarjha), aquest experiment pot incrementar la velocitat de l'unxip en un 70% en alguns sistemes.\n\nMés informació sobre com s'aconsegueix pot ser consultada en el seu repositori - https://github.com/saagarjha/unxip" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Dank an [@_saagarjha](https://twitter.com/_saagarjha), dieses Experiment kann die Unxipping-Geschwindigkeit bis zu 70% auf einigen Systemen beschleunigen.\n\nMehr Informationen wie dies erreicht wird sind über das Unxip-Repo erhältlich - https://github.com/saagarjha/unxip" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Χάριν στο χρήστη [@_saagarjha](https://twitter.com/_saagarjha), αυτή η πειραματική λειτουργία μπορεί να αυξήσει την ταχύτητα αποσυμπίεσης (unxipping) έως και 70% σε κάποια συστήματα.\n\nΓια περισσότερες πληροφορίες σχετικά με το πως επιτυγχάνεται αυτό μπορείτε να επισκεφθείτε το unxip repo - https://github.com/saagarjha/unxip" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Thanks to [@_saagarjha](https://twitter.com/_saagarjha), this experiment can increase unxipping speed by up to 70% for some systems.\n\nMore information on how this is accomplished can be seen on the unxip repo - https://github.com/saagarjha/unxip" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Gracias a [@_saagarjha](https://twitter.com/_saagarjha), este experimento puede aumentar la velocidad de liberación hasta en un 70 % para algunos sistemas.\n\nPuede ver más información sobre cómo se logra esto en el repositorio de unxip: https://github.com/saagarjha/unxip" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "[@_saagarjha](https://twitter.com/_saagarjha)n ansiosta tämä kokeilu voi nostaa purkamisnopeutta jopa 70 % joissakin järjestelmissä.\n\nLisätietoja siitä, miten tämä tehdään, on unxip-repossa - https://github.com/saagarjha/unxip" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Grâce à [@_saagarjha](https://twitter.com/_saagarjha), cette expérimentation peut augmenter jusqu'à 70% la vitesse de décompression pour certains systèmes.\n\nPour plus d'informations sur la façon dont cela est accompli, consultez le dépôt GitHub unxip - https://github.com/saagarjha/unxip" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "[@_saagarjha](https://twitter.com/_saagarjha) के लिए धन्यवाद, यह प्रयोग कुछ प्रणालियों के लिए 70% तक की गति को बढ़ा सकता है।\n\nइसे कैसे पूरा किया जाता है, इस बारे में अधिक जानकारी अनएक्सआईपी रेपो पर देखी जा सकती है - https://github.com/saagarjha/unxip" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Grazie a [@_saagarjha](https://twitter.com/_saagarjha), questa versione sperimentale può incrementare la velocità di unxipping fino al 70% in certi sistemi.\n\nMaggiori informazioni su come questo è possibile sono disponibili nel repository unxip - https://github.com/saagarjha/unxip" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "[@_saagarjha](https://twitter.com/_saagarjha) さんの方法で、一部のシステムで Unxip の速度が最大70%向上します。\n\n方法の詳細については、 unxipのリポジトリ (https://github.com/saagarjha/unxip)をご覧ください。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "[@_saagarjha](https://twitter.com/_saagarjha)님 덕분에 이 실험 기능을 이용하면 일부 시스템에서 압축 해제 속도를 최대 70%까지 향상시킬 수 있습니다.\n\n이를 수행하는 방법에 대한 자세한 내용은 unxip 저장소 (https://github.com/saagarjha/unxip)에서 확인할 수 있습니다." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Dankzij [@_saagarjha](https://twitter.com/_saagarjha), kan met dit experiment het uitpakken van Xcode tot 70% sneller gaan op sommige systemen.\n\nMeer informatie over hoe dit mogelijk wordt gemaakt kan worden gevonden op de unxip repo - https://github.com/saagarjha/unxip" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Dzięki [@_saagarjha](https://twitter.com/_saagarjha), wersja eksperymentalna może zwiększyć prędkość rozpakowywania o nawet 70% dla niektórych systemów.\n\nWięcej informacji na temat tego, jak to jest osiągane, można znaleźć w repozytorium unxip - https://github.com/saagarjha/unxip" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Graças à [@_saagarjha](https://twitter.com/_saagarjha), esse experimento pode acelerar o unxip em até 70% para algum sistemas.\n\nMais informações sobre como isso ocorre pode ser encontrada no repositório do unxip - https://github.com/saagarjha/unxip" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Благодаря [@_saagarjha](https://twitter.com/_saagarjha) этот эксперимент может увеличить скорость распаковки до 70% на некоторых системах.\n\nДополнительную информацию о том, как достигается такой результат, можно прочесть в репозитории unxip — https://github.com/saagarjha/unxip. " } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ขอขอบคุณ [@_saagarjha](https://twitter.com/_saagarjha) การทดลองนี้สามารถเพิ่มความเร็วในการ unxipping ได้มากถึง 70% สำหรับบางระบบ\n\nดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีการดำเนินการนี้ได้ที่ repo ของ unxip - https://github.com/saagarjha/unxip" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "[@_saagarjha](https://twitter.com/_saagarjha) sayesinde bu deney bazı sistemlerde unxip sürecini %70'e kadar hızlandırabilir.\n\nBu işlemin nasıl yapıldığına dair ayrıntılara unxip repo .- https://github.com/saagarjha/unxip adresinden bakabilirsiniz" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Завдяки [@_saagarjha](https://twitter.com/_saagarjha), цей експеримент може пришвидшити розпаковку майже на 70%. \n\nПодробиці про unxip тут — https://github.com/saagarjha/unxip" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "感谢[@_saagarjha](https://twitter.com/_saagarjha),此实验性功能可在部分系统上将xip解压速度提高70%。\n\n请从此仓库获取关于其实现细节的信息:https://github.com/saagarjha/unxip" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "感謝 [@_saagarjha](https://twitter.com/_saagarjha) 的努力,這個試驗版本可以在某些系統上加快 70% 的解壓縮速度。\n\n更多關於這項成就是如何達成的資訊,請參閱 unxip 的 repo - https://github.com/saagarjha/unxip" } } } }, "Filter" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "تصفيه" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Filtre" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Filter" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Φίλτρο" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Filtro" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Suodata" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Filtre" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "फ़िल्टर" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Filtra" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "フィルタ" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "필터" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Filter" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Filtr" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Filtrar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Фильтр" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "คัดกรอง" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Filtre" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Фільтр" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "过滤" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "過濾" } } } }, "FilterArchitecturesDescription" : { }, "FilterAvailableDescription" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "تصفية الإصدارات المتاحة" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Filtrar versions disponibles" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Verfügbare Versionen filtern" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Φιλτράρισμα διαθέσιμων εκδόσεων" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Filter available versions" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Filtrar versiones disponibles" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Suodata saatavilla olevat versiot" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Filtrer les versions disponibles" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "उपलब्ध संस्करणों को फ़िल्टर करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Filtra versioni disponibili" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "利用可能なバージョンのフィルタを適用する" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "이용 가능한 버전만 보기" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Filter beschikbare versies" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Filtruj dostępne wersje" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Filtrar versões disponíveis" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Отфильтровать доступные версии" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "คัดกรองเวอร์ชันที่พร้อมใช้งาน" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Uygun sürümleri filtrele" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Фільтрувати доступні версії" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "过滤可用版本" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "顯示可用的版本" } } } }, "FilterInstalledDescription" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "تصفية الإصدارات المثبتة" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Filtrar versions instal·lades" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Installierte Versionen filtern" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Φιλτράρισμα εγκατεστημένων εκδόσεων" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Filter installed versions" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Filtrar versiones instaladas" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Suodata asennetut versiot" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Filtrer les versions installées" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "स्थापित संस्करणों को फ़िल्टर करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Filtra versioni installate" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "インストール済みバージョンのフィルタを適用する" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "설치한 버전만 보기" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Filter geïnstalleerde versies" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Filtruj zainstalowane wersje" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Filtrar versões instaladas" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Отфильтровать установленные версии" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "คัดกรองเวอร์ชันที่ติดตั้ง" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yüklü sürümleri filtrele" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Фільтрувати інстальовані версії" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "过滤已安装版本" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "顯示已安裝的版本" } } } }, "Finishing" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "الانتهاء" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Acabant" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Abschließen" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Γίνεται ολοκλήρωση" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Finishing" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Finalizando" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Viimeistellään" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Finition" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "परिष्करण" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Completando" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "終了しています" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "마무리하는 중" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Finishing" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Kończenie" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Finalizando" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Завершение" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "กำลังจะเสร็จสิ้น" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Tamamlanıyor" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Завершення" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "正在完成" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "即將完成" } } } }, "General" : { "comment" : "General Preference Pane", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "عام" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "General" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Allgemein" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Γενικές" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "General" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Yleisasetukset" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Général" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सामान्य" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Generale" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "一般" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "일반" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Algemeen" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Ogólne" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Ajustes" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Общее" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ทั่วไป" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Genel" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Загальне" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "通用" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "一般" } } } }, "GithubRepo" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "مستودع GitHub" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Repositori de GitHub" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "GitHub-Repo" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "GitHub Repo" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "GitHub Repo" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "GitHub Repositorio" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "GitHub Repo" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Dépôt GitHub" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "GitHub रेपो" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "GitHub Repo" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "GitHub" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "GitHub 저장소" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "GitHub Repo" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Repozytorium GitHub" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Repositório GitHub" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Репозиторий GitHub" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "GitHub Repo" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "GitHub Repo" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "GitHub репозиторій" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "GitHub仓库" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "GitHub Repo" } } } }, "HelperClient.error" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "غير قادر على التواصل مع المساعد المميز." } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Incapaç de comunicar-se amb el servei privilegiat." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Kommunikation mit privilegiertem Helfer nicht möglich." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Αδυναμία επικοινωνίας με το βοηθητικό εργαλείο." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Unable to communicate with privileged helper." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "No se puede comunicar con el Asistente privilegiado." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Ei pysty kommunikoimaan etuoikeutetun avustajan kanssa." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Impossible de communiquer avec l'assistant privilégié." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "विशेषाधिकार प्राप्त सहायक के साथ संवाद करने में असमर्थ।" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Non riesco a comunicare con l'aiutante privilegiato." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "権限のあるヘルパーと通信できません。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "권한을 가진 도우미 (Privileged helper)와의 통신할 수 없습니다." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Kan niet communiceren met de privileged helper." } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Nie można komunikować się z helperem z uprawnieniami." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Não foi possível se comunicar com o ajudante." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Невозможно установить связь с привилегированным помощником." } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ไม่สามารถติดต่อกับตัวช่วยที่มีสิทธิพิเศษได้" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yardımcı araç ile iletişim kurulamadı." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Помилка комунікації з \"Privileged Helper\"." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "无法与提权帮助程序通信。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "無法與具有權限的輔助程式通訊。" } } } }, "HelperInstalled" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "تم تثبيت المساعد" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "El servei està instal·lat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Helfer ist installiert" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Το βοηθητικό εργαλείο δεν είναι εγκατεστημένο" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Helper is installed" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "El Asistente está instalado" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Apulainen on asennettu" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "L'assistant est installé" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सहायक स्थापित है" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Aiutante è installato" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "インストール済み" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "도우미 설치됨" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Helper is geïnstalleerd" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Helper jest zainstalowany" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Ajudante está instalado" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Помощник установлен" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ตัวช่วยถูกติดตั้งแล้ว" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yardımcı yüklendi" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Helper інстальовано" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "帮助程序已安装" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "已安裝輔助程式" } } } }, "HelperNotInstalled" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "لم يتم تثبيت المساعد" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "El servei no està instal·lat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Helfer ist nicht installiert" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Το βοηθητικό εργαλείο δεν είναι εγκατεστημένο" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Helper is not installed" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "El Asistente no está instalado." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Apulaista ei ole asennettu" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "L'assistant n'est pas installé" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सहायक स्थापित नहीं है" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Aiutante non è installato" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "未インストール" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "도우미 설치되지 않음" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Helper is niet geïnstalleerd" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Helper nie jest zainstalowany" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Ajudante não está instalado" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Помощник не установлен" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ตัวช่วยยังไม่ถูกติดตั้ง" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yardımcı yüklenmedi" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Helper не інстальовано" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "帮助程序未安装" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "尚未安裝輔助程式" } } } }, "IdenticalBuilds" : { "comment" : "Info Pane", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "بنيات متطابقة" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Builds idèntiques" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Identische Builds" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Πανομοιότυπες Εκδόσεις" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Identical Builds" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Compilaciones Idénticas" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Identtiset rakenteet" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Versions identiques" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Identical Builds" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Build Identiche" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ビルドの同一性" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "동일한 빌드" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Identieke Builds" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Takie same wersje" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Builds idênticos" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Идентичные выпуски" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "Builds ที่เหมือนกัน" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Benzer Yapılar" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Ідентичні Білди" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "重复版本" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "相同的建置版本" } } } }, "IdenticalBuilds.help" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "في بعض الأحيان يكون الإصدار التجريبي وإصدار الإصدار هو نفس الإصدار تمامًا. ستعرض Xcodes هذه الإصدارات معًا تلقائيًا." } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "A vegades alguna versió provisional i algna versió definitiva son exactament iguals. L'Xcodes mostrarà automàticament aquestes versions conjuntament." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Manchmal sind Prerelease- and Release-Version der exakt gleich Build. Xcodes zeigt diese beiden Versionen automatisch zusammen an." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Κάποιες φορές οι προεκδόσεις και εκδόσεις κυκλοφορίας είναι ακριβώς ίδιες. Το Xcodes θα εμφανίσει αυτόματα αυτές τις εκδόσεις μαζί." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Sometimes a prerelease and release version are the exact same build. Xcodes will automatically display these versions together." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "A veces, una versión preliminar y una versión de lanzamiento tienen exactamente la misma compilación. Xcodes mostrará automáticamente estas versiones juntas." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Joskus esijulkaisu ja julkaisuversio ovat täsmälleen sama rakenne. Xcodes näyttää nämä versiot automaattisesti yhdessä." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Parfois, la version préliminaire et la version finale sont exactement la même version. Xcodes affichera automatiquement ces versions ensemble." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कभी-कभी एक पूर्व-रिलीज़ और रिलीज़ संस्करण एक ही बिल्ड होते हैं। Xcodes स्वचालित रूप से इन संस्करणों को एक साथ प्रदर्शित करेगा।" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "A volte una versione di prerelease e release sono esattamente la stessa build. Xcodes mostrerà automatiamente queste versioni assieme." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "プレリリース版とリリース版が全く同じビルドであることがあります。Xcodesはこれらのバージョンを自動的に一緒に表示します。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "가끔 사전 출시 (prerelease) 버전과 출시 버전이 완전히 동일한 빌드인 경우가 있습니다. Xcodes는 자동으로 이 버전들을 함께 표시합니다." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Soms zijn een prerelease en release versie exact hetzelfde. Xcodes toont deze versies automatisch samen." } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Czasami wersja przedpremierowa i wydana są dokładnie takie same. Xcodes automatycznie wyświetla te wersje razem." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "As vezes, uma versão pré-lançamento e uma versão de lançamento são exatemente o mesmo build. Xcodes mostrará essas versões juntas automaticamente." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Иногда предварительная и релизная версии представляют собой один и тот же выпуск. Xcodes автоматически отображает эти версии вместе." } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "บางครั้งเวอร์ชันก่อนวางจำหน่ายและเวอร์ชันวางจำหน่ายจะเป็นรุ่นเดียวกัน Xcodes จะแสดงเวอร์ชันเหล่านี้พร้อมกันโดยอัตโนมัติ" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : " Bazen prerelase ve release sürümleri aynı yapı üzerindedir. Xcodes bu sürümleri birarada gösterecektir." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Буває що RC та Release насправді ідентичні (однаковий Build). У такому випадку Xcodes буде показувати обидві версії разом." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "有时预发布版本会与正式版使用相同的Build号。Xcodes会自动将这些版本显示为一个。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "有時候預先發行版與正式版的建置版本號會相同,Xcodes 會自動將這些版本整理在一起。" } } } }, "IncludePreRelease" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "تضمين الإصدارات التجريبية/التجريبية" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Incloure versions provisionals/beta" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Prerelease-/Beta-Versionen einschließen" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Συμπερίληψη προεκδόσεων/beta" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Include prerelease/beta versions" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Incluir versiones preliminares/beta" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Sisällytä esijulkaisu/beta versiot" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Inclure les versions préliminaires/bêta" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पूर्व-रिलीज़/बीटा संस्करण शामिल करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Includi versioni prerelease/beta" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "プレリリース版 / ベータ版を含む" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "사전 출시/베타 버전 포함" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Prerelease/beta versies toestaan" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Dołącz wersje beta/wstępne" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Incluir versōes pré-lançamento/beta" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Включить предварительные/бета-версии" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "รวมถึงเวอร์ชัน prerelease/beta " } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Prerelease/beta sürümlerini dahil et" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Також інсталювати RC / Beta версії" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "包含预发布/测试版本" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "包含預先發行及測試版" } } } }, "Install" : { "comment" : "Common", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "تثبيت" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Instal·lar" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Installieren" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Εγκατάσταση" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Install" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Instalar" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Asenna" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Installer" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "स्थापित करे" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Installa" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "インストール" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "설치" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Installeren" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Zainstaluj" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Instalar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Установить" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ติดตั้ง" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yükle" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Інсталювати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "安装" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "安裝" } } } }, "Install Apple Silicon" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "Install Apple Silicon" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Install Apple Silicon" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Install Apple Silicon" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Install Apple Silicon" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Install Apple Silicon" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Install Apple Silicon" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Install Apple Silicon" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Install Apple Silicon" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Install Apple Silicon" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Install Apple Silicon" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Install Apple Silicon" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Install Apple Silicon" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Install Apple Silicon" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Instalar Apple Silicon" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Install Apple Silicon" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ติดตั้ง Apple Silicon" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Install Apple Silicon" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Install Apple Silicon" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Install Apple Silicon" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Install Apple Silicon" } } } }, "Install Universal" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "Install Universal" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Install Universal" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Install Universal" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Install Universal" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Install Universal" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Install Universal" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Install Universal" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Install Universal" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Install Universal" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Install Universal" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Install Universal" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Install Universal" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Install Universal" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Instalar Universal" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Install Universal" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ติดตั้ง Universal" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Install Universal" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Install Universal" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Install Universal" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Install Universal" } } } }, "InstallationError.CodesignVerifyFailed" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "فشل التحقق من توقيع رمز Xcode الذي تم تنزيله بالإخراج التالي:\n%@" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "L'Xcode descarregat ha fallat en la verificació de la signatura de seguretat amb el següent missatge:\n%@" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Die heruntergeladene Version von Xcode hat die Code-Signing-Prüfung nicht bestanden, mit folgendem Hinweis:\n%@" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Αποτυχία επαλήθευσης ψηφιακής υπογραφής για το ληφθέν Xcode: \n%@" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "The downloaded Xcode failed code signing verification with the following output:\n%@" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "El Xcode descargado falló en la verificación de firma de código con el siguiente resultado:\n%@" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Ladattu Xcode versio epäonnistui koodin allekirjoitusvahvistuksessa seuraavalla lähdöllä:\n%@" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "La vérification de signature de code du Xcode téléchargé a échoué avec le résultat suivant :\n%@" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "डाउनलोड किया गया Xcode निम्नलिखित आउटपुट के साथ कोड हस्ताक्षर सत्यापन में विफल रहा:\n%@" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Xcode scaricato ha fallito le verifiche di firma con il seguente errore:\n%@" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ダウンロードしたXcodeは、以下の出力で署名の検証に失敗しました。\n%@" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "다운로드한 Xcode가 다음과 같이 코드 서명 확인에 실패했습니다.\n%@" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "De gedownloade Xcode is mislukt voor verificatie van code-ondertekening met de volgende melding:\n%@" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Pobrany Xcode nie przeszedł weryfikacji sygnatury kodu z wynikiem:\n%@" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "O Xcode baixado falhou a verificação de assinatura de código (code signing) com a seguinte saída:\n%@" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Загруженный Xcode не прошел проверку подписи сертификата со следующим выводом:\n%@" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "Xcode ที่ดาวน์โหลดมาล้มเหลวในการตรวจสอบการลงนามโค้ด โดยมีผลลัพธ์ดังต่อไปนี้:\n%@" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "İndirilmiş Xcode imza onayı işleminde şu hatayı verdi:\n%@" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Викачаний Xcode не зміг пройти перевірку підпису (code signing verification) з наступним повідомленням:\n%@" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "无法验证下载的Xcode版本的代码签名,并输出如下:\n%@" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "下載的 Xcode 由於以下原因無法驗證 Codesign 簽章\n%@" } } } }, "InstallationError.DamagedXIP" : { "comment" : "InstallationErrors", "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "الأرشيف \"%@\" تالف ولا يمكن توسيعه." } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "L'arxiu \"%@\" està malmès i no es pot expandir." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Das Archiv \"%@\" ist beschädigt und kann nicht entpackt werden." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Το αρχείο αρχειοθέτησης «%@» είναι κατεστραμμένο και δεν μπορεί να αποσυμπιεστεί." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "The archive \"%@\" is damaged and can't be expanded." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "El archivo \"%@\" está dañado y no se puede expandir." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Arkisto \"%@\" on vaurioitunut, eikä sitä voi laajentaa." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "L'archive \"%@\" est endommagée et ne peut pas être décompressée." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "संग्रह \"%@\" क्षतिग्रस्त है और इसे विस्तारित नहीं किया जा सकता है।" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "L'archivio \"%@\" è danneggiato e non può essere espanso." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "このアーカイブ \"%@\" は破損していて展開できません。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "아카이브 \"%@\"이(가) 손상되어 확장할 수 없습니다." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Het archief \"%@\" is beschadigd en kan niet worden uitgepakt." } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Archiwum \"%@\" jest uszkodzone i nie może zostać rozpakowane." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "O arquivamento \"%@\" está danificado e não pode ser expandido." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Архив \"%@\" поврежден и не может быть расширен." } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ไฟล์ \"%@\" เสียหายและไม่สามารถแตกไฟล์ได้" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "\"%@\" arşivi hasar görmüş ve açılamıyor." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Архів \"%@\" пошкоджено, його не можливо розпакувати." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "此压缩文件“%@”已损坏且无法解压。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "壓縮檔「%@」已經損壞並無法解壓縮。" } } } }, "InstallationError.FailedSecurityAssessment" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "فشل Xcode %@ في تقييم الأمان الخاص به بالنتيجة التالية:\n%@\nويظل مثبتًا في %@ إذا كنت ترغب في استخدامه على أي حال." } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "L'Xcode %@ ha fallat l'avaluació de seguretat amb el següent missatge:\n%@\nEs manté instal·lat a %@ Si desitges continuar de totes maneres." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Die Sicherheitsprüfung für Xcode %@ ist mit folgender Meldung gescheitert:\n%@\nXcode bleibt unter %@ installiert, für den Fall, dass es dennoch verwendet werden soll." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Το Xcode %@ απέτυχε κατά την αξιολόγηση ασφαλείας του με το ακόλουθο μήνυμα:\n%@\nΠαραμένει εγκατεστημένο στο %@ αν θέλετε να το χρησιμοποιήσετε παρ' όλα αυτά." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ failed its security assessment with the following output:\n%@\nIt remains installed at %@ if you wish to use it anyways." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ falló su evaluación de seguridad con el siguiente resultado:\n%@\nPermanece instalado en %@ si desea usarlo de todos modos." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Xcode versio %@ epäonnistui turvallisuusarvioinnissaan seuraavalla lähdöllä:\n%@\nSe pysyy asennettuna osoitteeseen %@, jos haluat käyttää sitä joka tapauksessa." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ a échoué à son évaluation de sécurité avec le résultat suivant :\n%@\nXcode reste installé à %@ si vous souhaitez l'utiliser quand même." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ निम्नलिखित आउटपुट के साथ अपने सुरक्षा मूल्यांकन में विफल रहा:\n%@\nयदि आप किसी भी तरह इसका उपयोग करना चाहते हैं तो यह %@ पर स्थापित रहता है।" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ ha fallito i check di sicurezza con il seguente errore:\n%@\nRimane installato in %@ se vuoi usarlo comunque." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ は、以下の出力でセキュリティ評価に失敗しました。\n%@\nそれでも使用したい場合は、%@にインストールされたままになっています。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@이(가) 다음과 같이 보안 평가에 실패했습니다.\n%@\n그래도 사용하려는 경우 %@에 설치된 상태로 유지됩니다." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "De Xcode %@ veiligheidsbeoordeling was onsuccesvol en gaf de volgende melding:\n%@\nXcode blijft geïnstalleerd op %@ als je deze toch wilt gebruiken." } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ nie przeszedł testu bezpieczeństwa z następującym wynikiem:\n%@\nPozostaje zainstalowany w %@, jeśli mimo to chcesz go używać." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ falhou suas checagens de segurança com a seguinte saída:\n%@\nAinda está instalado em %@ se você deseja usar ainda assim." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ не прошел оценку безопасности со следующим выводом:\n%@\nОн остается установленным в %@, если вы все равно хотите его использовать." } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ ไม่ผ่านการประเมินความปลอดภัย โดยมีผลลัพธ์ดังนี้:\n%@\nระบบจะติดตั้งไว้ที่ %@ หากคุณต้องการใช้งานต่อไป" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ güvenlik görevlerini gerçekleştirirken şu hatayı verdi:\n%@\nYine de devam etmek isterseniz uygulama %@ konumunda bulunmaya devam ediyor." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Xcode не пройшов перевірку безпеки (security assessment) з наступним повідомленням:\n%@\n\nВін залишиться інстальованим в %@, на випадок якщо ви забажаєте його використовувати." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@无法完成安全评估,并输出如下:\n%@\n但它依然被安装到了%@如果您依然想使用它的话。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "由於以下原因無法安全驗證 Xcode %@:\n%@\n如果你執意使用,該版本仍然安裝於 %@。" } } } }, "InstallationError.FailedToMoveXcodeToApplications" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "فشل في نقل Xcode إلى الدليل %@." } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "No s'ha pogut moure l'Xcode al directori %@." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Das Bewegen von Xcode in das %@-Verzeichnis ist nicht möglich." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Αδυναμία μετακίνησης του Xcode στον φάκελο %@." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Failed to move Xcode to the %@ directory." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "No se pudo mover Xcode al directorio %@." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Xcoden siirtäminen hakemistoon %@ epäonnistui." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Impossible de déplacer Xcode vers le répertoire %@." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Xcode को %@ निर्देशिका में ले जाने में विफल।" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Non sono riuscito a spostare Xcode nella directory %@ ." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Xcode を %@ に移動できませんでした。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Xcode를 %@ 디렉토리로 이동하지 못했습니다." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Het is mislukt om Xcode te verplaatsen naar de %@ map." } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Nie udało się przenieść Xcode do katalogu %@." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Falha ao mover Xcode para o diretório: %@" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Не удалось переместить Xcode в директорию %@." } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ไม่สามารถย้าย Xcode ไปยังไดเร็กทอรี %@ ได้" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ klasörüne taşınamadı." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Помилка при переміщенні Xcode до %@." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "无法将Xcode移动到目录%@。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "無法將 Xcode 移動至 %@ 目錄。" } } } }, "InstallationError.InvalidVersion" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "%@ ليس رقم إصدار صالح." } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "%@ no és un número de versió vàlid." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "%@ ist keine gültige Versionsnummer." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Η έκδοση %@ δεν είναι έγκυρη." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "%@ is not a valid version number." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "%@ no es un número de versión válido." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "%@ ei ole kelvollinen versionumero." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "%@ n'est pas un numéro de version valide." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "%@ मान्य संस्करण संख्या नहीं है।" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "%@ non è una versione valida." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "%@ は有効なバージョン番号ではありません。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "%@은(는) 유효한 버전 번호가 아닙니다." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "%@ is geen geldig versie nummer." } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "%@ nie jest prawidłowym numerem wersji." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "%@ não é uma versão válida." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "%@ не является допустимым номером версии." } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "%@ ไม่ใช่หมายเลขเวอร์ชันที่ถูกต้อง" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "%@ geçerli bir sürüm numarası değil." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "%@ не є коректним номером версії." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "%@不是一个正确的版本。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "%@ 不是個正確的版本號碼。" } } } }, "InstallationError.MissingSudoerPassword" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "كلمة المرور مفقودة. حاول مرة اخرى." } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Falta la contrassenya. Si us plau, torna-ho a intentar." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Passwort fehlt. Bitte erneut versuchen." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Λείπει το συνθηματικό. Δοκιμάστε ξανά." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Missing password. Please try again." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Falta la contraseña. Inténtalo de nuevo." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Salasana puuttuu. Yritä uudelleen." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Mot de passe manquant. Veuillez réessayer." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पासवर्ड अनुपलब्ध है। कृपया पुन: प्रयास करें।" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Manca la password. Prova di nuovo." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "パスワードが見つかりません。もう一度お試しください。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "비밀번호가 입력되지 않았습니다. 다시 시도해 주세요." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Wachtwoord ontbreekt. Probeer het opnieuw." } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Brak hasła. Spróbuj ponownie." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Faltando senha. Por favor, tente novamente." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Отсутствует пароль. Повторите попытку." } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ยังขาดรหัสผ่านอยู่ กรุณาลองอีกครั้ง" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Eksik şifre. Lütfen tekrar deneyin" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Не вистачає паролю. Спробуйте ще раз." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "缺少密码。请再试一次。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "找不到密碼,請再試一次。" } } } }, "InstallationError.MissingUsernameOrPassword" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "اسم المستخدم أو كلمة المرور مفقودة. حاول مرة اخرى." } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Falta l'usuari o la contrassenya. Si us plau, torna-ho a intentar." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Benutzername oder ein Passwort fehlt. Bitte erneut versuchen." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Λείπει το όνομα χρήστη ή το συνθηματικό. Δοκιμάστε ξανά." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Missing username or a password. Please try again." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Falta el nombre de usuario o una contraseña. Inténtalo de nuevo." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Käyttäjätunnus tai salasana puuttuu. Yritä uudelleen." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Nom d'utilisateur ou mot de passe manquant. Veuillez réessayer." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "उपयोगकर्ता नाम या पासवर्ड गुम है। कृपया पुन: प्रयास करें।" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Mancano username o password. Prova di nuovo" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ユーザ名またはパスワードが見つかりません。もう一度お試しください。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "사용자 이름 또는 비밀번호가 입력되지 않았습니다. 다시 시도해 주세요." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Ontbrekende gebruikersnaam of wachtwoord. Probeer het opnieuw." } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Brak nazwy użytkownika lub hasła. Spróbuj ponownie." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Faltando usuário ou senha. Por favor, tente novamente." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Отсутствует имя пользователя или пароль. Пожалуйста, попробуйте еще раз." } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ยังขาดชื่อผู้ใช้หรือรหัสผ่านอยู่ กรุณาลองอีกครั้ง" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Eksik kullanıcı adı veya şifre. Lütfen tekrar deneyin." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Не вистачає імені користувача чи паролю. Спробуйте ще раз." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "缺少用户名或密码。请再试一次。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "找不到使用者名稱或是密碼,請再試一次。" } } } }, "InstallationError.NoNonPrereleaseVersionAvailable" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "لا توجد إصدارات غير تجريبية متاحة." } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Cap versió que no sigui provisional disponible." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Keine Nicht-Prerelease-Versionen verfügbar." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Καμία προέκδοση διαθέσιμη." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "No non-prerelease versions available." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "No hay versiones que no sean preliminares disponibles." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Julkaisua edeltäviä versioita ei ole saatavilla." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Aucune version non préliminaire disponible." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कोई गैर-पूर्व-रिलीज़ संस्करण उपलब्ध नहीं है।" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Non sono disponibili versioni non-prerelease." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "プレリリース以外の利用可能なバージョンがありません。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "사전 출시 버전 이외에 사용 가능한 버전이 없습니다." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Geen niet-prerelease versies beschikbaar." } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Brak wersji nie-przedpremierowych." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Nenhuma versão não-pré-lançamento disponível." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Нет доступных не предварительных версий." } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ไม่มีเวอร์ชันที่ไม่ใช่รุ่นก่อนวางจำหน่ายที่พร้อมใช้งาน" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Prerelease olmayan sürümler yok." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Немає доступних Release версій." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "没有非预发布版以外的其他可用版本。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "目前沒有可用的非預先發行版本。" } } } }, "InstallationError.NoPrereleaseVersionAvailable" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "لا توجد إصدارات تجريبية متاحة." } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Cap versió provisional disponible." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Keine Prerelease-Versions verfügbar." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Καμία προέκδοση διαθέσιμη." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "No prerelease versions available." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "No hay versiones preliminares disponibles." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Esijulkaisuversioita ei saatavilla." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Aucune version préliminaire disponible." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कोई पूर्व-रिलीज़ संस्करण उपलब्ध नहीं है।" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Non sono disponibili versioni prerelease." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "利用可能なプレリリースバージョンがありません。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "사용 가능한 사전 출시 버전이 없습니다." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Geen prerelease versies beschikbaar." } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Brak wersji przedpremierowych." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Nenhuma versão de pré-lançamento disponível." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Предварительные версии недоступны." } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ไม่มีเวอร์ชันก่อนวางจำหน่ายที่พร้อมใช้งาน" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Prerelease sürümleri yok." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Немає доступних RC версій." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "没有可用的预发布版本。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "目前沒有可用的預先發行版本。" } } } }, "InstallationError.NotEnoughFreeSpaceToExpandArchive" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "لا يمكن توسيع الأرشيف \"%@\" لأن المجلد الحالي لا يحتوي على مساحة خالية كافية.\n\nقم بتوفير مساحة أكبر لتوسيع الأرشيف ثم قم بتثبيت Xcode %@ مرة أخرى لبدء التثبيت من حيث توقف." } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "L'arxiu \"%@\" no es pot expandir perquè el volum actual no té suficient espai lliure.\n\nAllibera més espai disponible per a poder expandir l'arxiu i llavors torna a instal·lar l'Xcode %@ des d'on ho havia deixat." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Das Archiv \"%@\" kann nicht entpackt werden, da auf dem aktuellen Volume nicht genügend freier Speicherplatz verfügbar ist.\n\nBitte stelle mehr Platz zur Verfügung, um das Archiv zu entpacken und installiere danach Xcode %@ erneut, um die Installation von dort zu beginnen wo sie beendet wurde." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Το αρχείο αρχειοθέτησης «%@» δεν μπορεί να αποσυμπιεστεί καθώς δεν έχει αρκετός διαθέσιμος χώρος σε αυτόν τον τόμο." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "The archive \"%@\" can’t be expanded because the current volume doesn’t have enough free space.\n\nMake more space available to expand the archive and then install Xcode %@ again to start installation from where it left off." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "El archivo \"%@\" no se puede expandir porque el volumen/disco actual no tiene suficiente espacio libre.\n\nHaga más espacio disponible para expandir el archivo y luego instale Xcode %@ nuevamente para comenzar la instalación desde donde lo dejó." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Arkistoa \"%@\" ei voi laajentaa, koska nykyisessä taltiossa ei ole tarpeeksi vapaata tilaa.\n\nAnna lisää tilaa arkiston laajentamiseksi ja asenna sitten Xcode versio %@ uudelleen aloittaaksesi asennuksen siitä, mihin se jäi." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "L'archive \"%@\" ne peut pas être décompressée car le volume actuel n'a pas assez d'espace libre.\n\nLibérez plus d'espace pour décompresser l'archive, puis réinstallez Xcode %@ pour démarrer l'installation là où elle s'est arrêtée." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "संग्रह \"%@\" का विस्तार नहीं किया जा सकता क्योंकि वर्तमान वॉल्यूम में पर्याप्त रिक्त स्थान नहीं है.\n\nसंग्रह का विस्तार करने के लिए अधिक स्थान उपलब्ध कराएं और फिर Xcode %@ को फिर से स्थापित करें ताकि स्थापना वहीं से शुरू की जा सके जहां से इसे छोड़ा था।" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "L'archivio \"%@\" non può essere espanso perché non c'è abbastanza spazio libero nel volume.\n\nAumenta lo spazio disponibile per poter espandere l'archivio e poi installa Xcode %@ nuovamente per riprendere da dove era arrivato." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "このボリュームの空き容量が不足しているため、アーカイブ \"%@\" を展開できません。\n\n空き容量を増やしてから Xcode %@ のインストールをもう一度お試しください。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "현재 볼륨에 여유 공간이 충분하지 않기 때문에 아카이브 \"%@\"을(를) 확장할 수 없습니다.\n\n아카이브를 확장하기 위해 더 많은 공간을 확보한 다음 Xcode %@을(를) 다시 설치하여 중단된 위치에서 설치를 시작하세요." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Het archief \"%@\" kan niet worden uitgepakt omdat het huidige volume niet voldoende vrije schijf ruimte heeft.\n\nMaak meer ruimte beschikbaar om het archief uit te pakken en installeer dan Xcode %@ opnieuw om de installatie voort te zetten." } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Nie można rozpakować archiwum \"%@\", ponieważ wolumin nie ma wystarczającej ilości wolnego miejsca.\n\nZrób więcej miejsca, aby rozpakować archiwum, a następnie zainstaluj ponownie Xcode %@, aby kontynuować instalację od miejsca, w którym została wstrzymana." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "O arquivamento \"%@\" não pode ser expandido porquê o volume atual não possui espaço disponível o suficiente.\n\nLibere espaço para expandir o arquivamento e então instale o Xcode %@ novamente para iniciar uma instalação de onde você parou." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Архив \"%@\" не может быть расширен, поскольку в текущем жестком диске недостаточно свободного места.\n\nОсвободите больше места для расширения архива, а затем снова установите Xcode %@, чтобы начать установку с того места, где он остановился." } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ไม่สามารถขยายไฟล์ \"%@\" ได้ เนื่องจากพื้นที่ว่างในไดรฟ์ปัจจุบันไม่เพียงพอ\n\nเพิ่มพื้นที่ว่างเพื่อขยายไฟล์ แล้วติดตั้ง Xcode %@ อีกครั้งเพื่อเริ่มการติดตั้งต่อจากจุดที่ค้างไว้" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "\"%@\" arşivi açılamıyor çünkü diskte yeterli alan bulunmuyor.\n\nArşivi açmak için daha fazla alan aç ve ondan sonra kaldığın yerden devam ederek Xcode %@ sürümünü tekrar kur." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Архів \"%@\" не можливо розпакувати, так як бракує місця.\n\nЗвільніть файлове сховище щоб вистачило місця на розпаковку Архіву, та спробуйте інсталювати Xcode %@ знову." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "此压缩文件“%@”无法解压因为当前宗卷没有足够的可用空间。\n\n请空出更多空间并再次安装Xcode %@以从失败处继续。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "由於磁碟空間不足,無法將壓縮檔「%@」解壓縮。\n\n請騰出更多磁碟空間以完成解壓縮,並重新安裝 Xcode %@。安裝步驟將會從上次停住的地方繼續。" } } } }, "InstallationError.PostInstallStepsNotPerformed.Installed" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "اكتمل التثبيت، ولكن لم يتم تنفيذ بعض خطوات ما بعد التثبيت تلقائيًا. سيتم تنفيذ ذلك عند تشغيل Xcode %@ لأول مرة." } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "L'instal·lació ha finalitzat, però algunes tasques post instal·lació no s'han executat de manera automàtica. S'executaran la primera vegada que obris l'Xcode %@." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Die Installation ist abgeschlossen, allerdings wurden einige Post-Installationsschritte nicht automatisch ausgeführt. Diese werden beim ersten Start von Xcode %@ ausgeführt." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Η εγκατάσταση ολοκληρώθηκε, αλλά κάποια μετά-εγκαταστατικά βήματα δεν εκτελέστηκαν αυτόματα. Αυτά τα βήματα θα πραγματοποιηθούν κατά την πρώτη εκκίνηση του Xcode %@." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Installation was completed, but some post-install steps weren't performed automatically. These will be performed when you first launch Xcode %@." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "La instalación se completó, pero algunos pasos posteriores a la instalación no se realizaron automáticamente. Estos se realizarán cuando inicie Xcode por primera vez %@." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Asennus valmistui, mutta joitain asennuksen jälkeisiä vaiheita ei suoritettu automaattisesti. Nämä suoritetaan, kun käynnistät Xcoden version %@." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "L'installation est terminée, mais certaines étapes de post-installation n'ont pas été effectuées automatiquement. Celles-ci seront exécutées lors du premier lancement de Xcode %@." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "स्थापना पूर्ण हो गई थी, लेकिन कुछ पोस्ट-इंस्टॉलेशन चरण स्वचालित रूप से निष्पादित नहीं किए गए थे। ये तब किए जाएंगे जब आप पहली बार Xcode %@ लॉन्च करेंगे।" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "L'installazione è completata, ma alcuni comandi di post installazione non sono partiti automaticamente. Verranno lanciati quando aprirai Xcode %@ per la prima volta." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "インストールは完了しましたが、いくつかのインストール後のタスクが自動的に実行されませんでした。このタスクは Xcode %@ を初回起動した際に実行されます。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "설치가 완료되었지만 일부 설치 후 단계가 자동으로 수행되지 않았습니다. 이는 Xcode %@를 처음 실행할 때 수행됩니다." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "De installatie is voltooid, maar sommige post-installatie taken zijn niet uitgevoerd. Deze taken zullen worden uitgevoerd wanneer je Xcode %@ voor het eerst start." } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Instalacja została zakończona, ale niektóre kroki po instalacji nie zostały wykonane automatycznie. Zostaną one wykonane przy pierwszym uruchomieniu Xcode %@." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "A instalação foi completada, mas alguns passos de pós-instalação não puderam ser performados automaticamente. Estes serão performados quando você rodar o Xcode %@ pela primeira vez." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Установка завершена, но некоторые шаги после установки не были выполнены автоматически. Они будут выполнены при первом запуске Xcode %@." } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "การติดตั้งเสร็จสมบูรณ์แล้ว แต่บางขั้นตอนหลังการติดตั้งไม่ได้ดำเนินการโดยอัตโนมัติ ขั้นตอนเหล่านี้จะดำเนินการเมื่อคุณเปิด Xcode %@ ครั้งแรก" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "İndirme tamamlandı, fakat bazı indirme sonrası aşamaları tamamlanamadı. Bunlar Xcode %@ uygulamasını ilk açtığınızda gerçekleşecek." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Установку завершено. Але деякі post-install кроки не виконано автоматично. Ці кроки буде виконано коли ви вперше запустите Xcode %@." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "安装已完成,但一些安装后准备步骤没有被自动执行。它们将会在您第一次运行Xcode %@时执行。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "已完成安裝,但有些安裝後步驟未完成。這些步驟在首次啟動 Xcode %@ 時會自動執行。" } } } }, "InstallationError.PostInstallStepsNotPerformed.NotInstalled" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "اكتمل التثبيت، ولكن لم يتم تنفيذ بعض خطوات ما بعد التثبيت تلقائيًا. يقوم Xcodes بتنفيذ هذه الخطوات باستخدام مساعد مميز، والذي يبدو أنه لم يتم تثبيته. يمكنك تثبيته من التفضيلات > المتقدم.\n\nسيتم تنفيذ هذه الخطوات عند تشغيل Xcode %@ لأول مرة." } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "L'instal·lació ha finalitzat, però algunes tasques post instal·lació no s'han executat de manera automàtica. L'Xcodes executa aquestes tasques amb el servei privilegiat, que sembla que no està instal·lat. El pots instal·lar des de les Preferències > Avançat.\n\nAquestes tasques s'executaran la primera vegada que obris l'Xcode %@." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Die Installation ist abgeschlossen, allerdings wurden einige Post-Installationsschritte nicht automatisch ausgeführt. Xcodes führt diese Schritte mittels eines privilegierten Helfers aus, welcher aber nicht installiert zu sein scheint. Er kann über Einstellungen > Erweitert installiert werden. Diese Schritte werden beim ersten Start von Xcode %@ ausgeführt." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Η εγκατάσταση ολοκληρώθηκε, αλλά κάποια μετά-εγκαταστατικά βήματα δεν εκτελέστηκαν αυτόματα. Το Xcodes εκτελεί αυτά τα βήματα μέσω ενός εργαλείου με δικαιώματα, το οποίο φαίνεται ως μη εγκατεστημένο. Μπορείτε να το εγκαταστήσετε πηγαίνοντας στο «Ρυθμίσεις» > «Προηγμένες».\n\nΑυτά τα βήματα θα πραγματοποιηθούν κατά την πρώτη εκκίνηση του Xcode %@." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Installation was completed, but some post-install steps weren't performed automatically. Xcodes performs these steps with a privileged helper, which appears to not be installed. You can install it from Preferences > Advanced.\n\nThese steps will be performed when you first launch Xcode %@." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "La instalación se completó, pero algunos pasos posteriores a la instalación no se realizaron automáticamente. Xcodes realiza estos pasos con un asistente privilegiado, que parece no estar instalado. Puede instalarlo desde Preferencias > Avanzado.\n\nEstos pasos se realizarán cuando inicie Xcode por primera vez %@." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Asennus valmistui, mutta joitain asennuksen jälkeisiä vaiheita ei suoritettu automaattisesti. Xcodes suorittaa nämä vaiheet etuoikeutetulla avustajalla, jota ei näytä olevan asennettuna. Voit asentaa sen kohdasta Asetukset > Lisäasetukset.\n\nNämä vaiheet suoritetaan, kun käynnistät Xcode version %@ ensimmäisen kerran.." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "L'installation est terminée, mais certaines étapes de post-installation n'ont pas été effectuées automatiquement. Xcodes effectue ces étapes avec un assistant privilégié, qui ne semble pas être installé. Vous pouvez l'installer depuis Préférences > Avancé.\n\nCes étapes seront effectuées lors du premier lancement de Xcode %@." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "स्थापना पूर्ण हो गई थी, लेकिन कुछ पोस्ट-इंस्टॉलेशन चरण स्वचालित रूप से निष्पादित नहीं किए गए थे। Xcodes इन चरणों को एक विशेषाधिकार प्राप्त सहायक के साथ निष्पादित करता है, जो स्थापित नहीं प्रतीत होता है। आप इसे Preferences > Advanced(वरीयताएँ> उन्नत) से स्थापित कर सकते हैं।\n\nजब आप पहली बार Xcode %@ लॉन्च करेंगे तो ये चरण निष्पादित किए जाएंगे।" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "L'installazione è completata, ma alcuni comandi di post installazione non sono partiti automaticamente. Xcodes lancia questi comandi con un aiutante privilegiato, che sembra non essere installato. Puoi instlalarlo da Preferenze > Avanzate.\n\nVerranno lanciati quando aprirai Xcode %@ per la prima volta." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "インストールは完了しましたが、いくつかのインストール後作業が自動的に実行されませんでした。Xcodes は権限のあるヘルパーを使ってタスクを実行しますが、ヘルパーがインストールされていません。環境設定 > 上級者向け でインストールできます。\n\nこのタスクは Xcode %@ を初回起動した際に実行されます。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "설치가 완료되었지만 일부 설치 후 단계가 자동으로 수행되지 않았습니다. Xcodes는 설치되지 않은 것으로 보이는 권한을 가진 도우미 (Privileged helper)를 사용하여 이러한 단계를 수행합니다. 권한을 가진 도우미는 환경설정 > 고급에서 설치할 수 있습니다.\n\n이 단계는 Xcode %@를 처음 실행할 때 수행됩니다." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "De installatie is voltooid, maar sommige post-installatie taken zijn niet uitgevoerd. Xcodes voert deze taken uit door middel van de privileged helper, maar deze lijkt niet te zijn geïnstalleerd. Je kunt deze installeren vanaf Voorkeuren > Geavanceerd.\n\nDeze taken zullen worden uitgevoerd wanneer je Xcode %@ voor het eerst start." } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Instalacja została zakończona, ale niektóre kroki po instalacji nie zostały wykonane automatycznie. Xcodes wykonuje te kroki z pomocą helpera z uprawnieniami, który nie jest zainstalowany. Możesz go zainstalować w Ustawienia > Zaawansowane.\n\nTe kroki zostaną wykonane przy pierwszym uruchomieniu Xcode %@." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "A instalação foi completada, mas alguns passos de pós-instalação não puderam ser performados automaticamente. Xcodes performa estes passos com o ajudante privilegiado, que aparentemente não está instalado. Você pode instalá-lo em Preferências > Avançado.\n\nEstes passos serão performados quando você rodar o Xcode %@ pela primeira vez." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Установка была завершена, но некоторые шаги после установки не были выполнены автоматически. Xcodes выполняет эти шаги с привилегированным помощником, который, похоже, не установлен. Вы можете установить его из Настройки > Дополнительно.\n\nЭти шаги будут выполнены при первом запуске Xcode %@." } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "การติดตั้งเสร็จสมบูรณ์แล้ว แต่บางขั้นตอนหลังการติดตั้งไม่ได้ดำเนินการโดยอัตโนมัติ Xcodes ดำเนินการตามขั้นตอนเหล่านี้ด้วยตัวช่วยที่มีสิทธิ์ ซึ่งดูเหมือนจะไม่ได้ติดตั้ง คุณสามารถติดตั้งได้จาก การตั้งค่า > ขั้นสูง\n\nขั้นตอนเหล่านี้จะดำเนินการเมื่อคุณเปิด Xcode %@ ครั้งแรก" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "İndirme tamamlandı, fakat bazı indirme sonrası aşamaları otomatik tamamlanamadı. Xcodes bu aşamaları bir ayrıcalıklı yardımcı aracı ile gerçekleştirir, ama bu araç yüklenmemiş gözüküyor. Yüklemek için Tercihler > Gelişmiş yolunu kullanabilirsiniz. \n\nBunlar Xcode %@ uygulamasını ilk açtığınızda gerçekleşecek." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Інсталяцію завершено. Але деякі кроки після інсталяції не було виконано автоматично. Xcodes виконує цю роботу за допомогою \"Privileged Helper\", який схоже що не інстальовано. Ви можете встановити його за допомогою Налаштування > Додатково.\n\nЦі кроки буде виконано коли ви вперше запустите Xcode %@." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "安装已完成,但一些安装后准备步骤没有被自动执行。Xcodes使用一个提权帮助程序来执行这些步骤,但帮助程序似乎没有被安装。您可以从 偏好设置 > 高级 中安装。\n\n这些步骤将会在您第一次运行Xcode %@时执行。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "已完成安裝,但有些安裝後步驟未自動執行。Xcodes 需要具有權限的輔助程式來執行這些步驟,但該程式尚未被安裝。你可以在 偏好設定 > 進階中安裝它。\n\n這些步驟在首次啟動 Xcode %@ 時會自動執行。" } } } }, "InstallationError.UnavailableVersion" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "تعذر العثور على الإصدار %@." } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "No s'ha trobat la versió %@." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Kann Version %@ nicht finden." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Η έκδοση %@ δεν βρέθηκε." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Could not find version %@." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "No se pudo encontrar la versión %@." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Versiota %@ ei löytynyt." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Impossible de trouver la version %@." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "संस्करण %@ नहीं मिल सका।" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Non trovo la versione %@." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "バージョン %@ が見つかりません。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "%@ 버전을 찾을 수 없습니다." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Kan versie %@ niet vinden." } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Nie można znaleźć wersji %@." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Não foi possível encontrar versão %@." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Не удалось найти версию %@." } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ไม่พบเวอร์ชัน %@" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "%@ sürümü bulunamadı." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Не можу знайти версію %@." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "无法找到版本%@。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "找不到版本 %@。" } } } }, "InstallationError.UnexpectedCodeSigningIdentity" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "لا يحتوي Xcode الذي تم تنزيله على هوية توقيع الرمز المتوقعة.\nيملك:\n%@\n%@\nمُتوقع:\n%@\n%@" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "The downloaded Xcode doesn't have the expected code signing identity.\nGot:\n%@\n%@\nExpected:\n%@\n%@" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Die heruntergeladene Version von Xcode hat nicht die erwartete Code-Signing-Identity.\nErhalten:\n%@\n%@\nErwartet:\n%@\n%@" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Το ληφθέν Xcode δεν έχει την αναμενόμενη ταυτότητα υπογραφής κώδικα.\nΥπάρχουσα:\n%@\n%@\nΑναμενόμενη:\n%@\n%@" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "The downloaded Xcode doesn't have the expected code signing identity.\nGot:\n%@\n%@\nExpected:\n%@\n%@" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "El Xcode descargado no tiene la identidad de firma de código esperada.\nObtuvo:\n%@\n%@\nEsperado:\n%@\n%@" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Ladatulla Xcodella ei ole odotettua koodin allekirjoitusidentiteettiä.\nSaatu:\n%@\n%@\nOletettu:\n%@\n%@" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Le Xcode téléchargé n'a pas l'identité de signature de code attendue.\nReçue :\n%@\n%@\nAttendue :\n%@\n%@" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "डाउनलोड किए गए Xcode में अपेक्षित कोड हस्ताक्षर पहचान नहीं है।\nमिला:\n%@\n%@अपेक्षित:\n%@\n%@" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Xcode scaricato non ha l'identità di firma corretta.\nGot:\n%@\n%@Ci aspettiamo:\n%@\n%@" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ダウンロードしたXcodeに想定された署名IDがありません。\nGot:\n%@\n%@\nExpected:\n%@\n%@" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "다운로드한 Xcode에 예상된 코드 서명 ID가 없습니다.\n받은 값:\n%@\n%@\n예상된 값:\n%@\n%@" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "De gedownloade Xcode heeft niet de verwachte code-ondertekeningsidentiteit.\nVerkregen:\n%@\n%@\nVerwacht:\n%@\n%@" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Pobrany Xcode nie ma oczekiwanej sygnatury kodu.\nOtrzymano:\n%@\n%@\nOczekiwano:\n%@\n%@" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "O Xcode baixado não possui a identidade de assinatura de código esperada.\nPossui:\n%@\n%@\nEsperada:\n%@\n%@" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Загруженный Xcode не имеет ожидаемого идентификатора подписи сертификата.\nПолучено:\n%@\n%@\nОжидается:\n%@\n%@" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "Xcode ที่ดาวน์โหลดมาไม่มีรหัสประจำตัวการลงนามตามที่ต้องการ\nได้รับ:\n%@\n%@\nต้องการ:\n%@\n%@" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "İndirilmiş Xcode beklenilen imza kimliğine sahip değil.\nEldeki:\n%@\n%@\nBeklenen:\n%@\n%@" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Викачаний Xcode має неочікуваний підпис (code signing identity).\nМає:\n%@\n%@\nОчікується:\n%@\n%@" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "下载的Xcode版本不包含期望的代码签名身份。\n读取到:\n%@\n%@\n期望是:\n%@\n%@" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "下載的 Xcode 含有未預期的簽章。\n簽章者:\n%@\n%@\n預期的簽章者:\n%@\n%@" } } } }, "InstallationError.UnsupportedFileFormat" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "لا يدعم Xcodes (حتى الآن) تثبيت Xcode من تنسيق الملف %@." } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "L'Xcodes no soporta (encara) instal·lar l'Xcode amb el format %@." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes unterstützt (bislang) nicht die Installation von Xcode per %@-Dateiformat." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Το Xcodes δεν υποστηρίζει (ακόμη) την εγκατάσταση του Xcode από αρχεία τύπου %@." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes doesn't (yet) support installing Xcode from the %@ file format." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes no admite (todavía) la instalación de Xcode desde el formato de archivo %@." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes ei (vielä) tue Xcoden asentamista %@ tiedostomuodosta." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes ne supporte pas (encore) l'installation de Xcode à partir du format de fichier %@." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes (अभी तक) % @ फ़ाइल स्वरूप से Xcode स्थापित करने का समर्थन नहीं करता है।" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes non supporta (per ora) l'installazione di Xcode dal formato %@ ." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes は %@ 形式でのインストールには(まだ)対応していません。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes는 (아직) %@ 파일 형식에서 Xcode 설치를 지원하지 않습니다." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes heeft (nog) geen ondersteuning om Xcode te installeren vanaf het %@ bestandsformaat." } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes nie obsługuje (jeszcze) instalowania Xcode z formatu %@." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes (ainda) não suporta instalação de Xcode no formato de arquivo %@." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes (пока) не поддерживает установку Xcode из формата файла %@." } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes ยังไม่รองรับการติดตั้ง Xcode จากรูปแบบไฟล์ %@" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes henüz Xcodu %@ dosya formatında yüklemeye (henüz) izin vermiyor." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes (поки що) не підтримує свою установку у %@ форматі." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes尚未支持从%@格式安装。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes 目前尚未支援由 %@ 檔案格式安裝 Xcode。" } } } }, "InstallationError.VersionAlreadyInstalled" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "%@ مثبت بالفعل في %@" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "%@ ja està instal·lada a %@" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "%@ ist bereits installiert unter %@" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Το %@ είναι ήδη εγκατεστημένο στο %@" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "%@ is already installed at %@" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "%@ ya está instalado en %@" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "%@ on jo asennettu osoitteeseen %@" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "%@ est déjà installé à %@" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "%@ पहले से ही %@ पर संस्थापित है" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "%@ è già installato in %@" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "%@ は既に %@ にインストールされています。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "%@은(는) 이미 %@에 설치되어 있습니다." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "%@ is al geïnstalleerd op %@" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "%@ jest już zainstalowany w %@." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "%@ já está instalada em %@" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "%@ уже установлен в %@" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "%@ ได้รับการติดตั้งแล้วที่ %@" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "%@ zaten %@ konumunda yüklü." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "%@ вже інстальовано до %@" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "%@已经被安装到%@" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "%@ 已經在 %@ 安裝" } } } }, "InstallationError.VersionNotInstalled" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "%@ غير مثبت." } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "%@ no està instal·lada." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "%@ ist nicht installiert." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Το %@ δεν είναι εγκατεστημένο." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "%@ is not installed." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "%@ no esta instalada." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "%@ ei ole asennettu." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "%@ n'est pas installé." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "%@ स्थापित नहीं किया गया।" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "%@ non è installato." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "%@ がインストールされていません。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "%@이(가) 설치되지 않았습니다." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "%@ is niet geïnstalleerd." } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "%@ nie jest zainstalowany." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "%@ não está instalada." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "%@ не установлен." } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "%@ ยังไม่ได้รับการติดตั้ง" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "%@ yüklenmedi." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "%@ не інстальовано." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "%@没有被安装。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "%@ 尚未安裝。" } } } }, "InstallationStepDescription" : { "comment" : "So if changing order, make sure the positional specficier is retained. ex: \"%3$@: Step %1$d of %2$d\"", "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "Step %1$d of %2$d: %3$@" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Pas %1$d de %2$d: %3$@" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Schritt %1$d von %2$d: %3$@" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Βήμα %1$d από %2$d: %3$@" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Step %1$d of %2$d: %3$@" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Paso %1$d de %2$d: %3$@" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Vaihe %1$d / %2$d: %3$@" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Étape %1$d sur %2$d : %3$@" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "%2$d का स्टेप %1$d: %3$@" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Passo %1$d di %2$d: %3$@" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ステップ %1$d / %2$d: %3$@" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "%1$d / %2$d 단계: %3$@" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Stap %1$d van %2$d: %3$@" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Krok %1$d z %2$d: %3$@" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Passo %1$d de %2$d: %3$@" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Шаг %1$d из %2$d: %3$@" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ขั้นตอนที่ %1$d จาก %2$d: %3$@" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Adım %1$d/%2$d: %3$@" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Крок %1$d з %2$d: %3$@" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "第%1$d步,共%2$d步:%3$@" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "步驟 %1$d 之 %2$d:%3$@" } } } }, "InstallDescription" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "تثبيت هذا الإصدار" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Instal·lar aquesta versió" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Diese Version installieren" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Εγκατάσταση αυτής της έκδοσης" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Install this version" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Instalar esta versión" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Asenna tämä versio" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Installer cette version" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "इस संस्करण को स्थापित करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Installa questa versione" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "このバージョンをインストール" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "이 버전 설치하기" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Installeer deze versie" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Zainstaluj tę wersję" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Instalar esta versão" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Установить эту версию" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ติดตั้งเวอร์ชันนี้" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Bu sürümü indir" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Інсталювати цю версію" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "安装此版本" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "安裝這個版本" } } } }, "InstallDirectory" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "دليل التركيب" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Directori d'instal·lació" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Installationsverzeichnis" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Φάκελος Εγκατάστασης" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Install Directory" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Directorio de instalación" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Install Directory" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Répertoire d'installation" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Install Directory" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Install Directory" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "インストール先" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "설치 경로" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Installatie Map" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Katalog instalacji" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Diretório de instalação" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Путь для установки" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ติดตั้งที่ไดเร็กทอรี" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yükleme Klasörü" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Шлях інсталяції" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "安装目录" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "安裝目錄" } } } }, "Installed Only" : { }, "InstallHelper" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "تثبيت المساعد" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Instal·lar el servei" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Helfer installieren" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Εγκατάσταση βοηθητικού εργαλείου" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Install helper" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Instalar Asistente" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Asenna apulainen" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Installer l'assistant'" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सहायक स्थापित करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Installa aiutante" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ヘルパーのインストール" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "도우미 설치" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Installeer helper" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Zainstaluj helpera" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Instalar ajudante" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Установить помощника" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ติดตั้งตัวช่วย" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yardımcıyı yükle" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Інсталювати Helper" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "安装帮助程序" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "安裝輔助程式" } } } }, "InstallPathDescription" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "يقوم Xcodes بالبحث والتثبيت في دليل واحد. بشكل افتراضي (والموصى به) هو الاحتفاظ بهذا /Applications.online. قد تؤدي أي تغييرات في مكان تخزين Xcode إلى توقف التطبيقات/الخدمات الأخرى عن العمل." } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "L'Xcodes busca i instal·la en un sol directori. Per defecte (recomanat) utilitza la carpeta /Applications. Qualsevol canvi d'aquest directory pot provocar que altres aplicatius/serveis deixin de funcionar." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes sucht and installiert in ein einzelnes Verzeichnis. Standard (und empfohlen beizubehalten) ist /Programme. Änderungen am Speicherort von Xcode können dazu führen, dass andere Apps/Dienste aufhören zu funktionieren. " } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Το Xcodes αναζητά και εγκαθιστά σε ένα και μόνο φάκελο. Κατά προεπιλογή (όπως και συνίσταται) αυτός είναι ο «Εφαρμογές». Όποιες αλλαγές στην τοποθεσία του Xcode μπορεί να έχει ως αποτέλεσμα την προβληματική λειτουργία άλλων εφαρμογών/υπηρεσιών." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes searches and installs to a single directory. By default (and recommended) is to keep this /Applications. Any changes to where Xcode is stored may result in other apps/services to stop working. " } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes searches and installs to a single directory. By default (and recommended) is to keep this /Applications. Any changes to where Xcode is stored may result in other apps/services to stop working. " } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes searches and installs to a single directory. By default (and recommended) is to keep this /Applications. Any changes to where Xcode is stored may result in other apps/services to stop working. " } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes recherche et installe dans un répertoire spécifique. Par défaut, il est recommandé de laisser /Applications. Tout changement modifiant l'emplacement de Xcode risque de provoquer des dysfonctionnements aux autres apps/services." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes searches and installs to a single directory. By default (and recommended) is to keep this /Applications. Any changes to where Xcode is stored may result in other apps/services to stop working. " } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes searches and installs to a single directory. By default (and recommended) is to keep this /Applications. Any changes to where Xcode is stored may result in other apps/services to stop working. " } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Xcodesは1つのディレクトリを検索し、そこにインストールします。デフォルト (推奨) は /Applications です。Xcodeの格納場所を変更すると、他のアプリケーションやサービスが動作しなくなる可能性があります。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes는 단일 경로를 탐색하고 설치를 수행합니다. 이 경로는 기본적으로 /Applications이며, 이 경로를 유지하는 것을 권장합니다. 경로를 변경하면 다른 앱/서비스가 정상적으로 동작하지 않을 수 있습니다." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes zoekt en installeert naar een enkele map. Standaard (en aanbevolen) is om dit in te stellen op /Applications. Wijzigingen die gemaakt worden in waar Xcode is opgeslagen kan resulteren in niet werkende apps/services. " } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes wyszukuje i instaluje w jednym katalogu. Domyślnie (i zalecane) jest to /Applications. Wszelkie zmiany w miejscu przechowywania Xcode mogą spowodować, że inne aplikacje/usługi przestaną działać." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes procura e instala em um diretório único. Por padrão (e recomendado) é mantido no /Applications. Qualquer mudança onde o Xcode é instalado pode resultar na falha de outros aplicativos/serviços." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes использует единый путь для поиска и установки выпусков Xcode. По умолчанию, рекомендуется оставить /Applications. Любые изменения в то, где находится Xcode, могут привести к тому, что другие приложения/сервисы могут перестать работать. " } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes จะค้นหาและติดตั้งลงในไดเรกทอรีเดียว โดยค่าเริ่มต้น (และแนะนำ) คือการเก็บข้อมูลนี้ไว้ใน /Applications การเปลี่ยนแปลงใด ๆ เกี่ยวกับตำแหน่งที่เก็บ Xcode อาจทำให้แอปพลิเคชัน/บริการอื่น ๆ หยุดทำงาน" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes bir klasörü arayıp oraya yükler. Varsayılan(ve önerilen) yöntem /Uygulamalar klasöründe tutmaktır. Xcode'un bulunduğu ortamdaki herhangi bir değişiklik başka bir uygulamanın/servisin çalışmasını durdurabilir." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes шукає інсталяції та встановлює нові версії у вказану директорію. За-умовчанням,до /Applications (рекомендується). \nБудь-які зміни шляху розташування Xcode можуть призвести до втрати дієздатності інших застосунків / сервісів. " } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes会在一个目录中检索及安装。默认(推荐)保持/Applications。任何对Xcode存储位置的变更都可能会导致其他App或服务停止工作。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes 搜尋並安裝軟體到這一個路徑。預設值是 /Applications(也不建議改動)。更改 Xcode 的安裝位置可能導致其他應用程式或服務停止運作。" } } } }, "LastChecked" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "آخر فحص: %@" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Última consulta: %@" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Letzte Prüfung: %@" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Τελευταίος έλεγχος: %@" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Last checked: %@" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Última comprobación: %@" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Viimeksi tarkistettu: %@" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Dernière vérification : %@" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पिछली बार चेक किया गया: %@" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Ultimo controllo: %@" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "前回の確認: %@" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "마지막 확인 시점: %@" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Laatste controle: %@" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Ostatnio sprawdzono: %@" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Ultima vez verificado: %@" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Последняя проверка: %@" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ตรวจสอบล่าสุดเมื่อ: %@" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Son kontrol: %@" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перевірено в останнє: %@" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "上次检查时间:%@" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "上一次檢查: %@" } } } }, "License" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "License" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Llicència" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Lizenz" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Άδεια χρήσης" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Licencia" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Lisenssi" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Licence" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "लाइसेंस" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Licenza" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ライセンス" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "라이센스" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Licentie" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Licencja" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Licença" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Лицензия" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ใบอนุญาต" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Lisans" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Ліцензія" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "许可协议" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "授權條款" } } } }, "LocalCachePath" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "مسار ذاكرة التخزين المؤقت المحلية" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Ruta de la memòria cau local" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Lokaler Cache-Pfad" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Διαδρομή Τοπικής Cache" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Local Cache Path" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ruta de caché local" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Paikallisen välimuistin polku" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Cache Local" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "स्थानीय कैश पथ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Percorso Cache Locale" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ローカルキャッシュパス" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "로컬 캐시 경로" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Lokaal Cache Pad" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Katalog cache" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Caminho de caches local" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Путь к локальному кешу" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ตำแหน่งที่เก็บไฟล์แคช" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Lokal Önbellek Konumu" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Локальний Кеш" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "本地缓存位置" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "本機快取路徑" } } } }, "LocalCachePathDescription" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "يقوم Xcodes بتخزين إصدارات Xcode المتوفرة مؤقتًا وتنزيل الإصدارات الجديدة مؤقتًا إلى الدليل" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "L'Xcodes guarda a la memòria cau les versions d'Xcode disponibles i hi descarrega temporalment les noves versions" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes speichert verfügbare Xcode-Versionen zwischen und lädt neue Versionen temporär in ein Verzeichnis." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Το Xcodes χρησιμοποιεί cache περιεχομένου για τις διαθέσιμες εκδόσεις του Xcode και κάνει προσωρινές λήψεις νέων εκδόσεων σε κάποιον φάκελο" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes caches available Xcode versions and temporary downloads new versions to a directory" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes almacena en caché versiones de Xcode disponibles y descargas temporalmente las nuevas versiones en un directorio" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes tallentaa saatavilla olevat Xcode-versiot välimuistiin ja lataa tilapäisesti uudet versiot hakemistoon" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes met en cache les versions de Xcode disponibles et télécharge temporairement les nouvelles versions dans le répertoire de cache local" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes उपलब्ध Xcode संस्करणों को कैश करता है और एक निर्देशिका में अस्थायी रूप से नए संस्करण डाउनलोड करता है" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes fa la cache delle versioni di Xcode disponibili e download temporanei in una directory." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Xcodesは、利用可能なXcodeのバージョンをキャッシュし、新しいバージョンをディレクトリに一時的にダウンロードします。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes는 사용 가능한 Xcode 버전을 캐시하고 임시로 디렉토리에 새 버전을 다운로드 합니다." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes cached beschikbare Xcode versies en download tijdelijk nieuwe versies naar een map" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes przechowuje w cache dostępne wersje Xcode oraz tymczasowo pobiera nowe pliki do tego katalogu." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes faz caches de versões disponíveis do Xcode e baixa temporariamente estas novas versões para um diretório." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes кеширует доступные версии Xcode и временно загружает новые версии в указанную директорию" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes แคชเวอร์ชัน Xcode ที่มีอยู่และดาวน์โหลดเวอร์ชันใหม่ชั่วคราวไปยังไดเร็กทอรี" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes, uygun Xcode sürümlerini ve geçici yeni indirilenleri bir klasörde önbelleğe alır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes кешує доступні версії Xcode та тимчасові файли викачувань нових версій в цю директорію" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes会缓存可用的Xcode版本并暂时下载新版本到一个目录。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes 會快取可用的 Xcode 版本並將新版本暫存下載至這個目錄" } } } }, "Login" : { "comment" : "ToolBar", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "تسجيل الدخول" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Inici de sessió" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Login" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Είσοδος" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Iniciar Sesión" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Kirjaudu sisään" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Connexion" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "लॉग इन करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Login" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ログイン" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "로그인" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Inloggen" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Zaloguj się" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Login" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Логин" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "เข้าสู่ระบบ" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Giriş" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Apple ID" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "登录" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "登入" } } } }, "LoginDescription" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "Open Login" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Obrir l'inici de sessió" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Login öffnen" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Άνοιγμα Εισόδου" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Open Login" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Abrir Inicio de Sesión" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Avaa kirjautuminen" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Ouvrir une connexion" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "लॉगिन खोलें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Apri Login" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ログイン画面を開く" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "로그인 창 열기" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Open Inloggen" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Otwórz okno logowania" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Abrir login" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Открыть окно логина" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "เปิดหน้าเข้าสู่ระบบ" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Giriş Yap" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Показати Apple ID" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "打开登录窗口" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "打開登入" } } } }, "MacOSRequirement" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "يتطلب نظام التشغيل macOS %@ أو أحدث" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "És necessari macOS %@ o posterior" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Erfordert macOS %@ oder neuer" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Απαιτείται macOS %@ ή μεταγενέστερη έκδοση" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Requires macOS %@ or later" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Requiere macOS %@ o posterior" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Vaatii macOS version %@ tai myöhemmän" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Nécessite macOS %@ ou une version ultérieure" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "macOS %@ या बाद के संस्करण की आवश्यकता है" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Richiede macOS %@ o successivo" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "macOS %@ 以降" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "macOS %@ 또는 이후 버전" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Vereist macOS %@ of hoger" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Wymaga systemu macOS %@ lub nowszego" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Necessário macOS %@ ou mais recente" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Требуется macOS %@ или новее" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ต้องใช้ macOS %@ หรือใหม่กว่า" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "macOS %@ veya sonrasını gerektirir" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Необхідна macOS %@ чи новіша" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "需要macOS %@及以上" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "需要 macOS %@ 或以上版本" } } } }, "MakeActive" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "Make active" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Fer-la activa" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Aktivieren" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Ορισμός ως ενεργής" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Make active" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Activarlo" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Aktivoi" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Activer" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सक्रिय बनाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Rendi attiva" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "アクティブにする" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "기본 버전으로 만들기" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Maak actief" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Ustaw jako aktywny" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Ativar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Активировать" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "เลือกใช้งาน" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Etkinleştir" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Зробити активним" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "激活" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "啟用" } } } }, "MakeActiveVersionDescription" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "اجعل هذا هو الإصدار النشط" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Fer aquesta versió activa" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Dies zur aktiven Version machen" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Ορισμός ως ενεργής έκδοσης" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Make this the active version" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Haz que esta sea la versión activa" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Tee tästä aktiivinen versio" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Activer" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "इसे सक्रिय संस्करण बनाएं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Rendi questa versione attiva" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "アクティブなバージョンにする" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "기본 버전으로 만들기" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Maak dit de actieve versie" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Ustaw jako aktywną wersję" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Ativar esta versão" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Сделать эту версию активной" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "เลือกเวอร์ชันนี้มาใช้งาน" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Bu sürümü aktif et" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Зробити цю версію активною" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "激活此版本" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "啟用這個版本" } } } }, "Menu.About" : { "comment" : "Menu", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "About Xcodes" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Sobre Xcodes" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Über Xcodes" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Πληροφορίες για το Xcodes" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "About Xcodes" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Acerca de Xcodes" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Tietoa Xcodesista" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "À propos de Xcodes" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "एक्सकोड के बारे में" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Informazioni di Xcodes" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Xcodesについて" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes 정보" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Over Xcodes" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "O Xcodes" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Sobre Xcodes" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "О Xcodes" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "เกี่ยวกับ Xcodes" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes Hakkında" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Про Xcodes" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "关于Xcodes" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "關於 Xcodes" } } } }, "Menu.Acknowledgements" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "شكر وتقدير Xcodes" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Agraïments d'Xcodes" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes Anerkennungen" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Αναγνωρίσεις του Xcodes" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes Acknowledgements" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes Agradecimientos" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes kunnianosoitukset" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Remerciements Xcodes" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes स्वीकृति" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Ringraziamenti di Xcodes" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "謝辞" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes 법적고지" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes Dankbetuigingen" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Podziękowania Xcodes" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Menções de Xcodes" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Юридическая информация Xcodes" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "คำขอบคุณจาก Xcodes" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes Teşekkür" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Подяки" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes版权声明" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes 版權宣告" } } } }, "Menu.CheckForUpdates" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "تحقق من وجود تحديثات..." } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Cerca actualitzacions..." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Prüfe auf Updates..." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Έλεγχος για ενημερώσεις..." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Check for Updates..." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Buscar Actualizaciones..." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Tarkasta uusia päivityksiä..." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Vérifier les mises à jour..." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "अद्यतनों के लिए जाँच करें..." } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Controllo aggiornamenti..." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "アップデートを確認…" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "업데이트 확인..." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Controleren op Updates..." } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Sprawdź aktualizacje..." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Verificar atualizações..." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Проверить наличие обновлений..." } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ตรวจสอบการอัปเดต..." } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Güncellemeleri Denetle..." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Перевірити Оновлення..." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "检查更新…" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "檢查更新版本⋯" } } } }, "Menu.GitHubRepo" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "مستودعات GitHub Xcodes" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Repositori de GitHub d'Xcodes" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes GitHub-Repo" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes GitHub Repo" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes GitHub Repo" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes GitHub Repositorio" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes GitHub Repo" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Dépôt GitHub Xcodes" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes GitHub रेपो" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes Repository di GitHub" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "GitHub" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes GitHub 저장소" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes GitHub Repo" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Repozytorium Xcodes na GitHubie" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Repositório Xcodes no GitHub" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "GitHub-репозиторий Xcodes" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes GitHub Repo" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes GitHub Repo" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes GitHub репозіторій" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes GitHub仓库" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes GitHub Repo" } } } }, "Menu.ReportABug" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "الإبلاغ عن خطأ" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Informa d'un error" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Bug melden" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Αναφορά σφάλματος" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Report a Bug" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Reportar un fallo" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Ilmoita bugi" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Signaler un Bogue" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "किसी बग की रिपोर्ट करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Segnala un Bug" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "不具合の報告" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "버그 제보하기" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Bug Rapporteren" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Zgłoś błąd" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Reportar um bug" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Сообщить об ошибке" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "รายงานจุดบกพร่อง" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Bir Sorun Bildir" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Повідомити про помилку" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "反馈问题" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "回報錯誤" } } } }, "Menu.RequestNewFeature" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "Request a New Feature" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Demana una nova funcionalitat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Neues Feature anfordern" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Αίτημα για νέα δυνατότητα" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Request a New Feature" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Solicitar una nueva función" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Pyydä uutta ominaisuutta" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Demander une Nouvelle Fonctionnalité" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "किसी नई सुविधा का अनुरोध करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Richiedi una nuova Feature" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "機能のリクエスト" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "새로운 기능 요청하기" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Nieuwe Feature Aanvragen" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Poproś o nową funkcję" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Solicitar uma nova funcionalidade" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Запросить новую функцию" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ต้องการฟีเจอร์ใหม่" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yeni Özellik Talep Et" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Запит на новий функціонал" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "建议新功能" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "請求新功能" } } } }, "Misc" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "Misc" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Misc" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Misc" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Misc" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Misc" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Misc" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Misc" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Misc" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Misc" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Misc" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Misc" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Misc" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Misc" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Misc" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Diversos" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Misc" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "อื่น ๆ" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Misc" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Misc" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "杂项" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "雜項" } } } }, "Moving" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "Moving to %@" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Movent a %@" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "In %@ bewegen" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Μετακίνηση σε %@" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Moving to %@" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Moviendo a %@" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Siirretään kohteeseen %@" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Déplacement vers %@" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "%@ पर जा रहा है" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Sposto in %@" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "%@ に移動しています" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "%@(으)로 이동 중" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Verplaatsen naar %@" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Przenoszenie do %@" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Movendo para %@" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Перемещение в %@" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "กำลังย้ายไปที่ %@" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "%@ Konumuna Taşınıyor" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Переміщення до %@" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "正在移动到%@" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "移動至 %@" } } } }, "Never" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "أبداً" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Mai" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Nie" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Ποτέ" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Never" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Nunca" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Ei koskaan" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Jamais" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कभी नहीं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Mai" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "なし" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "확인하지 않음" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Nooit" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Nigdy" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Nunca" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Никогда" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ไม่อีกเลย" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Daha Yeni" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Ніколи" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "从未检查" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "從未使用" } } } }, "Next" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "التالي" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Següent" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Nächstes" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Επόμενο" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Siguiente" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Seuraava" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Suivant" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "अगला" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Avanti" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "次へ" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "다음" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Volgende" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Dalej" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Próximo" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Далее" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ถัดไป" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Sonraki" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Далі" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "下一步" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "下一步" } } } }, "Notification.FinishedInstalling" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "انهاء التثبيت" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "S'ha acabat d'instal·lar" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Installation beendet" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Η εγκατάσταση ολοκληρώθηκε" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Finished installing" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Terminado de instalar" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Asennus valmis" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Installation terminée" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "स्थापना समाप्त हो गई" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Ho terminato l'installazione" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "インストールを完了しました" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "설치 완료" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Installatie voltooid" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Zakończono instalację" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Instalação finalizada" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Завершена установка" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ติดตั้งเสร็จแล้ว" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "İndirme tamamlandı" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Інстальовано" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "安装完成" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "已經完成安裝" } } } }, "Notification.NewVersionAvailable" : { "comment" : "Notifications", "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "New version is available" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Nova versió disponible" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Neue Version verfügbar" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Νέα έκδοση είναι διαθέσιμη" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "New version is available" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Una nueva versión está disponible" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Uusi versio on saatavilla" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Une nouvelle version est disponible" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नया संस्करण उपलब्ध है" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Una nuova versione è disponibile" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "新しいバージョンが利用可能です" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "새 버전을 사용할 수 있습니다" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Nieuwe versie beschikbaar" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Dostępna jest nowa wersja" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Nova versão disponível" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Доступна новая версия" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "เวอร์ชันใหม่พร้อมใช้งานแล้ว" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yeni sürüm mevcut" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Доступна нова версія" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "新版本可用" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "新版本已可供使用" } } } }, "Notification.NewXcodeVersion.Body" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "إصدارات Xcode الجديدة متاحة للتنزيل." } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Hi ha noves versions de l'Xcode disponibles per a descarregar." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Neue Xcode-Versionen stehen zum Download bereit." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Νέες εκδόσεις του Xcode είναι διαθέσιμες για λήψη." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "New Xcode versions are available to download." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Nuevas versiones de Xcode están disponibles para descargar." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Uusia Xcode-versioita on ladattavissa." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "De nouvelles versions de Xcode sont disponibles au téléchargement." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नए Xcode संस्करण डाउनलोड करने के लिए उपलब्ध हैं।" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nuove versioni di Xcode disponibili per il download." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "新しいバージョンがダウンロードできます。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "새로운 Xcode 버전을 다운로드 할 수 있습니다." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Nieuwe Xcode versies zijn beschikbaar om te downloaden." } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Dostępne są nowe wersje Xcode do pobrania." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Novas versões do Xcode estão disponíveis para baixar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Новые версии Xcode доступны для скачивания." } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "Xcode เวอร์ชันใหม่พร้อมให้ดาวน์โหลดแล้ว" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yeni Xcode sürümleri indirmek için mevcut." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Доступні нові версії Xcode" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "新的Xcode版本已经可以下载。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "新的 Xcode 版本已可供下載。" } } } }, "Notification.NewXcodeVersion.Title" : { "comment" : "Notifications", "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "إصدارات Xcode الجديدة" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Noves versions d'Xcode" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Neue Xcode-Versionen" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Νέες εκδόσεις του Xcode" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "New Xcode versions" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Nuevas versiones de Xcode" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Uudet Xcode-versiot" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Nouvelles versions de Xcode" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "नए Xcode संस्करण" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nuove versioni di Xcode" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "新しいXcodeのバージョン" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "새 Xcode 버전" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Nieuwe Xcode versies" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Nowe wersje Xcode" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Novas versões do Xcode" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Новые версии Xcode" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "Xcode เวอร์ชันใหม่" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yeni Xcode sürümü" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Нові версії Xcode" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "新Xcode版本可用" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "新的 Xcode 版本" } } } }, "Notifications" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "إشعارات" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Notificacions" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Benachrichtigungen" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Γνωστοποιήσεις" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Notificaciones" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Ilmoitukset" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Notifications" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सूचनाएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Notifiche" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "通知" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "알림" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Notificaties" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Powiadomienia" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Notificações" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Уведомления" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "การแจ้งเตือน" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Bildirimler" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Сповіщення" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "通知" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "通知" } } } }, "NotificationSettings" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "إعدادات الإشعار" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Preferències de notificacions" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Benachrichtigungs-Einstellungen" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Ρυθμίσεις γνωστοποιήσεων" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Notification Settings" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Configuración de las notificaciones" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Ilmoitusasetukset" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Paramètres de Notification" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "सूचना सेटिंग्स" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Impostazioni delle Notifiche" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "通知設定" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "알림 설정" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Notificatie Instellingen" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Ustawienia powiadomień" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Configurações de notificação" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Настройки уведомлений" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "การตั้งค่าการแจ้งเตือน" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Bildirim Ayarları" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Налаштування Сповіщень" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "通知设置" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "通知偏好設定" } } } }, "NoTrustedPhones" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "لا يحتوي حسابك على أي أرقام هواتف موثوقة، ولكنها مطلوبة للمصادقة الثنائية.\n\nراجع https://support.apple.com/HT204915." } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "El teu compte no té cap número de confiança, però se'n necessita un per a l'autenticació de dos factors.\n\nVeure https://support.apple.com/en-ca/HT204915." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Dein Account verfügt über keine vertrauenswürdigen Telefonnummern, diese sind aber für Zwei-Faktor-Authentifizierung erforderlich.\n\nInformationen dazu unter https://support.apple.com/HT204915." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Ο λογαριασμός σας δεν έχει κάποιους αξιόπιστους τηλεφωνικούς αριθμούς, όμως απαιτούνται για τον έλεγχο ταυτότητας δύο παραγόντων.\n\nΔείτε https://support.apple.com/HT204915." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Your account doesn't have any trusted phone numbers, but they're required for two-factor authentication.\n\nSee https://support.apple.com/HT204915." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Su cuenta no tiene números de teléfono de confianza, pero son necesarios para la autenticación de dos factores.\n\nVer https://support.apple.com/HT204915." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Tililläsi ei ole luotettuja puhelinnumeroita, mutta ne vaaditaan kaksivaiheiseen todentamiseen.\n\nKatso https://support.apple.com/HT204915." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Votre compte n'a aucun numéro de téléphone de confiance, mais ils sont requis pour l'authentification à deux facteurs.\n\nVoir https://support.apple.com/HT204915." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "आपके खाते में कोई विश्वसनीय फ़ोन नंबर नहीं है, लेकिन वे दो-कारक प्रमाणीकरण के लिए आवश्यक हैं।\n\nhttps://support.apple.com/HT204915 देखें।" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Il tuo account non ha dispositivi registrati, ma è richiesto dall'autenticazione a due fattori.\nVedi https://support.apple.com/HT204915." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "このアカウントに2要素認証に利用する、信頼できる電話番号がありません。\n\n詳しくはこちらをご確認ください。https://support.apple.com/HT204915" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "계정에 신뢰할 수 있는 전화번호가 없지만 이중 인증(2FA)에 필요합니다.\n\nhttps://support.apple.com/HT204915를 참조하세요." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Je account heeft geen vertrouwde telefoonnummers, dit is nodig voor two-factor authenticatie.\n\nZie https://support.apple.com/HT204915." } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Twoje konto nie ma żadnych zaufanych numerów telefonów, ale są one wymagane do autoryzacji dwuskładnikowej.\n\nZobacz https://support.apple.com/HT204915." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Sua conta não possui nenhum telefone confiável, mas é necessário para autenticação de dois fatores.\n\nVeja https://support.apple.com/en-ca/HT204915." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "В вашем аккаунтe нет доверенных телефонных номеров, но они необходимы для двухфакторной аутентификации.\n\nСм. https://support.apple.com/HT204915." } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "บัญชีของคุณไม่มีหมายเลขโทรศัพท์ที่เชื่อถือได้ แต่จำเป็นต้องใช้สำหรับการยืนยันตัวตนแบบสองปัจจัย\n\nดู https://support.apple.com/HT204915" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Hesabına tanımlı güvenli bir telefon numarası yok, fakat iki aşamalı doğrulama için gerekmektedir.\n\nDaha fazlası için https://support.apple.com/HT204915 adresine bakın." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Ваш аккаунт не має перевіреного телефонного номеру, що вимагається для двофакторної авторизації.\n\nДивіться https://support.apple.com/HT204915." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "您的账户没有任何信任的手机号,但这是两步验证所必须的。\n\n请参阅 https://support.apple.com/HT204915。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "你的帳號沒有任何已信任的手機號碼,但兩階段認證需要信任的手機號碼。\n\n請參閱 https://support.apple.com/HT204915。" } } } }, "NoXcodeSelected" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "لم يتم تحديد Xcode" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Cap Xcode seleccionat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Kein Xcode ausgewählt" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Καμία επιλογή Xcode" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "No Xcode Selected" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "No se ha seleccionado un Xcode" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Xcode versiota ei ole valittuna" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Aucun Xcode Sélectionné" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "कोई Xcode चयनित नहीं" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Nessun Xcode selezionato" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Xcodeが選択されていません" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "선택된 Xcode 없음" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Geen Xcode Geselecteerd" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Nie wybrano Xcode" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Nenhum Xcode selecionado" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Xcode не выбран" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ไม่ได้เลือก Xcode" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Xcode Sürümü Seçilmedi" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Не обрано Xcode" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "没有选定的Xcode版本" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "未選取 Xcode" } } } }, "OK" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "OK" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "D'acord" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "OK" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "OK" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Aceptar" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Ok" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "OK" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "OK" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "OK" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "OK" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "확인" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "OK" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "OK" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "OK" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "OK" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ตกลง" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "OK" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "OK" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "好" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "好" } } } }, "OnSelectDoNothing" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "احتفظ بالاسم كـ Xcode-X.X.X.app" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Mantenir el nom Xcode-X.X.X.app" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Name beibehalten als Xcode-X.X.X.app" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Διατήρηση ονόματος ως Xcode-X.X.X.app" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Keep name as Xcode-X.X.X.app" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Mantener el nombre como Xcode-X.X.X.app" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Keep name as Xcode-X.X.X.app" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Ne rien faire" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Keep name as Xcode-X.X.X.app" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Keep name as Xcode-X.X.X.app" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "名前をXcode-X.X.X.appのまま維持する" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "앱 이름을 Xcode-X.X.X.app 형태로 유지하기" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Behoud naam als Xcode-X.X.X.app" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Zachowaj nazwę jako Xcode-X.X.X.app" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Manter nome como Xcode-X.X.X.app" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Сохранять формат имени Xcode-X.X.X.app" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ใช้ชื่อว่า Xcode-X.X.X.app" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Uygulama ismini Xcode-X.X.X.app gibi tut." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Зберігати імʼя як Xcode-X.X.X.app" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "保持名称为Xcode-X.X.X.app" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "保留 Xcode-X.X.X.app 名稱" } } } }, "OnSelectDoNothingDescription" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "On select, will keep the name as the version eg. Xcode-13.4.1.app" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Al seleccionar aquesta opció es mantindrà el nom amb la versió, p.e. Xcode-13.4.1.app" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Bei Auswahl wird der Name mit Version beibehalten, z. B. Xcode-13.4.1.app" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Κατά την επιλογή, θα διατηρηθεί το όνομα και η έκδοση π.χ. Xcode-13.4.1.app" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "On select, will keep the name as the version eg. Xcode-13.4.1.app" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Al seleccionar, mantener el nombre como la versión p.ej. Xcode-13.4.1.app" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "On select, will keep the name as the version eg. Xcode-13.4.1.app" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Lors de la sélection, gardera le numéro de version. Exemple: Xcode-13.4.1.app" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "On select, will keep the name as the version eg. Xcode-13.4.1.app" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "On select, will keep the name as the version eg. Xcode-13.4.1.app" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "選択時、バージョン付きの名前を維持します。例) Xcode-13.4.1.app" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "앱 이름에 버전을 포함하여 Xcode-13.4.1.app과 같은 형식을 유지합니다." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Wanneer ingeschakeld, zal de bestandsnaam altijd het versienummer bevatten. Bijvoorbeeld Xcode-13.4.1.app" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Po wybraniu, nazwa pozostanie w formacie z numerem wersji, np. Xcode-13.4.1.app" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Quando selecionado, será mantido o nome com a versão ex. Xcode-13.4.1.app" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Если выбрано, формат имени будет содержать версию, прим. Xcode-13.4.1.app" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "เมื่อเลือก จะเก็บชื่อไว้เป็นเวอร์ชัน เช่น Xcode-13.4.1.app" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Seçildiğinde, ismi Xcode-13.4.1.app örneğindeki gibi tutar." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "При виборі, зберігати імʼя як версію, наприклад,\nXcode-13.4.1.app" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "选中时,将会保持各版本的名称。例如Xcode-13.4.1.app。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "選擇時,會保留有版本的檔名,如 Xcode-13.4.1.app" } } } }, "OnSelectRenameXcode" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "قم دائمًا بإعادة التسمية إلى Xcode.app" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Always rename to Xcode.app" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Immer in Xcode.app umbenennen" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Μετονομασία πάντα σε Xcode.app" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Always rename to Xcode.app" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Always rename to Xcode.app" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Always rename to Xcode.app" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Toujours renommer en Xcode.app" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Always rename to Xcode.app" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Always rename to Xcode.app" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "名前を常にXcode.appに変更する" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "앱 이름을 Xcode.app으로 변경하기" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Altijd hernoemen naar Xcode.app" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Zawsze zmieniaj nazwę na Xcode.app" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Sempre renomear para Xcode.app" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Всегда переименовывать в Xcode.app" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "เปลี่ยนชื่อเป็น Xcode.app เสมอ" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Her zaman Xcode.app şeklinde ismi değiştir" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Завжди змінювати імʼя на Xcode.app" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "总是重命名为Xcode.app" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "總是重新命名為 Xcode.app" } } } }, "OnSelectRenameXcodeDescription" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "عند التحديد، سيحاول تلقائيًا إعادة تسمية Xcode النشط إلى Xcode.app، وإعادة تسمية Xcode.app السابق إلى اسم الإصدار." } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Al seleccionar aquesta opció, s'intentarà canviar el nom de la versió activa d'Xcode a Xcode.app; canviant així també el nom de l'antiga Xcode.app a el nom amb versió." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Bei Auswahl wird versucht das aktive Xcode in Xcode.app umzubenennen. Die vorherige Xcode.app wird dazu in den Versionsnamen umbenannt." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Κατά την επιλογή, θα γίνει αυτόματα προσπάθεια μετονομασίας του ενεργού Xcode σε Xcode.app, μετονομάζοντας το προηγούμενο Xcode.app στο αντίστοιχο όνομα της έκδοσής του." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "On select, will automatically try and rename the active Xcode to Xcode.app, renaming the previous Xcode.app to the version name." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "On select, will automatically try and rename the active Xcode to Xcode.app, renaming the previous Xcode.app to the version name." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "On select, will automatically try and rename the active Xcode to Xcode.app, renaming the previous Xcode.app to the version name." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Lors de la sélection, toujours essayer de renommer la version active de Xcode en Xcode.app, en renommant l'ancienne Xcode.app avec le nom de version." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "On select, will automatically try and rename the active Xcode to Xcode.app, renaming the previous Xcode.app to the version name." } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "On select, will automatically try and rename the active Xcode to Xcode.app, renaming the previous Xcode.app to the version name." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "選択時、自動的にアクティブなXcodeをXcode.appに、以前のXcode.appをバージョン付きの名前に変更します。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "기본 버전으로 선택한 Xcode의 앱 이름을 Xcode.app으로 변경하고, 이전 Xcode.app의 이름은 버전을 포함한 형식으로 변경합니다." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Wanneer ingeschakeld, zal automatisch de actieve Xcode hernoemd worden naar Xcode.app, de vorige Xcode.app zal hernoemd worden naar de desbetreffende versie." } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Po wybraniu, automatycznie zmienia aktywną wersję Xcode na Xcode.app, a poprzednie Xcode.app zostanie zmienione na nazwę wersji." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Ao selecionar, tentar renomear o Xcode ativo para Xcode.app, renomeando o Xcode.app anterior para um nome com a versão." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Если выбрано, будет выполнена попытка переименовать активный Xcode в Xcode.app, а предыдущий Xcode.app в формат имени с версией." } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "เมื่อเลือก จะพยายามเปลี่ยนชื่อ Xcode ที่ใช้งานอยู่เป็น Xcode.app โดยอัตโนมัติ โดยเปลี่ยนชื่อ Xcode.app ก่อนหน้าเป็นชื่อเวอร์ชันแทน" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Seçildiğinde, aktif olan Xcode'u Xcode.app olarak isimlendirmeye çalışır ve eski Xcode ismine sürüm ismi ekler." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "При виборі, автоматично перейменовувати активний Xcode на Xcode.app, змінюючи імʼя попереднього Xcode.app на його версію." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "选中时,会自动尝试重命名活跃的Xcode为Xcode.app,将之前的Xcode.app重命名为包含版本的名称。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "選擇時,會自動嘗試將目前啟用的 Xcode 重新命名為 Xcode.app,而將先前的 Xcode.app 重新命名為有版本的檔名。" } } } }, "Open" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "افتح" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Obrir" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Öffnen" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Άνοιγμα" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Abrir" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Avaa" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Ouvrir" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "खोलना" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Apri" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "開く" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "열기" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Open" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Otwórz" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Abrir" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Открыть" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "เปิด" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Aç" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Запустити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "打开" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "打開" } } } }, "Open In Rosetta" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "فتح في روزيتا" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Open In Rosetta" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Open In Rosetta" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Άνοιγμα με Rosetta" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Open In Rosetta" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Open In Rosetta" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Ouvrir avec Rosetta" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Open In Rosetta" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Open In Rosetta" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Open In Rosetta" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Rosetta를 사용하여 열기" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Open In Rosetta" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Open In Rosetta" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Abrir com Rosetta" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Открыть в Rosetta" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "เปิดด้วย Rosetta" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Rosetta ile Aç" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Запустити через Rosetta" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "在Rosetta中打开" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "使用 Rosetta 打開" } } } }, "OpenDescription" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "افتح هذا الإصدار" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Obrir aquesta versió" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Diese Version öffnen" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Άνοιγμα αυτής της έκδοσης" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Open this version" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Abrir esta versión" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Avaa tämä versio" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Ouvrir cette version" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "इस संस्करण को खोलें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Apri questa versione" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "このバージョンを開く" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "이 버전 열기" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Open deze versie" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Otwórz tę wersję" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Abrir essa versão" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Открыть эту версию" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "เปิดเวอร์ชันนี้" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Bu sürümü aç" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Запустити цю версію" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "打开此版本" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "打開這個版本" } } } }, "Password" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "كلمة المرور:" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Contrassenya:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Passwort:" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Συνθηματικό:" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Password:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Contraseña:" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Salasana:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Mot de passe :" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पासवर्ड:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Password:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "パスワード:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "비밀번호:" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Wachtwoord:" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Hasło:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Senha:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Пароль:" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "รหัสผ่าน:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Şifre:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Пароль:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "密码:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "密碼:" } } } }, "Perform post-install steps" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "تنفيذ خطوات ما بعد التثبيت" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Perform post-install steps" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Führe Post-Installationsschritte durch" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Εκτέλεση μετά-εγκαταστατικών βημάτων" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Perform post-install steps" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Perform post-install steps" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Exécuter des étapes post-installation" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Perform post-install steps" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Perform post-install steps" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Perform post-install steps" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "설치 후 단계 수행" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Perform post-install steps" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Perform post-install steps" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Executar passos pós-instalação" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Perform post-install steps" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ดำเนินการตามขั้นตอนหลังการติดตั้ง" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yükleme sonrası adımları uygula" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Виконати пост-інсталяційні кроки" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "执行安装后准备步骤" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "執行安裝後步驟" } } } }, "PIN" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "PIN" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "PIN" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "PIN" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "PIN" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "PIN" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "PIN" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "PIN" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "PIN" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "PIN" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "PIN" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "PIN" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "PIN" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "PIN" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "PIN" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "PIN" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "PIN" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "รหัส PIN" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "PIN" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "PIN" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "PIN" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "PIN" } } } }, "PIN not set" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "PIN not set" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "PIN not set" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "PIN not set" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "PIN not set" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "PIN not set" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "PIN not set" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "PIN not set" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "PIN not set" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "PIN not set" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "PIN not set" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "PIN not set" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "PIN not set" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "PIN not set" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "PIN not set" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "PIN não definido" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "PIN not set" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ไม่ได้ตั้งรหัส PIN" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "PIN not set" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "PIN not set" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "没有设置 PIN" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "PIN not set" } } } }, "Platforms" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "المنصات" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Platforms" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Plattformen" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Πλατφόρμες" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Platforms" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Platforms" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Platforms" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Platforms" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Platforms" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Platforms" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Platforms" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "플랫폼" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Platforms" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Platforms" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Plataformas" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Платформы" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "แพลตฟอร์ม" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Platformlar" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Платформи" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "平台" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "平台" } } } }, "PlatformsDescription" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "Installed Platforms" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Installed Platforms" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Installed Platforms" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Installed Platforms" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Installed Platforms" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Installed Platforms" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Installed Platforms" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Installed Platforms" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Installed Platforms" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Installed Platforms" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Installed Platforms" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "설치된 플랫폼 보기" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Installed Platforms" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Installed Platforms" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Plataformas instaladas" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Установленные платформы" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "แพลตฟอร์มที่ติดตั้งแล้ว" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Installed Platforms" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Installed Platforms" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "已安装的平台" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "已安裝的平台" } } } }, "PlatformsList.Title" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "فيما يلي قائمة بالأنظمة الأساسية المثبتة على هذا الجهاز." } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Below are a list of platforms that are installed on this machine. " } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Hier siehst du eine Liste der auf diesem Rechner installierten Plattformen." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Ακολουθεί λίστα με τις πλατφόρμες που είναι εγκατεστημένες σε αυτό το σύστημα." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Below are a list of platforms that are installed on this machine. " } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Below are a list of platforms that are installed on this machine. " } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Below are a list of platforms that are installed on this machine. " } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Ci-dessous une liste des plateformes installées sur cette machine." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Below are a list of platforms that are installed on this machine. " } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Below are a list of platforms that are installed on this machine. " } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Below are a list of platforms that are installed on this machine. " } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "현재 기기에 설치된 플랫폼 목록입니다." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Below are a list of platforms that are installed on this machine. " } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Below are a list of platforms that are installed on this machine. " } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Abaixo contém uma lista de plataformas que estão instalada nesse computador." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Below are a list of platforms that are installed on this machine. " } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ด้านล่างนี้เป็นรายการของแพลตฟอร์มที่ได้รับการติดตั้งบนเครื่องนี้" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Below are a list of platforms that are installed on this machine. " } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Нижче наведений список платформ що доступні на цій машині" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "下面是在这台设备上已经安装的平台列表。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "下列是本機器安裝的平台名單。" } } } }, "Preferences" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "التفضيلات" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Preferències" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Einstellungen" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Προτιμήσεις" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Preferences" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Preferencias" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Asetukset" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Préférences" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "प्राथमिकताएँ" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Preferenze" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "環境設定" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "환경설정" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Voorkeuren" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Ustawienia" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Preferências" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Hастройки" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "การตั้งค่า" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Tercihler" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Налаштування" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "偏好设置" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "偏好設定" } } } }, "PreferencesDescription" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "افتح التفضيلات" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Obrir les preferències" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Einstellungen öffnen" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Άνοιγμα Προτιμήσεων" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Open Preferences" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Abrir Preferencias" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Avaa asetukset" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Ouvrir les Préférences" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "प्राथमिकताएँ खोलें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Apri Preferenze" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "環境設定を開く" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "환경설정 열기" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Open Voorkeuren" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Otwórz ustawienia" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Abrir preferências" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Открыть настройки" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "เปิดการตั้งค่า" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Tercihleri Aç" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Відкрити Налаштування" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "打开偏好设置" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "打開偏好設定" } } } }, "PrivilegedHelper" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "مساعد مميز" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Servei Privilegiat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Privilegierter Helfer" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Βοηθητικό Εργαλείο με δικαιώματα" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Privileged Helper" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Asistente privilegiado" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Etuoikeutettu auttaja" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Assistant Privilégié" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "विशेषाधिकार प्राप्त सहायक" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Aiutante Privilegiato" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "権限のあるヘルパー" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "권한을 가진 도우미 (Privileged Helper)" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Privileged Helper" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Helper z uprawnieniami" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Ajudante privilegiado" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Привилегированный помощник" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ตัวช่วยที่มีสิทธิพิเศษ" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Ayrıcalıklı Yardımcı" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Privileged Helper" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "提权帮助程序" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "具有權限的輔助程式" } } } }, "PrivilegedHelperDescription" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "يستخدم Xcodes مساعدًا متميزًا منفصلاً لأداء المهام كجذر. هذه هي الأشياء التي تتطلب استخدام Sudo في سطر الأوامر، بما في ذلك خطوات ما بعد التثبيت وتبديل إصدارات Xcode باستخدام xcode-select.\n\nسيُطلب منك كلمة مرور حساب macOS لتثبيته." } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "L'Xcodes utilitza, a part, un servei que executa tasques com a root. Aquestes tasques son coses que requereixin la comanda sudo a la línia de comandes, incloent-hi les tasques al finalitzar l'instal·lació i canviar les versions d'Xcode seleccionades utilitzant xcode-select.\n\nEl sistema operatiu et demanarà la teva contrassenya per poder-lo instal·lar." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes verwendet einen separaten privilegierten Helfer, um Aufgaben als root zu erledigen. Das sind Dinge, die sudo in der Kommandozeile erfordern würden, einschließlich Post-Installationsschritte sowie das Umstellen von Xcode-Versionen mit xcode-select.\n\nUm ihn zu installieren, erfolgt eine Aufforderung zur Eingabe des Passworts für Dein macOS-Benutzerkonto." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Το Xcodes χρησιμοποιεί ένα ξεχωριστό εργαλείο με δικαιώματα ώστε να εκτελέσει εργασίες ως root. Αυτές είναι εργασίες που θα απαιτούσαν sudo στη γραμμή εντολών, συμπεριλαμβανομένων μετά-εγκαταστατικών βημάτων και αλλαγής μεταξύ εκδόσεων του Xcode με το xcode-select.\n\nΘα σας ζητηθεί το συνθηματικό του λογαριασμού σας στο macOS για την εγκατάσταση του." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes uses a separate privileged helper to perform tasks as root. These are things that would require sudo on the command line, including post-install steps and switching Xcode versions with xcode-select.\n\nYou'll be prompted for your macOS account password to install it." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes utiliza un asistente privilegiado independiente para realizar tareas como root. Estas son cosas que requerirían sudo en la línea de comandos, incluidos los pasos posteriores a la instalación y el cambio de versiones de Xcode con xcode-select.\n\nSe le pedirá la contraseña de su cuenta de macOS para instalarlo." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes käyttää erillistä etuoikeutettua avustajaa tehtävien suorittamiseen pääkäyttäjänä. Nämä ovat asioita, jotka edellyttävät sudo komentoa komentorivillä, mukaan lukien asennuksen jälkeiset vaiheet ja Xcode-versioiden vaihtaminen xcode-selectillä.\n\nSinua pyydetään antamaan macOS-tilisi salasana sen asentamiseksi." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes utilise un assistant privilégié distinct pour effectuer des tâches en tant que root. Ce sont des tâches qui nécessiteraient sudo sur la ligne de commande, y compris les étapes de post-installation et le changement de version de Xcode avec xcode-select.\n\nVous serez invité à saisir le mot de passe de votre compte macOS pour l'installer." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes कार्यों को रूट के रूप में करने के लिए एक अलग विशेषाधिकार प्राप्त सहायक का उपयोग करता है। ये ऐसी चीजें हैं जिनके लिए कमांड लाइन पर sudo की आवश्यकता होगी, जिसमें पोस्ट-इंस्टॉल चरण और xcode-select के साथ Xcode संस्करण स्विच करना शामिल है।\n\nइसे इंस्टॉल करने के लिए आपको अपने macOS अकाउंट पासवर्ड के लिए कहा जाएगा।" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes usa un aiutante privilegiato per svolgere dei compiti come root. Si tratta di comandi che normalmente richiederebbero sudo da linea di comando, incluse fasi di post-install e modificare la versione di Xcode con xcode-select.\n\nTi verrà richiesta la password del tuo account di macOS per installarlo." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Xcodesは、rootとしてタスクを実行するために、別の権限のあるヘルパーを使用します。インストール後の手順や xcode-select による Xcode のバージョン切り替えなど、コマンドラインで sudo を必要とするものです。\n\nインストールには、macOS アカウントのパスワードの入力が必要です。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes는 별도의 권한을 가진 도우미를 사용하여 작업을 루트로서 수행합니다. 설치 후 단계와 xcode-select로 Xcode 버전을 전환하는 것과 같이 커맨드 라인에서 sudo가 필요한 작업이 이에 해당합니다.\n\n설치하려면 macOS 계정 암호를 입력하라는 메시지가 표시됩니다." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes gebruikt een separate privilged helper om taken uit te voeren als root. Dit zijn operaties die een sudo vereisen op de command line, inclusief post-installatie stappen en het wijzigen van Xcode versies met xcode-select.\n\nJe zult worden gevraagd om je macOS account wachtwoord om deze te installeren." } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes używa oddzielnego helpera z uprawnieniami do wykonywania zadań jako root. Są to rzeczy, które wymagałyby użycia komendy sudo w wierszu poleceń, w tym kroki po instalacji oraz przełączanie wersji Xcode za pomocą xcode-select.\n\nZostaniesz poproszony o podanie hasła do swojego konta macOS, aby go zainstalować." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes usa um ajudante privilegiado separado para performar atividades como raiz. São atividades que iriam requerir sudo na linha de comando, incluindo passos pós-instalação e trocar versões do Xcode com xcode-select.\n\nVocê será pedido para instalá-lo na sua conta do macOS." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes использует отдельный привилегированный помощник для выполнения задач от имени root-пользователя. Это команды, которые потребуют sudo в командной строке, включая шаги после установки и переключение версий Xcode с помощью xcode-select.\n\nВам будет предложено указать пароль от вашей учетной записи macOS для его установки." } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes ใช้ตัวช่วยที่มีสิทธิพิเศษแยกต่างหากเพื่อดำเนินการต่าง ๆ ในฐานะ root สิ่งเหล่านี้จำเป็นต้องใช้คำสั่ง sudo บน Command Line รวมถึงขั้นตอนหลังการติดตั้งและการสลับเวอร์ชัน Xcode ด้วยคำสั่ง xcode-select\n\nระบบจะขอให้คุณป้อนรหัสผ่านของบัญชี macOS ของคุณเพื่อทำการติดตั้ง" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes, root görevlerini yerine getirmek için bir Ayrıcalıklı yardımcı aracı kullanır. Bunlar komut satırındaki sudo gerektiren, yükleme sonrası adımlarını sağlayan ve Xcode sürümü değiştiren xcode-select gibi komutlardan ibarettir.\n\nBunu yüklemek için macOS hesap şifrenizi girmeniz istenecektir." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes використовує спеціальний \"Privileged Helper\" для запуску задач від імені root. Це включає, наприклад, sudo в терміналі, та кроки після інсталяції або перемикання версії Xcode за допомогою xcode-select.\n\nБуде запит на ваш пароль від Mac щоб інсталювати цей помічник." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes使用一个独立的提权帮助程序来以root身份执行任务。就是那些需要在命令行中用sudo执行的命令。包括一些安装后准备步骤以及用xcode-select切换Xcode版本。\n\n您需要提供当前用户的密码来安装它。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes 會利用一個分開的具有權限的輔助程式以 root 身份執行特定工作,例如那些在命令列中通常需使用 sudo 的命令,包含安裝程序後的步驟以及使用 xcode-select 選擇 Xcode 版本。\n\n安裝此程式時,你將會需要輸入 macOS 帳號的密碼。" } } } }, "Refresh" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "ينعش" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Actualitzar" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Aktualisieren" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Ανανέωση" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Refresh" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Refrescar" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Päivitä" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Actualiser" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "ताज़ा करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Ricarica" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "再読み込み" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "갱신" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Verversen" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Odśwież" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Atualizar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Обновить" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "รีเฟรช" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yenile" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Оновити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "刷新" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "重新整理" } } } }, "RefreshDescription" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "تحديث قائمة Xcode" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Actualitzar la llista d'Xcode" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Xcode-Liste aktualisieren" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Ανανέωση λίστας των Xcode" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Refresh Xcode List" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Refrescar lista de Xcodes" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Päivitä Xcode lista" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Actualiser la liste des Xcode" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Xcode सूची ताज़ा करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Ricarica Lista Xcode" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Xcodeの一覧を再度取得する" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Xcode 목록 갱신" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Ververs Xcode Lijst" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Odśwież listę Xcode" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Atualizar lista de Xcode" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Обновить список Xcode" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "รีเฟรชรายการของ Xcode" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Xcode Listesini Yenile" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Оновити список Xcode" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "刷新Xcode列表" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "更新 Xcode 版本列表" } } } }, "Release" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "اصدار" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Versió" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Release" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Κυκλοφορία" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Release" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Liberado" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Julkaisuversio" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Version" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "रिलीज़" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Rilasci" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "リリース" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "출시 버전" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Release" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Stabilne" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Release" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Pелиз" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ปล่อย" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kararlı Sürüm" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Реліз" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "正式版" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "正式版" } } } }, "ReleaseDate" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "تاريخ الاصدار" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Data de la versió" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Release-Datum" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Ημερομηνία κυκλοφορίας" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Release Date" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Fecha de lanzamiento" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Julkaisupäivä" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Date de Sortie" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "रिलीज़ दिनांक" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Data di Rilascio" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "リリース日" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "출시일" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Release Datum" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Data wydania" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Data de lançamento" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Дата выпуска" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "วันที่ปล่อย" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yayınlanma Tarihi" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Дата релізу" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "发布日期" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "發行日期" } } } }, "ReleaseNotes.help" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "عرض ملاحظات الإصدار" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Veure les notes de la versió" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Release-Notes anzeigen" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Σημειώσεις κυκλοφορίας" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "View Release Notes" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Ver Notas del Lanzamiento" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Lue julkaisutiedot" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Consulter les Notes de Mise á Jour" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "रिलीज़ नोट्स देखें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Vedi Note di Rilascio" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "リリースノートを表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "릴리즈 노트 보기" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Toon Release Notes" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Wyświetl notatki wydania" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Visualizar notas de lançamento" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Просмотреть примечания к выпуску" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ดูข้อมูลการปล่อย" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yayınlanma Notlarını Görüntüle" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Переглянути деталі релізу" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "查看更新说明" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "檢視版本附註" } } } }, "ReleaseOnly" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "الإصدار فقط" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Només versions oficials" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Nur Release" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Εκδόσεις κυκλοφορίας μόνο" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Release only" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Solo Liberados" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Vain julkaisuversiot" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Seulement le versions finales" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "केवल रिलीज़ करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Solo Rilasci" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "リリース版のみ" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "출시 버전만" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Alleen release" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Tylko stabilne" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Somente release" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Только релиз" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ปล่อยเท่านั้น" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yalnızca Kararlı Sürüm" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "лише Release" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "只显示正式版" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "只顯示正式版" } } } }, "Required" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "مطلوب" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Requerida" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Erforderlich" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Απαιτείται" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Requerido" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Vaaditaan" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Requis" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "आवश्यक" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Obbligatorio" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "必須" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "필수 입력" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Vereist" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Wymagane" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Obrigatório" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Обязательно" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "จำเป็น" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Gerekli" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Вимагається" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "必填" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "必要" } } } }, "RevealInFinder" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "تظهر في البحث" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Obrir al Finder" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Im Finder anzeigen" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Αποκάλυψη στο Finder" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Reveal in Finder" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar en el Finder" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Näytä finderissa" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Ouvrir dans le Finder" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "खोजक में प्रकट करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Mostra in Finder" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Finderに表示" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Finder에서 보기" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Toon in Finder" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Pokaż w Finderze" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Abrir no Finder" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Показать в Finder" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "แสดงใน Finder" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Finder'da Görüntüle" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Показати у Finder" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "在访达中显示" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "在 Finder 中顯示" } } } }, "SDKs" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "SDKs" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "SDKs" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "SDKs" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "SDKs" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "SDKs" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "SDKs" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "SDKs" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "SDKs" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "SDKs" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "SDK" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "SDKs" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "SDKs" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "SDK" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "SDKs" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "SDK" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "SDKs" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "SDKler" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "SDK" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "SDK" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "SDK 清單" } } } }, "SecurityKeyPinDescription" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "Insert your physical security key and enter the PIN" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Insert your physical security key and enter the PIN" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Insert your physical security key and enter the PIN" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Insert your physical security key and enter the PIN" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Insert your physical security key and enter the PIN" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Insert your physical security key and enter the PIN" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Insert your physical security key and enter the PIN" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Insert your physical security key and enter the PIN" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Insert your physical security key and enter the PIN" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Insert your physical security key and enter the PIN" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Insert your physical security key and enter the PIN" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Insert your physical security key and enter the PIN" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Insert your physical security key and enter the PIN" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Insert your physical security key and enter the PIN" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Insira sua chave de segurança física e digite o PIN" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Insert your physical security key and enter the PIN" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ใส่คีย์ความปลอดภัยของคุณและป้อน PIN" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Insert your physical security key and enter the PIN" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Insert your physical security key and enter the PIN" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Insert your physical security key and enter the PIN" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Insert your physical security key and enter the PIN" } } } }, "SecurityKeyTouchDescription" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "Touch your security key to verify that it’s you" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Touch your security key to verify that it’s you" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Touch your security key to verify that it’s you" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Touch your security key to verify that it’s you" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Touch your security key to verify that it’s you" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Touch your security key to verify that it’s you" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Touch your security key to verify that it’s you" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Touch your security key to verify that it’s you" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Touch your security key to verify that it’s you" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Touch your security key to verify that it’s you" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Touch your security key to verify that it’s you" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Touch your security key to verify that it’s you" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Touch your security key to verify that it’s you" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Touch your security key to verify that it’s you" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Toque na sua chave de segurança para verificar que é você" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Touch your security key to verify that it’s you" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "แตะคีย์ความปลอดภัยของคุณเพื่อยืนยันว่าเป็นคุณ" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Touch your security key to verify that it’s you" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Touch your security key to verify that it’s you" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Touch your security key to verify that it’s you" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Touch your security key to verify that it’s you" } } } }, "Select" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "حدد" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Seleccionar" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Auswählen" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Επιλογή" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Seleccionar" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Valitse" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Sélectionner" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "चयन करे" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Seleziona" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "アクティブにする" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "선택" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Selecteer" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Wybierz" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Selecionar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Выбрать" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "เลือก" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Seç" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Обрати" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "选定" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "選取" } } } }, "Selected" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "المحدد" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Seleccionat" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Ausgewählt" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Επιλεγμένα" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Seleccionado" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Valittu" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Sélectionné" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "चयनित" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Selezionato" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "アクティブ" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "선택됨" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Geselecteerd" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Wybrany" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Selecionado(s)" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Выбрано" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ถูกเลือก" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Seçili" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Обрано" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "已选定" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "已選取" } } } }, "SelectTrustedPhone" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "حدد رقم هاتف موثوقًا به لتلقي رمز مكون من %d عبر رسالة نصية قصيرة:" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Selecciona un número de mòbil de confiança on rebre el codi de %d dígits a través d'SMS:" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Wähle eine vertrauenswürdige Telefonnummer aus, um einen %d-stelligen Code via SMS zum empfangen:" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Επιλέξτε έναν αξιόπιστο τηλεφωνικό αριθμό για να λάβετε έναν %d-ψήφιο κωδικό με SMS:" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Select a trusted phone number to receive a %d digit code via SMS:" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Selecciona un número de teléfono de confianza para recibir un código de %d dígitos por SMS:" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Valitse luotettu puhelinnumero saadaksesi %d numeroisen koodin tekstiviestinä:" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Sélectionnez le numéro de téléphone de confiance pour recevoir un code à %d chiffres par SMS :" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "SMS के द्वारा %d अंक कोड प्राप्त करने के लिए विश्वसनीय फ़ोन नंबर चुनें:" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Seleziona un numero di telefono registrato per ricevere il codice di %d cifre via SMS:" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "信頼できる電話番号を選択し、%d桁のコードをSMSで受信します。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "SMS를 통해 %d 자리 코드를 수신하려면 신뢰할 수 있는 전화번호를 선택하세요." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Selecteer een vertrouwd telefoonnumer on een %d code te ontvangen via SMS:" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Wybierz zaufany numer telefonu, aby otrzymać %d-cyfrowy kod przez SMS:" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Selecione um número de telefone confiável para receber um código de %d dígitos via SMS:" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Выберите доверенный номер телефона для получения %d цифрового кода по SMS:" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "เลือกหมายเลขโทรศัพท์ที่เชื่อถือได้เพื่อรับรหัส %d หลักผ่าน SMS:" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "%d rakamlı kodu SMS olarak almak için güvenilir telefon numarasını seç:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Виберіть довірений номер телефону щоб отримати %d-значний код в СМС:" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "请选择一个信任的手机号来从短信接收%d位代码:" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "請輸入一個你想用來接收 %d 位數密碼簡訊的電話號碼:" } } } }, "SendSMS" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "Send SMS" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Enviar SMS" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "SMS senden" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Αποστολή SMS" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Send SMS" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Enviar SMS" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Lähetä tekstiviesti" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Envoyer un SMS" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "SMS भेजें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Manda SMS" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "SMS を送信" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "SMS 보내기" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Verstuur SMS" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Wyślij SMS" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Enviar SMS" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Отправить SMS" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ส่ง SMS" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "SMS Gönder" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Надіслати СМС" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "发送短信" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "傳送簡訊" } } } }, "ShowOpenInRosetta" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "إظهار خيار فتح في Rosetta" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Show Open In Rosetta option" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Show Open In Rosetta option" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Εμφάνιση της επιλογής «Άνοιγμα με Rosetta»" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Show Open In Rosetta option" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Show Open In Rosetta option" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Show Open In Rosetta option" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Afficher l'option Ouvrir avec Rosetta" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Show Open In Rosetta option" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Show Open In Rosetta option" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Rosettaで開くオプションを表示する" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "'Rosetta를 사용하여 열기' 옵션 표시하기" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Show Open In Rosetta option" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Pokaż opcję Otwórz w Rosetta" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Mostrar opção abrir com Rosetta" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Показать опцию «Открыть в Rosetta»" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "แสดงตัวเลือกให้เปิดใน Rosetta" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Rosetta ile açma seçeneğini göster" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Показувати опцію \"Запустити через Rosetta\"" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "显示“在Rosetta中打开”选项" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "顯示「使用 Rosetta 打開」選項" } } } }, "ShowOpenInRosettaDescription" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "سيُظهر خيار \"فتح في Rosetta\" الأماكن التي تتوفر فيها وظائف \"فتح\" أخرى. ملاحظة: سيظهر هذا فقط لأجهزة Apple Silicon." } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Open in Rosetta option will show where other \"Open\" functions are available. Note: This will only show for Apple Silicon machines." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Open in Rosetta option will show where other \"Open\" functions are available. Note: This will only show for Apple Silicon machines." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Η επιλογή «Άνοιγμα με Rosetta» θα εμφανίζεται όπου είναι διαθέσιμες οι υπόλοιπες επιλογές «Άνοιγμα». Σημείωση: Θα εμφανίζεται μόνο σε συστήματα με Apple Silicon." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Open in Rosetta option will show where other \"Open\" functions are available. Note: This will only show for Apple Silicon machines." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Open in Rosetta option will show where other \"Open\" functions are available. Note: This will only show for Apple Silicon machines." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Open in Rosetta option will show where other \"Open\" functions are available. Note: This will only show for Apple Silicon machines." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Ouvrir avec Rosetta s'affichera d'autres options \"Ouvrir\" sont disponibles. Note: Uniquement pour les machines Apple Silicon." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Open in Rosetta option will show where other \"Open\" functions are available. Note: This will only show for Apple Silicon machines." } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Open in Rosetta option will show where other \"Open\" functions are available. Note: This will only show for Apple Silicon machines." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Rosettaで開くオプションは \"開く\" 機能が利用できる場所に表示されます。注意: これはApple Siliconマシンにのみ表示されます。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "다른 '열기' 옵션이 가능한 경우 'Rosetta를 사용하여 열기' 옵션을 함께 표시합니다. (이 옵션은 Apple Silicon 기기에서 표시됩니다.)" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Open in Rosetta option will show where other \"Open\" functions are available. Note: This will only show for Apple Silicon machines." } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Opcja Otwórz w Rosetta będzie wyświetlana, gdy dostępne są inne funkcje \"Otwórz\". Uwaga: Będzie to działać tylko na urządzeniach z Apple Silicon." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Opção de abrir com o Rosetta será mostrar onde outras opções \"Abrir\" estão disponíveis. Nota: Isso só será mostrado pra máquinas Apple Silicon." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Опция «Открыть в Rosetta» будет показана там, где доступны другие функции «Открыть». Примечание: опция будет отображаться только для компьютеров Apple Silicon." } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ตัวเลือก \"เปิดใน Rosetta\" จะแสดงเมื่อมีตัวเลือกในการเปิดอื่น ๆ ที่พร้อมใช้งานอยู่เท่านั้น หมายเหตุ: ตัวเลือกนี้จะแสดงเฉพาะเครื่อง Apple Silicon เท่านั้น" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Rosetta ile açma opsiyonu diğer uygun \"Açma\" fonksiyonlarını gösterecektir. Not: Bu sadece Apple Silikon makinelerinde gözükecektir." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Опція \"Запустити через Rosetta\" зʼявиться там де наразі знаходиться функція \"Запустити\".\nПримітка: Доступно лише для Apple Silicon машин." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "“在Rosetta中打开”将会在有其他“打开”方式可用时显示。注:此选项只会在Apple Silicon设备上显示。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "「使用 Rosetta 打開」選項會在「打開」選項所在的區塊顯示。注意:這個選項只會在 Apple Silicon 機器顯示。" } } } }, "SignIn" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "تسجيل الدخول" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Iniciar sessió" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Anmelden" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Σύνδεση" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Sign In" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Iniciar Sesión" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Kirjaudu sisään" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Connecter" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "साइन इन करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Entra" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "サインイン" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "로그인하기" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Inloggen" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Zaloguj się" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Entrar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Войти" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "เข้าสู่ระบบ" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Giriş Yap" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Увійти" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "登录" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "登入" } } } }, "SignInWithApple" : { "comment" : "SignIn", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "تسجيل الدخول بواسطة ابل " } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Inicia sessió amb el teu Apple ID." } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Mit Deiner Apple-ID anmelden." } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Συνδεθείτε με το Apple ID σας." } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Sign in with your Apple ID." } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Inicia sesión con tu ID de Apple." } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Kirjaudu sisään Apple ID:lläsi." } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Connectez-vous avec votre identifiant Apple." } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "अपनी Apple ID से साइन इन करें." } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Entra con il tuo Apple ID." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Apple IDでサインインしてください。" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Apple ID로 로그인하세요." } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Log in met je Apple ID." } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Zaloguj się przy użyciu Apple ID." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Entrar com o seu Apple ID." } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Войти со своим Apple ID." } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "เข้าสู่ระบบด้วย Apple ID" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Apple ID ile Giriş Yap." } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Вхід з вашим Apple ID." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "使用您的Apple ID登录。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "使用您的 Apple ID 登入。" } } } }, "SignOut" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "تسجيل الخروج" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Tancar sessió" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Abmelden" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Αποσύνδεση" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Sign Out" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Cerrar Sesión" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Kirjaudu ulos" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Déconnecter" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "साइन आउट करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Esci" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "サインアウト" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "로그아웃" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Uitloggen" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Wyloguj się" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Sair" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Выход" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ออกจากระบบ" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Oturumu Kapat" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Логаут" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "登出" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "登出" } } } }, "StopInstallation" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "وقف التثبيت" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Aturar l'instal·lació" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Installation stoppen" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Διακοπή εγκατάστασης" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Stop installation" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Detener Instalación" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Lopeta asennus" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Arrêter l'installation" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "स्थापना रोकें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Ferma istallazione" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "インストールを中止" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "설치 중지" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Stop installatie" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Przerwij instalację" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Interromper instalação" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Остановить установку" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "หยุดการติดตั้ง" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "İndirmeyi durdur" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Зупинити інсталяцію" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "停止安装" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "停止安裝" } } } }, "Support.Xcodes" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "xcodesدعم " } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Support Xcodes" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Support Xcodes" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Υποστηρίξτε το Xcodes" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Support Xcodes" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Support Xcodes" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Support Xcodes" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Support Xcodes" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Support Xcodes" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Support Xcodes" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Support Xcodes" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes 후원하기" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Support Xcodes" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Support Xcodes" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Ajude o Xcodes" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Поддержать Xcodes" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "สนับสนุน Xcodes" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Support Xcodes" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Підтримати Xcodes" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "支持Xcodes" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "支持 Xcodes" } } } }, "TerminateAfterLastWindowClosed" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "Terminate App after last window is closed" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Terminate App after last window is closed" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Terminate App after last window is closed" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Terminate App after last window is closed" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Terminate App after last window is closed" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Terminate App after last window is closed" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Terminate App after last window is closed" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Terminate App after last window is closed" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Terminate App after last window is closed" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Terminate App after last window is closed" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Terminate App after last window is closed" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Terminate App after last window is closed" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Terminate App after last window is closed" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Terminate App after last window is closed" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Encerrar o aplicativo quando a ultima janela for fechada" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Terminate App after last window is closed" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ยุติการทำงานของแอปเมื่อหน้าต่างสุดท้ายถูกปิดลง" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Terminate App after last window is closed" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Terminate App after last window is closed" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "在最后一个窗口关闭后终止App" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "在最後一個窗口關閉後終止 App" } } } }, "TrashingArchive" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "نقل الأرشيف إلى سلة المهملات" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Movent l'arxiu comprimit a la paperera" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Archiv in den Papierkorb bewegen" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Μετακίνηση αρχειοθήκης στον Κάδο" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Moving archive to the Trash" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Mover archivo a la Papelera" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Siirretään arkistoa roskakoriin" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Déplacement de l'archive vers la corbeille" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "संग्रह को ट्रैश में ले जाया जा रहा है" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Sposto archivio nel Cestino" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "アーカイブをゴミ箱に移動しています" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "아카이브를 휴지통으로 이동" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Bezig met archief naar Prullenbak verplaatsen" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Przenoszenie archiwum do kosza" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Movendo arquivo para a lixeira" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Перемещение архива в корзину" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "กำลังย้ายไฟล์ไปที่ถังขยะ" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Arşiv Çöp Kutusuna taşınıyor" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Видалення архіву" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "正在将压缩文件移动到废纸篓" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "正在將封存檔移動至垃圾桶" } } } }, "Unarchiving" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "إلغاء الأرشفة (قد يستغرق هذا بعض الوقت)" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Descomprimint (pot trigar una estona)" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Entpacken (Dies kann etwas dauern)" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Αποσυμπίεση (Ίσως χρειαστεί λίγος χρόνος)" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Unarchiving (This can take a while)" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Desarchivando (esto puede llevar un tiempo)" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Poistetaan arkistointi (tämä voi kestää hetken)" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Décompression (cela peut prendre un certain temps)" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Unarchiving (इसमें कुछ समय लग सकता है)" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Espandendo (Potrà metterci un po')" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "アーカイブを展開しています (時間がかかることがあります)" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "언아카이빙 중 (시간이 좀 걸릴 수 있습니다)" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Dearchiveren (Dit kan even duren)" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Rozpakowywanie (To może chwilę potrwać)" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Desarquivando (Pode demorar um pouco)" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Разархивирование (это может занять некоторое время)" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "กำลังแตกไฟล์ (อาจใช้เวลาสักครู่)" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Arşivden Çıkarılıyor (Bu biraz sürebilir)" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Розпаковка (Може бути довго)" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "正在解压(这将需要一些时间)" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "解壓縮中 (可能需要一點時間)" } } } }, "Uninstall" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "الغاء التثبيت" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Desinstal·lar" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Deinstallieren" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Απεγκατάσταση" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Desinstalar" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Poista" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Désinstaller" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "असंस्थापित करे" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Disinstalla" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "アンインストール" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "제거" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Deinstalleren" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Odinstaluj" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Desinstalar" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Удалить" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ถอนการติดตั้ง" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Kaldır" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Видалити" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "卸载" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "解除安裝" } } } }, "Universal" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "Universal" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Universal" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Universal" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Universal" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Universal" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Universal" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Universal" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Universal" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Universal" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Universal" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Universal" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Universal" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Universal" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Universal" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Universal" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "Universal" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Universal" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Universal" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Universal" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Universal" } } } }, "UnxipExperiment" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "تجربة فك الضغط" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Experiment d'Unxip" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Unxip-Experiment" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Πείραμα Unxip" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Unxip Experiment" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Experimento Unxip" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Unxip Kokeilu" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Expérimentation Unxip" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "अनज़िप प्रयोग" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Unxip Sperimentale" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Unxip Experiment" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Unxip 실험" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Unxip Experiment" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Eksperyment Unxip" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Experimento Unxip" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Экспериментальный Unxip" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "Unxip Experiment" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Tecrübeyi çıkart" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Експеримент Unxip" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Unxip Experiment" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Unxip 試驗" } } } }, "UpdatedAt" : { "comment" : "MainWindow", "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "تم التحديث في" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Actualitzat el" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Aktualisiert am" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Τελευταία ενημέρωση" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Updated at" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Actualizado en" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Päivitetty ajankohtana" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Mis à jour le " } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "पर अपडेट किया गया" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Aggiorna a" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "前回の更新:" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "마지막 업데이트 시점" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Bijgewerkt op" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Zaktualizowano o" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Atualizado em" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Обновлено" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "อัปเดตเมื่อ" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Güncellenme Zamanı:" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Оновлено" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "更新于" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "上一次檢查:" } } } }, "Updates" : { "comment" : "Updates Preference Pane", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "التحديثات" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Actualitzacions" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Updates" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Ενημερώσεις" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Actualizaciones" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Päivitysasetukset" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Mise à jour" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "अपडेट" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Aggiornamenti" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "アップデート" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "업데이트" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Updates" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Aktualizacje" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Atualizações" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Обновления" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "อัปเดต" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Güncellemeler" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Оновлення" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "更新" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "更新" } } } }, "UseUnxipExperiment" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "عند فك الضغط استخدم التجربة" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Quan es fa l'unxip, utilitza l'experiment" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Beim Unxipping, Experiment verwenden" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Χρήση πειραματικού εργαλείου κατά το unxipping" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "When unxipping, use experiment" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Al descomprimir, usar experimento" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Kun purat xip-tiedoston, käytä kokeiluversiota" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Lors de la décompression Unxip, utiliser l'expérimentation" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "अनज़िपिंग करते समय, प्रयोग का उपयोग करें" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Quando fai unxipping, usa sperimentale" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "試験的な Unxip の実装を使う" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "unxipping 시 실험 기능 사용" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Bij unxipping, gebruik expiriment" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Podczas rozpakowywania, użyj wersji eksperymentalnej" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Quando performar unxipping, use experiment" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "При выполнении unxip будет использован экспериментальный функционал" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "ใช้ unxip ในการแตกไฟล์" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Unxip yaparken dene" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Експериментальний метод розпаковки" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "使用实验性功能解压xip" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "解壓縮 (Unxip) 時,使用這個試驗版本" } } } }, "Versions" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "Versions" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Versions" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Versionen" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Εκδόσεις" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Versiones" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Versiot" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Versions" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "संस्करण" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Versioni" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "バージョン" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "버전" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Versies" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Wersje" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Versões" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Версии" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "เวอร์ชัน" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Sürümler" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Версії" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "版本" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "版本" } } } }, "VersionWithBuild" : { "comment" : "About", "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "إصدار ٪@ (٪@)" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Versió %@ (%@)" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Version %@ (%@)" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Έκδοση %@ (%@)" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Version %@ (%@)" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Versión %@ (%@)" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Versio %@ (%@)" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Version %@ (%@)" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "संस्करण %@ (%@)" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Versione %@ (%@)" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "バージョン %@ (%@)" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "버전 %@ (%@)" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Versie %@ (%@)" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Wersja %@ (%@)" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Versão %@ (%@)" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Версия %@ (%@)" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "เวอร์ชัน %@ (%@)" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Sürüm %@ (%@)" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Версія %@ (%@)" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "版本%@ (%@)" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "版本 %@ (%@)" } } } }, "WWDC.Message" : { "comment" : "WWDC", "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "👨🏻‍💻👩🏼‍💻 سعيد WWDC %@! 👨🏽‍💻🧑🏻‍💻" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "👨🏻‍💻👩🏼‍💻 Feliç WWDC %@! 👨🏽‍💻🧑🏻‍💻" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "👨🏻‍💻👩🏼‍💻 Happy WWDC %@! 👨🏽‍💻🧑🏻‍💻" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "👨🏻‍💻👩🏼‍💻 Χαρούμενο WWDC %@! 👨🏽‍💻🧑🏻‍💻" } }, "en" : { "stringUnit" : { "state" : "translated", "value" : "👨🏻‍💻👩🏼‍💻 Happy WWDC %@! 👨🏽‍💻🧑🏻‍💻" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "👨🏻‍💻👩🏼‍💻 Happy WWDC %@! 👨🏽‍💻🧑🏻‍💻" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "👨🏻‍💻👩🏼‍💻 Hyvää WWDC tapahtumaa %@! 👨🏽‍💻🧑🏻‍💻" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "👨🏻‍💻👩🏼‍💻 Happy WWDC %@! 👨🏽‍💻🧑🏻‍💻" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "👨🏻‍💻👩🏼‍💻 Happy WWDC %@! 👨🏽‍💻🧑🏻‍💻" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "👨🏻‍💻👩🏼‍💻 Happy WWDC %@! 👨🏽‍💻🧑🏻‍💻" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "👨🏻‍💻👩🏼‍💻 Happy WWDC %@! 👨🏽‍💻🧑🏻‍💻" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "👨🏻‍💻👩🏼‍💻 Happy WWDC %@! 👨🏽‍💻🧑🏻‍💻" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "👨🏻‍💻👩🏼‍💻 Happy WWDC %@! 👨🏽‍💻🧑🏻‍💻" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "👨🏻‍💻👩🏼‍💻 Wesołego WWDC %@! 👨🏽‍💻🧑🏻‍💻" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "👨🏻‍💻👩🏼‍💻 Feliz WWDC %@! 👨🏽‍💻🧑🏻‍💻" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "👨🏻‍💻👩🏼‍💻 Счастливого WWDC %@! 👨🏽‍💻🧑🏻‍💻" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "👨🏻‍💻👩🏼‍💻 Happy WWDC %@! 👨🏽‍💻🧑🏻‍💻" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "👨🏻‍💻👩🏼‍💻 WWDC %@ haftanız kutlu olsun! 👨🏽‍💻🧑🏻‍💻" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "👨🏻‍💻👩🏼‍💻 Веселого WWDC %@! 👨🏽‍💻🧑🏻‍💻" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "👨🏻‍💻👩🏼‍💻 Happy WWDC %@! 👨🏽‍💻🧑🏻‍💻" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "👨🏻‍💻👩🏼‍💻 慶祝 WWDC %@!👨🏽‍💻🧑🏻‍💻" } } } }, "Xcode" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "Xcode" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Xcode" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Xcode" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Xcode" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Xcode" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Xcode" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Xcode" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Xcode" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Xcode" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Xcode" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Xcode" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Xcode" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Xcode" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Xcode" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Xcode" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "Xcode" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Xcode" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Xcode" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Xcode" } } } }, "Xcodes" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes" } }, "ca" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes" } }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes" } }, "el" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes" } }, "es" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes" } }, "fi" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes" } }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes" } }, "hi" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes" } }, "it" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes" } }, "ko" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes" } }, "nl" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes" } }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes" } }, "ru" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes" } }, "th" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes" } }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes" } }, "uk" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes" } } } } }, "version" : "1.0" } ================================================ FILE: Xcodes/Resources/Xcodes.entitlements ================================================ ================================================ FILE: Xcodes/Resources/XcodesIcon.icon/icon.json ================================================ { "fill" : { "automatic-gradient" : "extended-srgb:0.00000,0.47843,1.00000,1.00000" }, "groups" : [ { "layers" : [ { "glass" : false, "hidden" : false, "image-name-specializations" : [ { "value" : "Light.png" }, { "appearance" : "dark", "value" : "Dark.png" }, { "appearance" : "tinted", "value" : "Mono.png" } ], "name" : "Dark", "position" : { "scale" : 1, "translation-in-points" : [ 0, 0 ] } } ], "shadow" : { "kind" : "neutral", "opacity" : 0.5 }, "specular" : false, "translucency" : { "enabled" : false, "value" : 0.5 } } ], "supported-platforms" : { "squares" : [ "macOS" ] } } ================================================ FILE: Xcodes/Resources/XcodesTest.entitlements ================================================ ================================================ FILE: Xcodes/Resources/aria2c.LICENSE ================================================ GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. ================================================ FILE: Xcodes/XcodesApp.swift ================================================ import AppKit import Sparkle import SwiftUI @main struct XcodesApp: App { @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate: AppDelegate @SwiftUI.Environment(\.scenePhase) private var scenePhase: ScenePhase @SwiftUI.Environment(\.openURL) var openURL: OpenURLAction @StateObject private var appState = AppState() @StateObject private var updater = ObservableUpdater() var body: some Scene { Window("Xcodes", id: "main") { MainWindow() .environmentObject(appState) .environmentObject(updater) // This is intentionally used on a View, and not on a WindowGroup, // so that it's triggered when an individual window's phase changes instead of all window phases. // When used on a View it's also invoked on launch, which doesn't occur with a WindowGroup. // FB8954581 ScenePhase read from App doesn't return a value on launch .onChange(of: scenePhase) { newScenePhase in guard !isTesting else { return } if case .active = newScenePhase { appState.updateIfNeeded() appState.updateInstalledRuntimes() } } } .commands { CommandGroup(replacing: .appInfo) { Button("Menu.About") { appDelegate.showAboutWindow() } } CommandGroup(after: .appInfo) { Button("Menu.CheckForUpdates") { updater.checkForUpdates() } } CommandGroup(after: CommandGroupPlacement.newItem) { Button("Refresh") { appState.update() } .keyboardShortcut(KeyEquivalent("r")) .disabled(appState.isUpdating) } XcodeCommands(appState: appState) CommandGroup(replacing: CommandGroupPlacement.help) { Button("Menu.GitHubRepo") { let xcodesRepoURL = URL(string: "https://github.com/XcodesOrg/XcodesApp/")! openURL(xcodesRepoURL) } Divider() Button("Menu.ReportABug") { let bugReportURL = URL(string: "https://github.com/XcodesOrg/XcodesApp/issues/new?assignees=&labels=bug&template=bug_report.md&title=")! openURL(bugReportURL) } Button("Menu.RequestNewFeature") { let featureRequestURL = URL(string: "https://github.com/XcodesOrg/XcodesApp/issues/new?assignees=&labels=enhancement&template=feature_request.md&title=")! openURL(featureRequestURL) } } } #if os(macOS) Settings { PreferencesView() .environmentObject(appState) .environmentObject(updater) .alert(item: $appState.presentedPreferenceAlert, content: { presentedAlert in alert(for: presentedAlert) }) } Window("Platforms", id: "platforms") { PlatformsListView() .environmentObject(appState) .alert(item: $appState.presentedPreferenceAlert, content: { presentedAlert in alert(for: presentedAlert) }) } #endif } private func alert(for alertType: XcodesPreferencesAlert) -> Alert { switch alertType { case let .deletePlatform(runtime): return Alert( title: Text(String(format: localizeString("Alert.DeletePlatform.Title"), runtime.name)), primaryButton: .destructive( Text("Alert.DeletePlatform.PrimaryButton"), action: { Task { do { try await self.appState.deleteRuntime(runtime: runtime) } catch { var errorString: String if let error = error as? String { errorString = error } else { errorString = error.localizedDescription } self.appState.presentedPreferenceAlert = .generic(title: "Error", message: errorString) } } } ), secondaryButton: .cancel(Text("Cancel")) ) case let .generic(title, message): return Alert( title: Text(title), message: Text(message), dismissButton: .default( Text("OK"), action: { appState.presentedAlert = nil } ) ) } } } class AppDelegate: NSObject, NSApplicationDelegate { private lazy var aboutWindow = configure(NSWindow( contentRect: .zero, styleMask: [.closable, .resizable, .miniaturizable, .titled], backing: .buffered, defer: false )) { $0.title = localizeString("About") $0.contentView = NSHostingView(rootView: AboutView(showAcknowledgementsWindow: showAcknowledgementsWindow)) $0.isReleasedWhenClosed = false } private let acknowledgementsWindow = configure(NSWindow( contentRect: .zero, styleMask: [.closable, .resizable, .miniaturizable, .titled], backing: .buffered, defer: false )) { $0.title = localizeString("Acknowledgements") $0.contentView = NSHostingView(rootView: AcknowledgmentsView()) $0.isReleasedWhenClosed = false } /// If we wanted to use only SwiftUI API to do this we could make a new WindowGroup and use openURL and handlesExternalEvents. /// WindowGroup lets the user open more than one window right now, which is a little strange for an About window. /// (It's also weird that the main Xcode list window can be opened more than once, there should only be one.) /// To work around this, an AppDelegate holds onto a single instance of an NSWindow that is shown here. /// FB8954588 Scene / WindowGroup is missing API to limit the number of windows that can be created func showAboutWindow() { aboutWindow.center() aboutWindow.makeKeyAndOrderFront(nil) } func showAcknowledgementsWindow() { acknowledgementsWindow.center() acknowledgementsWindow.makeKeyAndOrderFront(nil) } func applicationDidFinishLaunching(_: Notification) {} func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return Current.defaults.bool(forKey: "terminateAfterLastWindowClosed") ?? false } } func localizeString(_ key: String, comment: String = "") -> String { return String(localized: String.LocalizationValue(key)) } ================================================ FILE: Xcodes/XcodesKit/.gitignore ================================================ .DS_Store /.build /Packages /*.xcodeproj xcuserdata/ DerivedData/ .swiftpm/config/registries.json .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata .netrc ================================================ FILE: Xcodes/XcodesKit/Package.swift ================================================ // swift-tools-version: 5.7 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "XcodesKit", platforms: [.macOS(.v13)], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( name: "XcodesKit", targets: ["XcodesKit"]), ], dependencies: [ // Dependencies declare other packages that this package depends on. .package(url: "https://github.com/RobotsAndPencils/AsyncHTTPNetworkService", branch: "main"), .package(url: "https://github.com/mxcl/Path.swift", from: "1.0.0"), ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. // Targets can depend on other targets in this package, and on products in packages this package depends on. .target( name: "XcodesKit", dependencies: [ .product(name: "AsyncNetworkService", package: "AsyncHTTPNetworkService"), .product(name: "Path", package: "Path.swift") ]), .testTarget( name: "XcodesKitTests", dependencies: ["XcodesKit"]), ] ) ================================================ FILE: Xcodes/XcodesKit/README.md ================================================ # XcodesKit A description of this package. ================================================ FILE: Xcodes/XcodesKit/Sources/XcodesKit/Extensions/Foundation.swift ================================================ import Foundation extension NSRegularExpression { func firstString(in string: String, options: NSRegularExpression.MatchingOptions = []) -> String? { let range = NSRange(location: 0, length: string.utf16.count) guard let firstMatch = firstMatch(in: string, options: options, range: range), let resultRange = Range(firstMatch.range, in: string) else { return nil } return String(string[resultRange]) } } ================================================ FILE: Xcodes/XcodesKit/Sources/XcodesKit/Extensions/Logger.swift ================================================ import Foundation import os.log extension Logger { private static var subsystem = Bundle.main.bundleIdentifier! static public let appState = Logger(subsystem: subsystem, category: "appState") static public let helperClient = Logger(subsystem: subsystem, category: "helperClient") static public let subprocess = Logger(subsystem: subsystem, category: "subprocess") } ================================================ FILE: Xcodes/XcodesKit/Sources/XcodesKit/Models/Runtimes/CoreSimulatorImage.swift ================================================ // // CoreSimulatorImage.swift // // // Created by Matt Kiazyk on 2023-01-08. // import Foundation public struct CoreSimulatorPlist: Decodable { public let images: [CoreSimulatorImage] public init(images: [CoreSimulatorImage]) { self.images = images } } public struct CoreSimulatorImage: Decodable, Identifiable, Equatable { public var id: String { return uuid } public let uuid: String public let path: [String: String] public let runtimeInfo: CoreSimulatorRuntimeInfo public init(uuid: String, path: [String : String], runtimeInfo: CoreSimulatorRuntimeInfo) { self.uuid = uuid self.path = path self.runtimeInfo = runtimeInfo } public static func == (lhs: CoreSimulatorImage, rhs: CoreSimulatorImage) -> Bool { lhs.id == rhs.id } } public struct CoreSimulatorRuntimeInfo: Decodable { public let build: String public let supportedArchitectures: [Architecture]? public init(build: String, supportedArchitectures: [Architecture]? = nil) { self.build = build self.supportedArchitectures = supportedArchitectures } } ================================================ FILE: Xcodes/XcodesKit/Sources/XcodesKit/Models/Runtimes/RuntimeInstallState.swift ================================================ // // RuntimeInstallState.swift // // // Created by Matt Kiazyk on 2023-11-23. // import Foundation import Path public enum RuntimeInstallState: Equatable, Hashable { case notInstalled case installing(RuntimeInstallationStep) case installed var notInstalled: Bool { switch self { case .notInstalled: return true default: return false } } var installing: Bool { switch self { case .installing: return true default: return false } } var installed: Bool { switch self { case .installed: return true default: return false } } } ================================================ FILE: Xcodes/XcodesKit/Sources/XcodesKit/Models/Runtimes/RuntimeInstallationStep.swift ================================================ // // RuntimeInstallationStep.swift // // // Created by Matt Kiazyk on 2023-11-23. // import Foundation public enum RuntimeInstallationStep: Equatable, CustomStringConvertible, Hashable { case downloading(progress: Progress) case installing case trashingArchive public var description: String { "(\(stepNumber)/\(stepCount)) \(message)" } public var message: String { switch self { case .downloading: return localizeString("Downloading") case .installing: return localizeString("Installing") case .trashingArchive: return localizeString("TrashingArchive") } } public var stepNumber: Int { switch self { case .downloading: return 1 case .installing: return 2 case .trashingArchive: return 3 } } public var stepCount: Int { 3 } } ================================================ FILE: Xcodes/XcodesKit/Sources/XcodesKit/Models/Runtimes/Runtimes.swift ================================================ import Foundation public struct DownloadableRuntimesResponse: Codable { public let sdkToSimulatorMappings: [SDKToSimulatorMapping] public let sdkToSeedMappings: [SDKToSeedMapping] public let refreshInterval: Int public let downloadables: [DownloadableRuntime] public let version: String } public struct DownloadableRuntime: Codable, Identifiable, Hashable { public let category: Category public let simulatorVersion: SimulatorVersion public let source: String? public let architectures: [Architecture]? public let dictionaryVersion: Int public let contentType: ContentType public let platform: Platform public let identifier: String public let version: String public let fileSize: Int public let hostRequirements: HostRequirements? public let name: String public let authentication: Authentication? public var url: URL? { if let source { return URL(string: source)! } return nil } public var downloadPath: String? { url?.path } // dynamically updated - not decoded public var installState: RuntimeInstallState = .notInstalled public var sdkBuildUpdate: [String]? enum CodingKeys: CodingKey { case category case simulatorVersion case source case dictionaryVersion case contentType case platform case identifier case version case fileSize case hostRequirements case name case authentication case sdkBuildUpdate case architectures } var betaNumber: Int? { enum Regex { static let shared = try! NSRegularExpression(pattern: "b[0-9]+") } guard var foundString = Regex.shared.firstString(in: identifier) else { return nil } foundString.removeFirst() return Int(foundString)! } var completeVersion: String { makeVersion(for: simulatorVersion.version, betaNumber: betaNumber) } public var visibleIdentifier: String { return platform.shortName + " " + completeVersion } func makeVersion(for osVersion: String, betaNumber: Int?) -> String { let betaSuffix = betaNumber.flatMap { "-beta\($0)" } ?? "" return osVersion + betaSuffix } public var downloadFileSizeString: String { return ByteCountFormatter.string(fromByteCount: Int64(fileSize), countStyle: .file) } public var id: String { return visibleIdentifier } public static func == (lhs: DownloadableRuntime, rhs: DownloadableRuntime) -> Bool { return lhs.identifier == rhs.identifier } } public struct SDKToSeedMapping: Codable { public let buildUpdate: String public let platform: DownloadableRuntime.Platform public let seedNumber: Int } public struct SDKToSimulatorMapping: Codable { public let sdkBuildUpdate: String public let simulatorBuildUpdate: String public let sdkIdentifier: String public let downloadableIdentifiers: [String]? } extension DownloadableRuntime { public struct SimulatorVersion: Codable, Hashable { public let buildUpdate: String public let version: String } public struct HostRequirements: Codable, Hashable { let maxHostVersion: String? let excludedHostArchitectures: [String]? let minHostVersion: String? let minXcodeVersion: String? } public enum Authentication: String, Codable { case virtual = "virtual" } public enum Category: String, Codable { case simulator = "simulator" } public enum ContentType: String, Codable { case diskImage = "diskImage" case package = "package" case cryptexDiskImage = "cryptexDiskImage" } public enum Platform: String, Codable { case iOS = "com.apple.platform.iphoneos" case macOS = "com.apple.platform.macosx" case watchOS = "com.apple.platform.watchos" case tvOS = "com.apple.platform.appletvos" case visionOS = "com.apple.platform.xros" public var order: Int { switch self { case .iOS: return 1 case .macOS: return 2 case .watchOS: return 3 case .tvOS: return 4 case .visionOS: return 5 } } public var shortName: String { switch self { case .iOS: return "iOS" case .macOS: return "macOS" case .watchOS: return "watchOS" case .tvOS: return "tvOS" case .visionOS: return "visionOS" } } } } public struct InstalledRuntime: Decodable { let build: String let deletable: Bool let identifier: UUID let kind: Kind let lastUsedAt: Date? let path: String let platformIdentifier: Platform let runtimeBundlePath: String let runtimeIdentifier: String let signatureState: String let state: String let version: String let sizeBytes: Int? let supportedArchitectures: [Architecture]? } extension InstalledRuntime { enum Kind: String, Decodable { case diskImage = "Disk Image" case bundled = "Bundled with Xcode" case legacyDownload = "Legacy Download" } enum Platform: String, Decodable { case tvOS = "com.apple.platform.appletvsimulator" case iOS = "com.apple.platform.iphonesimulator" case watchOS = "com.apple.platform.watchsimulator" case visionOS = "com.apple.platform.xrsimulator" var asPlatformOS: DownloadableRuntime.Platform { switch self { case .watchOS: return .watchOS case .iOS: return .iOS case .tvOS: return .tvOS case .visionOS: return .visionOS } } } } ================================================ FILE: Xcodes/XcodesKit/Sources/XcodesKit/Models/XcodeInstallState.swift ================================================ // // InstallState.swift // // // Created by Matt Kiazyk on 2023-06-06. // import Foundation import Path public enum XcodeInstallState: Equatable { case notInstalled case installing(XcodeInstallationStep) case installed(Path) var notInstalled: Bool { switch self { case .notInstalled: return true default: return false } } var installing: Bool { switch self { case .installing: return true default: return false } } var installed: Bool { switch self { case .installed: return true default: return false } } } ================================================ FILE: Xcodes/XcodesKit/Sources/XcodesKit/Models/XcodeInstallationStep.swift ================================================ // // InstallationStep.swift // // // Created by Matt Kiazyk on 2023-06-06. // import Foundation // A numbered step public enum XcodeInstallationStep: Equatable, CustomStringConvertible { case authenticating case downloading(progress: Progress) case unarchiving case moving(destination: String) case trashingArchive case checkingSecurity case finishing public var description: String { "(\(stepNumber)/\(stepCount)) \(message)" } public var message: String { switch self { case .authenticating: return localizeString("Authenticating") case .downloading: return localizeString("Downloading") case .unarchiving: return localizeString("Unarchiving") case .moving(let destination): return String(format: localizeString("Moving"), destination) case .trashingArchive: return localizeString("TrashingArchive") case .checkingSecurity: return localizeString("CheckingSecurity") case .finishing: return localizeString("Finishing") } } public var stepNumber: Int { switch self { case .authenticating: return 1 case .downloading: return 2 case .unarchiving: return 3 case .moving: return 4 case .trashingArchive: return 5 case .checkingSecurity: return 6 case .finishing: return 7 } } public var stepCount: Int { 7 } } func localizeString(_ key: String, comment: String = "") -> String { if #available(macOS 12, *) { return String(localized: String.LocalizationValue(key)) } else { return NSLocalizedString(key, comment: comment) } } ================================================ FILE: Xcodes/XcodesKit/Sources/XcodesKit/Models/XcodeReleases/Architecture.swift ================================================ // // Architecture.swift // XcodesKit // // Created by Matt Kiazyk on 2025-08-23. // import Foundation /// The name of an Architecture. public enum Architecture: String, Codable, Equatable, Hashable, Identifiable, CaseIterable { public var id: Self { self } /// The Arm64 architecture (Apple Silicon) case arm64 = "arm64" /// The X86\_64 architecture (64-bit Intel) case x86_64 = "x86_64" public var displayString: String { switch self { case .arm64: return "Apple Silicon" case .x86_64: return "Intel" } } public var iconName: String { switch self { case .arm64: return "m4.button.horizontal" case .x86_64: return "cpu.fill" } } } public enum ArchitectureVariant: String, Codable, Equatable, Hashable, Identifiable, CaseIterable { public var id: Self { self } case universal case appleSilicon public var displayString: String { switch self { case .appleSilicon: return "Apple Silicon" case .universal: return "Universal" } } public var iconName: String { switch self { case .appleSilicon: return "m4.button.horizontal" case .universal: return "cpu.fill" } } } extension Array where Element == Architecture { public var isAppleSilicon: Bool { self == [.arm64] } public var isUniversal: Bool { self.contains([.arm64, .x86_64]) } } ================================================ FILE: Xcodes/XcodesKit/Sources/XcodesKit/Models/XcodeReleases/Checksums.swift ================================================ // // Checksums.swift // xcodereleases // // Created by Xcode Releases on 9/17/20. // Copyright © 2020 Xcode Releases. All rights reserved. // import Foundation public struct Checksums: Codable { public let sha1: String? public init(sha1: String? = nil) { self.sha1 = sha1 } } ================================================ FILE: Xcodes/XcodesKit/Sources/XcodesKit/Models/XcodeReleases/Compilers.swift ================================================ // // Compiler.swift // xcodereleases // // Created by Xcode Releases on 4/4/18. // Copyright © 2018 Xcode Releases. All rights reserved. // import Foundation public struct Compilers: Codable { public let gcc: Array? public let llvm_gcc: Array? public let llvm: Array? public let clang: Array? public let swift: Array? public init(gcc: XcodeVersion? = nil, llvm_gcc: XcodeVersion? = nil, llvm: XcodeVersion? = nil, clang: XcodeVersion? = nil, swift: XcodeVersion? = nil) { self.gcc = gcc.map { [$0] } self.llvm_gcc = llvm_gcc.map { [$0] } self.llvm = llvm.map { [$0] } self.clang = clang.map { [$0] } self.swift = swift.map { [$0] } } public init(gcc: Array?, llvm_gcc: Array?, llvm: Array?, clang: Array?, swift: Array?) { self.gcc = gcc?.isEmpty == true ? nil : gcc self.llvm_gcc = llvm_gcc?.isEmpty == true ? nil : llvm_gcc self.llvm = llvm?.isEmpty == true ? nil : llvm self.clang = clang?.isEmpty == true ? nil : clang self.swift = swift?.isEmpty == true ? nil : swift } } ================================================ FILE: Xcodes/XcodesKit/Sources/XcodesKit/Models/XcodeReleases/Link.swift ================================================ // // Link.swift // xcodereleases // // Created by Xcode Releases on 4/5/18. // Copyright © 2018 Xcode Releases. All rights reserved. // import Foundation public struct Link: Codable { public let url: URL public let sizeMB: Int? /// The platforms supported by this link, if applicable. public var architectures: [Architecture]? // public init(_ string: String, _ size: Int? = nil, _ architectures: [Architecture]? = nil) { // self.url = URL(string: string)! // self.sizeMB = size // self.architectures = architectures // } } public struct Links: Codable { public let download: Link? public let notes: Link? public init(download: Link? = nil, notes: Link? = nil) { self.download = download self.notes = notes } } ================================================ FILE: Xcodes/XcodesKit/Sources/XcodesKit/Models/XcodeReleases/Release.swift ================================================ // // Release.swift // xcodereleases // // Created by Xcode Releases on 4/4/18. // Copyright © 2018 Xcode Releases. All rights reserved. // import Foundation public enum Release: Codable { public enum CodingKeys: String, CodingKey { case gm, gmSeed, rc, beta, dp, release } public var isGM: Bool { guard case .gm = self else { return false } return true } case gm case gmSeed(Int) case rc(Int) case beta(Int) case dp(Int) case release public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) if let _ = try container.decodeIfPresent(Bool.self, forKey: .gm) { self = .gm } else if let v = try container.decodeIfPresent(Int.self, forKey: .gmSeed) { self = .gmSeed(v) } else if let v = try container.decodeIfPresent(Int.self, forKey: .rc) { self = .rc(v) } else if let v = try container.decodeIfPresent(Int.self, forKey: .beta) { self = .beta(v) } else if let v = try container.decodeIfPresent(Int.self, forKey: .dp) { self = .dp(v) } else if let _ = try container.decodeIfPresent(Bool.self, forKey: .release) { self = .release } else { fatalError("Unreachable") } } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) switch self { case .gm: try container.encode(true, forKey: .gm) case .gmSeed(let v): try container.encode(v, forKey: .gmSeed) case .rc(let v): try container.encode(v, forKey: .rc) case .beta(let v): try container.encode(v, forKey: .beta) case .dp(let v): try container.encode(v, forKey: .dp) case .release: try container.encode(true, forKey: .release) } } } ================================================ FILE: Xcodes/XcodesKit/Sources/XcodesKit/Models/XcodeReleases/SDKs.swift ================================================ // // SDKs.swift // xcodereleases // // Created by Xcode Releases on 4/4/18. // Copyright © 2018 Xcode Releases. All rights reserved. // import Foundation public struct SDKs: Codable { public let macOS: Array? public let iOS: Array? public let watchOS: Array? public let tvOS: Array? public let visionOS: Array? public init(macOS: XcodeVersion? = nil, iOS: XcodeVersion? = nil, watchOS: XcodeVersion? = nil, tvOS: XcodeVersion? = nil, visionOS: XcodeVersion? = nil) { self.macOS = macOS.map { [$0] } self.iOS = iOS.map { [$0] } self.watchOS = watchOS.map { [$0] } self.tvOS = tvOS.map { [$0] } self.visionOS = visionOS.map { [$0] } } public init(macOS: Array?, iOS: XcodeVersion? = nil, watchOS: XcodeVersion? = nil, tvOS: XcodeVersion? = nil, visionOS: XcodeVersion? = nil) { self.macOS = macOS?.isEmpty == true ? nil : macOS self.iOS = iOS.map { [$0] } self.watchOS = watchOS.map { [$0] } self.tvOS = tvOS.map { [$0] } self.visionOS = visionOS.map { [$0] } } public init(macOS: Array?, iOS: Array?, watchOS: XcodeVersion? = nil, tvOS: XcodeVersion? = nil, visionOS: XcodeVersion? = nil) { self.macOS = macOS?.isEmpty == true ? nil : macOS self.iOS = iOS?.isEmpty == true ? nil : iOS self.watchOS = watchOS.map { [$0] } self.tvOS = tvOS.map { [$0] } self.visionOS = visionOS.map { [$0] } } public init(macOS: Array?, iOS: Array?, watchOS: Array?, tvOS: XcodeVersion? = nil, visionOS: XcodeVersion? = nil) { self.macOS = macOS?.isEmpty == true ? nil : macOS self.iOS = iOS?.isEmpty == true ? nil : iOS self.watchOS = watchOS?.isEmpty == true ? nil : watchOS self.tvOS = tvOS.map { [$0] } self.visionOS = visionOS.map { [$0] } } public init(macOS: Array?, iOS: Array?, watchOS: Array?, tvOS: Array?, visionOS: Array?) { self.macOS = macOS?.isEmpty == true ? nil : macOS self.iOS = iOS?.isEmpty == true ? nil : iOS self.watchOS = watchOS?.isEmpty == true ? nil : watchOS self.tvOS = tvOS?.isEmpty == true ? nil : tvOS self.visionOS = visionOS?.isEmpty == true ? nil : visionOS } } ================================================ FILE: Xcodes/XcodesKit/Sources/XcodesKit/Models/XcodeReleases/XcodeRelease.swift ================================================ // // Xcode.swift // xcodereleases // // Created by Xcode Releases on 4/3/18. // Copyright © 2018 Xcode Releases. All rights reserved. // import Foundation public struct XcodeRelease: Codable { public let name: String public let version: XcodeVersion public let date: YMD public let requires: String public let sdks: SDKs? public let compilers: Compilers? public let links: Links? public let checksums: Checksums? public var architectures: [Architecture]? { return links.flatMap { $0.download?.architectures } } public init(name: String = "Xcode", version: XcodeVersion, date: (Int, Int, Int), requires: String, sdks: SDKs? = nil, compilers: Compilers? = nil, links: Links? = nil, checksums: Checksums? = nil) { self.name = name self.version = version; self.date = YMD(date); self.requires = requires; self.sdks = sdks; self.compilers = compilers self.links = links self.checksums = checksums } } ================================================ FILE: Xcodes/XcodesKit/Sources/XcodesKit/Models/XcodeReleases/XcodeVersion.swift ================================================ // // Version.swift // xcodereleases // // Created by Xcode Releases on 4/4/18. // Copyright © 2018 Xcode Releases. All rights reserved. // import Foundation public typealias V = XcodeVersion public struct XcodeVersion: Codable { public let number: String? public let build: String? public let release: Release public init(_ build: String, _ number: String? = nil, _ release: Release = .release) { self.number = number; self.build = build; self.release = release } public init(number: String, _ build: String? = nil, _ release: Release = .release) { self.number = number; self.build = build; self.release = release } } ================================================ FILE: Xcodes/XcodesKit/Sources/XcodesKit/Models/XcodeReleases/YMD.swift ================================================ // // YMD.swift // xcodereleases // // Created by Xcode Releases on 4/4/18. // Copyright © 2018 Xcode Releases. All rights reserved. // import Foundation public struct YMD: Codable { public let year: Int public let month: Int public let day: Int public init(_ ymd: (Int, Int, Int)) { self.year = ymd.0; self.month = ymd.1; self.day = ymd.2 } public init(_ year: Int, _ month: Int, _ day: Int) { self.year = year; self.month = month; self.day = day } } ================================================ FILE: Xcodes/XcodesKit/Sources/XcodesKit/Services/RuntimeService.swift ================================================ import Foundation import AsyncNetworkService import Path extension URL { static let downloadableRuntimes = URL(string: "https://devimages-cdn.apple.com/downloads/xcode/simulators/index2.dvtdownloadableindex")! } public struct RuntimeService { var networkService: AsyncHTTPNetworkService public enum Error: LocalizedError, Equatable { case unavailableRuntime(String) case failedMountingDMG } public init() { networkService = AsyncHTTPNetworkService() } public func downloadableRuntimes() async throws -> DownloadableRuntimesResponse { let urlRequest = URLRequest(url: .downloadableRuntimes) // Apple gives a plist for download let (data, _) = try await networkService.requestData(urlRequest, validators: []) do { let decodedResponse = try PropertyListDecoder().decode(DownloadableRuntimesResponse.self, from: data) return decodedResponse } catch { print("error: \(error)") throw error } } public func installedRuntimes() async throws -> [InstalledRuntime] { // This only uses the Selected Xcode, so we don't know what other SDK's have been installed in previous versions let output = try await Current.shell.installedRuntimes() let decoder = JSONDecoder() decoder.dateDecodingStrategy = .iso8601 let outputDictionary = try decoder.decode([String: InstalledRuntime].self, from: output.out.data(using: .utf8)!) return outputDictionary.values.sorted { first, second in return first.identifier.uuidString.compare(second.identifier.uuidString, options: .numeric) == .orderedAscending } } /// Loops through `/Library/Developer/CoreSimulator/images/images.plist` which contains a list of downloaded Simuator Runtimes /// This is different then using `simctl` (`installedRuntimes()`) which only returns the installed runtimes for the selected xcode version. public func localInstalledRuntimes() async throws -> [CoreSimulatorImage] { guard let path = Path("/Library/Developer/CoreSimulator/images/images.plist") else { throw "Could not find images.plist for CoreSimulators" } guard let infoPlistData = FileManager.default.contents(atPath: path.string) else { throw "Could not get data from \(path.string)" } do { let infoPlist: CoreSimulatorPlist = try PropertyListDecoder().decode(CoreSimulatorPlist.self, from: infoPlistData) return infoPlist.images } catch { throw error } } public func installRuntimeImage(dmgURL: URL) async throws { _ = try await Current.shell.installRuntimeImage(dmgURL) } public func mountDMG(dmgUrl: URL) async throws -> URL { let resultPlist = try await Current.shell.mountDmg(dmgUrl) let dict = try? (PropertyListSerialization.propertyList(from: resultPlist.out.data(using: .utf8)!, format: nil) as? NSDictionary) let systemEntities = dict?["system-entities"] as? NSArray guard let path = systemEntities?.compactMap ({ ($0 as? NSDictionary)?["mount-point"] as? String }).first else { throw Error.failedMountingDMG } return URL(fileURLWithPath: path) } public func unmountDMG(mountedURL: URL) async throws { _ = try await Current.shell.unmountDmg(mountedURL) } public func expand(pkgPath: Path, expandedPkgPath: Path) async throws { _ = try await Current.shell.expandPkg(pkgPath.url, expandedPkgPath.url) } public func createPkg(pkgPath: Path, expandedPkgPath: Path) async throws { _ = try await Current.shell.createPkg(pkgPath.url, expandedPkgPath.url) } public func installPkg(pkgPath: Path, expandedPkgPath: Path) async throws { _ = try await Current.shell.installPkg(pkgPath.url, expandedPkgPath.url.absoluteString) } public func deleteRuntime(identifier: String) async throws { do { _ = try await Current.shell.deleteRuntime(identifier) } catch { if let executionError = error as? ProcessExecutionError { throw executionError.standardError } throw error } } } extension String: Error {} ================================================ FILE: Xcodes/XcodesKit/Sources/XcodesKit/Shell/Process.swift ================================================ import Foundation import Path import os.log public typealias ProcessOutput = (status: Int32, out: String, err: String) extension Process { static func run(_ executable: Path, workingDirectory: URL? = nil, input: String? = nil, _ arguments: String...) async throws -> ProcessOutput { return try run(executable.url, workingDirectory: workingDirectory, input: input, arguments) } static func run(_ executable: Path, workingDirectory: URL? = nil, input: String? = nil, _ arguments: String...) throws -> ProcessOutput { return try run(executable.url, workingDirectory: workingDirectory, input: input, arguments) } static func run(_ executable: URL, workingDirectory: URL? = nil, input: String? = nil, _ arguments: [String]) throws -> ProcessOutput { let process = Process() process.currentDirectoryURL = workingDirectory ?? executable.deletingLastPathComponent() process.executableURL = executable process.arguments = arguments let (stdout, stderr) = (Pipe(), Pipe()) process.standardOutput = stdout process.standardError = stderr if let input = input { let inputPipe = Pipe() process.standardInput = inputPipe.fileHandleForReading inputPipe.fileHandleForWriting.write(Data(input.utf8)) inputPipe.fileHandleForWriting.closeFile() } do { Logger.subprocess.info("Process.run executable: \(executable), input: \(input ?? ""), arguments: \(arguments.joined(separator: ", "))") try process.run() process.waitUntilExit() let output = String(data: stdout.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) ?? "" let error = String(data: stderr.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) ?? "" Logger.subprocess.info("Process.run output: \(output)") if !error.isEmpty { Logger.subprocess.error("Process.run error: \(error)") } guard process.terminationReason == .exit, process.terminationStatus == 0 else { throw ProcessExecutionError(process: process, standardOutput: output, standardError: error) } return (process.terminationStatus, output, error) } catch { throw error } } } public struct ProcessExecutionError: Error { public let process: Process public let standardOutput: String public let standardError: String public init(process: Process, standardOutput: String, standardError: String) { self.process = process self.standardOutput = standardOutput self.standardError = standardError } } ================================================ FILE: Xcodes/XcodesKit/Sources/XcodesKit/Shell/XcodesShell.swift ================================================ import Foundation import Path public struct XcodesShell { public var installedRuntimes: () async throws -> ProcessOutput = { try await Process.run(Path.root.usr.bin.join("xcrun"), "simctl", "runtime", "list", "-j") } public var mountDmg: (URL) async throws -> ProcessOutput = { try await Process.run(Path.root.usr.bin.join("hdiutil"), "attach", "-nobrowse", "-plist", $0.path) } public var unmountDmg: (URL) async throws -> ProcessOutput = { try await Process.run(Path.root.usr.bin.join("hdiutil"), "detach", $0.path) } public var expandPkg: (URL, URL) async throws -> ProcessOutput = { try await Process.run(Path.root.usr.sbin.join("pkgutil"), "--verbose", "--expand", $0.path, $1.path) } public var createPkg: (URL, URL) async throws -> ProcessOutput = { try await Process.run(Path.root.usr.sbin.join("pkgutil"), "--flatten", $0.path, $1.path) } public var installPkg: (URL, String) async throws -> ProcessOutput = { try await Process.run(Path.root.usr.sbin.join("installer"), "-pkg", $0.path, "-target", $1) } public var installRuntimeImage: (URL) async throws -> ProcessOutput = { try await Process.run(Path.root.usr.bin.join("xcrun"), "simctl", "runtime", "add", $0.path) } public var deleteRuntime: (String) async throws -> ProcessOutput = { try await Process.run(Path.root.usr.bin.join("xcrun"), "simctl", "runtime", "delete", $0) } public var archs: (URL) throws -> ProcessOutput = { try Process.run(Path.root.usr.bin.join("lipo"), "-archs", $0.path) } } ================================================ FILE: Xcodes/XcodesKit/Sources/XcodesKit/XcodesKitEnvironment.swift ================================================ import Foundation public struct XcodesKitEnvironment { public var shell = XcodesShell() } public var Current = XcodesKitEnvironment() ================================================ FILE: Xcodes/XcodesKit/Tests/XcodesKitTests/XcodesKitTests.swift ================================================ import XCTest @testable import XcodesKit final class XcodesKitTests: XCTestCase { func testExample() throws { // This is an example of a functional test case. // Use XCTAssert and related functions to verify your tests produce the correct // results. XCTAssertEqual(XcodesKit().text, "Hello, World!") } } ================================================ FILE: Xcodes.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 54; objects = { /* Begin PBXBuildFile section */ 15F5B8902CCF09B900705E2F /* CryptoKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 15F5B88F2CCF09B900705E2F /* CryptoKit.framework */; }; 33027E342CA8C18800CB387C /* LibFido2Swift in Frameworks */ = {isa = PBXBuildFile; productRef = 334A932B2CA885A400A5E079 /* LibFido2Swift */; }; 3328073F2CA5E2C80036F691 /* SignInSecurityKeyPinView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3328073E2CA5E2C80036F691 /* SignInSecurityKeyPinView.swift */; }; 332807412CA5EA820036F691 /* SignInSecurityKeyTouchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 332807402CA5EA820036F691 /* SignInSecurityKeyTouchView.swift */; }; 36741BFD291E4FDB00A85AAE /* DownloadPreferencePane.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36741BFC291E4FDB00A85AAE /* DownloadPreferencePane.swift */; }; 36741BFF291E50F500A85AAE /* FileError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36741BFE291E50F500A85AAE /* FileError.swift */; }; 536CFDD2263C94DE00026CE0 /* SignedInView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 536CFDD1263C94DE00026CE0 /* SignedInView.swift */; }; 536CFDD4263C9A8000026CE0 /* XcodesSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 536CFDD3263C9A8000026CE0 /* XcodesSheet.swift */; }; 53CBAB2C263DCC9100410495 /* XcodesAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53CBAB2B263DCC9100410495 /* XcodesAlert.swift */; }; 63EAA4EB259944450046AB8F /* ProgressButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63EAA4EA259944450046AB8F /* ProgressButton.swift */; }; 9DD4FFCB2B13EC1800C974F1 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 9DD4FFCA2B13EC1800C974F1 /* Localizable.xcstrings */; }; B0403CF02AD92D7B00137C09 /* ReleaseNotesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0403CEF2AD92D7B00137C09 /* ReleaseNotesView.swift */; }; B0403CF22AD934B600137C09 /* CompatibilityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0403CF12AD934B600137C09 /* CompatibilityView.swift */; }; B0403CF42AD9381D00137C09 /* SDKsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0403CF32AD9381D00137C09 /* SDKsView.swift */; }; B0403CF62AD9849E00137C09 /* CompilersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0403CF52AD9849E00137C09 /* CompilersView.swift */; }; B0403CF82AD991F800137C09 /* UnselectedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0403CF72AD991F800137C09 /* UnselectedView.swift */; }; B0403CFA2AD9942A00137C09 /* NotInstalledStateButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0403CF92AD9942A00137C09 /* NotInstalledStateButtons.swift */; }; B0403CFC2AD9A6BF00137C09 /* InstalledStateButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0403CFB2AD9A6BF00137C09 /* InstalledStateButtons.swift */; }; B0403CFE2ADA712C00137C09 /* InfoPaneControls.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0403CFD2ADA712C00137C09 /* InfoPaneControls.swift */; }; B0C6AD042AD6E65700E64698 /* ReleaseDateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C6AD032AD6E65700E64698 /* ReleaseDateView.swift */; }; B0C6AD0B2AD9178E00E64698 /* IdenticalBuildView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C6AD0A2AD9178E00E64698 /* IdenticalBuildView.swift */; }; B0C6AD0D2AD91D7900E64698 /* IconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C6AD0C2AD91D7900E64698 /* IconView.swift */; }; BDBAB7452B9FF55800694B0B /* TrailingIconLabelStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDBAB7442B9FF55800694B0B /* TrailingIconLabelStyle.swift */; }; CA11E7BA2598476C00D2EE1C /* XcodeCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA11E7B92598476C00D2EE1C /* XcodeCommands.swift */; }; CA2518EC25A7FF2B00F08414 /* AppStateUpdateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA2518EB25A7FF2B00F08414 /* AppStateUpdateTests.swift */; }; CA25192A25A9644800F08414 /* XcodeInstallState.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA25192925A9644800F08414 /* XcodeInstallState.swift */; }; CA378F992466567600A58CE0 /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA378F982466567600A58CE0 /* AppState.swift */; }; CA39711924495F0E00AFFB77 /* AppStoreButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA39711824495F0E00AFFB77 /* AppStoreButtonStyle.swift */; }; CA42DD7325AEB04300BC0B0C /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA42DD7225AEB04300BC0B0C /* Logger.swift */; }; CA44901F2463AD34003D8213 /* Tag.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA44901E2463AD34003D8213 /* Tag.swift */; }; CA452BB0259FD9770072DFA4 /* ProgressIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA452BAF259FD9770072DFA4 /* ProgressIndicator.swift */; }; CA452BC1259FDDFE0072DFA4 /* Stub-version.plist in Resources */ = {isa = PBXBuildFile; fileRef = CA452BBF259FDDFE0072DFA4 /* Stub-version.plist */; }; CA452BEB25A236500072DFA4 /* Stub-0.0.0.Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = CA452BEA25A236500072DFA4 /* Stub-0.0.0.Info.plist */; }; CA5D781E257365D6008EDE9D /* PinCodeTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA5D781D257365D6008EDE9D /* PinCodeTextView.swift */; }; CA61A6E0259835580008926E /* Xcode.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA61A6DF259835580008926E /* Xcode.swift */; }; CA735109257BF96D00EA9CF8 /* AttributedText.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA735108257BF96D00EA9CF8 /* AttributedText.swift */; }; CA73510D257BFCEF00EA9CF8 /* NSAttributedString+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA73510C257BFCEF00EA9CF8 /* NSAttributedString+.swift */; }; CA9FF83F2594FBC000E47BAF /* Licenses.rtf in Resources */ = {isa = PBXBuildFile; fileRef = CA9FF83E2594FBC000E47BAF /* Licenses.rtf */; }; CA9FF84E2595079F00E47BAF /* ScrollingTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA9FF84D2595079F00E47BAF /* ScrollingTextView.swift */; }; CA9FF8522595080100E47BAF /* AcknowledgementsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA9FF8512595080100E47BAF /* AcknowledgementsView.swift */; }; CA9FF8662595130600E47BAF /* View+IsHidden.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA9FF8652595130600E47BAF /* View+IsHidden.swift */; }; CA9FF877259528CC00E47BAF /* Version+XcodeReleases.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA9FF876259528CC00E47BAF /* Version+XcodeReleases.swift */; }; CA9FF87B2595293E00E47BAF /* DataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA9FF87A2595293E00E47BAF /* DataSource.swift */; }; CA9FF88125955C7000E47BAF /* AvailableXcode.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA9FF88025955C7000E47BAF /* AvailableXcode.swift */; }; CA9FF8872595607900E47BAF /* InstalledXcode.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA9FF8862595607900E47BAF /* InstalledXcode.swift */; }; CA9FF8B12595967A00E47BAF /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA9FF8B02595967A00E47BAF /* main.swift */; }; CA9FF8CF25959A9700E47BAF /* HelperXPCShared.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA9FF8CE25959A9700E47BAF /* HelperXPCShared.swift */; }; CA9FF8D025959A9700E47BAF /* HelperXPCShared.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA9FF8CE25959A9700E47BAF /* HelperXPCShared.swift */; }; CA9FF8DB25959B4000E47BAF /* XPCDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA9FF8DA25959B4000E47BAF /* XPCDelegate.swift */; }; CA9FF8E025959BAA00E47BAF /* ConnectionVerifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA9FF8DF25959BAA00E47BAF /* ConnectionVerifier.swift */; }; CA9FF8E625959BB800E47BAF /* AuditTokenHack.m in Sources */ = {isa = PBXBuildFile; fileRef = CA9FF8E525959BB800E47BAF /* AuditTokenHack.m */; }; CA9FF9362595B44700E47BAF /* HelperClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA9FF9352595B44700E47BAF /* HelperClient.swift */; }; CAA1CB2D255A5262003FD669 /* AppleAPI in Frameworks */ = {isa = PBXBuildFile; productRef = CAA1CB2C255A5262003FD669 /* AppleAPI */; }; CAA1CB35255A5AD5003FD669 /* SignInCredentialsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAA1CB34255A5AD5003FD669 /* SignInCredentialsView.swift */; }; CAA1CB45255A5B60003FD669 /* SignIn2FAView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAA1CB44255A5B60003FD669 /* SignIn2FAView.swift */; }; CAA1CB49255A5C97003FD669 /* SignInSMSView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAA1CB48255A5C97003FD669 /* SignInSMSView.swift */; }; CAA1CB4D255A5CFD003FD669 /* SignInPhoneListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAA1CB4C255A5CFD003FD669 /* SignInPhoneListView.swift */; }; CAA8587C25A2B37900ACF8C0 /* IsTesting.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAA8587B25A2B37900ACF8C0 /* IsTesting.swift */; }; CAA8589325A2B77E00ACF8C0 /* aria2c in Copy aria2c */ = {isa = PBXBuildFile; fileRef = CAA8588025A2B63A00ACF8C0 /* aria2c */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; CAA8589B25A2B83000ACF8C0 /* Aria2CError.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAA8589A25A2B83000ACF8C0 /* Aria2CError.swift */; }; CAA858C425A2BE4E00ACF8C0 /* Downloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAA858C325A2BE4E00ACF8C0 /* Downloader.swift */; }; CAA858CD25A3D8BC00ACF8C0 /* ErrorHandling in Frameworks */ = {isa = PBXBuildFile; productRef = CAA858CC25A3D8BC00ACF8C0 /* ErrorHandling */; }; CAA858DB25A3E11F00ACF8C0 /* aria2-release-1.35.0.tar.gz in Resources */ = {isa = PBXBuildFile; fileRef = CAA858DA25A3E11F00ACF8C0 /* aria2-release-1.35.0.tar.gz */; }; CAB3AB0E25BCA6C200BF1B04 /* AppStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAD2E7B72449575100113D76 /* AppStateTests.swift */; }; CABFA9BB2592EEEA00380FEE /* DateFormatter+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9BA2592EEEA00380FEE /* DateFormatter+.swift */; }; CABFA9BD2592EEEA00380FEE /* Environment.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9A92592EEE900380FEE /* Environment.swift */; }; CABFA9BF2592EEEA00380FEE /* URLSession+DownloadTaskPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9B32592EEEA00380FEE /* URLSession+DownloadTaskPublisher.swift */; }; CABFA9C12592EEEA00380FEE /* Version+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9A82592EEE900380FEE /* Version+.swift */; }; CABFA9C22592EEEA00380FEE /* Publisher+Resumable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9B02592EEEA00380FEE /* Publisher+Resumable.swift */; }; CABFA9C32592EEEA00380FEE /* Downloads.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9B92592EEEA00380FEE /* Downloads.swift */; }; CABFA9C52592EEEA00380FEE /* FileManager+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9B82592EEEA00380FEE /* FileManager+.swift */; }; CABFA9C72592EEEA00380FEE /* Entry+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9B22592EEEA00380FEE /* Entry+.swift */; }; CABFA9C92592EEEA00380FEE /* URLRequest+Apple.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9AB2592EEE900380FEE /* URLRequest+Apple.swift */; }; CABFA9CA2592EEEA00380FEE /* AppState+Update.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9A72592EEE900380FEE /* AppState+Update.swift */; }; CABFA9CC2592EEEA00380FEE /* Path+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9AE2592EEE900380FEE /* Path+.swift */; }; CABFA9CD2592EEEA00380FEE /* Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9AC2592EEE900380FEE /* Foundation.swift */; }; CABFA9CE2592EEEA00380FEE /* Version+Xcode.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9A62592EEE900380FEE /* Version+Xcode.swift */; }; CABFA9CF2592EEEA00380FEE /* Process.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9B42592EEEA00380FEE /* Process.swift */; }; CABFA9E42592F08E00380FEE /* Version in Frameworks */ = {isa = PBXBuildFile; productRef = CABFA9E32592F08E00380FEE /* Version */; }; CABFA9EE2592F0CC00380FEE /* SwiftSoup in Frameworks */ = {isa = PBXBuildFile; productRef = CABFA9ED2592F0CC00380FEE /* SwiftSoup */; }; CABFA9F82592F0F900380FEE /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = CABFA9F72592F0F900380FEE /* KeychainAccess */; }; CABFA9FD2592F13300380FEE /* LegibleError in Frameworks */ = {isa = PBXBuildFile; productRef = CABFA9FC2592F13300380FEE /* LegibleError */; }; CABFAA2D2592FBFC00380FEE /* Configure.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFAA2B2592FBFC00380FEE /* Configure.swift */; }; CABFAA432593104F00380FEE /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFAA422593104F00380FEE /* AboutView.swift */; }; CABFAA492593162500380FEE /* Bundle+InfoPlistValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFAA482593162500380FEE /* Bundle+InfoPlistValues.swift */; }; CAC28188259EE27200B8AB0B /* CombineExpectations in Frameworks */ = {isa = PBXBuildFile; productRef = CAC28187259EE27200B8AB0B /* CombineExpectations */; }; CAC281CD259F97FA00B8AB0B /* ObservingProgressIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAC281CC259F97FA00B8AB0B /* ObservingProgressIndicator.swift */; }; CAC281E2259FA44600B8AB0B /* Bundle+XcodesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAC281E1259FA44600B8AB0B /* Bundle+XcodesTests.swift */; }; CAC281E7259FA45A00B8AB0B /* Environment+Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAC281E6259FA45A00B8AB0B /* Environment+Mock.swift */; }; CAC9F92D25BCDA4400B4965F /* HelperInstallState.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAC9F92C25BCDA4400B4965F /* HelperInstallState.swift */; }; CAD2E7A22449574E00113D76 /* XcodesApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAD2E7A12449574E00113D76 /* XcodesApp.swift */; }; CAD2E7A42449574E00113D76 /* XcodeListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAD2E7A32449574E00113D76 /* XcodeListView.swift */; }; CAD2E7A62449575000113D76 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CAD2E7A52449575000113D76 /* Assets.xcassets */; }; CAD2E7A92449575000113D76 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CAD2E7A82449575000113D76 /* Preview Assets.xcassets */; }; CAE4247F259A666100B8B246 /* MainWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAE4247E259A666100B8B246 /* MainWindow.swift */; }; CAE42487259A68A300B8B246 /* XcodeListCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAE42486259A68A300B8B246 /* XcodeListCategory.swift */; }; CAE4248C259A68B800B8B246 /* Optional+IsNotNil.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAE4248B259A68B800B8B246 /* Optional+IsNotNil.swift */; }; CAE424B4259A764700B8B246 /* AppState+Install.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAE424B3259A764700B8B246 /* AppState+Install.swift */; }; CAFBDB912598FE80003DCC5A /* SelectedXcode.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFBDB902598FE80003DCC5A /* SelectedXcode.swift */; }; CAFBDB952598FE96003DCC5A /* FocusedValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFBDB942598FE96003DCC5A /* FocusedValues.swift */; }; CAFBDC4E2599B33D003DCC5A /* MainToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFBDC4D2599B33D003DCC5A /* MainToolbar.swift */; }; CAFBDC68259A308B003DCC5A /* InfoPane.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFBDC67259A308B003DCC5A /* InfoPane.swift */; }; CAFBDC6C259A3098003DCC5A /* View+Conditional.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFBDC6B259A3098003DCC5A /* View+Conditional.swift */; }; CAFE4AAC25B7D2C70064FE51 /* GeneralPreferencePane.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFE4AAB25B7D2C70064FE51 /* GeneralPreferencePane.swift */; }; CAFE4AB425B7D3AF0064FE51 /* AdvancedPreferencePane.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFE4AB325B7D3AF0064FE51 /* AdvancedPreferencePane.swift */; }; CAFE4ABC25B7D54B0064FE51 /* UpdatesPreferencePane.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFE4ABB25B7D54B0064FE51 /* UpdatesPreferencePane.swift */; }; CAFFFED8259CDA5000903F81 /* XcodeListViewRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFFFED7259CDA5000903F81 /* XcodeListViewRow.swift */; }; D93F95C12E0C8C1A00238FB5 /* TagView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D93F95C02E0C8C1A00238FB5 /* TagView.swift */; }; E689540325BE8C64000EBCEA /* DockProgress in Frameworks */ = {isa = PBXBuildFile; productRef = E689540225BE8C64000EBCEA /* DockProgress */; }; E81D7EA02805250100A205FC /* Collection+.swift in Sources */ = {isa = PBXBuildFile; fileRef = E81D7E9F2805250100A205FC /* Collection+.swift */; }; E832EAF82B0FBCF4001B570D /* RuntimeInstallationStepDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E832EAF72B0FBCF4001B570D /* RuntimeInstallationStepDetailView.swift */; }; E83FDC442CBB649100679C6B /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = E83FDC432CBB649100679C6B /* Sparkle */; }; E84B7D0D2B296A8900DBDA2B /* NavigationSplitViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = E84B7D0C2B296A8900DBDA2B /* NavigationSplitViewWrapper.swift */; }; E84E4F522B323A5F003F3959 /* CornerRadiusModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = E84E4F512B323A5F003F3959 /* CornerRadiusModifier.swift */; }; E84E4F542B333864003F3959 /* PlatformsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E84E4F532B333864003F3959 /* PlatformsListView.swift */; }; E84E4F572B335094003F3959 /* OrderedCollections in Frameworks */ = {isa = PBXBuildFile; productRef = E84E4F562B335094003F3959 /* OrderedCollections */; }; E862D43B2CC8B26F00BAA376 /* SRP in Frameworks */ = {isa = PBXBuildFile; productRef = E862D43A2CC8B26F00BAA376 /* SRP */; }; E86671272B309D2F0048559A /* PlatformsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E86671262B309D2F0048559A /* PlatformsView.swift */; }; E87AB3C52939B65E00D72F43 /* Hardware.swift in Sources */ = {isa = PBXBuildFile; fileRef = E87AB3C42939B65E00D72F43 /* Hardware.swift */; }; E87DD6EB25D053FA00D86808 /* Progress+.swift in Sources */ = {isa = PBXBuildFile; fileRef = E87DD6EA25D053FA00D86808 /* Progress+.swift */; }; E89342FA25EDCC17007CF557 /* NotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E89342F925EDCC17007CF557 /* NotificationManager.swift */; }; E8977EA325C11E1500835F80 /* PreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8977EA225C11E1500835F80 /* PreferencesView.swift */; }; E8B20CBF2A2EDEC20057D816 /* SDKs+Xcode.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8B20CBE2A2EDEC20057D816 /* SDKs+Xcode.swift */; }; E8C0EB1A291EF43E0081528A /* XcodesKit in Frameworks */ = {isa = PBXBuildFile; productRef = E8C0EB19291EF43E0081528A /* XcodesKit */; }; E8C0EB1C291EF9A10081528A /* AppState+Runtimes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8C0EB1B291EF9A10081528A /* AppState+Runtimes.swift */; }; E8CBDB8927ADE32300B22292 /* unxip in Copy aria2c */ = {isa = PBXBuildFile; fileRef = E8CBDB8627ADD92000B22292 /* unxip */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; E8CBDB8B27AE02FF00B22292 /* ExperiementsPreferencePane.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8CBDB8A27AE02FF00B22292 /* ExperiementsPreferencePane.swift */; }; E8D0296F284B029800647641 /* BottomStatusBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8D0296E284B029800647641 /* BottomStatusBar.swift */; }; E8D655C0288DD04700A139C2 /* SelectedActionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8D655BF288DD04700A139C2 /* SelectedActionType.swift */; }; E8DA461125FAF7FB002E85EF /* NotificationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8DA461025FAF7FB002E85EF /* NotificationsView.swift */; }; E8E98A9025D8631800EC89A0 /* InstallationStepRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFBC3FF259AC17F00E2A3D8 /* InstallationStepRowView.swift */; }; E8E98A9625D863D700EC89A0 /* InstallationStepDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8E98A9525D863D700EC89A0 /* InstallationStepDetailView.swift */; }; E8EEAD1D2E79174F00BE67E8 /* XcodesIcon.icon in Resources */ = {isa = PBXBuildFile; fileRef = E8EEAD1C2E79174F00BE67E8 /* XcodesIcon.icon */; }; E8F44A1E296B4CD7002D6592 /* Path in Frameworks */ = {isa = PBXBuildFile; productRef = E8F44A1D296B4CD7002D6592 /* Path */; }; E8FA00542B5B109800769CE0 /* com.xcodesorg.xcodesapp.Helper in Copy Helper */ = {isa = PBXBuildFile; fileRef = CA9FF8AE2595967A00E47BAF /* com.xcodesorg.xcodesapp.Helper */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; E8FD5727291EE4AC001E004C /* AsyncNetworkService in Frameworks */ = {isa = PBXBuildFile; productRef = E8FD5726291EE4AC001E004C /* AsyncNetworkService */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ CA9FF8B9259596A000E47BAF /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = CAD2E7962449574E00113D76 /* Project object */; proxyType = 1; remoteGlobalIDString = CA9FF8AD2595967A00E47BAF; remoteInfo = com.robotsandpencils.XcodesApp.Helper; }; CAD2E7B42449575100113D76 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = CAD2E7962449574E00113D76 /* Project object */; proxyType = 1; remoteGlobalIDString = CAD2E79D2449574E00113D76; remoteInfo = XcodesMac; }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ CA9FF8AC2595967A00E47BAF /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = /usr/share/man/man1/; dstSubfolderSpec = 0; files = ( ); runOnlyForDeploymentPostprocessing = 1; }; CA9FF8BB259596B500E47BAF /* Copy Helper */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = Contents/Library/LaunchServices; dstSubfolderSpec = 1; files = ( E8FA00542B5B109800769CE0 /* com.xcodesorg.xcodesapp.Helper in Copy Helper */, ); name = "Copy Helper"; runOnlyForDeploymentPostprocessing = 0; }; CAA8589225A2B76F00ACF8C0 /* Copy aria2c */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 6; files = ( E8CBDB8927ADE32300B22292 /* unxip in Copy aria2c */, CAA8589325A2B77E00ACF8C0 /* aria2c in Copy aria2c */, ); name = "Copy aria2c"; runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 15F5B88F2CCF09B900705E2F /* CryptoKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CryptoKit.framework; path = System/Library/Frameworks/CryptoKit.framework; sourceTree = SDKROOT; }; 3328073E2CA5E2C80036F691 /* SignInSecurityKeyPinView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInSecurityKeyPinView.swift; sourceTree = ""; }; 332807402CA5EA820036F691 /* SignInSecurityKeyTouchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInSecurityKeyTouchView.swift; sourceTree = ""; }; 36741BFC291E4FDB00A85AAE /* DownloadPreferencePane.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DownloadPreferencePane.swift; sourceTree = ""; }; 36741BFE291E50F500A85AAE /* FileError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileError.swift; sourceTree = ""; }; 536CFDD1263C94DE00026CE0 /* SignedInView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignedInView.swift; sourceTree = ""; }; 536CFDD3263C9A8000026CE0 /* XcodesSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XcodesSheet.swift; sourceTree = ""; }; 53CBAB2B263DCC9100410495 /* XcodesAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XcodesAlert.swift; sourceTree = ""; }; 63EAA4EA259944450046AB8F /* ProgressButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressButton.swift; sourceTree = ""; }; 9DD4FFCA2B13EC1800C974F1 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; }; B0403CEF2AD92D7B00137C09 /* ReleaseNotesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReleaseNotesView.swift; sourceTree = ""; }; B0403CF12AD934B600137C09 /* CompatibilityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompatibilityView.swift; sourceTree = ""; }; B0403CF32AD9381D00137C09 /* SDKsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDKsView.swift; sourceTree = ""; }; B0403CF52AD9849E00137C09 /* CompilersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompilersView.swift; sourceTree = ""; }; B0403CF72AD991F800137C09 /* UnselectedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnselectedView.swift; sourceTree = ""; }; B0403CF92AD9942A00137C09 /* NotInstalledStateButtons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotInstalledStateButtons.swift; sourceTree = ""; }; B0403CFB2AD9A6BF00137C09 /* InstalledStateButtons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstalledStateButtons.swift; sourceTree = ""; }; B0403CFD2ADA712C00137C09 /* InfoPaneControls.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoPaneControls.swift; sourceTree = ""; }; B0C6AD032AD6E65700E64698 /* ReleaseDateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReleaseDateView.swift; sourceTree = ""; }; B0C6AD0A2AD9178E00E64698 /* IdenticalBuildView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdenticalBuildView.swift; sourceTree = ""; }; B0C6AD0C2AD91D7900E64698 /* IconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconView.swift; sourceTree = ""; }; BDBAB7442B9FF55800694B0B /* TrailingIconLabelStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrailingIconLabelStyle.swift; sourceTree = ""; }; CA11E7B92598476C00D2EE1C /* XcodeCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XcodeCommands.swift; sourceTree = ""; }; CA2518EB25A7FF2B00F08414 /* AppStateUpdateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppStateUpdateTests.swift; sourceTree = ""; }; CA25192925A9644800F08414 /* XcodeInstallState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XcodeInstallState.swift; sourceTree = ""; }; CA378F982466567600A58CE0 /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = ""; }; CA39711824495F0E00AFFB77 /* AppStoreButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppStoreButtonStyle.swift; sourceTree = ""; }; CA42DD7225AEB04300BC0B0C /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; CA44901E2463AD34003D8213 /* Tag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tag.swift; sourceTree = ""; }; CA452BAF259FD9770072DFA4 /* ProgressIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressIndicator.swift; sourceTree = ""; }; CA452BBF259FDDFE0072DFA4 /* Stub-version.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Stub-version.plist"; sourceTree = ""; }; CA452BEA25A236500072DFA4 /* Stub-0.0.0.Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Stub-0.0.0.Info.plist"; sourceTree = ""; }; CA538A0C255A4F1A00E64DD7 /* AppleAPI */ = {isa = PBXFileReference; lastKnownFileType = folder; name = AppleAPI; path = Xcodes/AppleAPI; sourceTree = ""; }; CA5D781D257365D6008EDE9D /* PinCodeTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinCodeTextView.swift; sourceTree = ""; }; CA61A6DF259835580008926E /* Xcode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Xcode.swift; sourceTree = ""; }; CA735108257BF96D00EA9CF8 /* AttributedText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedText.swift; sourceTree = ""; }; CA73510C257BFCEF00EA9CF8 /* NSAttributedString+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSAttributedString+.swift"; sourceTree = ""; }; CA8FB5F8256E0F9400469DA5 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; CA8FB61C256E115700469DA5 /* .github */ = {isa = PBXFileReference; lastKnownFileType = folder; path = .github; sourceTree = ""; }; CA8FB64D256E17B100469DA5 /* XcodesTest.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = XcodesTest.entitlements; sourceTree = ""; }; CA9FF8242594F10700E47BAF /* AcknowledgementsGenerator */ = {isa = PBXFileReference; lastKnownFileType = folder; name = AcknowledgementsGenerator; path = Xcodes/AcknowledgementsGenerator; sourceTree = ""; }; CA9FF83E2594FBC000E47BAF /* Licenses.rtf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.rtf; path = Licenses.rtf; sourceTree = ""; }; CA9FF84D2595079F00E47BAF /* ScrollingTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollingTextView.swift; sourceTree = ""; }; CA9FF8512595080100E47BAF /* AcknowledgementsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AcknowledgementsView.swift; sourceTree = ""; }; CA9FF8652595130600E47BAF /* View+IsHidden.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+IsHidden.swift"; sourceTree = ""; }; CA9FF876259528CC00E47BAF /* Version+XcodeReleases.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Version+XcodeReleases.swift"; sourceTree = ""; }; CA9FF87A2595293E00E47BAF /* DataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataSource.swift; sourceTree = ""; }; CA9FF88025955C7000E47BAF /* AvailableXcode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvailableXcode.swift; sourceTree = ""; }; CA9FF8862595607900E47BAF /* InstalledXcode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstalledXcode.swift; sourceTree = ""; }; CA9FF8AE2595967A00E47BAF /* com.xcodesorg.xcodesapp.Helper */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = com.xcodesorg.xcodesapp.Helper; sourceTree = BUILT_PRODUCTS_DIR; }; CA9FF8B02595967A00E47BAF /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; CA9FF8C22595988B00E47BAF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; CA9FF8C32595989800E47BAF /* launchd.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = launchd.plist; sourceTree = ""; }; CA9FF8CE25959A9700E47BAF /* HelperXPCShared.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelperXPCShared.swift; sourceTree = ""; }; CA9FF8DA25959B4000E47BAF /* XPCDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XPCDelegate.swift; sourceTree = ""; }; CA9FF8DF25959BAA00E47BAF /* ConnectionVerifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionVerifier.swift; sourceTree = ""; }; CA9FF8E425959BB800E47BAF /* AuditTokenHack.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AuditTokenHack.h; sourceTree = ""; }; CA9FF8E525959BB800E47BAF /* AuditTokenHack.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AuditTokenHack.m; sourceTree = ""; }; CA9FF8EA25959BDD00E47BAF /* com.xcodesorg.xcodesapp.Helper-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "com.xcodesorg.xcodesapp.Helper-Bridging-Header.h"; sourceTree = ""; }; CA9FF9052595A28400E47BAF /* com.xcodesorg.xcodesapp.HelperTest.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = com.xcodesorg.xcodesapp.HelperTest.entitlements; sourceTree = ""; }; CA9FF9252595A7EB00E47BAF /* Scripts */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Scripts; sourceTree = ""; }; CA9FF9352595B44700E47BAF /* HelperClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelperClient.swift; sourceTree = ""; }; CAA1CB34255A5AD5003FD669 /* SignInCredentialsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInCredentialsView.swift; sourceTree = ""; }; CAA1CB44255A5B60003FD669 /* SignIn2FAView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignIn2FAView.swift; sourceTree = ""; }; CAA1CB48255A5C97003FD669 /* SignInSMSView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInSMSView.swift; sourceTree = ""; }; CAA1CB4C255A5CFD003FD669 /* SignInPhoneListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInPhoneListView.swift; sourceTree = ""; }; CAA8587B25A2B37900ACF8C0 /* IsTesting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IsTesting.swift; sourceTree = ""; }; CAA8588025A2B63A00ACF8C0 /* aria2c */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = aria2c; sourceTree = ""; }; CAA8588A25A2B69300ACF8C0 /* aria2c.LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = aria2c.LICENSE; sourceTree = ""; }; CAA8589A25A2B83000ACF8C0 /* Aria2CError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Aria2CError.swift; sourceTree = ""; }; CAA858C325A2BE4E00ACF8C0 /* Downloader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Downloader.swift; sourceTree = ""; }; CAA858DA25A3E11F00ACF8C0 /* aria2-release-1.35.0.tar.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = "aria2-release-1.35.0.tar.gz"; sourceTree = ""; }; CABFA9A02592EAF500380FEE /* R&PLogo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "R&PLogo.png"; sourceTree = ""; }; CABFA9A12592EAFB00380FEE /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; CABFA9A32592ED5700380FEE /* Apple.paw */ = {isa = PBXFileReference; lastKnownFileType = file; name = Apple.paw; path = ../xcodes/Apple.paw; sourceTree = ""; }; CABFA9A62592EEE900380FEE /* Version+Xcode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Version+Xcode.swift"; sourceTree = ""; }; CABFA9A72592EEE900380FEE /* AppState+Update.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppState+Update.swift"; sourceTree = ""; }; CABFA9A82592EEE900380FEE /* Version+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Version+.swift"; sourceTree = ""; }; CABFA9A92592EEE900380FEE /* Environment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Environment.swift; sourceTree = ""; }; CABFA9AB2592EEE900380FEE /* URLRequest+Apple.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLRequest+Apple.swift"; sourceTree = ""; }; CABFA9AC2592EEE900380FEE /* Foundation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Foundation.swift; sourceTree = ""; }; CABFA9AE2592EEE900380FEE /* Path+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Path+.swift"; sourceTree = ""; }; CABFA9B02592EEEA00380FEE /* Publisher+Resumable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Publisher+Resumable.swift"; sourceTree = ""; }; CABFA9B22592EEEA00380FEE /* Entry+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Entry+.swift"; sourceTree = ""; }; CABFA9B32592EEEA00380FEE /* URLSession+DownloadTaskPublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLSession+DownloadTaskPublisher.swift"; sourceTree = ""; }; CABFA9B42592EEEA00380FEE /* Process.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Process.swift; sourceTree = ""; }; CABFA9B82592EEEA00380FEE /* FileManager+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileManager+.swift"; sourceTree = ""; }; CABFA9B92592EEEA00380FEE /* Downloads.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Downloads.swift; sourceTree = ""; }; CABFA9BA2592EEEA00380FEE /* DateFormatter+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateFormatter+.swift"; sourceTree = ""; }; CABFA9D42592EF6300380FEE /* DECISIONS.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = DECISIONS.md; sourceTree = ""; }; CABFAA2B2592FBFC00380FEE /* Configure.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Configure.swift; path = Xcodes/Backend/Configure.swift; sourceTree = SOURCE_ROOT; }; CABFAA422593104F00380FEE /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = ""; }; CABFAA482593162500380FEE /* Bundle+InfoPlistValues.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+InfoPlistValues.swift"; sourceTree = ""; }; CAC281CC259F97FA00B8AB0B /* ObservingProgressIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObservingProgressIndicator.swift; sourceTree = ""; }; CAC281E1259FA44600B8AB0B /* Bundle+XcodesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+XcodesTests.swift"; sourceTree = ""; }; CAC281E6259FA45A00B8AB0B /* Environment+Mock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Environment+Mock.swift"; sourceTree = ""; }; CAC9F92C25BCDA4400B4965F /* HelperInstallState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelperInstallState.swift; sourceTree = ""; }; CAD2E79E2449574E00113D76 /* Xcodes.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Xcodes.app; sourceTree = BUILT_PRODUCTS_DIR; }; CAD2E7A12449574E00113D76 /* XcodesApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XcodesApp.swift; sourceTree = ""; }; CAD2E7A32449574E00113D76 /* XcodeListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XcodeListView.swift; sourceTree = ""; }; CAD2E7A52449575000113D76 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; CAD2E7A82449575000113D76 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; CAD2E7AD2449575000113D76 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; CAD2E7AE2449575000113D76 /* Xcodes.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Xcodes.entitlements; sourceTree = ""; }; CAD2E7B32449575100113D76 /* XcodesTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = XcodesTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; CAD2E7B72449575100113D76 /* AppStateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppStateTests.swift; sourceTree = ""; }; CAD2E7B92449575100113D76 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; CAE4247E259A666100B8B246 /* MainWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainWindow.swift; sourceTree = ""; }; CAE42486259A68A300B8B246 /* XcodeListCategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XcodeListCategory.swift; sourceTree = ""; }; CAE4248B259A68B800B8B246 /* Optional+IsNotNil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Optional+IsNotNil.swift"; sourceTree = ""; }; CAE424B3259A764700B8B246 /* AppState+Install.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AppState+Install.swift"; sourceTree = ""; }; CAFBC3FF259AC17F00E2A3D8 /* InstallationStepRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstallationStepRowView.swift; sourceTree = ""; }; CAFBC421259ACF8000E2A3D8 /* ObservingProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObservingProgressView.swift; sourceTree = ""; }; CAFBDB902598FE80003DCC5A /* SelectedXcode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectedXcode.swift; sourceTree = ""; }; CAFBDB942598FE96003DCC5A /* FocusedValues.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FocusedValues.swift; sourceTree = ""; }; CAFBDBA525990C76003DCC5A /* SimpleXPCApp.LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = SimpleXPCApp.LICENSE; sourceTree = ""; }; CAFBDC4D2599B33D003DCC5A /* MainToolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainToolbar.swift; sourceTree = ""; }; CAFBDC67259A308B003DCC5A /* InfoPane.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoPane.swift; sourceTree = ""; }; CAFBDC6B259A3098003DCC5A /* View+Conditional.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Conditional.swift"; sourceTree = ""; }; CAFE4AAB25B7D2C70064FE51 /* GeneralPreferencePane.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneralPreferencePane.swift; sourceTree = ""; }; CAFE4AB325B7D3AF0064FE51 /* AdvancedPreferencePane.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdvancedPreferencePane.swift; sourceTree = ""; }; CAFE4ABB25B7D54B0064FE51 /* UpdatesPreferencePane.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpdatesPreferencePane.swift; sourceTree = ""; }; CAFFFED7259CDA5000903F81 /* XcodeListViewRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XcodeListViewRow.swift; sourceTree = ""; }; CAFFFEEE259CEAC400903F81 /* RingProgressViewStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RingProgressViewStyle.swift; sourceTree = ""; }; D93F95C02E0C8C1A00238FB5 /* TagView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagView.swift; sourceTree = ""; }; E81D7E9F2805250100A205FC /* Collection+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Collection+.swift"; sourceTree = ""; }; E832EAF72B0FBCF4001B570D /* RuntimeInstallationStepDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuntimeInstallationStepDetailView.swift; sourceTree = ""; }; E84B7D0C2B296A8900DBDA2B /* NavigationSplitViewWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationSplitViewWrapper.swift; sourceTree = ""; }; E84E4F512B323A5F003F3959 /* CornerRadiusModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CornerRadiusModifier.swift; sourceTree = ""; }; E84E4F532B333864003F3959 /* PlatformsListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlatformsListView.swift; sourceTree = ""; }; E856BB73291EDD3D00DC438B /* XcodesKit */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = XcodesKit; path = Xcodes/XcodesKit; sourceTree = ""; }; E86671262B309D2F0048559A /* PlatformsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlatformsView.swift; sourceTree = ""; }; E87AB3C42939B65E00D72F43 /* Hardware.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Hardware.swift; sourceTree = ""; }; E87DD6EA25D053FA00D86808 /* Progress+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Progress+.swift"; sourceTree = ""; }; E89342F925EDCC17007CF557 /* NotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationManager.swift; sourceTree = ""; }; E8977EA225C11E1500835F80 /* PreferencesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesView.swift; sourceTree = ""; }; E8B20CBE2A2EDEC20057D816 /* SDKs+Xcode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SDKs+Xcode.swift"; sourceTree = ""; }; E8C0EB1B291EF9A10081528A /* AppState+Runtimes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppState+Runtimes.swift"; sourceTree = ""; }; E8CBDB8627ADD92000B22292 /* unxip */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = unxip; sourceTree = ""; }; E8CBDB8A27AE02FF00B22292 /* ExperiementsPreferencePane.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExperiementsPreferencePane.swift; sourceTree = ""; }; E8D0296E284B029800647641 /* BottomStatusBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomStatusBar.swift; sourceTree = ""; }; E8D655BF288DD04700A139C2 /* SelectedActionType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectedActionType.swift; sourceTree = ""; }; E8DA461025FAF7FB002E85EF /* NotificationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsView.swift; sourceTree = ""; }; E8E98A9525D863D700EC89A0 /* InstallationStepDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstallationStepDetailView.swift; sourceTree = ""; }; E8EEAD1C2E79174F00BE67E8 /* XcodesIcon.icon */ = {isa = PBXFileReference; lastKnownFileType = folder.iconcomposer.icon; path = XcodesIcon.icon; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ CA9FF8AB2595967A00E47BAF /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; CAD2E79B2449574E00113D76 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 15F5B8902CCF09B900705E2F /* CryptoKit.framework in Frameworks */, 33027E342CA8C18800CB387C /* LibFido2Swift in Frameworks */, CABFA9E42592F08E00380FEE /* Version in Frameworks */, CABFA9FD2592F13300380FEE /* LegibleError in Frameworks */, E689540325BE8C64000EBCEA /* DockProgress in Frameworks */, CABFA9F82592F0F900380FEE /* KeychainAccess in Frameworks */, E83FDC442CBB649100679C6B /* Sparkle in Frameworks */, E862D43B2CC8B26F00BAA376 /* SRP in Frameworks */, CAA858CD25A3D8BC00ACF8C0 /* ErrorHandling in Frameworks */, E8C0EB1A291EF43E0081528A /* XcodesKit in Frameworks */, E8FD5727291EE4AC001E004C /* AsyncNetworkService in Frameworks */, CAA1CB2D255A5262003FD669 /* AppleAPI in Frameworks */, CABFA9EE2592F0CC00380FEE /* SwiftSoup in Frameworks */, E84E4F572B335094003F3959 /* OrderedCollections in Frameworks */, E8F44A1E296B4CD7002D6592 /* Path in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; CAD2E7B02449575100113D76 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( CAC28188259EE27200B8AB0B /* CombineExpectations in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 63EAA4E9259944340046AB8F /* Common */ = { isa = PBXGroup; children = ( D93F95C02E0C8C1A00238FB5 /* TagView.swift */, CAC281CC259F97FA00B8AB0B /* ObservingProgressIndicator.swift */, 63EAA4EA259944450046AB8F /* ProgressButton.swift */, CA452BAF259FD9770072DFA4 /* ProgressIndicator.swift */, 536CFDD3263C9A8000026CE0 /* XcodesSheet.swift */, 53CBAB2B263DCC9100410495 /* XcodesAlert.swift */, E84B7D0C2B296A8900DBDA2B /* NavigationSplitViewWrapper.swift */, BDBAB7442B9FF55800694B0B /* TrailingIconLabelStyle.swift */, ); path = Common; sourceTree = ""; }; CA452BBD259FDDBF0072DFA4 /* Fixtures */ = { isa = PBXGroup; children = ( CA452BEA25A236500072DFA4 /* Stub-0.0.0.Info.plist */, CA452BBF259FDDFE0072DFA4 /* Stub-version.plist */, ); path = Fixtures; sourceTree = ""; }; CA452BE025A2354D0072DFA4 /* Recovered References */ = { isa = PBXGroup; children = ( CAFBC421259ACF8000E2A3D8 /* ObservingProgressView.swift */, CAFFFEEE259CEAC400903F81 /* RingProgressViewStyle.swift */, ); name = "Recovered References"; sourceTree = ""; }; CA538A12255A4F7C00E64DD7 /* Frameworks */ = { isa = PBXGroup; children = ( 15F5B88F2CCF09B900705E2F /* CryptoKit.framework */, ); name = Frameworks; sourceTree = ""; }; CA9FF8552595082000E47BAF /* About */ = { isa = PBXGroup; children = ( CABFAA422593104F00380FEE /* AboutView.swift */, CA9FF8512595080100E47BAF /* AcknowledgementsView.swift */, CA9FF84D2595079F00E47BAF /* ScrollingTextView.swift */, ); path = About; sourceTree = ""; }; CA9FF8AF2595967A00E47BAF /* com.xcodesorg.xcodesapp.Helper */ = { isa = PBXGroup; children = ( CA9FF8E425959BB800E47BAF /* AuditTokenHack.h */, CA9FF8E525959BB800E47BAF /* AuditTokenHack.m */, CA9FF8EA25959BDD00E47BAF /* com.xcodesorg.xcodesapp.Helper-Bridging-Header.h */, CA9FF8DF25959BAA00E47BAF /* ConnectionVerifier.swift */, CA42DD7225AEB04300BC0B0C /* Logger.swift */, CA9FF8B02595967A00E47BAF /* main.swift */, CA9FF8DA25959B4000E47BAF /* XPCDelegate.swift */, CA9FF8C22595988B00E47BAF /* Info.plist */, CA9FF8C32595989800E47BAF /* launchd.plist */, CA9FF9052595A28400E47BAF /* com.xcodesorg.xcodesapp.HelperTest.entitlements */, CAFBDBA525990C76003DCC5A /* SimpleXPCApp.LICENSE */, ); path = com.xcodesorg.xcodesapp.Helper; sourceTree = ""; }; CA9FF8CD25959A7600E47BAF /* HelperXPCShared */ = { isa = PBXGroup; children = ( CA9FF8CE25959A9700E47BAF /* HelperXPCShared.swift */, ); path = HelperXPCShared; sourceTree = ""; }; CAA1CB50255A5D16003FD669 /* SignIn */ = { isa = PBXGroup; children = ( CA735108257BF96D00EA9CF8 /* AttributedText.swift */, CA73510C257BFCEF00EA9CF8 /* NSAttributedString+.swift */, CA5D781D257365D6008EDE9D /* PinCodeTextView.swift */, 3328073E2CA5E2C80036F691 /* SignInSecurityKeyPinView.swift */, 332807402CA5EA820036F691 /* SignInSecurityKeyTouchView.swift */, CAA1CB34255A5AD5003FD669 /* SignInCredentialsView.swift */, CAA1CB44255A5B60003FD669 /* SignIn2FAView.swift */, CAA1CB48255A5C97003FD669 /* SignInSMSView.swift */, CAA1CB4C255A5CFD003FD669 /* SignInPhoneListView.swift */, 536CFDD1263C94DE00026CE0 /* SignedInView.swift */, ); path = SignIn; sourceTree = ""; }; CABFAA142592F73000380FEE /* XcodeList */ = { isa = PBXGroup; children = ( CA39711824495F0E00AFFB77 /* AppStoreButtonStyle.swift */, CAFBC3FF259AC17F00E2A3D8 /* InstallationStepRowView.swift */, CAFBDC4D2599B33D003DCC5A /* MainToolbar.swift */, CA44901E2463AD34003D8213 /* Tag.swift */, CAE42486259A68A300B8B246 /* XcodeListCategory.swift */, CAD2E7A32449574E00113D76 /* XcodeListView.swift */, CAFFFED7259CDA5000903F81 /* XcodeListViewRow.swift */, E8D0296E284B029800647641 /* BottomStatusBar.swift */, ); path = XcodeList; sourceTree = ""; }; CABFAA172592F77E00380FEE /* Backend */ = { isa = PBXGroup; children = ( CA378F982466567600A58CE0 /* AppState.swift */, E8C0EB1B291EF9A10081528A /* AppState+Runtimes.swift */, CAE424B3259A764700B8B246 /* AppState+Install.swift */, CABFA9A72592EEE900380FEE /* AppState+Update.swift */, CAA8589A25A2B83000ACF8C0 /* Aria2CError.swift */, CA9FF88025955C7000E47BAF /* AvailableXcode.swift */, CABFAA2B2592FBFC00380FEE /* Configure.swift */, CABFAA482593162500380FEE /* Bundle+InfoPlistValues.swift */, CA9FF87A2595293E00E47BAF /* DataSource.swift */, CABFA9BA2592EEEA00380FEE /* DateFormatter+.swift */, CABFA9B92592EEEA00380FEE /* Downloads.swift */, CAA858C325A2BE4E00ACF8C0 /* Downloader.swift */, CABFA9B22592EEEA00380FEE /* Entry+.swift */, CABFA9A92592EEE900380FEE /* Environment.swift */, 36741BFE291E50F500A85AAE /* FileError.swift */, CABFA9B82592EEEA00380FEE /* FileManager+.swift */, CAFBDB942598FE96003DCC5A /* FocusedValues.swift */, CABFA9AC2592EEE900380FEE /* Foundation.swift */, CA9FF9352595B44700E47BAF /* HelperClient.swift */, CAC9F92C25BCDA4400B4965F /* HelperInstallState.swift */, CA9FF8862595607900E47BAF /* InstalledXcode.swift */, CAA8587B25A2B37900ACF8C0 /* IsTesting.swift */, E89342F925EDCC17007CF557 /* NotificationManager.swift */, CAE4248B259A68B800B8B246 /* Optional+IsNotNil.swift */, CABFA9AE2592EEE900380FEE /* Path+.swift */, CABFA9B42592EEEA00380FEE /* Process.swift */, CABFA9B02592EEEA00380FEE /* Publisher+Resumable.swift */, CAFBDB902598FE80003DCC5A /* SelectedXcode.swift */, CABFA9AB2592EEE900380FEE /* URLRequest+Apple.swift */, CABFA9B32592EEEA00380FEE /* URLSession+DownloadTaskPublisher.swift */, CABFA9A82592EEE900380FEE /* Version+.swift */, CA9FF876259528CC00E47BAF /* Version+XcodeReleases.swift */, CABFA9A62592EEE900380FEE /* Version+Xcode.swift */, CA61A6DF259835580008926E /* Xcode.swift */, CA25192925A9644800F08414 /* XcodeInstallState.swift */, CA11E7B92598476C00D2EE1C /* XcodeCommands.swift */, E87DD6EA25D053FA00D86808 /* Progress+.swift */, E81D7E9F2805250100A205FC /* Collection+.swift */, E8D655BF288DD04700A139C2 /* SelectedActionType.swift */, E87AB3C42939B65E00D72F43 /* Hardware.swift */, E8B20CBE2A2EDEC20057D816 /* SDKs+Xcode.swift */, ); path = Backend; sourceTree = ""; }; CABFAA1A2592F7D900380FEE /* Frontend */ = { isa = PBXGroup; children = ( E8E98A9425D863B100EC89A0 /* InfoPane */, 63EAA4E9259944340046AB8F /* Common */, CA9FF8552595082000E47BAF /* About */, CAFE4AAA25B7D29B0064FE51 /* Preferences */, CAA1CB50255A5D16003FD669 /* SignIn */, CABFAA142592F73000380FEE /* XcodeList */, CAE4247E259A666100B8B246 /* MainWindow.swift */, CAFBDC6B259A3098003DCC5A /* View+Conditional.swift */, CA9FF8652595130600E47BAF /* View+IsHidden.swift */, ); path = Frontend; sourceTree = ""; }; CABFAA1D2592F7F200380FEE /* Resources */ = { isa = PBXGroup; children = ( E8EEAD1C2E79174F00BE67E8 /* XcodesIcon.icon */, CAA858DA25A3E11F00ACF8C0 /* aria2-release-1.35.0.tar.gz */, CAA8588025A2B63A00ACF8C0 /* aria2c */, CAA8588A25A2B69300ACF8C0 /* aria2c.LICENSE */, CAD2E7A52449575000113D76 /* Assets.xcassets */, CAD2E7AD2449575000113D76 /* Info.plist */, CA9FF83E2594FBC000E47BAF /* Licenses.rtf */, CAD2E7AE2449575000113D76 /* Xcodes.entitlements */, CA8FB64D256E17B100469DA5 /* XcodesTest.entitlements */, E8CBDB8627ADD92000B22292 /* unxip */, 9DD4FFCA2B13EC1800C974F1 /* Localizable.xcstrings */, ); path = Resources; sourceTree = ""; }; CAD2E7952449574E00113D76 = { isa = PBXGroup; children = ( E856BB73291EDD3D00DC438B /* XcodesKit */, CA8FB5F8256E0F9400469DA5 /* README.md */, CABFA9D42592EF6300380FEE /* DECISIONS.md */, CABFA9A02592EAF500380FEE /* R&PLogo.png */, CABFA9A32592ED5700380FEE /* Apple.paw */, CABFA9A12592EAFB00380FEE /* LICENSE */, CA8FB61C256E115700469DA5 /* .github */, CA9FF9252595A7EB00E47BAF /* Scripts */, CA9FF8242594F10700E47BAF /* AcknowledgementsGenerator */, CA538A0C255A4F1A00E64DD7 /* AppleAPI */, CAD2E7A02449574E00113D76 /* Xcodes */, CAD2E7B62449575100113D76 /* XcodesTests */, CA9FF8AF2595967A00E47BAF /* com.xcodesorg.xcodesapp.Helper */, CA9FF8CD25959A7600E47BAF /* HelperXPCShared */, CAD2E79F2449574E00113D76 /* Products */, CA538A12255A4F7C00E64DD7 /* Frameworks */, CA452BE025A2354D0072DFA4 /* Recovered References */, ); sourceTree = ""; }; CAD2E79F2449574E00113D76 /* Products */ = { isa = PBXGroup; children = ( CAD2E79E2449574E00113D76 /* Xcodes.app */, CAD2E7B32449575100113D76 /* XcodesTests.xctest */, CA9FF8AE2595967A00E47BAF /* com.xcodesorg.xcodesapp.Helper */, ); name = Products; sourceTree = ""; }; CAD2E7A02449574E00113D76 /* Xcodes */ = { isa = PBXGroup; children = ( CAD2E7A12449574E00113D76 /* XcodesApp.swift */, CABFAA1A2592F7D900380FEE /* Frontend */, CABFAA172592F77E00380FEE /* Backend */, CABFAA1D2592F7F200380FEE /* Resources */, CAD2E7A72449575000113D76 /* Preview Content */, ); path = Xcodes; sourceTree = ""; }; CAD2E7A72449575000113D76 /* Preview Content */ = { isa = PBXGroup; children = ( CAD2E7A82449575000113D76 /* Preview Assets.xcassets */, ); path = "Preview Content"; sourceTree = ""; }; CAD2E7B62449575100113D76 /* XcodesTests */ = { isa = PBXGroup; children = ( CA452BBD259FDDBF0072DFA4 /* Fixtures */, CAC281E1259FA44600B8AB0B /* Bundle+XcodesTests.swift */, CAC281E6259FA45A00B8AB0B /* Environment+Mock.swift */, CAD2E7B72449575100113D76 /* AppStateTests.swift */, CA2518EB25A7FF2B00F08414 /* AppStateUpdateTests.swift */, CAD2E7B92449575100113D76 /* Info.plist */, ); path = XcodesTests; sourceTree = ""; }; CAFE4AAA25B7D29B0064FE51 /* Preferences */ = { isa = PBXGroup; children = ( CAFE4AB325B7D3AF0064FE51 /* AdvancedPreferencePane.swift */, 36741BFC291E4FDB00A85AAE /* DownloadPreferencePane.swift */, CAFE4AAB25B7D2C70064FE51 /* GeneralPreferencePane.swift */, CAFE4ABB25B7D54B0064FE51 /* UpdatesPreferencePane.swift */, E8977EA225C11E1500835F80 /* PreferencesView.swift */, E8DA461025FAF7FB002E85EF /* NotificationsView.swift */, E8CBDB8A27AE02FF00B22292 /* ExperiementsPreferencePane.swift */, E84E4F532B333864003F3959 /* PlatformsListView.swift */, ); path = Preferences; sourceTree = ""; }; E8E98A9425D863B100EC89A0 /* InfoPane */ = { isa = PBXGroup; children = ( B0403CEF2AD92D7B00137C09 /* ReleaseNotesView.swift */, B0403CF32AD9381D00137C09 /* SDKsView.swift */, B0403CF52AD9849E00137C09 /* CompilersView.swift */, B0403CF12AD934B600137C09 /* CompatibilityView.swift */, CAFBDC67259A308B003DCC5A /* InfoPane.swift */, B0403CFD2ADA712C00137C09 /* InfoPaneControls.swift */, B0403CF72AD991F800137C09 /* UnselectedView.swift */, B0403CF92AD9942A00137C09 /* NotInstalledStateButtons.swift */, B0403CFB2AD9A6BF00137C09 /* InstalledStateButtons.swift */, E8E98A9525D863D700EC89A0 /* InstallationStepDetailView.swift */, B0C6AD032AD6E65700E64698 /* ReleaseDateView.swift */, B0C6AD0A2AD9178E00E64698 /* IdenticalBuildView.swift */, B0C6AD0C2AD91D7900E64698 /* IconView.swift */, E832EAF72B0FBCF4001B570D /* RuntimeInstallationStepDetailView.swift */, E86671262B309D2F0048559A /* PlatformsView.swift */, E84E4F512B323A5F003F3959 /* CornerRadiusModifier.swift */, ); path = InfoPane; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ CA9FF8AD2595967A00E47BAF /* com.xcodesorg.xcodesapp.Helper */ = { isa = PBXNativeTarget; buildConfigurationList = CA9FF8B52595967A00E47BAF /* Build configuration list for PBXNativeTarget "com.xcodesorg.xcodesapp.Helper" */; buildPhases = ( CA9FF8AA2595967A00E47BAF /* Sources */, CA9FF8AB2595967A00E47BAF /* Frameworks */, CA9FF8AC2595967A00E47BAF /* CopyFiles */, ); buildRules = ( ); dependencies = ( ); name = com.xcodesorg.xcodesapp.Helper; productName = com.robotsandpencils.XcodesApp.Helper; productReference = CA9FF8AE2595967A00E47BAF /* com.xcodesorg.xcodesapp.Helper */; productType = "com.apple.product-type.tool"; }; CAD2E79D2449574E00113D76 /* Xcodes */ = { isa = PBXNativeTarget; buildConfigurationList = CAD2E7BC2449575100113D76 /* Build configuration list for PBXNativeTarget "Xcodes" */; buildPhases = ( CAD2E79A2449574E00113D76 /* Sources */, CAD2E79B2449574E00113D76 /* Frameworks */, CA9FF8292594F33200E47BAF /* Generate Acknowledgements */, CAD2E79C2449574E00113D76 /* Resources */, CA9FF8BB259596B500E47BAF /* Copy Helper */, CAA8589225A2B76F00ACF8C0 /* Copy aria2c */, ); buildRules = ( ); dependencies = ( CA9FF8BA259596A000E47BAF /* PBXTargetDependency */, ); name = Xcodes; packageProductDependencies = ( CAA1CB2C255A5262003FD669 /* AppleAPI */, CABFA9E32592F08E00380FEE /* Version */, CABFA9ED2592F0CC00380FEE /* SwiftSoup */, CABFA9F72592F0F900380FEE /* KeychainAccess */, CABFA9FC2592F13300380FEE /* LegibleError */, CAA858CC25A3D8BC00ACF8C0 /* ErrorHandling */, E689540225BE8C64000EBCEA /* DockProgress */, E8FD5726291EE4AC001E004C /* AsyncNetworkService */, E8C0EB19291EF43E0081528A /* XcodesKit */, E8F44A1D296B4CD7002D6592 /* Path */, E84E4F562B335094003F3959 /* OrderedCollections */, E83FDC432CBB649100679C6B /* Sparkle */, 334A932B2CA885A400A5E079 /* LibFido2Swift */, E862D43A2CC8B26F00BAA376 /* SRP */, ); productName = XcodesMac; productReference = CAD2E79E2449574E00113D76 /* Xcodes.app */; productType = "com.apple.product-type.application"; }; CAD2E7B22449575100113D76 /* XcodesTests */ = { isa = PBXNativeTarget; buildConfigurationList = CAD2E7BF2449575100113D76 /* Build configuration list for PBXNativeTarget "XcodesTests" */; buildPhases = ( CAD2E7AF2449575100113D76 /* Sources */, CAD2E7B02449575100113D76 /* Frameworks */, CAD2E7B12449575100113D76 /* Resources */, ); buildRules = ( ); dependencies = ( CAD2E7B52449575100113D76 /* PBXTargetDependency */, ); name = XcodesTests; packageProductDependencies = ( CAC28187259EE27200B8AB0B /* CombineExpectations */, ); productName = XcodesMacTests; productReference = CAD2E7B32449575100113D76 /* XcodesTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ CAD2E7962449574E00113D76 /* Project object */ = { isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1220; LastUpgradeCheck = 1140; ORGANIZATIONNAME = "Robots and Pencils"; TargetAttributes = { CA9FF8AD2595967A00E47BAF = { CreatedOnToolsVersion = 12.2; }; CAD2E79D2449574E00113D76 = { CreatedOnToolsVersion = 11.4; }; CAD2E7B22449575100113D76 = { CreatedOnToolsVersion = 11.4; TestTargetID = CAD2E79D2449574E00113D76; }; }; }; buildConfigurationList = CAD2E7992449574E00113D76 /* Build configuration list for PBXProject "Xcodes" */; compatibilityVersion = "Xcode 9.3"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, hi, fr, ru, es, "zh-Hans", tr, ko, it, ja, "zh-Hant", de, uk, fi, ca, "pt-BR", nl, pl, ar, th, ); mainGroup = CAD2E7952449574E00113D76; packageReferences = ( CABFA9E22592F08E00380FEE /* XCRemoteSwiftPackageReference "Version" */, CABFA9EC2592F0CC00380FEE /* XCRemoteSwiftPackageReference "SwiftSoup" */, CABFA9F62592F0F900380FEE /* XCRemoteSwiftPackageReference "KeychainAccess" */, CABFA9FB2592F13300380FEE /* XCRemoteSwiftPackageReference "LegibleError" */, CAA858CB25A3D8BC00ACF8C0 /* XCRemoteSwiftPackageReference "ErrorHandling" */, CAC28186259EE27200B8AB0B /* XCRemoteSwiftPackageReference "CombineExpectations" */, E689540125BE8C64000EBCEA /* XCRemoteSwiftPackageReference "DockProgress" */, E8FD5725291EE4AC001E004C /* XCRemoteSwiftPackageReference "AsyncHTTPNetworkService" */, E8F44A1C296B4CD7002D6592 /* XCRemoteSwiftPackageReference "Path" */, E84E4F552B335094003F3959 /* XCRemoteSwiftPackageReference "swift-collections" */, E83FDC422CBB649100679C6B /* XCRemoteSwiftPackageReference "Sparkle" */, 33027E282CA8BB5800CB387C /* XCRemoteSwiftPackageReference "LibFido2Swift" */, ); productRefGroup = CAD2E79F2449574E00113D76 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( CAD2E79D2449574E00113D76 /* Xcodes */, CAD2E7B22449575100113D76 /* XcodesTests */, CA9FF8AD2595967A00E47BAF /* com.xcodesorg.xcodesapp.Helper */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ CAD2E79C2449574E00113D76 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( CAD2E7A92449575000113D76 /* Preview Assets.xcassets in Resources */, 9DD4FFCB2B13EC1800C974F1 /* Localizable.xcstrings in Resources */, E8EEAD1D2E79174F00BE67E8 /* XcodesIcon.icon in Resources */, CA9FF83F2594FBC000E47BAF /* Licenses.rtf in Resources */, CAA858DB25A3E11F00ACF8C0 /* aria2-release-1.35.0.tar.gz in Resources */, CAD2E7A62449575000113D76 /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; CAD2E7B12449575100113D76 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( CA452BC1259FDDFE0072DFA4 /* Stub-version.plist in Resources */, CA452BEB25A236500072DFA4 /* Stub-0.0.0.Info.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ CA9FF8292594F33200E47BAF /* Generate Acknowledgements */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( ); inputPaths = ( "$(SRCROOT)/Xcodes.xcodeproj", "$(SRCROOT)/**/*.LICENSE", ); name = "Generate Acknowledgements"; outputFileListPaths = ( ); outputPaths = ( "$(SRCROOT)/Xcodes/Resources/Licenses.rtf", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "cd \"${SRCROOT}/Xcodes/AcknowledgementsGenerator\"\nxcrun -sdk macosx swift run AcknowledgementsGenerator \\\n -p \"${SRCROOT}/Xcodes.xcodeproj\" \\\n -o \"${SRCROOT}/Xcodes/Resources/Licenses.rtf\"\n"; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ CA9FF8AA2595967A00E47BAF /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( CA9FF8D025959A9700E47BAF /* HelperXPCShared.swift in Sources */, CA42DD7325AEB04300BC0B0C /* Logger.swift in Sources */, CA9FF8DB25959B4000E47BAF /* XPCDelegate.swift in Sources */, CA9FF8E625959BB800E47BAF /* AuditTokenHack.m in Sources */, CA9FF8B12595967A00E47BAF /* main.swift in Sources */, CA9FF8E025959BAA00E47BAF /* ConnectionVerifier.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; CAD2E79A2449574E00113D76 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( CA9FF8CF25959A9700E47BAF /* HelperXPCShared.swift in Sources */, CA735109257BF96D00EA9CF8 /* AttributedText.swift in Sources */, CAFBDC4E2599B33D003DCC5A /* MainToolbar.swift in Sources */, CA11E7BA2598476C00D2EE1C /* XcodeCommands.swift in Sources */, CAA8589B25A2B83000ACF8C0 /* Aria2CError.swift in Sources */, 536CFDD2263C94DE00026CE0 /* SignedInView.swift in Sources */, CABFAA492593162500380FEE /* Bundle+InfoPlistValues.swift in Sources */, CA9FF8662595130600E47BAF /* View+IsHidden.swift in Sources */, CAE4248C259A68B800B8B246 /* Optional+IsNotNil.swift in Sources */, 3328073F2CA5E2C80036F691 /* SignInSecurityKeyPinView.swift in Sources */, B0C6AD0D2AD91D7900E64698 /* IconView.swift in Sources */, CA9FF9362595B44700E47BAF /* HelperClient.swift in Sources */, B0C6AD042AD6E65700E64698 /* ReleaseDateView.swift in Sources */, CAA8587C25A2B37900ACF8C0 /* IsTesting.swift in Sources */, CABFA9CA2592EEEA00380FEE /* AppState+Update.swift in Sources */, CA44901F2463AD34003D8213 /* Tag.swift in Sources */, CABFA9BF2592EEEA00380FEE /* URLSession+DownloadTaskPublisher.swift in Sources */, CAFE4AAC25B7D2C70064FE51 /* GeneralPreferencePane.swift in Sources */, CABFA9BB2592EEEA00380FEE /* DateFormatter+.swift in Sources */, CAFE4ABC25B7D54B0064FE51 /* UpdatesPreferencePane.swift in Sources */, CABFA9BD2592EEEA00380FEE /* Environment.swift in Sources */, CABFA9C32592EEEA00380FEE /* Downloads.swift in Sources */, E8CBDB8B27AE02FF00B22292 /* ExperiementsPreferencePane.swift in Sources */, E8E98A9625D863D700EC89A0 /* InstallationStepDetailView.swift in Sources */, CA378F992466567600A58CE0 /* AppState.swift in Sources */, CAD2E7A42449574E00113D76 /* XcodeListView.swift in Sources */, CAA1CB45255A5B60003FD669 /* SignIn2FAView.swift in Sources */, CABFA9C52592EEEA00380FEE /* FileManager+.swift in Sources */, CABFA9CD2592EEEA00380FEE /* Foundation.swift in Sources */, B0403CFC2AD9A6BF00137C09 /* InstalledStateButtons.swift in Sources */, 36741BFF291E50F500A85AAE /* FileError.swift in Sources */, CA9FF8872595607900E47BAF /* InstalledXcode.swift in Sources */, E84E4F522B323A5F003F3959 /* CornerRadiusModifier.swift in Sources */, B0403CF22AD934B600137C09 /* CompatibilityView.swift in Sources */, B0403CFE2ADA712C00137C09 /* InfoPaneControls.swift in Sources */, D93F95C12E0C8C1A00238FB5 /* TagView.swift in Sources */, 53CBAB2C263DCC9100410495 /* XcodesAlert.swift in Sources */, 332807412CA5EA820036F691 /* SignInSecurityKeyTouchView.swift in Sources */, CA61A6E0259835580008926E /* Xcode.swift in Sources */, CAE4247F259A666100B8B246 /* MainWindow.swift in Sources */, CA452BB0259FD9770072DFA4 /* ProgressIndicator.swift in Sources */, B0403CF02AD92D7B00137C09 /* ReleaseNotesView.swift in Sources */, CAFE4AB425B7D3AF0064FE51 /* AdvancedPreferencePane.swift in Sources */, CA9FF84E2595079F00E47BAF /* ScrollingTextView.swift in Sources */, E832EAF82B0FBCF4001B570D /* RuntimeInstallationStepDetailView.swift in Sources */, CABFA9C12592EEEA00380FEE /* Version+.swift in Sources */, BDBAB7452B9FF55800694B0B /* TrailingIconLabelStyle.swift in Sources */, E8D655C0288DD04700A139C2 /* SelectedActionType.swift in Sources */, 36741BFD291E4FDB00A85AAE /* DownloadPreferencePane.swift in Sources */, E84E4F542B333864003F3959 /* PlatformsListView.swift in Sources */, E86671272B309D2F0048559A /* PlatformsView.swift in Sources */, CA9FF8522595080100E47BAF /* AcknowledgementsView.swift in Sources */, CABFA9CE2592EEEA00380FEE /* Version+Xcode.swift in Sources */, CAFBDB912598FE80003DCC5A /* SelectedXcode.swift in Sources */, CAA1CB49255A5C97003FD669 /* SignInSMSView.swift in Sources */, CA25192A25A9644800F08414 /* XcodeInstallState.swift in Sources */, E8DA461125FAF7FB002E85EF /* NotificationsView.swift in Sources */, CAA1CB35255A5AD5003FD669 /* SignInCredentialsView.swift in Sources */, E81D7EA02805250100A205FC /* Collection+.swift in Sources */, E8B20CBF2A2EDEC20057D816 /* SDKs+Xcode.swift in Sources */, CA9FF877259528CC00E47BAF /* Version+XcodeReleases.swift in Sources */, CABFAA2D2592FBFC00380FEE /* Configure.swift in Sources */, E84B7D0D2B296A8900DBDA2B /* NavigationSplitViewWrapper.swift in Sources */, CA73510D257BFCEF00EA9CF8 /* NSAttributedString+.swift in Sources */, CAFBDB952598FE96003DCC5A /* FocusedValues.swift in Sources */, B0403CF42AD9381D00137C09 /* SDKsView.swift in Sources */, CAC9F92D25BCDA4400B4965F /* HelperInstallState.swift in Sources */, E87DD6EB25D053FA00D86808 /* Progress+.swift in Sources */, CAC281CD259F97FA00B8AB0B /* ObservingProgressIndicator.swift in Sources */, CABFA9C22592EEEA00380FEE /* Publisher+Resumable.swift in Sources */, B0C6AD0B2AD9178E00E64698 /* IdenticalBuildView.swift in Sources */, CAFBDC68259A308B003DCC5A /* InfoPane.swift in Sources */, B0403CF82AD991F800137C09 /* UnselectedView.swift in Sources */, E87AB3C52939B65E00D72F43 /* Hardware.swift in Sources */, CAA1CB4D255A5CFD003FD669 /* SignInPhoneListView.swift in Sources */, CAFBDC6C259A3098003DCC5A /* View+Conditional.swift in Sources */, CABFA9CF2592EEEA00380FEE /* Process.swift in Sources */, CAFFFED8259CDA5000903F81 /* XcodeListViewRow.swift in Sources */, CABFA9C72592EEEA00380FEE /* Entry+.swift in Sources */, B0403CFA2AD9942A00137C09 /* NotInstalledStateButtons.swift in Sources */, CAE424B4259A764700B8B246 /* AppState+Install.swift in Sources */, CAE42487259A68A300B8B246 /* XcodeListCategory.swift in Sources */, CAA858C425A2BE4E00ACF8C0 /* Downloader.swift in Sources */, B0403CF62AD9849E00137C09 /* CompilersView.swift in Sources */, E8977EA325C11E1500835F80 /* PreferencesView.swift in Sources */, CA9FF87B2595293E00E47BAF /* DataSource.swift in Sources */, CABFA9C92592EEEA00380FEE /* URLRequest+Apple.swift in Sources */, CABFAA432593104F00380FEE /* AboutView.swift in Sources */, E8D0296F284B029800647641 /* BottomStatusBar.swift in Sources */, E8C0EB1C291EF9A10081528A /* AppState+Runtimes.swift in Sources */, E8E98A9025D8631800EC89A0 /* InstallationStepRowView.swift in Sources */, CABFA9CC2592EEEA00380FEE /* Path+.swift in Sources */, CAD2E7A22449574E00113D76 /* XcodesApp.swift in Sources */, 63EAA4EB259944450046AB8F /* ProgressButton.swift in Sources */, 536CFDD4263C9A8000026CE0 /* XcodesSheet.swift in Sources */, E89342FA25EDCC17007CF557 /* NotificationManager.swift in Sources */, CA5D781E257365D6008EDE9D /* PinCodeTextView.swift in Sources */, CA39711924495F0E00AFFB77 /* AppStoreButtonStyle.swift in Sources */, CA9FF88125955C7000E47BAF /* AvailableXcode.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; CAD2E7AF2449575100113D76 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( CAC281E7259FA45A00B8AB0B /* Environment+Mock.swift in Sources */, CAC281E2259FA44600B8AB0B /* Bundle+XcodesTests.swift in Sources */, CA2518EC25A7FF2B00F08414 /* AppStateUpdateTests.swift in Sources */, CAB3AB0E25BCA6C200BF1B04 /* AppStateTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ CA9FF8BA259596A000E47BAF /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = CA9FF8AD2595967A00E47BAF /* com.xcodesorg.xcodesapp.Helper */; targetProxy = CA9FF8B9259596A000E47BAF /* PBXContainerItemProxy */; }; CAD2E7B52449575100113D76 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = CAD2E79D2449574E00113D76 /* Xcodes */; targetProxy = CAD2E7B42449575100113D76 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ CA8FB635256E154800469DA5 /* Test */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGNING_SUBJECT_ORGANIZATIONAL_UNIT = ZU6GR6B2FY; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MACOSX_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Test; }; CA8FB636256E154800469DA5 /* Test */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = XcodesIcon; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; CODE_SIGNING_SUBJECT_ORGANIZATIONAL_UNIT = ZU6GR6B2FY; CODE_SIGN_ENTITLEMENTS = Xcodes/Resources/XcodesTest.entitlements; CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 34; DEVELOPMENT_ASSET_PATHS = "\"Xcodes/Preview Content\""; DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = NO; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = Xcodes/Resources/Info.plist; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; MARKETING_VERSION = 3.0.2; PRODUCT_BUNDLE_IDENTIFIER = com.xcodesorg.xcodesapp; PRODUCT_NAME = Xcodes; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; }; name = Test; }; CA8FB637256E154800469DA5 /* Test */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = XcodesTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", "@loader_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; PRODUCT_BUNDLE_IDENTIFIER = com.robotsandpencils.XcodesAppTests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Xcodes.app/Contents/MacOS/Xcodes"; }; name = Test; }; CA9FF8B22595967A00E47BAF /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CODE_SIGNING_SUBJECT_ORGANIZATIONAL_UNIT = ZU6GR6B2FY; CODE_SIGN_STYLE = Automatic; CREATE_INFOPLIST_SECTION_IN_BINARY = YES; DEVELOPMENT_TEAM = ZU6GR6B2FY; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = "$(SRCROOT)/$(TARGET_NAME)/Info.plist"; MARKETING_VERSION = 2.0.0; OTHER_LDFLAGS = ( "-sectcreate", __TEXT, __launchd_plist, "$(SRCROOT)/${TARGET_NAME}/launchd.plist", ); PRODUCT_BUNDLE_IDENTIFIER = com.xcodesorg.xcodesapp.Helper; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_OBJC_BRIDGING_HEADER = "com.xcodesorg.xcodesapp.Helper/com.xcodesorg.xcodesapp.Helper-Bridging-Header.h"; SWIFT_VERSION = 5.0; }; name = Debug; }; CA9FF8B32595967A00E47BAF /* Test */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CODE_SIGNING_SUBJECT_ORGANIZATIONAL_UNIT = ZU6GR6B2FY; CODE_SIGN_ENTITLEMENTS = com.xcodesorg.xcodesapp.Helper/com.xcodesorg.xcodesapp.HelperTest.entitlements; CODE_SIGN_STYLE = Manual; CREATE_INFOPLIST_SECTION_IN_BINARY = YES; DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = NO; INFOPLIST_FILE = "$(SRCROOT)/$(TARGET_NAME)/Info.plist"; MARKETING_VERSION = 2.0.0; OTHER_LDFLAGS = ( "-sectcreate", __TEXT, __launchd_plist, "$(SRCROOT)/${TARGET_NAME}/launchd.plist", ); PRODUCT_BUNDLE_IDENTIFIER = com.xcodesorg.xcodesapp.Helper; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_OBJC_BRIDGING_HEADER = "com.xcodesorg.xcodesapp.Helper/com.xcodesorg.xcodesapp.Helper-Bridging-Header.h"; SWIFT_VERSION = 5.0; }; name = Test; }; CA9FF8B42595967A00E47BAF /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CODE_SIGNING_SUBJECT_ORGANIZATIONAL_UNIT = ZU6GR6B2FY; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; CREATE_INFOPLIST_SECTION_IN_BINARY = YES; DEVELOPMENT_TEAM = ZU6GR6B2FY; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = "$(SRCROOT)/$(TARGET_NAME)/Info.plist"; MARKETING_VERSION = 2.0.0; OTHER_LDFLAGS = ( "-sectcreate", __TEXT, __launchd_plist, "$(SRCROOT)/${TARGET_NAME}/launchd.plist", ); PRODUCT_BUNDLE_IDENTIFIER = com.xcodesorg.xcodesapp.Helper; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_OBJC_BRIDGING_HEADER = "com.xcodesorg.xcodesapp.Helper/com.xcodesorg.xcodesapp.Helper-Bridging-Header.h"; SWIFT_VERSION = 5.0; }; name = Release; }; CAD2E7BA2449575100113D76 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGNING_SUBJECT_ORGANIZATIONAL_UNIT = ZU6GR6B2FY; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MACOSX_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; CAD2E7BB2449575100113D76 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGNING_SUBJECT_ORGANIZATIONAL_UNIT = ZU6GR6B2FY; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MACOSX_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-O"; }; name = Release; }; CAD2E7BD2449575100113D76 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = XcodesIcon; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; CODE_SIGNING_SUBJECT_ORGANIZATIONAL_UNIT = ZU6GR6B2FY; CODE_SIGN_ENTITLEMENTS = Xcodes/Resources/Xcodes.entitlements; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 34; DEVELOPMENT_ASSET_PATHS = "\"Xcodes/Preview Content\""; DEVELOPMENT_TEAM = ZU6GR6B2FY; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = Xcodes/Resources/Info.plist; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; MARKETING_VERSION = 3.0.2; PRODUCT_BUNDLE_IDENTIFIER = com.xcodesorg.xcodesapp; PRODUCT_NAME = Xcodes; SWIFT_VERSION = 5.0; }; name = Debug; }; CAD2E7BE2449575100113D76 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = XcodesIcon; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; CODE_SIGNING_SUBJECT_ORGANIZATIONAL_UNIT = ZU6GR6B2FY; CODE_SIGN_ENTITLEMENTS = Xcodes/Resources/Xcodes.entitlements; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 34; DEVELOPMENT_ASSET_PATHS = "\"Xcodes/Preview Content\""; DEVELOPMENT_TEAM = ZU6GR6B2FY; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = Xcodes/Resources/Info.plist; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; MARKETING_VERSION = 3.0.2; PRODUCT_BUNDLE_IDENTIFIER = com.xcodesorg.xcodesapp; PRODUCT_NAME = Xcodes; SWIFT_VERSION = 5.0; }; name = Release; }; CAD2E7C02449575100113D76 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = ZU6GR6B2FY; INFOPLIST_FILE = XcodesTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", "@loader_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; PRODUCT_BUNDLE_IDENTIFIER = com.robotsandpencils.XcodesAppTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Xcodes.app/Contents/MacOS/Xcodes"; }; name = Debug; }; CAD2E7C12449575100113D76 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = ZU6GR6B2FY; INFOPLIST_FILE = XcodesTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", "@loader_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; PRODUCT_BUNDLE_IDENTIFIER = com.xcodesorg.xcodesapp.XcodesAppTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Xcodes.app/Contents/MacOS/Xcodes"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ CA9FF8B52595967A00E47BAF /* Build configuration list for PBXNativeTarget "com.xcodesorg.xcodesapp.Helper" */ = { isa = XCConfigurationList; buildConfigurations = ( CA9FF8B22595967A00E47BAF /* Debug */, CA9FF8B32595967A00E47BAF /* Test */, CA9FF8B42595967A00E47BAF /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; CAD2E7992449574E00113D76 /* Build configuration list for PBXProject "Xcodes" */ = { isa = XCConfigurationList; buildConfigurations = ( CAD2E7BA2449575100113D76 /* Debug */, CA8FB635256E154800469DA5 /* Test */, CAD2E7BB2449575100113D76 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; CAD2E7BC2449575100113D76 /* Build configuration list for PBXNativeTarget "Xcodes" */ = { isa = XCConfigurationList; buildConfigurations = ( CAD2E7BD2449575100113D76 /* Debug */, CA8FB636256E154800469DA5 /* Test */, CAD2E7BE2449575100113D76 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; CAD2E7BF2449575100113D76 /* Build configuration list for PBXNativeTarget "XcodesTests" */ = { isa = XCConfigurationList; buildConfigurations = ( CAD2E7C02449575100113D76 /* Debug */, CA8FB637256E154800469DA5 /* Test */, CAD2E7C12449575100113D76 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ 33027E282CA8BB5800CB387C /* XCRemoteSwiftPackageReference "LibFido2Swift" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/kinoroy/LibFido2Swift"; requirement = { branch = main; kind = branch; }; }; CAA858CB25A3D8BC00ACF8C0 /* XCRemoteSwiftPackageReference "ErrorHandling" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/RobotsAndPencils/ErrorHandling"; requirement = { kind = upToNextMinorVersion; minimumVersion = 0.1.0; }; }; CABFA9E22592F08E00380FEE /* XCRemoteSwiftPackageReference "Version" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/mxcl/Version"; requirement = { kind = upToNextMinorVersion; minimumVersion = 1.0.3; }; }; CABFA9EC2592F0CC00380FEE /* XCRemoteSwiftPackageReference "SwiftSoup" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/scinfu/SwiftSoup"; requirement = { kind = upToNextMinorVersion; minimumVersion = 2.0.0; }; }; CABFA9F62592F0F900380FEE /* XCRemoteSwiftPackageReference "KeychainAccess" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/kishikawakatsumi/KeychainAccess"; requirement = { kind = upToNextMinorVersion; minimumVersion = 3.2.0; }; }; CABFA9FB2592F13300380FEE /* XCRemoteSwiftPackageReference "LegibleError" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/mxcl/LegibleError"; requirement = { kind = upToNextMinorVersion; minimumVersion = 1.0.1; }; }; CAC28186259EE27200B8AB0B /* XCRemoteSwiftPackageReference "CombineExpectations" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/groue/CombineExpectations"; requirement = { kind = upToNextMinorVersion; minimumVersion = 0.6.0; }; }; E689540125BE8C64000EBCEA /* XCRemoteSwiftPackageReference "DockProgress" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/sindresorhus/DockProgress"; requirement = { kind = upToNextMinorVersion; minimumVersion = 4.3.1; }; }; E83FDC422CBB649100679C6B /* XCRemoteSwiftPackageReference "Sparkle" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/sparkle-project/Sparkle"; requirement = { kind = upToNextMajorVersion; minimumVersion = 2.6.4; }; }; E84E4F552B335094003F3959 /* XCRemoteSwiftPackageReference "swift-collections" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/apple/swift-collections.git"; requirement = { kind = upToNextMajorVersion; minimumVersion = 1.0.5; }; }; E8F44A1C296B4CD7002D6592 /* XCRemoteSwiftPackageReference "Path" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/mxcl/Path.swift"; requirement = { kind = upToNextMajorVersion; minimumVersion = 1.0.0; }; }; E8FD5725291EE4AC001E004C /* XCRemoteSwiftPackageReference "AsyncHTTPNetworkService" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/RobotsAndPencils/AsyncHTTPNetworkService"; requirement = { branch = main; kind = branch; }; }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ 334A932B2CA885A400A5E079 /* LibFido2Swift */ = { isa = XCSwiftPackageProductDependency; productName = LibFido2Swift; }; CAA1CB2C255A5262003FD669 /* AppleAPI */ = { isa = XCSwiftPackageProductDependency; productName = AppleAPI; }; CAA858CC25A3D8BC00ACF8C0 /* ErrorHandling */ = { isa = XCSwiftPackageProductDependency; package = CAA858CB25A3D8BC00ACF8C0 /* XCRemoteSwiftPackageReference "ErrorHandling" */; productName = ErrorHandling; }; CABFA9E32592F08E00380FEE /* Version */ = { isa = XCSwiftPackageProductDependency; package = CABFA9E22592F08E00380FEE /* XCRemoteSwiftPackageReference "Version" */; productName = Version; }; CABFA9ED2592F0CC00380FEE /* SwiftSoup */ = { isa = XCSwiftPackageProductDependency; package = CABFA9EC2592F0CC00380FEE /* XCRemoteSwiftPackageReference "SwiftSoup" */; productName = SwiftSoup; }; CABFA9F72592F0F900380FEE /* KeychainAccess */ = { isa = XCSwiftPackageProductDependency; package = CABFA9F62592F0F900380FEE /* XCRemoteSwiftPackageReference "KeychainAccess" */; productName = KeychainAccess; }; CABFA9FC2592F13300380FEE /* LegibleError */ = { isa = XCSwiftPackageProductDependency; package = CABFA9FB2592F13300380FEE /* XCRemoteSwiftPackageReference "LegibleError" */; productName = LegibleError; }; CAC28187259EE27200B8AB0B /* CombineExpectations */ = { isa = XCSwiftPackageProductDependency; package = CAC28186259EE27200B8AB0B /* XCRemoteSwiftPackageReference "CombineExpectations" */; productName = CombineExpectations; }; E689540225BE8C64000EBCEA /* DockProgress */ = { isa = XCSwiftPackageProductDependency; package = E689540125BE8C64000EBCEA /* XCRemoteSwiftPackageReference "DockProgress" */; productName = DockProgress; }; E83FDC432CBB649100679C6B /* Sparkle */ = { isa = XCSwiftPackageProductDependency; package = E83FDC422CBB649100679C6B /* XCRemoteSwiftPackageReference "Sparkle" */; productName = Sparkle; }; E84E4F562B335094003F3959 /* OrderedCollections */ = { isa = XCSwiftPackageProductDependency; package = E84E4F552B335094003F3959 /* XCRemoteSwiftPackageReference "swift-collections" */; productName = OrderedCollections; }; E862D43A2CC8B26F00BAA376 /* SRP */ = { isa = XCSwiftPackageProductDependency; productName = SRP; }; E8C0EB19291EF43E0081528A /* XcodesKit */ = { isa = XCSwiftPackageProductDependency; productName = XcodesKit; }; E8F44A1D296B4CD7002D6592 /* Path */ = { isa = XCSwiftPackageProductDependency; package = E8F44A1C296B4CD7002D6592 /* XCRemoteSwiftPackageReference "Path" */; productName = Path; }; E8FD5726291EE4AC001E004C /* AsyncNetworkService */ = { isa = XCSwiftPackageProductDependency; package = E8FD5725291EE4AC001E004C /* XCRemoteSwiftPackageReference "AsyncHTTPNetworkService" */; productName = AsyncNetworkService; }; /* End XCSwiftPackageProductDependency section */ }; rootObject = CAD2E7962449574E00113D76 /* Project object */; } ================================================ FILE: Xcodes.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: Xcodes.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: Xcodes.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved ================================================ { "object": { "pins": [ { "package": "AsyncNetworkService", "repositoryURL": "https://github.com/RobotsAndPencils/AsyncHTTPNetworkService", "state": { "branch": "main", "revision": "97770856c4e429f880d4b4dd68cfaf286dc00c30", "version": null } }, { "package": "big-num", "repositoryURL": "https://github.com/adam-fowler/big-num", "state": { "branch": null, "revision": "5c5511ad06aeb2b97d0868f7394e14a624bfb1c7", "version": "2.0.2" } }, { "package": "CombineExpectations", "repositoryURL": "https://github.com/groue/CombineExpectations", "state": { "branch": null, "revision": "989a92221899929ab8347a5878aa2b16db8b81ca", "version": "0.6.0" } }, { "package": "DockProgress", "repositoryURL": "https://github.com/sindresorhus/DockProgress", "state": { "branch": null, "revision": "d4f23b5a8f5ca0fac393eb7ba78c2fe3e32e52da", "version": "4.3.1" } }, { "package": "ErrorHandling", "repositoryURL": "https://github.com/RobotsAndPencils/ErrorHandling", "state": { "branch": null, "revision": "7be837fcb515447c0776805c3288fb7d5181ec68", "version": "0.1.0" } }, { "package": "KeychainAccess", "repositoryURL": "https://github.com/kishikawakatsumi/KeychainAccess", "state": { "branch": null, "revision": "8d33ffd6f74b3bcfc99af759d4204c6395a3f918", "version": "3.2.1" } }, { "package": "LegibleError", "repositoryURL": "https://github.com/mxcl/LegibleError", "state": { "branch": null, "revision": "909e9bab3ded97350b28a5ab41dd745dd8aa9710", "version": "1.0.4" } }, { "package": "LibFido2Swift", "repositoryURL": "https://github.com/kinoroy/LibFido2Swift", "state": { "branch": "main", "revision": "b87a93300c5b35307c9f26ae490963196bd927f1", "version": null } }, { "package": "Path.swift", "repositoryURL": "https://github.com/mxcl/Path.swift", "state": { "branch": null, "revision": "8e355c28e9393c42e58b18c54cace2c42c98a616", "version": "1.4.1" } }, { "package": "Sparkle", "repositoryURL": "https://github.com/sparkle-project/Sparkle/", "state": { "branch": null, "revision": "0ef1ee0220239b3776f433314515fd849025673f", "version": "2.6.4" } }, { "package": "swift-collections", "repositoryURL": "https://github.com/apple/swift-collections.git", "state": { "branch": null, "revision": "a902f1823a7ff3c9ab2fba0f992396b948eda307", "version": "1.0.5" } }, { "package": "swift-crypto", "repositoryURL": "https://github.com/apple/swift-crypto", "state": { "branch": null, "revision": "ddb07e896a2a8af79512543b1c7eb9797f8898a5", "version": "1.1.7" } }, { "package": "swift-srp", "repositoryURL": "https://github.com/xcodesOrg/swift-srp", "state": { "branch": "main", "revision": "543aa0122a0257b992f6c7d62d18a26e3dffb8fe", "version": null } }, { "package": "SwiftSoup", "repositoryURL": "https://github.com/scinfu/SwiftSoup", "state": { "branch": null, "revision": "aeb5b4249c273d1783a5299e05be1b26e061ea81", "version": "2.0.0" } }, { "package": "Version", "repositoryURL": "https://github.com/mxcl/Version", "state": { "branch": null, "revision": "087c91fedc110f9f833b14ef4c32745dabca8913", "version": "1.0.3" } } ] }, "version": 1 } ================================================ FILE: Xcodes.xcodeproj/xcshareddata/xcschemes/Xcodes.xcscheme ================================================ ================================================ FILE: Xcodes.xcodeproj/xcshareddata/xcschemes/com.robotsandpencils.XcodesApp.Helper.xcscheme ================================================ ================================================ FILE: XcodesTests/AppStateTests.swift ================================================ import AppleAPI import Combine import CombineExpectations import Path import Version import XCTest import XcodesKit @testable import Xcodes class AppStateTests: XCTestCase { var subject: AppState! override func setUpWithError() throws { Current = .mock subject = AppState() } func test_ParseCertificateInfo_Succeeds() throws { let sampleRawInfo = """ Executable=/Applications/Xcode-10.1.app/Contents/MacOS/Xcode Identifier=com.apple.dt.Xcode Format=app bundle with Mach-O thin (x86_64) CodeDirectory v=20200 size=434 flags=0x2000(library-validation) hashes=6+5 location=embedded Signature size=4485 Authority=Software Signing Authority=Apple Code Signing Certification Authority Authority=Apple Root CA Info.plist entries=39 TeamIdentifier=59GAB85EFG Sealed Resources version=2 rules=13 files=253327 Internal requirements count=1 size=68 """ let info = subject.parseCertificateInfo(sampleRawInfo) XCTAssertEqual(info.authority, ["Software Signing", "Apple Code Signing Certification Authority", "Apple Root CA"]) XCTAssertEqual(info.teamIdentifier, "59GAB85EFG") XCTAssertEqual(info.bundleIdentifier, "com.apple.dt.Xcode") } func test_VerifySecurityAssessment_Fails() throws { Current.shell.spctlAssess = { _ in Fail(error: ProcessExecutionError(process: Process(), standardOutput: "stdout", standardError: "stderr")) .eraseToAnyPublisher() } let installedXcode = InstalledXcode(path: Path("/Applications/Xcode-0.0.0.app")!)! let recorder = subject.verifySecurityAssessment(of: installedXcode).record() let completion = try wait(for: recorder.completion, timeout: 1, description: "Completion") if case let .failure(error as InstallationError) = completion { XCTAssertEqual(error, InstallationError.failedSecurityAssessment(xcode: installedXcode, output: "stdout\nstderr")) } else { XCTFail() } } func test_VerifySecurityAssessment_Succeeds() throws { Current.shell.spctlAssess = { _ in Just((0, "", "")).setFailureType(to: Error.self).eraseToAnyPublisher() } let installedXcode = InstalledXcode(path: Path("/Applications/Xcode-0.0.0.app")!)! let recorder = subject.verifySecurityAssessment(of: installedXcode).record() try wait(for: recorder.finished, timeout: 1, description: "Finished") } func test_Install_FullHappyPath_Apple() throws { // Available xcode doesn't necessarily have build identifier subject.allXcodes = [ .init(version: Version("0.0.0")!, installState: .notInstalled, selected: false, icon: nil), .init(version: Version("0.0.0-Beta.1")!, installState: .notInstalled, selected: false, icon: nil), .init(version: Version("0.0.0-Beta.2")!, installState: .notInstalled, selected: false, icon: nil), ] // It hasn't been downloaded Current.files.fileExistsAtPath = { path in if path == (Path.xcodesApplicationSupport/"Xcode-0.0.0.xip").string { return false } else { return true } } Xcodes.Current.network.validateSession = { return Just(()) .setFailureType(to: Error.self).eraseToAnyPublisher() } Xcodes.Current.network.dataTask = { urlRequest in // Don't have a valid session if urlRequest.url! == URLRequest.olympusSession.url! { return Fail(error: AuthenticationError.invalidSession) .eraseToAnyPublisher() } // It's an available release version else if urlRequest.url! == URLRequest.downloads.url! { let downloads = Downloads(resultCode: 0, resultsString: nil, downloads: [Download(name: "Xcode 0.0.0", files: [Download.File(remotePath: "https://apple.com/xcode.xip", fileSize: 9484444)], dateModified: Date())]) let encoder = JSONEncoder() encoder.dateEncodingStrategy = .formatted(.downloadsDateModified) let downloadsData = try! encoder.encode(downloads) return Just( ( data: downloadsData, response: HTTPURLResponse(url: urlRequest.url!, statusCode: 200, httpVersion: nil, headerFields: nil)! ) ) .setFailureType(to: Error.self) .eraseToAnyPublisher() } return Just( ( data: Data(), response: HTTPURLResponse(url: urlRequest.url!, statusCode: 200, httpVersion: nil, headerFields: nil)! ) ) .setFailureType(to: Error.self) .eraseToAnyPublisher() } // It downloads and updates progress let progress = Progress(totalUnitCount: 100) Current.network.downloadTask = { (url, saveLocation, _) -> (Progress, AnyPublisher<(saveLocation: URL, response: URLResponse), Error>) in return ( progress, Deferred { Future { promise in // Need this to run after the Promise has returned to the caller. This makes the test async, requiring waiting for an expectation. DispatchQueue.main.async { for i in 0...100 { progress.completedUnitCount = Int64(i) } promise(.success((saveLocation: saveLocation, response: HTTPURLResponse(url: url, statusCode: 200, httpVersion: nil, headerFields: nil)!))) } } } .eraseToAnyPublisher() ) } // It's a valid .app Current.shell.codesignVerify = { _ in Just( ProcessOutput( status: 0, out: "", err: """ TeamIdentifier=\(XcodeTeamIdentifier) Authority=\(XcodeCertificateAuthority[0]) Authority=\(XcodeCertificateAuthority[1]) Authority=\(XcodeCertificateAuthority[2]) """) ) .setFailureType(to: Error.self) .eraseToAnyPublisher() } // Helper is already installed subject.helperInstallState = .installed let allXcodesRecorder = subject.$allXcodes.record() let installRecorder = subject.install( .version(AvailableXcode(version: Version("0.0.0")!, url: URL(string: "https://apple.com/xcode.xip")!, filename: "mock.xip", releaseDate: nil)), downloader: .urlSession ).record() try wait(for: installRecorder.finished, timeout: 1, description: "Finished") let allXcodesElements = try wait(for: allXcodesRecorder.availableElements, timeout: 1, description: "All Xcodes Elements") XCTAssertEqual( allXcodesElements.map { $0.map(\.installState) }, [ [XcodeInstallState.notInstalled, .notInstalled, .notInstalled], [.installing(.downloading(progress: progress)), .notInstalled, .notInstalled], [.installing(.unarchiving), .notInstalled, .notInstalled], [.installing(.moving(destination: "/Applications/Xcode-0.0.0.app")), .notInstalled, .notInstalled], [.installing(.trashingArchive), .notInstalled, .notInstalled], [.installing(.checkingSecurity), .notInstalled, .notInstalled], [.installing(.finishing), .notInstalled, .notInstalled], [.installed(Path("/Applications/Xcode-0.0.0.app")!), .notInstalled, .notInstalled] ] ) } func test_Install_FullHappyPath_XcodeReleases() throws { // Available xcode has build identifier subject.allXcodes = [ .init(version: Version("0.0.0+ABC123")!, installState: .notInstalled, selected: false, icon: nil), .init(version: Version("0.0.0-Beta.1+DEF456")!, installState: .notInstalled, selected: false, icon: nil), .init(version: Version("0.0.0-Beta.2+GHI789")!, installState: .notInstalled, selected: false, icon: nil) ] // It hasn't been downloaded Current.files.fileExistsAtPath = { path in if path == (Path.xcodesApplicationSupport/"Xcode-0.0.0.xip").string { return false } else { return true } } Xcodes.Current.network.dataTask = { urlRequest in // Don't have a valid session if urlRequest.url! == URLRequest.olympusSession.url! { return Fail(error: AuthenticationError.invalidSession) .eraseToAnyPublisher() } // It's an available release version else if urlRequest.url! == URLRequest.downloads.url! { let downloads = Downloads(resultCode: 0, resultsString: nil, downloads: [Download(name: "Xcode 0.0.0", files: [Download.File(remotePath: "https://apple.com/xcode.xip", fileSize: 9494944)], dateModified: Date())]) let encoder = JSONEncoder() encoder.dateEncodingStrategy = .formatted(.downloadsDateModified) let downloadsData = try! encoder.encode(downloads) return Just( ( data: downloadsData, response: HTTPURLResponse(url: urlRequest.url!, statusCode: 200, httpVersion: nil, headerFields: nil)! ) ) .setFailureType(to: Error.self) .eraseToAnyPublisher() } return Just( ( data: Data(), response: HTTPURLResponse(url: urlRequest.url!, statusCode: 200, httpVersion: nil, headerFields: nil)! ) ) .setFailureType(to: Error.self) .eraseToAnyPublisher() } // It downloads and updates progress let progress = Progress(totalUnitCount: 100) Current.network.downloadTask = { (url, saveLocation, _) -> (Progress, AnyPublisher<(saveLocation: URL, response: URLResponse), Error>) in return ( progress, Deferred { Future { promise in // Need this to run after the Promise has returned to the caller. This makes the test async, requiring waiting for an expectation. DispatchQueue.main.async { for i in 0...100 { progress.completedUnitCount = Int64(i) } promise(.success((saveLocation: saveLocation, response: HTTPURLResponse(url: url, statusCode: 200, httpVersion: nil, headerFields: nil)!))) } } } .eraseToAnyPublisher() ) } // It's a valid .app Current.shell.codesignVerify = { _ in Just( ProcessOutput( status: 0, out: "", err: """ TeamIdentifier=\(XcodeTeamIdentifier) Authority=\(XcodeCertificateAuthority[0]) Authority=\(XcodeCertificateAuthority[1]) Authority=\(XcodeCertificateAuthority[2]) """) ) .setFailureType(to: Error.self) .eraseToAnyPublisher() } // Helper is already installed subject.helperInstallState = .installed let allXcodesRecorder = subject.$allXcodes.record() let installRecorder = subject.install( .version(AvailableXcode(version: Version("0.0.0")!, url: URL(string: "https://apple.com/xcode.xip")!, filename: "mock.xip", releaseDate: nil)), downloader: .urlSession ).record() try wait(for: installRecorder.finished, timeout: 1, description: "Finished") let allXcodesElements = try wait(for: allXcodesRecorder.availableElements, timeout: 1, description: "All Xcodes Elements") XCTAssertEqual( allXcodesElements.map { $0.map(\.installState) }, [ [XcodeInstallState.notInstalled, .notInstalled, .notInstalled], [.installing(.downloading(progress: progress)), .notInstalled, .notInstalled], [.installing(.unarchiving), .notInstalled, .notInstalled], [.installing(.moving(destination: "/Applications/Xcode-0.0.0.app")), .notInstalled, .notInstalled], [.installing(.trashingArchive), .notInstalled, .notInstalled], [.installing(.checkingSecurity), .notInstalled, .notInstalled], [.installing(.finishing), .notInstalled, .notInstalled], [.installed(Path("/Applications/Xcode-0.0.0.app")!), .notInstalled, .notInstalled] ] ) } func test_Install_NotEnoughFreeSpace() throws { Current.shell.unxip = { _ in Fail(error: ProcessExecutionError( process: Process(), standardOutput: "xip: signing certificate was \"Development Update\" (validation not attempted)", standardError: "xip: error: The archive “Xcode-12.4.0-Release.Candidate+12D4e.xip” can’t be expanded because the selected volume doesn’t have enough free space." )) .eraseToAnyPublisher() } let archiveURL = URL(fileURLWithPath: "/Users/user/Library/Application Support/Xcode-0.0.0.xip") let recorder = subject.unarchiveAndMoveXIP( availableXcode: AvailableXcode( version: Version("0.0.0")!, url: URL(string: "https://developer.apple.com")!, filename: "Xcode-0.0.0.xip", releaseDate: nil ), at: archiveURL, to: URL(string: "/Applications/Xcode-0.0.0.app")! ).record() let completion = try wait(for: recorder.completion, timeout: 1, description: "Completion") if case let .failure(error as InstallationError) = completion { XCTAssertEqual( error, InstallationError.notEnoughFreeSpaceToExpandArchive(archivePath: Path(url: archiveURL)!, version: Version("0.0.0")!) ) } else { XCTFail() } } } ================================================ FILE: XcodesTests/AppStateUpdateTests.swift ================================================ import Path import CryptoKit import Version @testable import Xcodes import XCTest import CommonCrypto import BigNum import SRP class AppStateUpdateTests: XCTestCase { var subject: AppState! override func setUpWithError() throws { Current = .mock subject = AppState() } func testDoesNotReplaceInstallState() throws { subject.allXcodes = [ Xcode(version: Version("0.0.0")!, installState: .installing(.unarchiving), selected: false, icon: nil) ] subject.updateAllXcodes( availableXcodes: [ AvailableXcode(version: Version("0.0.0")!, url: URL(string: "https://apple.com/xcode.xip")!, filename: "mock.xip", releaseDate: nil) ], installedXcodes: [ ], selectedXcodePath: nil ) XCTAssertEqual(subject.allXcodes[0].installState, .installing(.unarchiving)) } func testRemovesUninstalledVersion() throws { subject.allXcodes = [ Xcode(version: Version("0.0.0")!, installState: .installed(Path("/Applications/Xcode-0.0.0.app")!), selected: true, icon: NSImage(systemSymbolName: "app.fill", accessibilityDescription: nil)) ] subject.updateAllXcodes( availableXcodes: [ AvailableXcode(version: Version("0.0.0")!, url: URL(string: "https://apple.com/xcode.xip")!, filename: "mock.xip", releaseDate: nil) ], installedXcodes: [ ], selectedXcodePath: nil ) XCTAssertEqual(subject.allXcodes[0].installState, .notInstalled) } func testDeterminesIfInstalledByBuildMetadataAlone() throws { Current.defaults.string = { key in if key == "dataSource" { return "apple" } else { return nil } } subject.allXcodes = [ ] subject.updateAllXcodes( availableXcodes: [ // Note "GM" prerelease identifier AvailableXcode(version: Version("0.0.0-GM+ABC123")!, url: URL(string: "https://apple.com/xcode.xip")!, filename: "mock.xip", releaseDate: nil) ], installedXcodes: [ InstalledXcode(path: Path("/Applications/Xcode-0.0.0.app")!)! ], selectedXcodePath: nil ) XCTAssertEqual(subject.allXcodes[0].version, Version("0.0.0+ABC123")!) XCTAssertEqual(subject.allXcodes[0].installState, .installed(Path("/Applications/Xcode-0.0.0.app")!)) XCTAssertEqual(subject.allXcodes[0].selected, false) } func testAdjustedVersionsAreUsedToLookupAvailableXcode() throws { Current.defaults.string = { key in if key == "dataSource" { return "apple" } else { return nil } } subject.allXcodes = [ ] subject.updateAllXcodes( availableXcodes: [ // Note "GM" prerelease identifier AvailableXcode(version: Version("0.0.0-GM+ABC123")!, url: URL(string: "https://apple.com/xcode.xip")!, filename: "mock.xip", releaseDate: nil, sdks: .init(iOS: .init("14.3"))) ], installedXcodes: [ InstalledXcode(path: Path("/Applications/Xcode-0.0.0.app")!)! ], selectedXcodePath: nil ) XCTAssertEqual(subject.allXcodes[0].version, Version("0.0.0+ABC123")!) XCTAssertEqual(subject.allXcodes[0].installState, .installed(Path("/Applications/Xcode-0.0.0.app")!)) XCTAssertEqual(subject.allXcodes[0].selected, false) // XCModel types aren't equatable, so just check for non-nil for now XCTAssertNotNil(subject.allXcodes[0].sdks) } func testAppendingInstalledVersionThatIsNotAvailable() { subject.allXcodes = [ ] subject.updateAllXcodes( availableXcodes: [ AvailableXcode(version: Version("1.2.3")!, url: URL(string: "https://apple.com/xcode.xip")!, filename: "mock.xip", releaseDate: nil, sdks: .init(iOS: .init("14.3"))) ], installedXcodes: [ // There's a version installed which for some reason isn't listed online InstalledXcode(path: Path("/Applications/Xcode-0.0.0.app")!)! ], selectedXcodePath: nil ) XCTAssertEqual(subject.allXcodes.map(\.version), [Version("1.2.3")!, Version("0.0.0+ABC123")!]) } func testIdenticalBuilds_KeepsReleaseVersion_WithNeitherInstalled() { Current.defaults.string = { key in if key == "dataSource" { return "xcodeReleases" } else { return nil } } subject.allXcodes = [ ] subject.updateAllXcodes( availableXcodes: [ AvailableXcode(version: Version("12.4.0+12D4e")!, url: URL(string: "https://apple.com/xcode.xip")!, filename: "mock.xip", releaseDate: nil), AvailableXcode(version: Version("12.4.0-RC+12D4e")!, url: URL(string: "https://apple.com/xcode.xip")!, filename: "mock.xip", releaseDate: nil), ], installedXcodes: [ ], selectedXcodePath: nil ) XCTAssertEqual(subject.allXcodes.map(\.version), [Version("12.4.0+12D4e")!]) XCTAssertEqual(subject.allXcodes.map(\.identicalBuilds), [[XcodeID(version: Version("12.4.0+12D4e")!), XcodeID(version: Version("12.4.0-RC+12D4e")!)]]) } func testIdenticalBuilds_DoNotMergeReleaseVersions() { Current.defaults.string = { key in if key == "dataSource" { return "xcodeReleases" } else { return nil } } subject.allXcodes = [ ] subject.updateAllXcodes( availableXcodes: [ AvailableXcode(version: Version("3.2.3+10M2262")!, url: URL(string: "https://apple.com/xcode.xip")!, filename: "mock.xip", releaseDate: nil), AvailableXcode(version: Version("3.2.3+10M2262")!, url: URL(string: "https://apple.com/xcode.xip")!, filename: "mock.xip", releaseDate: nil), ], installedXcodes: [ ], selectedXcodePath: nil ) XCTAssertEqual(subject.allXcodes.map(\.version), [Version("3.2.3+10M2262")!, Version("3.2.3+10M2262")!]) XCTAssertEqual(subject.allXcodes.map(\.identicalBuilds), [[], []]) } func testIdenticalBuilds_KeepsReleaseVersion_WithPrereleaseInstalled() { Current.defaults.string = { key in if key == "dataSource" { return "xcodeReleases" } else { return nil } } subject.allXcodes = [ ] Current.files.contentsAtPath = { path in if path.contains("Info.plist") { return """ CFBundleIdentifier com.apple.dt.Xcode CFBundleShortVersionString 12.4.0 """.data(using: .utf8) } else if path.contains("version.plist") { return """ ProductBuildVersion 12D4e """.data(using: .utf8) } else { return nil } } subject.updateAllXcodes( availableXcodes: [ AvailableXcode(version: Version("12.4.0+12D4e")!, url: URL(string: "https://apple.com/xcode.xip")!, filename: "mock.xip", releaseDate: nil), AvailableXcode(version: Version("12.4.0-RC+12D4e")!, url: URL(string: "https://apple.com/xcode.xip")!, filename: "mock.xip", releaseDate: nil), ], installedXcodes: [ InstalledXcode(path: Path("/Applications/Xcode-12.4.0-RC.app")!)! ], selectedXcodePath: nil ) XCTAssertEqual(subject.allXcodes.map(\.version), [Version("12.4.0+12D4e")!]) XCTAssertEqual(subject.allXcodes.map(\.identicalBuilds), [[XcodeID(version: Version("12.4.0+12D4e")!), XcodeID(version: Version("12.4.0-RC+12D4e")!)]]) } func testIdenticalBuilds_AppleDataSource_DoNotMergeVersionsWithoutBuildIdentifiers() { Current.defaults.string = { key in if key == "dataSource" { return "apple" } else { return nil } } subject.allXcodes = [ ] subject.updateAllXcodes( availableXcodes: [ AvailableXcode(version: Version("12.4.0")!, url: URL(string: "https://apple.com/xcode.xip")!, filename: "mock.xip", releaseDate: nil), AvailableXcode(version: Version("12.3.0-RC")!, url: URL(string: "https://apple.com/xcode.xip")!, filename: "mock.xip", releaseDate: nil), ], installedXcodes: [ ], selectedXcodePath: nil ) XCTAssertEqual(subject.allXcodes.map(\.version), [Version("12.4.0")!, Version("12.3.0-RC")!]) XCTAssertEqual(subject.allXcodes.map(\.identicalBuilds), [[], []]) } func sha256(data : Data) -> Data { var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH)) data.withUnsafeBytes { _ = CC_SHA256($0.baseAddress, CC_LONG(data.count), &hash) } return Data(hash) } func testSIRP() throws { /* Obtaind by running fastlane spaceauth --verbose -u anand.appleseed@example.com with some custom logging Starting SIRP Apple ID login a: {"a":"VLEKLa+n2cyeYNWbECm45CuS4kCdCxodlTDGlW1FKaUyOrv/RbtN2sM0pVE12zI7k3VkocPC3rN5DZBIkahR6I8JHj/J97dtTvzsR+aNRWTYCT2HGP1PBI0QArp3eitAbFqTWI4+Zw+oOnV8+AYdH/wjbq7gOK4C4dvIHE+FzRwIlmguPb5qu2r47R9W3y1msVdoUGlFBOMOMb7Gyq7F0MaEIFH63lNzGomwq74mfss/cFqurd6fxU+Y7tdVTPZw1GWyBEPiXWpk8sNm2zE+S6zWo5tOsICprU75IC9galh1igfzN7VNe0SUFLNFTbFK+Bb1SFAOrAbBZOmyOG5uSQ==","accountName":"anand.appleseed@example.com","protocols":["s2k","s2k_fo"]} Received SIRP signin init response: {"iteration"=>20309, "salt"=>"fIjNflgqSJXACWwwvhDU+w==", "protocol"=>"s2k", "b"=>"PMbU75wwG6rDTySXn2ASWyfQuPoW5ham15SzIscpInwOPE2uk7sePsW4ra0dHcLDUMFQn/LgBggIKOo7YZ9hf1VReiAzXwSKSHdJHjHUURTC2eNpANGUPO1qzuXYgc/MP3MR+GipKHsz+KTLT+8wLjNaiCIHsL/7evJBMw9QqiwhyXlAIm5mGZfhdTVbGpLz2/QzrFmI6pUTrHpio6m1Q74DH3FBxxIeiIcuEdGdeVt9iUweowBRyf2woasTvSV1fbMQbl+lsWPwzt/a73+J30eOGFdSubqSVYh2pV0OLqRz7zPzJars12teCWUV+0WUIaxb14Mp7tlmqcTPuqZe9w==", "c"=>"d-533-eccbc4e9-9564-11ef-84a6-018111c8cc60:PRN"} encrypted_password: 40532b4de9353fc537dc62ee84eacebd7ecb5ec26efca98bd01b0380e302100f 32 bb: 7672345903537871991962715758896796468138571329014139041563495295907370682045347022183702983061785424983278857706335295545994877883818377653653442007828499221881058994644619578239367613808278802931379172730746665773282250642455690715139985911303055104847971308813151718669484181874342088801251592138154023949370621963319928723678385968989085032385411532317797659749008135679504901238396934480214258070495365760319076978872181485178648397361564241555189629889320567561713407566532187413091018319494367244540399242523126294027225387432028960726767445027313453858210115810946641002311734776929442587065438110307439763191 x: 726436461883978586175291668001486484510457416477927591386889224605776454162 u: 49415306980415573732801389514223606278850554555635359953422678270536095422201 private 23161374166158551996079451276150657702422963034121842124445818241826569345033578345120284496449280736328513302994568402583647660370960353252836732307301957364261384324957527103960720408713825427474127658415917826326829664923997096839970397226662116904369925262192683131695683487505523842260218490007066160096482662715246662018133837725691586205535995663334471723776536640973591229093933458552240634178864509015968350855952558520147559154646484379002445961375384929682566525908284011230815908584648931495968206840416022306138033496705677078512266958633477047047323620540878121579549170392075029336954975132431417099801 S: 4f75b6ea99c2d7d121357cce80c75c8e1bf74a65e8f66f75f8f66a481301afb8bebf0e54a3fac4f8bfdd60c77d6e670c87968b341f62175e25eb1d4f496e4e6596e1a387f2840688a35002419b70115b7902a46544cc7b31eb4c909c0acaeb752835d1562a687c431421510ebc7535c007a2bd12a4f7696c8c96a75a491b1eb9189ade2bef23dd5b0bb962b4f03e7fba7f6ba6fe67ba34cc18647daf3e474876f85dac5a15eb51c99d1ed78783579ffd6c8b6911f72564d87dc8f76519c8fc1535b83743ed5f7d6b8461d7154ce2a874cbb45bf63018352b9b997fbafbd6b15eac2a544a801c0152470796f3b69a84a4a653e5186b30efeeb148ff3c32ab8e08 K: c5207f707186a52f1adee41bf0a7bc41e51e6dffc25cdaeca8acb7de2710b20a hN: 65908899099613711898698321155848703477601840791750658211391687862255842366922 hG: 23094592799618609623465742609366149076596436609130823198107630312273622653270 hxor 73599884097654065452785151481733181870375477364472235597514429707329935690908 response: {"accountName":"anand.appleseed@example.com","c":"d-533-eccbc4e9-9564-11ef-84a6-018111c8cc60:PRN","m1":"f/Bkq8gBTYxl7SyiRd4SXTyE/jM/g6E0mVyZIQDIsPg=","m2":"R2rgqC9cMAtWiXUImOrvs4oF+ccibf8KaFsZQ22WokM=","rememberMe":false} */ let publicKey = Data(base64Encoded: "VLEKLa+n2cyeYNWbECm45CuS4kCdCxodlTDGlW1FKaUyOrv/RbtN2sM0pVE12zI7k3VkocPC3rN5DZBIkahR6I8JHj/J97dtTvzsR+aNRWTYCT2HGP1PBI0QArp3eitAbFqTWI4+Zw+oOnV8+AYdH/wjbq7gOK4C4dvIHE+FzRwIlmguPb5qu2r47R9W3y1msVdoUGlFBOMOMb7Gyq7F0MaEIFH63lNzGomwq74mfss/cFqurd6fxU+Y7tdVTPZw1GWyBEPiXWpk8sNm2zE+S6zWo5tOsICprU75IC9galh1igfzN7VNe0SUFLNFTbFK+Bb1SFAOrAbBZOmyOG5uSQ==".data(using: .utf8)!) let clientKeys = SRPKeyPair(public: .init([UInt8](publicKey!)), private: .init(BigNum("23161374166158551996079451276150657702422963034121842124445818241826569345033578345120284496449280736328513302994568402583647660370960353252836732307301957364261384324957527103960720408713825427474127658415917826326829664923997096839970397226662116904369925262192683131695683487505523842260218490007066160096482662715246662018133837725691586205535995663334471723776536640973591229093933458552240634178864509015968350855952558520147559154646484379002445961375384929682566525908284011230815908584648931495968206840416022306138033496705677078512266958633477047047323620540878121579549170392075029336954975132431417099801")!)) let password = sha256(data: "example".data(using: .utf8)!) let salt = Data(base64Encoded: "fIjNflgqSJXACWwwvhDU+w==".data(using: .utf8)!)! let iterations: Int = 20309 let derivedKeyLength: Int = 32 var keyArray = Array(repeating: 0, count: derivedKeyLength) let result:Int32 = keyArray.withUnsafeMutableBytes { keyBytes -> Int32 in let keyBuffer = UnsafeMutablePointer(keyBytes.baseAddress!.assumingMemoryBound(to: UInt8.self)) return password.withUnsafeBytes { passwordDigestBytes -> Int32 in let passwordBuffer = UnsafePointer(passwordDigestBytes.baseAddress!.assumingMemoryBound(to: UInt8.self)) return salt.withUnsafeBytes { saltBytes -> Int32 in let saltBuffer = UnsafePointer(saltBytes.baseAddress!.assumingMemoryBound(to: UInt8.self)) return CCKeyDerivationPBKDF( CCPBKDFAlgorithm(kCCPBKDF2), passwordBuffer, password.count, saltBuffer, salt.count, CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA256), UInt32(iterations), keyBuffer, derivedKeyLength) } } } let expectedKey: [UInt8] = [0x40, 0x53, 0x2b, 0x4d, 0xe9, 0x35, 0x3f, 0xc5, 0x37, 0xdc, 0x62, 0xee, 0x84, 0xea, 0xce, 0xbd, 0x7e, 0xcb, 0x5e, 0xc2, 0x6e, 0xfc, 0xa9, 0x8b, 0xd0, 0x1b, 0x03, 0x80, 0xe3, 0x02, 0x10, 0x0f] XCTAssertEqual(expectedKey, keyArray) let decodedB = Data(base64Encoded: "PMbU75wwG6rDTySXn2ASWyfQuPoW5ham15SzIscpInwOPE2uk7sePsW4ra0dHcLDUMFQn/LgBggIKOo7YZ9hf1VReiAzXwSKSHdJHjHUURTC2eNpANGUPO1qzuXYgc/MP3MR+GipKHsz+KTLT+8wLjNaiCIHsL/7evJBMw9QqiwhyXlAIm5mGZfhdTVbGpLz2/QzrFmI6pUTrHpio6m1Q74DH3FBxxIeiIcuEdGdeVt9iUweowBRyf2woasTvSV1fbMQbl+lsWPwzt/a73+J30eOGFdSubqSVYh2pV0OLqRz7zPzJars12teCWUV+0WUIaxb14Mp7tlmqcTPuqZe9w==".data(using: .utf8)!)! let client = SRPClient(configuration: SRPConfiguration(.N2048)) let sharedSecret = try client.calculateSharedSecret(password: Data(keyArray), salt: [UInt8](salt), clientKeys: clientKeys, serverPublicKey: .init([UInt8](decodedB))) let accountName = "anand.appleseed@example.com" let m1 = client.calculateClientProof(username: accountName, salt: [UInt8](salt), clientPublicKey: clientKeys.public, serverPublicKey: .init([UInt8](decodedB)), sharedSecret: .init(sharedSecret.bytes)) let m2 = client.calculateServerProof(clientPublicKey: clientKeys.public, clientProof: m1, sharedSecret: .init([UInt8](sharedSecret.bytes))) XCTAssertEqual(Data(m1).base64EncodedString(), "f/Bkq8gBTYxl7SyiRd4SXTyE/jM/g6E0mVyZIQDIsPg=") XCTAssertEqual(Data(m2).base64EncodedString(), "R2rgqC9cMAtWiXUImOrvs4oF+ccibf8KaFsZQ22WokM=") } } ================================================ FILE: XcodesTests/Bundle+XcodesTests.swift ================================================ import Foundation extension Bundle { static var xcodesTests: Bundle { Bundle(for: BundleMember.self) } } private class BundleMember {} ================================================ FILE: XcodesTests/Environment+Mock.swift ================================================ import Combine import Foundation @testable import Xcodes extension Xcodes.Environment { static var mock = Xcodes.Environment( shell: .mock, files: .mock, network: .mock, keychain: .mock, defaults: .mock, date: Date.mock, helper: .mock ) } extension Shell { static var processOutputMock: ProcessOutput = (0, "", "") static var mock = Shell( unxip: { _ in return Just(Shell.processOutputMock).setFailureType(to: Error.self).eraseToAnyPublisher() }, spctlAssess: { _ in return Just(Shell.processOutputMock).setFailureType(to: Error.self).eraseToAnyPublisher() }, codesignVerify: { _ in return Just(Shell.processOutputMock).setFailureType(to: Error.self).eraseToAnyPublisher() }, buildVersion: { return Just(Shell.processOutputMock).setFailureType(to: Error.self).eraseToAnyPublisher() }, xcodeBuildVersion: { _ in return Just(Shell.processOutputMock).setFailureType(to: Error.self).eraseToAnyPublisher() }, getUserCacheDir: { return Just(Shell.processOutputMock).setFailureType(to: Error.self).eraseToAnyPublisher() }, touchInstallCheck: { _, _, _ in return Just(Shell.processOutputMock).setFailureType(to: Error.self).eraseToAnyPublisher() }, xcodeSelectPrintPath: { return Just(Shell.processOutputMock).setFailureType(to: Error.self).eraseToAnyPublisher() } ) } extension Files { static var mock = Files( fileExistsAtPath: { _ in return true }, moveItem: { _, _ in return }, contentsAtPath: { path in if path.contains("Info.plist") { let url = Bundle.xcodesTests.url(forResource: "Stub-0.0.0.Info", withExtension: "plist")! return try? Data(contentsOf: url) } else if path.contains("version.plist") { let url = Bundle.xcodesTests.url(forResource: "Stub-version", withExtension: "plist")! return try? Data(contentsOf: url) } else { return nil } }, removeItem: { _ in }, trashItem: { _ in return URL(fileURLWithPath: "\(NSHomeDirectory())/.Trash") }, createFile: { _, _, _ in return true }, createDirectory: { _, _, _ in }, installedXcodes: { _ in [] } ) } extension Network { static var mock = Network( dataTask: { url in Just((data: Data(), response: HTTPURLResponse(url: url.url!, statusCode: 200, httpVersion: nil, headerFields: nil)! as URLResponse)) .setFailureType(to: Error.self) .eraseToAnyPublisher() }, downloadTask: { url, saveLocation, _ in return ( Progress(), Just((saveLocation, HTTPURLResponse(url: url, statusCode: 200, httpVersion: nil, headerFields: nil)!)) .setFailureType(to: Error.self) .eraseToAnyPublisher() ) }, validateSession: { return Just(()) .setFailureType(to: Error.self) .eraseToAnyPublisher() } ) } extension Keychain { static var mock = Keychain( getString: { _ in return nil }, set: { _, _ in }, remove: { _ in } ) } extension Defaults { static var mock = Defaults( string: { _ in nil }, date: { _ in nil }, setDate: { _, _ in }, set: { _, _ in }, removeObject: { _ in } ) } extension Date { static var mock = { Date(timeIntervalSince1970: 1609479735) } } extension Helper { static var mock = Helper( install: { }, checkIfLatestHelperIsInstalled: { Just(false).eraseToAnyPublisher() }, getVersion: { Just("").setFailureType(to: Error.self).eraseToAnyPublisher() }, switchXcodePath: { _ in Just(()).setFailureType(to: Error.self).eraseToAnyPublisher() }, devToolsSecurityEnable: { Just(()).setFailureType(to: Error.self).eraseToAnyPublisher() }, addStaffToDevelopersGroup: { Just(()).setFailureType(to: Error.self).eraseToAnyPublisher() }, acceptXcodeLicense: { _ in Just(()).setFailureType(to: Error.self).eraseToAnyPublisher() }, runFirstLaunch: { _ in Just(()).setFailureType(to: Error.self).eraseToAnyPublisher() } ) } ================================================ FILE: XcodesTests/Fixtures/Stub-0.0.0.Info.plist ================================================ CFBundleIdentifier com.apple.dt.Xcode CFBundleShortVersionString 0.0.0 ================================================ FILE: XcodesTests/Fixtures/Stub-version.plist ================================================ ProductBuildVersion ABC123 ================================================ FILE: XcodesTests/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString 1.0 CFBundleVersion 1 ================================================ FILE: com.xcodesorg.xcodesapp.Helper/AuditTokenHack.h ================================================ // From https://github.com/securing/SimpleXPCApp/ #import @interface NSXPCConnection(PrivateAuditToken) @property (nonatomic, readonly) audit_token_t auditToken; @end @interface AuditTokenHack : NSObject +(NSData *)getAuditTokenDataFromNSXPCConnection:(NSXPCConnection *)connection; @end ================================================ FILE: com.xcodesorg.xcodesapp.Helper/AuditTokenHack.m ================================================ // From https://github.com/securing/SimpleXPCApp/ #import "AuditTokenHack.h" @implementation AuditTokenHack + (NSData *)getAuditTokenDataFromNSXPCConnection:(NSXPCConnection *)connection { audit_token_t auditToken = connection.auditToken; return [NSData dataWithBytes:&auditToken length:sizeof(audit_token_t)]; } @end ================================================ FILE: com.xcodesorg.xcodesapp.Helper/ConnectionVerifier.swift ================================================ // From https://github.com/securing/SimpleXPCApp/ import Foundation import os.log class ConnectionVerifier { private static func prepareCodeReferencesFromAuditToken(connection: NSXPCConnection, secCodeOptional: inout SecCode?, secStaticCodeOptional: inout SecStaticCode?) -> Bool { let auditTokenData = AuditTokenHack.getAuditTokenData(from: connection) let attributesDictrionary = [ kSecGuestAttributeAudit : auditTokenData ] if SecCodeCopyGuestWithAttributes(nil, attributesDictrionary as CFDictionary, SecCSFlags(rawValue: 0), &secCodeOptional) != errSecSuccess { Logger.connectionVerifier.error("Couldn't get SecCode with the audit token") return false } guard let secCode = secCodeOptional else { Logger.connectionVerifier.error("Couldn't unwrap the secCode") return false } SecCodeCopyStaticCode(secCode, SecCSFlags(rawValue: 0), &secStaticCodeOptional) guard let _ = secStaticCodeOptional else { Logger.connectionVerifier.error("Couldn't unwrap the secStaticCode") return false } return true } private static func verifyHardenedRuntimeAndProblematicEntitlements(secStaticCode: SecStaticCode) -> Bool { var signingInformationOptional: CFDictionary? = nil if SecCodeCopySigningInformation(secStaticCode, SecCSFlags(rawValue: kSecCSDynamicInformation), &signingInformationOptional) != errSecSuccess { Logger.connectionVerifier.error("Couldn't obtain signing information") return false } guard let signingInformation = signingInformationOptional else { return false } let signingInformationDict = signingInformation as NSDictionary let signingFlagsOptional = signingInformationDict.object(forKey: "flags") as? UInt32 if let signingFlags = signingFlagsOptional { let hardenedRuntimeFlag: UInt32 = 0x10000 if (signingFlags & hardenedRuntimeFlag) != hardenedRuntimeFlag { Logger.connectionVerifier.error("Hardened runtime is not set for the sender") return false } } else { return false } let entitlementsOptional = signingInformationDict.object(forKey: "entitlements-dict") as? NSDictionary guard let entitlements = entitlementsOptional else { return false } Logger.connectionVerifier.info("Entitlements are \(entitlements)") let problematicEntitlements = [ "com.apple.security.get-task-allow", "com.apple.security.cs.disable-library-validation", "com.apple.security.cs.allow-dyld-environment-variables" ] // Skip this check for debug builds because they'll have the get-task-allow entitlement #if !DEBUG for problematicEntitlement in problematicEntitlements { if let presentEntitlement = entitlements.object(forKey: problematicEntitlement) { if presentEntitlement as! Int == 1 { Logger.connectionVerifier.error("The sender has \(problematicEntitlement) entitlement set to true") return false } } } #endif return true } private static func verifyWithRequirementString(secCode: SecCode) -> Bool { // Code Signing Requirement Language // https://developer.apple.com/library/archive/documentation/Security/Conceptual/CodeSigningGuide/RequirementLang/RequirementLang.html#//apple_ref/doc/uid/TP40005929-CH5-SW1 let requirementString = "identifier \"\(clientBundleID)\" and info [CFBundleShortVersionString] >= \"1.0.0\" and anchor apple generic and certificate leaf[subject.OU] = \"\(subjectOrganizationalUnit)\"" as NSString var secRequirement: SecRequirement? = nil if SecRequirementCreateWithString(requirementString as CFString, SecCSFlags(rawValue: 0), &secRequirement) != errSecSuccess { Logger.connectionVerifier.error("Couldn't create the requirement string") return false } if SecCodeCheckValidity(secCode, SecCSFlags(rawValue: 0), secRequirement) != errSecSuccess { Logger.connectionVerifier.error("NSXPC client does not meet the requirements") return false } return true } public static func isValid(connection: NSXPCConnection) -> Bool { var secCodeOptional: SecCode? = nil var secStaticCodeOptional: SecStaticCode? = nil if !prepareCodeReferencesFromAuditToken(connection: connection, secCodeOptional: &secCodeOptional, secStaticCodeOptional: &secStaticCodeOptional) { return false } if !verifyHardenedRuntimeAndProblematicEntitlements(secStaticCode: secStaticCodeOptional!) { return false } if !verifyWithRequirementString(secCode: secCodeOptional!) { return false } return true } } ================================================ FILE: com.xcodesorg.xcodesapp.Helper/Info.plist ================================================ CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleShortVersionString $(MARKETING_VERSION) CODE_SIGNING_SUBJECT_ORGANIZATIONAL_UNIT $(CODE_SIGNING_SUBJECT_ORGANIZATIONAL_UNIT) SMAuthorizedClients identifier "com.xcodesorg.xcodesapp" and info [CFBundleShortVersionString] >= "1.0.0" and anchor apple generic and certificate leaf[subject.OU] = "$(CODE_SIGNING_SUBJECT_ORGANIZATIONAL_UNIT)" ================================================ FILE: com.xcodesorg.xcodesapp.Helper/Logger.swift ================================================ import Foundation import os.log extension Logger { private static var subsystem = Bundle.main.bundleIdentifier! static let connectionVerifier = Logger(subsystem: subsystem, category: "connectionVerifier") static let xpcDelegate = Logger(subsystem: subsystem, category: "xpcDelegate") } ================================================ FILE: com.xcodesorg.xcodesapp.Helper/SimpleXPCApp.LICENSE ================================================ MIT License Copyright (c) 2020 securing Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: com.xcodesorg.xcodesapp.Helper/XPCDelegate.swift ================================================ import Foundation import os.log class XPCDelegate: NSObject, NSXPCListenerDelegate, HelperXPCProtocol { // MARK: - NSXPCListenerDelegate func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool { guard ConnectionVerifier.isValid(connection: newConnection) else { return false } newConnection.exportedInterface = NSXPCInterface(with: HelperXPCProtocol.self) newConnection.exportedObject = self newConnection.resume() return true } // MARK: - HelperXPCProtocol func getVersion(completion: @escaping (String) -> Void) { Logger.xpcDelegate.info("\(#function)") completion(Bundle.main.infoDictionary!["CFBundleShortVersionString"] as! String) } func xcodeSelect(absolutePath: String, completion: @escaping (Error?) -> Void) { Logger.xpcDelegate.info("\(#function)") guard URL(fileURLWithPath: absolutePath).hasDirectoryPath else { completion(XPCDelegateError(.invalidXcodePath)) return } run( url: URL(fileURLWithPath: "/usr/bin/xcode-select"), arguments: ["-s", absolutePath], completion: completion ) } func devToolsSecurityEnable(completion: @escaping (Error?) -> Void) { run(url: URL(fileURLWithPath: "/usr/sbin/DevToolsSecurity"), arguments: ["-enable"], completion: completion) } func addStaffToDevelopersGroup(completion: @escaping (Error?) -> Void) { run(url: URL(fileURLWithPath: "/usr/sbin/dseditgroup"), arguments: ["-o", "edit", "-t", "group", "-a", "staff", "_developer"], completion: completion) } func acceptXcodeLicense(absoluteXcodePath: String, completion: @escaping (Error?) -> Void) { run(url: URL(fileURLWithPath: absoluteXcodePath + "/Contents/Developer/usr/bin/xcodebuild"), arguments: ["-license", "accept"], completion: completion) } func runFirstLaunch(absoluteXcodePath: String, completion: @escaping (Error?) -> Void) { run(url: URL(fileURLWithPath: absoluteXcodePath + "/Contents/Developer/usr/bin/xcodebuild"), arguments: ["-runFirstLaunch"], completion: completion) } } // MARK: - Run private func run(url: URL, arguments: [String], completion: @escaping (Error?) -> Void) { Logger.xpcDelegate.info("Run executable: \(url), arguments: \(arguments.joined(separator: ", "))") let process = Process() process.executableURL = url process.arguments = arguments do { try process.run() process.waitUntilExit() completion(nil) } catch { completion(error) } } // MARK: - Errors struct XPCDelegateError: CustomNSError { enum Code: Int { case invalidXcodePath } let code: Code init(_ code: Code) { self.code = code } // MARK: - CustomNSError static var errorDomain: String { "XPCDelegateError" } var errorCode: Int { code.rawValue } var errorUserInfo: [String : Any] { switch code { case .invalidXcodePath: return [ NSLocalizedDescriptionKey: "Invalid Xcode path.", NSLocalizedFailureReasonErrorKey: "Xcode path must be absolute." ] } } } ================================================ FILE: com.xcodesorg.xcodesapp.Helper/com.xcodesorg.xcodesapp.Helper-Bridging-Header.h ================================================ #import "AuditTokenHack.h" ================================================ FILE: com.xcodesorg.xcodesapp.Helper/com.xcodesorg.xcodesapp.HelperTest.entitlements ================================================ ================================================ FILE: com.xcodesorg.xcodesapp.Helper/launchd.plist ================================================ Label com.xcodesorg.xcodesapp.Helper MachServices com.xcodesorg.xcodesapp.Helper ================================================ FILE: com.xcodesorg.xcodesapp.Helper/main.swift ================================================ import Foundation let listener = NSXPCListener.init(machServiceName: machServiceName) let delegate = XPCDelegate() listener.delegate = delegate listener.resume() RunLoop.main.run() ================================================ FILE: nextstep.md ================================================ ## The next step As Xcodes continues to grow beyond a small little utility used by Apple Developers all over the world to make their lives easier when it comes to downloading, managing and saving their sanity with Xcode, it was time to move the repo away from Robots and Pencils and into it's own managed org. Starting April 21, 2023, all Xcodes repos are now contained under the `XcodesOrg` organization. https://github.com/XcodesOrg This change will have no effect on the tools at all, but allows the utilites to grow to the next level. I'm (Matt Kiazyk) still the owner and current sole maintainer, and have no plans to stop. Xcodes would not be where it is without contributors over the years. It would also not be where it is without each and every one of you sharing the tool. So from myself - thank you! Matt Kiazyk - maintainer @XcodesApp @mattkiazyk