Compare commits
1 Commits
main
...
bug/ps-675
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b54371dacd |
@@ -7,12 +7,6 @@
|
||||
"commands": [
|
||||
"dotnet-format"
|
||||
]
|
||||
},
|
||||
"cake.tool": {
|
||||
"version": "2.2.0",
|
||||
"commands": [
|
||||
"dotnet-cake"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
51
.github/CODEOWNERS
vendored
@@ -1,51 +0,0 @@
|
||||
# Please sort into logical groups with comment headers. Sort groups in order of specificity.
|
||||
# For example, default owners should always be the first group.
|
||||
# Sort lines alphabetically within these groups to avoid accidentally adding duplicates.
|
||||
#
|
||||
# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners
|
||||
|
||||
# Default file owners
|
||||
* @bitwarden/dept-development-mobile
|
||||
|
||||
|
||||
## Auth team files ##
|
||||
|
||||
## Platform team files ##
|
||||
appIcons @bitwarden/team-platform-dev
|
||||
|
||||
## Vault team files ##
|
||||
src/watchOS @bitwarden/team-vault-dev
|
||||
|
||||
## Tools team files ##
|
||||
src/Core/Services/EmailForwarders @bitwarden/team-tools-dev
|
||||
|
||||
## Crowdin Sync files ##
|
||||
src/Core/Resources/Localization @bitwarden/team-tools-dev
|
||||
src/watchOS/bitwarden/bitwarden\ WatchKit\ Extension/Localization @bitwarden/team-tools-dev
|
||||
store/apple @bitwarden/team-tools-dev
|
||||
store/google @bitwarden/team-tools-dev
|
||||
|
||||
## Locales ##
|
||||
src/Core/Resources/Localization/AppResources.Designer.cs
|
||||
src/Core/Resources/Localization/AppResources.resx
|
||||
src/watchOS/bitwarden/bitwarden\ WatchKit\ Extension/Localization/en.lproj
|
||||
store/apple/en
|
||||
store/google/en
|
||||
|
||||
## Utils ##
|
||||
store/google/Publisher
|
||||
|
||||
## These workflows have joint ownership ##
|
||||
.github/workflows/build.yml @bitwarden/dept-bre @bitwarden/dept-development-mobile
|
||||
.github/workflows/build-beta.yml @bitwarden/dept-bre @bitwarden/dept-development-mobile
|
||||
.github/workflows/cleanup-rc-branch.yml @bitwarden/dept-bre @bitwarden/dept-development-mobile
|
||||
.github/workflows/release.yml @bitwarden/dept-bre @bitwarden/dept-development-mobile
|
||||
.github/workflows/version-auto-bump.yml @bitwarden/dept-bre @bitwarden/dept-development-mobile
|
||||
.github/workflows/version-bump.yml @bitwarden/dept-bre @bitwarden/dept-development-mobile
|
||||
|
||||
# Shared ownership for version bump automation
|
||||
src/App/Platforms/Android/AndroidManifest.xml
|
||||
src/iOS.Autofill/Info.plist
|
||||
src/iOS.Extension/Info.plist
|
||||
src/iOS.ShareExtension/Info.plist
|
||||
src/App/Platforms/iOS/Info.plist
|
||||
18
.github/ISSUE_TEMPLATE/bug.yml
vendored
@@ -6,20 +6,8 @@ body:
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report!
|
||||
|
||||
> [!WARNING]
|
||||
> Testing the new Bitwarden Beta apps? Submit your report in [bitwarden/android](https://github.com/bitwarden/android) or [bitwarden/ios](https://github.com/bitwarden/ios)
|
||||
|
||||
|
||||
Please do not submit feature requests. The [Community Forums](https://community.bitwarden.com) has a section for submitting, voting for, and discussing product feature requests.
|
||||
- type: checkboxes
|
||||
id: production
|
||||
attributes:
|
||||
label: Production Build
|
||||
options:
|
||||
- label: I'm using the legacy Bitwarden app pubicly available in App Store / Play Store and I'm aware that Bitwarden Beta bugs should be reported in [bitwarden/android](https://github.com/bitwarden/android) or [bitwarden/ios](https://github.com/bitwarden/ios)
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reproduce
|
||||
attributes:
|
||||
@@ -85,3 +73,9 @@ body:
|
||||
description: What version of our software are you running? (go to "Settings" → "About" in the app)
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: beta
|
||||
attributes:
|
||||
label: Beta
|
||||
options:
|
||||
- label: Using a pre-release version of the application.
|
||||
|
||||
12
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,14 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Native Android Beta Bug Reports
|
||||
url: https://github.com/bitwarden/android/issues
|
||||
about: Bugs found in the new native Android Beta app should be reported in [bitwarden/android](https://github.com/bitwarden/android)
|
||||
- name: Native iOS BETA Bug Reports
|
||||
url: https://github.com/bitwarden/ios/issues
|
||||
about: Bugs found in the new native iOS Beta app should be reported in [bitwarden/ios](https://github.com/bitwarden/ios)
|
||||
- name: Customer Support
|
||||
url: https://bitwarden.com/contact/
|
||||
about: Please contact our customer support for account issues and general customer support.
|
||||
- name: Report mobile autofill failure
|
||||
url: https://docs.google.com/forms/d/e/1FAIpQLScMopHyN7KGJs8hW562VTzbIGL4KcFnx0wJcsW0GYE1BnPiGA/viewform
|
||||
about: We are aware of some situations where the Bitwarden mobile app will not autofill information correctly. This is something the Bitwarden team is actively working on but need your help as a community and active Bitwarden users!
|
||||
@@ -18,6 +9,9 @@ contact_links:
|
||||
- name: Bitwarden Community Forums
|
||||
url: https://community.bitwarden.com
|
||||
about: Please visit the community forums for general community discussion, support and the development roadmap.
|
||||
- name: Customer Support
|
||||
url: https://bitwarden.com/contact/
|
||||
about: Please contact our customer support for account issues and general customer support.
|
||||
- name: Security Issues
|
||||
url: https://hackerone.com/bitwarden
|
||||
about: We use HackerOne to manage security disclosures.
|
||||
|
||||
8
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -22,7 +22,7 @@
|
||||
|
||||
|
||||
## Before you submit
|
||||
- Please check for formatting errors (`dotnet format --verify-no-changes`) (required)
|
||||
- Please add **unit tests** where it makes sense to do so (encouraged but not required)
|
||||
- If this change requires a **documentation update** - notify the documentation team
|
||||
- If this change has particular **deployment requirements** - notify the DevOps team
|
||||
- [ ] I have checked for formatting errors (`dotnet tool run dotnet-format --check`) (required)
|
||||
- [ ] I have added **unit tests** where it makes sense to do so (encouraged but not required)
|
||||
- [ ] This change requires a **documentation update** (notify the documentation team)
|
||||
- [ ] This change has particular **deployment requirements** (notify the DevOps team)
|
||||
|
||||
26
.github/labeler.yml
vendored
@@ -1,26 +0,0 @@
|
||||
android:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- src/App/*
|
||||
- src/Core/*
|
||||
- src/Android/*
|
||||
- 'src/Xamarin.AndroidX.Credentials/*'
|
||||
|
||||
iOS:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- src/App/*
|
||||
- src/Core/*
|
||||
- lib/ios/*
|
||||
- src/iOS/*
|
||||
- 'src/iOS.Autofill/*'
|
||||
- 'src/iOS.Core/*'
|
||||
- 'src/iOS.Extension/*'
|
||||
- 'src/iOS.ShareExtension/*'
|
||||
- 'src/iOS.Widget/*'
|
||||
- src/watchOS/*
|
||||
|
||||
watchOS:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- src/watchOS/*
|
||||
37
.github/renovate.json
vendored
@@ -1,37 +0,0 @@
|
||||
{
|
||||
"enabled": false,
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:base",
|
||||
"github>bitwarden/renovate-config:pin-actions",
|
||||
":combinePatchMinorReleases",
|
||||
":dependencyDashboard",
|
||||
":maintainLockFilesWeekly",
|
||||
":pinAllExceptPeerDependencies",
|
||||
":prConcurrentLimit10",
|
||||
":rebaseStalePrs",
|
||||
":separateMajorReleases",
|
||||
"group:monorepos",
|
||||
"schedule:weekends"
|
||||
],
|
||||
"enabledManagers": ["github-actions", "npm", "nuget"],
|
||||
"commitMessagePrefix": "[deps]:",
|
||||
"commitMessageTopic": "{{depName}}",
|
||||
"packageRules": [
|
||||
{
|
||||
"groupName": "gh minor",
|
||||
"matchManagers": ["github-actions"],
|
||||
"matchUpdateTypes": ["minor", "patch"]
|
||||
},
|
||||
{
|
||||
"groupName": "npm minor",
|
||||
"matchManagers": ["npm"],
|
||||
"matchUpdateTypes": ["minor", "patch"]
|
||||
},
|
||||
{
|
||||
"groupName": "nuget minor",
|
||||
"matchManagers": ["nuget"],
|
||||
"matchUpdateTypes": ["minor", "patch"]
|
||||
}
|
||||
]
|
||||
}
|
||||
12
.github/resources/export-options-app-store.plist
vendored
@@ -7,17 +7,13 @@
|
||||
<key>provisioningProfiles</key>
|
||||
<dict>
|
||||
<key>com.8bit.bitwarden</key>
|
||||
<string>Dist: Bitwarden</string>
|
||||
<string>Dist: Bitwarden 2021</string>
|
||||
<key>com.8bit.bitwarden.autofill</key>
|
||||
<string>Dist: Autofill</string>
|
||||
<string>Dist: Autofill 2021</string>
|
||||
<key>com.8bit.bitwarden.find-login-action-extension</key>
|
||||
<string>Dist: Extension</string>
|
||||
<string>Dist: Extension 2021</string>
|
||||
<key>com.8bit.bitwarden.share-extension</key>
|
||||
<string>Dist: Share Extension</string>
|
||||
<key>com.8bit.bitwarden.watchkitapp</key>
|
||||
<string>Dist: Bitwarden Watch App</string>
|
||||
<key>com.8bit.bitwarden.watchkitapp.watchkitextension</key>
|
||||
<string>Dist: Bitwarden Watch App Extension</string>
|
||||
<string>Dist: Share Extension 2021</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
BIN
.github/secrets/app_fdroid-keystore.jks.gpg
vendored
Normal file
BIN
.github/secrets/app_play-keystore.jks.gpg
vendored
Normal file
BIN
.github/secrets/app_upload-keystore.jks.gpg
vendored
Normal file
BIN
.github/secrets/bitwarden-mobile-key.p12.gpg
vendored
Normal file
BIN
.github/secrets/dist_autofill.mobileprovision.gpg
vendored
Normal file
BIN
.github/secrets/dist_bitwarden.mobileprovision.gpg
vendored
Normal file
BIN
.github/secrets/dist_extension.mobileprovision.gpg
vendored
Normal file
BIN
.github/secrets/dist_share_extension.mobileprovision.gpg
vendored
Normal file
3
.github/secrets/google-services.json.gpg
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
<EFBFBD>
|
||||
K<>Y#<23>(<28><><EFBFBD><EFBFBD>EI߄T?)l<><6C><EFBFBD><18><><10>"=<3D>|<7C>'e<><0E>m<EFBFBD>/~<7E><>'F<><46>><3E><><EFBFBD><EFBFBD>l<EFBFBD>b<EFBFBD>[<5B>+R<><52>iL<69><4C>"<22><><EFBFBD>~V:<3A><>p<EFBFBD>a<17>ڵel%8t<38><74>튖<EFBFBD>y<<3C>n<EFBFBD><6E><EFBFBD>aU<61>w<16>JD<4A><44><1F><>We<57>9<EFBFBD><39><EFBFBD><EFBFBD><x8d<38>O<EFBFBD>j\<14>ד<EFBFBD><D793><EFBFBD>Vq<56><71>
|
||||
Ǻ<EFBFBD>-<2D>#<23><><11><>]$<24>(<28>l,<2C>Br<42><02><>d<><64><EFBFBD>a-<2D><><EFBFBD>:<3A><>:<3A><04>9b,!Em<02><19><>Qf<>D<EFBFBD>g<EFBFBD><06><0E>x(P<>ȡ~<7E><EFBFBD><CDB9> <09><>[<06><>!:<3A>;f<><66>
|
||||
BIN
.github/secrets/iphone-distribution-cert.p12.gpg
vendored
Normal file
BIN
.github/secrets/play_creds.json.gpg
vendored
Normal file
BIN
.github/secrets/store_fdroid-keystore.jks.gpg
vendored
Normal file
13
.github/workflows/automatic-issue-responses.yml
vendored
@@ -1,3 +1,4 @@
|
||||
---
|
||||
name: Automatic responses
|
||||
on:
|
||||
issues:
|
||||
@@ -6,14 +7,14 @@ on:
|
||||
jobs:
|
||||
close-issue:
|
||||
name: 'Close issue with automatic response'
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-20.04
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
# Feature request
|
||||
- if: github.event.label.name == 'feature-request'
|
||||
name: Feature request
|
||||
uses: peter-evans/close-issue@276d7966e389d888f011539a86c8920025ea0626 # v3.0.1
|
||||
uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0
|
||||
with:
|
||||
comment: |
|
||||
We use GitHub issues as a place to track bugs and other development related issues. The [Bitwarden Community Forums](https://community.bitwarden.com/) has a [Feature Requests](https://community.bitwarden.com/c/feature-requests) section for submitting, voting for, and discussing requests like this one.
|
||||
@@ -24,7 +25,7 @@ jobs:
|
||||
# Intended behavior
|
||||
- if: github.event.label.name == 'intended-behavior'
|
||||
name: Intended behaviour
|
||||
uses: peter-evans/close-issue@276d7966e389d888f011539a86c8920025ea0626 # v3.0.1
|
||||
uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0
|
||||
with:
|
||||
comment: |
|
||||
Your issue appears to be describing the intended behavior of the software. If you want this to be changed, it would be a feature request.
|
||||
@@ -37,7 +38,7 @@ jobs:
|
||||
# Customer support request
|
||||
- if: github.event.label.name == 'customer-support'
|
||||
name: Customer Support request
|
||||
uses: peter-evans/close-issue@276d7966e389d888f011539a86c8920025ea0626 # v3.0.1
|
||||
uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0
|
||||
with:
|
||||
comment: |
|
||||
We use GitHub issues as a place to track bugs and other development related issues. Your issue appears to be a support request, or would otherwise be better handled by our dedicated Customer Success team.
|
||||
@@ -48,14 +49,14 @@ jobs:
|
||||
# Resolved
|
||||
- if: github.event.label.name == 'resolved'
|
||||
name: Resolved
|
||||
uses: peter-evans/close-issue@276d7966e389d888f011539a86c8920025ea0626 # v3.0.1
|
||||
uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0
|
||||
with:
|
||||
comment: |
|
||||
We’ve closed this issue, as it appears the original problem has been resolved. If this happens again or continues to be an problem, please respond to this issue with any additional detail to assist with reproduction and root cause analysis.
|
||||
# Stale
|
||||
- if: github.event.label.name == 'stale'
|
||||
name: Stale
|
||||
uses: peter-evans/close-issue@276d7966e389d888f011539a86c8920025ea0626 # v3.0.1
|
||||
uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0
|
||||
with:
|
||||
comment: |
|
||||
As we haven’t heard from you about this problem in some time, this issue will now be closed.
|
||||
|
||||
349
.github/workflows/build-beta.yml
vendored
@@ -1,349 +0,0 @@
|
||||
name: Build Beta
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
ref:
|
||||
description: 'Branch or tag to build'
|
||||
required: true
|
||||
default: 'main'
|
||||
type: string
|
||||
|
||||
env:
|
||||
main_app_folder_path: src/App
|
||||
main_app_project_path: src/App/App.csproj
|
||||
target-net-version: net8.0
|
||||
|
||||
jobs:
|
||||
setup:
|
||||
name: Setup
|
||||
runs-on: ubuntu-22.04
|
||||
outputs:
|
||||
rc_branch_exists: ${{ steps.branch-check.outputs.rc_branch_exists }}
|
||||
hotfix_branch_exists: ${{ steps.branch-check.outputs.hotfix_branch_exists }}
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
with:
|
||||
submodules: 'true'
|
||||
|
||||
- name: Check if special branches exist
|
||||
id: branch-check
|
||||
run: |
|
||||
if [[ $(git ls-remote --heads origin rc) ]]; then
|
||||
echo "rc_branch_exists=1" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "rc_branch_exists=0" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
if [[ $(git ls-remote --heads origin hotfix-rc) ]]; then
|
||||
echo "hotfix_branch_exists=1" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "hotfix_branch_exists=0" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
ios:
|
||||
name: Apple iOS
|
||||
runs-on: macos-14
|
||||
needs: setup
|
||||
env:
|
||||
_IOS_FOLDER_PATH: src/App/Platforms/iOS
|
||||
_APP_OUTPUT_NAME: App
|
||||
_APP_CI_OUTPUT_FILENAME: App_x64_Debug
|
||||
steps:
|
||||
- name: Set XCode version
|
||||
uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1.6.0
|
||||
with:
|
||||
xcode-version: 15.1
|
||||
|
||||
- name: Setup NuGet
|
||||
uses: nuget/setup-nuget@a21f25cd3998bf370fde17e3f1b4c12c175172f9 # v2.0.0
|
||||
with:
|
||||
nuget-version: 6.4.0
|
||||
|
||||
- name: Set up .NET
|
||||
uses: actions/setup-dotnet@6bd8b7f7774af54e05809fcc5431931b3eb1ddee # v4.0.1
|
||||
with:
|
||||
dotnet-version: '8.0.x'
|
||||
|
||||
# This step might be obsolete at some point as .NET MAUI workloads
|
||||
# are starting to come pre-installed on the GH Actions build agents.
|
||||
- name: Install MAUI Workload
|
||||
run: dotnet workload install maui --ignore-failed-sources
|
||||
|
||||
- name: Print environment
|
||||
run: |
|
||||
nuget help | grep Version
|
||||
dotnet --info
|
||||
echo "GitHub ref: $GITHUB_REF"
|
||||
echo "GitHub event: $GITHUB_EVENT"
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ inputs.ref }}
|
||||
submodules: 'true'
|
||||
|
||||
- name: Login to Azure - CI Subscription
|
||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
||||
with:
|
||||
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
||||
|
||||
- name: Retrieve secrets
|
||||
id: retrieve-secrets
|
||||
uses: bitwarden/gh-actions/get-keyvault-secrets@main
|
||||
with:
|
||||
keyvault: "bitwarden-ci"
|
||||
secrets: "appcenter-ios-token"
|
||||
|
||||
- name: Download Provisioning Profiles secrets
|
||||
env:
|
||||
ACCOUNT_NAME: bitwardenci
|
||||
CONTAINER_NAME: profiles
|
||||
run: |
|
||||
mkdir -p $HOME/secrets
|
||||
profiles=(
|
||||
"dist_beta_autofill.mobileprovision"
|
||||
"dist_beta_bitwarden.mobileprovision"
|
||||
"dist_beta_extension.mobileprovision"
|
||||
"dist_beta_share_extension.mobileprovision"
|
||||
"dist_beta_bitwarden_watch_app.mobileprovision"
|
||||
"dist_beta_bitwarden_watch_app_extension.mobileprovision"
|
||||
)
|
||||
|
||||
for FILE in "${profiles[@]}"
|
||||
do
|
||||
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME --name $FILE \
|
||||
--file $HOME/secrets/$FILE --output none
|
||||
done
|
||||
|
||||
- name: Download Google Services secret
|
||||
env:
|
||||
ACCOUNT_NAME: bitwardenci
|
||||
CONTAINER_NAME: mobile
|
||||
FILE: GoogleService-Info.plist
|
||||
run: |
|
||||
mkdir -p $HOME/secrets
|
||||
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME --name $FILE \
|
||||
--file $HOME/secrets/$FILE --output none
|
||||
|
||||
- name: Increment version
|
||||
run: |
|
||||
BUILD_NUMBER=$((100 + $GITHUB_RUN_NUMBER))
|
||||
echo "##### Setting CFBundleVersion $BUILD_NUMBER"
|
||||
|
||||
echo "### CFBundleVersion $BUILD_NUMBER" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./${{ env._IOS_FOLDER_PATH }}/Info.plist
|
||||
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.Extension/Info.plist
|
||||
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.Autofill/Info.plist
|
||||
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.ShareExtension/Info.plist
|
||||
cd src/watchOS/bitwarden
|
||||
agvtool new-version -all $BUILD_NUMBER
|
||||
|
||||
- name: Update Entitlements
|
||||
run: |
|
||||
echo "##### Updating Entitlements"
|
||||
perl -0777 -pi.bak -e 's/<key>aps-environment<\/key>\s*<string>development<\/string>/<key>aps-environment<\/key>\n\t<string>beta<\/string>/' ./${{ env._IOS_FOLDER_PATH }}/Entitlements.plist
|
||||
|
||||
- name: Get certificates
|
||||
run: |
|
||||
mkdir -p $HOME/certificates
|
||||
az keyvault secret show --id https://bitwarden-ci.vault.azure.net/certificates/ios-distribution |
|
||||
jq -r .value | base64 -d > $HOME/certificates/ios-distribution.p12
|
||||
|
||||
- name: Set up Keychain
|
||||
env:
|
||||
KEYCHAIN_PASSWORD: ${{ secrets.IOS_KEYCHAIN_PASSWORD }}
|
||||
MOBILE_KEY_PASSWORD: ${{ secrets.IOS_KEY_PASSWORD }}
|
||||
DIST_CERT_PASSWORD: ${{ secrets.IOS_DIST_CERT_PASSWORD }}
|
||||
run: |
|
||||
security create-keychain -p $KEYCHAIN_PASSWORD build.keychain
|
||||
security default-keychain -s build.keychain
|
||||
security unlock-keychain -p $KEYCHAIN_PASSWORD build.keychain
|
||||
security set-keychain-settings -lut 1200 build.keychain
|
||||
|
||||
security import $HOME/certificates/ios-distribution.p12 -k build.keychain -P "" -T /usr/bin/codesign \
|
||||
-T /usr/bin/security
|
||||
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $KEYCHAIN_PASSWORD build.keychain
|
||||
|
||||
- name: Set up provisioning profiles
|
||||
run: |
|
||||
AUTOFILL_PROFILE_PATH=$HOME/secrets/dist_beta_autofill.mobileprovision
|
||||
BITWARDEN_PROFILE_PATH=$HOME/secrets/dist_beta_bitwarden.mobileprovision
|
||||
EXTENSION_PROFILE_PATH=$HOME/secrets/dist_beta_extension.mobileprovision
|
||||
SHARE_EXTENSION_PROFILE_PATH=$HOME/secrets/dist_beta_share_extension.mobileprovision
|
||||
WATCH_APP_PROFILE_PATH=$HOME/secrets/dist_beta_bitwarden_watch_app.mobileprovision
|
||||
WATCH_APP_EXTENSION_PROFILE_PATH=$HOME/secrets/dist_beta_bitwarden_watch_app_extension.mobileprovision
|
||||
PROFILES_DIR_PATH=$HOME/Library/MobileDevice/Provisioning\ Profiles
|
||||
|
||||
mkdir -p "$PROFILES_DIR_PATH"
|
||||
|
||||
AUTOFILL_UUID=$(grep UUID -A1 -a $AUTOFILL_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
|
||||
cp $AUTOFILL_PROFILE_PATH "$PROFILES_DIR_PATH/$AUTOFILL_UUID.mobileprovision"
|
||||
|
||||
BITWARDEN_UUID=$(grep UUID -A1 -a $BITWARDEN_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
|
||||
cp $BITWARDEN_PROFILE_PATH "$PROFILES_DIR_PATH/$BITWARDEN_UUID.mobileprovision"
|
||||
|
||||
EXTENSION_UUID=$(grep UUID -A1 -a $EXTENSION_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
|
||||
cp $EXTENSION_PROFILE_PATH "$PROFILES_DIR_PATH/$EXTENSION_UUID.mobileprovision"
|
||||
|
||||
SHARE_EXTENSION_UUID=$(grep UUID -A1 -a $SHARE_EXTENSION_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
|
||||
cp $SHARE_EXTENSION_PROFILE_PATH "$PROFILES_DIR_PATH/$SHARE_EXTENSION_UUID.mobileprovision"
|
||||
|
||||
WATCH_APP_UUID=$(grep UUID -A1 -a $WATCH_APP_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
|
||||
cp $WATCH_APP_PROFILE_PATH "$PROFILES_DIR_PATH/$WATCH_APP_UUID.mobileprovision"
|
||||
|
||||
WATCH_APP_EXTENSION_UUID=$(grep UUID -A1 -a $WATCH_APP_EXTENSION_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
|
||||
cp $WATCH_APP_EXTENSION_PROFILE_PATH "$PROFILES_DIR_PATH/$WATCH_APP_EXTENSION_UUID.mobileprovision"
|
||||
|
||||
- name: Restore packages
|
||||
run: |
|
||||
dotnet restore
|
||||
dotnet tool restore
|
||||
|
||||
- name: Setup iOS build CAKE (Testing)
|
||||
run: dotnet cake build.cake --target iOS --variant beta
|
||||
|
||||
- name: Bulid WatchApp
|
||||
run: |
|
||||
echo "##### Build WatchApp with Release Configuration"
|
||||
xcodebuild archive -workspace ./src/watchOS/bitwarden/bitwarden.xcodeproj/project.xcworkspace -configuration Release -scheme bitwarden\ WatchKit\ App -archivePath ./src/watchOS/bitwarden
|
||||
|
||||
echo "##### Done"
|
||||
|
||||
- name: Archive Build for App Store
|
||||
shell: pwsh
|
||||
run: |
|
||||
Write-Output "##### Archive for Release ios-arm64"
|
||||
dotnet publish ${{ env.main_app_project_path }} -c Release -f ${{ env.target-net-version }}-ios /p:RuntimeIdentifier=ios-arm64 /p:ArchiveOnBuild=true /p:MtouchUseLlvm=false
|
||||
|
||||
Write-Output "##### Done"
|
||||
|
||||
- name: Archive Build for Mobile Automation
|
||||
shell: pwsh
|
||||
run: |
|
||||
Write-Output "##### Archive Debug for iossimulator-x64"
|
||||
dotnet build ${{ env.main_app_project_path }} -c Debug -f ${{ env.target-net-version }}-ios /p:RuntimeIdentifier=iossimulator-x64 /p:ArchiveOnBuild=true /p:MtouchUseLlvm=false
|
||||
|
||||
Write-Output "##### Done"
|
||||
ls ~/Library/Developer/Xcode/Archives
|
||||
|
||||
- name: Export .ipa for App Store
|
||||
env:
|
||||
EXPORT_OPTIONS_PATH: ./.github/resources/export-options-app-store.plist
|
||||
EXPORT_PATH: ./bitwarden-export
|
||||
run: |
|
||||
ARCHIVE_PATH="$HOME/Library/Developer/Xcode/Archives/*/*.xcarchive"
|
||||
|
||||
xcodebuild -exportArchive -archivePath $ARCHIVE_PATH -exportPath $EXPORT_PATH \
|
||||
-exportOptionsPlist $EXPORT_OPTIONS_PATH
|
||||
|
||||
- name: Export .app for Automation CI
|
||||
env:
|
||||
ARCHIVE_PATH: ./${{ env.main_app_folder_path }}/bin/Debug/${{ env.target-net-version }}-ios/iossimulator-x64
|
||||
EXPORT_PATH: ./bitwarden-export
|
||||
run: |
|
||||
zip -r -q ${{ env._APP_CI_OUTPUT_FILENAME }}.app.zip $ARCHIVE_PATH
|
||||
mv ${{ env._APP_CI_OUTPUT_FILENAME }}.app.zip $EXPORT_PATH
|
||||
|
||||
- name: Show Bitwarden Export
|
||||
shell: bash
|
||||
run: ls -a -R ./bitwarden-export
|
||||
|
||||
- name: Copy all dSYMs files to upload
|
||||
env:
|
||||
EXPORT_PATH: ./bitwarden-export
|
||||
WATCH_ARCHIVE_DSYMS_PATH: ./src/watchOS/bitwarden.xcarchive/dSYMs/
|
||||
WATCH_DSYMS_EXPORT_PATH: ./bitwarden-export/Watch_dSYMs
|
||||
run: |
|
||||
ARCHIVE_DSYMS_PATH="$HOME/Library/Developer/Xcode/Archives/*/*.xcarchive/dSYMs"
|
||||
|
||||
cp -r -v $ARCHIVE_DSYMS_PATH $EXPORT_PATH
|
||||
mkdir $WATCH_DSYMS_EXPORT_PATH
|
||||
cp -r -v $WATCH_ARCHIVE_DSYMS_PATH $WATCH_DSYMS_EXPORT_PATH
|
||||
|
||||
- name: Upload App Store .ipa & dSYMs artifacts
|
||||
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4
|
||||
with:
|
||||
name: Bitwarden iOS
|
||||
path: |
|
||||
./bitwarden-export/Bitwarden*.ipa
|
||||
./bitwarden-export/dSYMs/*.*
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload .app file for Automation CI
|
||||
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4
|
||||
with:
|
||||
name: ${{ env._APP_CI_OUTPUT_FILENAME }}.app.zip
|
||||
path: ./bitwarden-export/${{ env._APP_CI_OUTPUT_FILENAME }}.app.zip
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Install AppCenter CLI
|
||||
run: npm install -g appcenter-cli
|
||||
|
||||
- name: Upload dSYMs to App Center
|
||||
env:
|
||||
APPCENTER_IOS_TOKEN: ${{ steps.retrieve-secrets.outputs.appcenter-ios-token }}
|
||||
run: appcenter crashes upload-symbols -a bitwarden/bitwarden -s "./bitwarden-export/dSYMs" --token $APPCENTER_IOS_TOKEN
|
||||
|
||||
- name: Upload Watch dSYMs to Firebase Crashlytics
|
||||
run: |
|
||||
echo "##### Uploading Watch dSYMs to Firebase"
|
||||
find "$HOME/Library/Developer/XCode/DerivedData" -name "upload-symbols" -exec chmod +x {} \; -exec {} -gsp "./src/watchOS/bitwarden/GoogleService-Info.plist" -p ios "./bitwarden-export/Watch_dSYMs" \;
|
||||
|
||||
- name: Validate app in App Store
|
||||
env:
|
||||
APPLE_ID_USERNAME: ${{ secrets.APPLE_ID_USERNAME }}
|
||||
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
|
||||
run: |
|
||||
xcrun altool --validate-app --type ios --file "./bitwarden-export/Bitwarden Beta.ipa" \
|
||||
--username "$APPLE_ID_USERNAME" --password "$APPLE_ID_PASSWORD"
|
||||
shell: bash
|
||||
|
||||
- name: Deploy to App Store
|
||||
env:
|
||||
APPLE_ID_USERNAME: ${{ secrets.APPLE_ID_USERNAME }}
|
||||
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
|
||||
run: |
|
||||
xcrun altool --upload-app --type ios --file "./bitwarden-export/Bitwarden Beta.ipa" \
|
||||
--username "$APPLE_ID_USERNAME" --password "$APPLE_ID_PASSWORD"
|
||||
|
||||
check-failures:
|
||||
name: Check for failures
|
||||
if: always()
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- setup
|
||||
- ios
|
||||
steps:
|
||||
- name: Check if any job failed
|
||||
if: |
|
||||
(github.ref == 'refs/heads/main'
|
||||
|| github.ref == 'refs/heads/rc'
|
||||
|| github.ref == 'refs/heads/hotfix-rc')
|
||||
&& contains(needs.*.result, 'failure')
|
||||
run: exit 1
|
||||
|
||||
- name: Login to Azure - CI Subscription
|
||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
||||
if: failure()
|
||||
with:
|
||||
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
||||
|
||||
- name: Retrieve secrets
|
||||
id: retrieve-secrets
|
||||
uses: bitwarden/gh-actions/get-keyvault-secrets@main
|
||||
if: failure()
|
||||
with:
|
||||
keyvault: "bitwarden-ci"
|
||||
secrets: "devops-alerts-slack-webhook-url"
|
||||
|
||||
- name: Notify Slack on failure
|
||||
uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0
|
||||
if: failure()
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }}
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
768
.github/workflows/build.yml
vendored
52
.github/workflows/cleanup-rc-branch.yml
vendored
@@ -1,52 +0,0 @@
|
||||
name: Cleanup RC Branch
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v**
|
||||
|
||||
jobs:
|
||||
delete-rc:
|
||||
name: Delete RC Branch
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Login to Azure - CI Subscription
|
||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
||||
with:
|
||||
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
||||
|
||||
- name: Retrieve bot secrets
|
||||
id: retrieve-bot-secrets
|
||||
uses: bitwarden/gh-actions/get-keyvault-secrets@main
|
||||
with:
|
||||
keyvault: bitwarden-ci
|
||||
secrets: "github-pat-bitwarden-devops-bot-repo-scope"
|
||||
|
||||
- name: Checkout main
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
with:
|
||||
ref: main
|
||||
token: ${{ steps.retrieve-bot-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }}
|
||||
|
||||
- name: Check if a RC branch exists
|
||||
id: branch-check
|
||||
run: |
|
||||
hotfix_rc_branch_check=$(git ls-remote --heads origin hotfix-rc | wc -l)
|
||||
rc_branch_check=$(git ls-remote --heads origin rc | wc -l)
|
||||
|
||||
if [[ "${hotfix_rc_branch_check}" -gt 0 ]]; then
|
||||
echo "hotfix-rc branch exists." | tee -a $GITHUB_STEP_SUMMARY
|
||||
echo "name=hotfix-rc" >> $GITHUB_OUTPUT
|
||||
elif [[ "${rc_branch_check}" -gt 0 ]]; then
|
||||
echo "rc branch exists." | tee -a $GITHUB_STEP_SUMMARY
|
||||
echo "name=rc" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Delete RC branch
|
||||
env:
|
||||
BRANCH_NAME: ${{ steps.branch-check.outputs.name }}
|
||||
run: |
|
||||
if ! [[ -z "$BRANCH_NAME" ]]; then
|
||||
git push --quiet origin --delete $BRANCH_NAME
|
||||
echo "Deleted $BRANCH_NAME branch." | tee -a $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
40
.github/workflows/crowdin-pull.yml
vendored
@@ -1,57 +1,49 @@
|
||||
---
|
||||
name: Crowdin Sync
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs: {}
|
||||
schedule:
|
||||
- cron: '0 0 * * 5'
|
||||
|
||||
jobs:
|
||||
crowdin-sync:
|
||||
name: Autosync
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-20.04
|
||||
env:
|
||||
_CROWDIN_PROJECT_ID: "269690"
|
||||
steps:
|
||||
- name: Generate GH App token
|
||||
uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ secrets.BW_GHAPP_ID }}
|
||||
private-key: ${{ secrets.BW_GHAPP_KEY }}
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||
|
||||
- name: Login to Azure - CI Subscription
|
||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
||||
- name: Login to Azure
|
||||
uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
|
||||
with:
|
||||
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
||||
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
||||
|
||||
- name: Retrieve secrets
|
||||
id: retrieve-secrets
|
||||
uses: bitwarden/gh-actions/get-keyvault-secrets@main
|
||||
uses: Azure/get-keyvault-secrets@80ccd3fafe5662407cc2e55f202ee34bfff8c403
|
||||
with:
|
||||
keyvault: "bitwarden-ci"
|
||||
secrets: "crowdin-api-token, github-gpg-private-key, github-gpg-private-key-passphrase"
|
||||
keyvault: "bitwarden-prod-kv"
|
||||
secrets: "crowdin-api-token"
|
||||
|
||||
- name: Download translations
|
||||
uses: crowdin/github-action@61ac8b980551f674046220c3e104bddae2916ac5 # v2.0.0
|
||||
uses: crowdin/github-action@e39093fd75daae7859c68eded4b43d42ec78d8ea
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
|
||||
with:
|
||||
config: crowdin.yml
|
||||
crowdin_branch_name: main
|
||||
crowdin_branch_name: master
|
||||
upload_sources: false
|
||||
upload_translations: false
|
||||
download_translations: true
|
||||
github_user_name: "bitwarden-devops-bot"
|
||||
github_user_email: "106330231+bitwarden-devops-bot@users.noreply.github.com"
|
||||
github_user_name: "github-actions"
|
||||
github_user_email: "<>"
|
||||
commit_message: "Autosync the updated translations"
|
||||
localization_branch_name: crowdin-auto-sync
|
||||
create_pull_request: true
|
||||
pull_request_title: "Autosync Crowdin Translations"
|
||||
pull_request_body: "Autosync the updated translations"
|
||||
gpg_private_key: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key }}
|
||||
gpg_passphrase: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key-passphrase }}
|
||||
|
||||
9
.github/workflows/enforce-labels.yml
vendored
@@ -1,3 +1,4 @@
|
||||
---
|
||||
name: Enforce PR labels
|
||||
|
||||
on:
|
||||
@@ -6,10 +7,10 @@ on:
|
||||
jobs:
|
||||
enforce-label:
|
||||
name: EnforceLabel
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Enforce Label
|
||||
uses: yogevbd/enforce-label-action@a3c219da6b8fa73f6ba62b68ff09c469b3a1c024 # 2.2.2
|
||||
uses: yogevbd/enforce-label-action@8d1e1709b1011e6d90400a0e6cf7c0b77aa5efeb
|
||||
with:
|
||||
BANNED_LABELS: "hold,needs-qa"
|
||||
BANNED_LABELS_DESCRIPTION: "PRs with the hold or needs-qa labels cannot be merged"
|
||||
BANNED_LABELS: "hold"
|
||||
BANNED_LABELS_DESCRIPTION: "PRs on hold cannot be merged"
|
||||
|
||||
17
.github/workflows/pr-labeler.yml
vendored
@@ -1,17 +0,0 @@
|
||||
name: "Pull Request Labeler"
|
||||
|
||||
on:
|
||||
pull_request_target: {}
|
||||
|
||||
jobs:
|
||||
labeler:
|
||||
name: "Pull Request Labeler"
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Label PR
|
||||
uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5.0.0
|
||||
with:
|
||||
sync-labels: true
|
||||
195
.github/workflows/release.yml
vendored
@@ -1,5 +1,5 @@
|
||||
---
|
||||
name: Release
|
||||
run-name: Release ${{ inputs.release_type }}
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -13,21 +13,16 @@ on:
|
||||
- Initial Release
|
||||
- Redeploy
|
||||
- Dry Run
|
||||
fdroid_publish:
|
||||
description: 'Publish to f-droid store'
|
||||
required: true
|
||||
default: true
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Create Release
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-20.04
|
||||
outputs:
|
||||
branch-name: ${{ steps.branch.outputs.branch-name }}
|
||||
steps:
|
||||
- name: Branch check
|
||||
if: inputs.release_type != 'Dry Run'
|
||||
if: github.event.inputs.release_type != 'Dry Run'
|
||||
run: |
|
||||
if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix-rc" ]]; then
|
||||
echo "==================================="
|
||||
@@ -37,69 +32,40 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0
|
||||
|
||||
- name: Check Release Version
|
||||
id: version
|
||||
uses: bitwarden/gh-actions/release-version-check@main
|
||||
uses: bitwarden/gh-actions/release-version-check@8f055ef543c7433c967a1b9b04a0f230923233bb
|
||||
with:
|
||||
release-type: ${{ inputs.release_type }}
|
||||
release-type: ${{ github.event.inputs.release_type }}
|
||||
project-type: xamarin
|
||||
file: src/App/Platforms/Android/AndroidManifest.xml
|
||||
file: src/Android/Properties/AndroidManifest.xml
|
||||
|
||||
- name: Get branch name
|
||||
id: branch
|
||||
run: |
|
||||
BRANCH_NAME=$(basename ${{ github.ref }})
|
||||
echo "branch-name=$BRANCH_NAME" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Create GitHub deployment
|
||||
if: ${{ inputs.release_type != 'Dry Run' }}
|
||||
uses: chrnorm/deployment-action@55729fcebec3d284f60f5bcabbd8376437d696b1 # v2.0.7
|
||||
id: deployment
|
||||
with:
|
||||
token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
initial-status: 'in_progress'
|
||||
environment: 'production'
|
||||
description: 'Deployment ${{ steps.version.outputs.version }} from branch ${{ steps.branch.outputs.branch-name }}'
|
||||
task: release
|
||||
echo "::set-output name=branch-name::$BRANCH_NAME"
|
||||
|
||||
- name: Download all artifacts
|
||||
if: ${{ inputs.release_type != 'Dry Run' }}
|
||||
uses: bitwarden/gh-actions/download-artifacts@main
|
||||
uses: dawidd6/action-download-artifact@575b1e4167df67acf7e692af784566618b23c71e # v2.17.10
|
||||
with:
|
||||
workflow: build.yml
|
||||
workflow_conclusion: success
|
||||
branch: ${{ steps.branch.outputs.branch-name }}
|
||||
skip_unpack: true
|
||||
|
||||
- name: Dry Run - Download all artifacts
|
||||
if: ${{ inputs.release_type == 'Dry Run' }}
|
||||
uses: bitwarden/gh-actions/download-artifacts@main
|
||||
with:
|
||||
workflow: build.yml
|
||||
workflow_conclusion: success
|
||||
branch: main
|
||||
skip_unpack: true
|
||||
|
||||
- name: Unzip release assets
|
||||
run: |
|
||||
unzip bw-android-apk-sha256.txt.zip -d bw-android-apk-sha256.txt
|
||||
unzip bw-fdroid-apk-sha256.txt.zip -d bw-fdroid-apk-sha256.txt
|
||||
unzip com.x8bit.bitwarden-fdroid.apk.zip -d com.x8bit.bitwarden-fdroid.apk
|
||||
unzip com.x8bit.bitwarden.aab.zip -d com.x8bit.bitwarden.aab
|
||||
unzip com.x8bit.bitwarden.apk.zip -d com.x8bit.bitwarden.apk
|
||||
- name: Prep Bitwarden iOS release asset
|
||||
run: zip -r Bitwarden\ iOS.zip Bitwarden\ iOS
|
||||
|
||||
- name: Create release
|
||||
if: ${{ inputs.release_type != 'Dry Run' }}
|
||||
uses: ncipollo/release-action@2c591bcc8ecdcd2db72b97d6147f871fcd833ba5 # v1.14.0
|
||||
if: github.event.inputs.release_type != 'Dry Run'
|
||||
uses: ncipollo/release-action@40bb172bd05f266cf9ba4ff965cb61e9ee5f6d01 # v1.9.0
|
||||
with:
|
||||
artifacts: "./com.x8bit.bitwarden.aab/com.x8bit.bitwarden.aab,
|
||||
./com.x8bit.bitwarden.apk/com.x8bit.bitwarden.apk,
|
||||
./com.x8bit.bitwarden-fdroid.apk/com.x8bit.bitwarden-fdroid.apk,
|
||||
./Bitwarden iOS.zip,
|
||||
./bw-android-apk-sha256.txt/bw-android-apk-sha256.txt,
|
||||
./bw-fdroid-apk-sha256.txt/bw-fdroid-apk-sha256.txt"
|
||||
./Bitwarden iOS.zip"
|
||||
commit: ${{ github.sha }}
|
||||
tag: v${{ steps.version.outputs.version }}
|
||||
name: Version ${{ steps.version.outputs.version }}
|
||||
@@ -107,55 +73,32 @@ jobs:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
draft: true
|
||||
|
||||
- name: Update deployment status to Success
|
||||
if: ${{ inputs.release_type != 'Dry Run' && success() }}
|
||||
uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
|
||||
with:
|
||||
token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
state: 'success'
|
||||
deployment-id: ${{ steps.deployment.outputs.deployment_id }}
|
||||
|
||||
- name: Update deployment status to Failure
|
||||
if: ${{ inputs.release_type != 'Dry Run' && failure() }}
|
||||
uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
|
||||
with:
|
||||
token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
state: 'failure'
|
||||
deployment-id: ${{ steps.deployment.outputs.deployment_id }}
|
||||
|
||||
|
||||
f-droid:
|
||||
name: F-Droid Release
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-20.04
|
||||
needs: release
|
||||
if: inputs.fdroid_publish
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0
|
||||
|
||||
- name: Download F-Droid .apk artifact
|
||||
if: ${{ inputs.release_type != 'Dry Run' }}
|
||||
uses: bitwarden/gh-actions/download-artifacts@main
|
||||
uses: dawidd6/action-download-artifact@575b1e4167df67acf7e692af784566618b23c71e # v2.17.10
|
||||
with:
|
||||
workflow: build.yml
|
||||
workflow_conclusion: success
|
||||
branch: ${{ needs.release.outputs.branch-name }}
|
||||
|
||||
- name: Dry Run - Download F-Droid .apk artifact
|
||||
if: ${{ inputs.release_type == 'Dry Run' }}
|
||||
uses: bitwarden/gh-actions/download-artifacts@main
|
||||
with:
|
||||
workflow: build.yml
|
||||
workflow_conclusion: success
|
||||
branch: main
|
||||
name: com.x8bit.bitwarden-fdroid.apk
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
|
||||
uses: actions/setup-node@1f8c6b94b26d0feae1e387ca63ccbdc44d27b561 # v2.5.1
|
||||
with:
|
||||
node-version: '16.x'
|
||||
node-version: '10.x'
|
||||
|
||||
- name: Set up F-Droid server
|
||||
run: pip install git+https://gitlab.com/fdroid/fdroidserver.git
|
||||
run: |
|
||||
sudo apt-get -qq update
|
||||
sudo apt-get -qqy install --no-install-recommends fdroidserver wget
|
||||
|
||||
- name: Set up Git credentials
|
||||
env:
|
||||
@@ -168,85 +111,49 @@ jobs:
|
||||
|
||||
- name: Print environment
|
||||
run: |
|
||||
echo "Node Version: $(node --version)"
|
||||
echo "NPM Version: $(npm --version)"
|
||||
echo "Git Version: $(git --version)"
|
||||
echo "F-Droid Server Version: $(fdroid --version)"
|
||||
node --version
|
||||
npm --version
|
||||
git --version
|
||||
echo "GitHub ref: $GITHUB_REF"
|
||||
echo "GitHub event: $GITHUB_EVENT"
|
||||
|
||||
- name: Install Node dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Login to Azure - CI Subscription
|
||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
||||
with:
|
||||
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
||||
|
||||
- name: Retrieve secrets
|
||||
id: retrieve-secrets
|
||||
uses: bitwarden/gh-actions/get-keyvault-secrets@main
|
||||
with:
|
||||
keyvault: "bitwarden-ci"
|
||||
secrets: "github-gpg-private-key,
|
||||
github-gpg-private-key-passphrase,
|
||||
github-pat-bitwarden-devops-bot-mobile-fdroid"
|
||||
|
||||
- name: Import GPG key
|
||||
uses: crazy-max/ghaction-import-gpg@01dd5d3ca463c7f10f7f4f7b4f177225ac661ee4 # v6.1.0
|
||||
with:
|
||||
gpg_private_key: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key }}
|
||||
passphrase: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key-passphrase }}
|
||||
git_user_signingkey: true
|
||||
git_commit_gpgsign: true
|
||||
|
||||
- name: Setup git
|
||||
run: |
|
||||
git config --local user.email "106330231+bitwarden-devops-bot@users.noreply.github.com"
|
||||
git config --local user.name "bitwarden-devops-bot"
|
||||
|
||||
- name: Download secrets
|
||||
- name: Decrypt secrets
|
||||
env:
|
||||
ACCOUNT_NAME: bitwardenci
|
||||
CONTAINER_NAME: mobile
|
||||
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
|
||||
run: |
|
||||
mkdir -p $HOME/secrets
|
||||
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME \
|
||||
--name store_fdroid-keystore.jks --file ./store/fdroid/keystore.jks --output none
|
||||
mkdir -p ~/secrets
|
||||
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||
--output ./store/fdroid/keystore.jks ./.github/secrets/store_fdroid-keystore.jks.gpg
|
||||
|
||||
- name: Compile for F-Droid Store
|
||||
env:
|
||||
FDROID_STORE_KEYSTORE_PASSWORD: ${{ secrets.FDROID_STORE_KEYSTORE_PASSWORD }}
|
||||
run: |
|
||||
# Create required directories.
|
||||
cd $GITHUB_WORKSPACE
|
||||
mkdir dist
|
||||
mkdir -p store/temp/fdroid
|
||||
mkdir -p store/fdroid/repo
|
||||
|
||||
# Configure F-Droid server.
|
||||
cp CNAME dist/
|
||||
chmod 600 store/fdroid/config.yml store/fdroid/keystore.jks
|
||||
cp CNAME ./dist
|
||||
cd store
|
||||
chmod 600 fdroid/config.py fdroid/keystore.jks
|
||||
mkdir -p temp/fdroid
|
||||
TEMP_DIR="$GITHUB_WORKSPACE/store/temp/fdroid"
|
||||
echo "keypass: $FDROID_STORE_KEYSTORE_PASSWORD" >> store/fdroid/config.yml
|
||||
echo "keystorepass: $FDROID_STORE_KEYSTORE_PASSWORD" >> store/fdroid/config.yml
|
||||
echo "local_copy_dir: $TEMP_DIR" >> store/fdroid/config.yml
|
||||
mv $GITHUB_WORKSPACE/com.x8bit.bitwarden-fdroid.apk store/fdroid/repo/
|
||||
|
||||
# Run update and deploy.
|
||||
cd store/fdroid
|
||||
cd fdroid
|
||||
echo "keypass=\"$FDROID_STORE_KEYSTORE_PASSWORD\"" >>config.py
|
||||
echo "keystorepass=\"$FDROID_STORE_KEYSTORE_PASSWORD\"" >>config.py
|
||||
echo "local_copy_dir=\"$TEMP_DIR\"" >>config.py
|
||||
mkdir -p repo
|
||||
mv $GITHUB_WORKSPACE/com.x8bit.bitwarden-fdroid.apk ./repo/
|
||||
fdroid update
|
||||
fdroid deploy
|
||||
cd ../..
|
||||
|
||||
# Move files for distribution.
|
||||
rm -rf store/temp/fdroid/archive
|
||||
mv -v store/temp/fdroid dist
|
||||
cp store/fdroid/index.html store/fdroid/btn.png store/fdroid/qr.png dist/fdroid
|
||||
fdroid server update
|
||||
cd ..
|
||||
rm -rf temp/fdroid/archive
|
||||
mv -v temp/fdroid ../dist
|
||||
cd fdroid
|
||||
cp index.html btn.png qr.png ../../dist/fdroid
|
||||
cd $GITHUB_WORKSPACE
|
||||
|
||||
- name: Deploy to gh-pages
|
||||
if: ${{ inputs.release_type != 'Dry Run' }}
|
||||
env:
|
||||
TOKEN: ${{ steps.retrieve-secrets.outputs.github-pat-bitwarden-devops-bot-mobile-fdroid }}
|
||||
run: |
|
||||
git remote set-url origin https://git:${TOKEN}@github.com/${GITHUB_REPOSITORY}.git
|
||||
npm run deploy -- -u "bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>"
|
||||
if: github.event.inputs.release_type != 'Dry Run'
|
||||
run: npm run deploy
|
||||
|
||||
7
.github/workflows/stale-bot.yml
vendored
@@ -1,3 +1,4 @@
|
||||
---
|
||||
name: 'Close stale issues and PRs'
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -7,10 +8,10 @@ on:
|
||||
jobs:
|
||||
stale:
|
||||
name: 'Check for stale issues and PRs'
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: 'Run stale action'
|
||||
uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0
|
||||
uses: actions/stale@3cc123766321e9f15a6676375c154ccffb12a358 # v5.0.0
|
||||
with:
|
||||
stale-issue-label: 'needs-reply'
|
||||
stale-pr-label: 'needs-changes'
|
||||
@@ -26,4 +27,4 @@ jobs:
|
||||
|
||||
If you’re still working on this, please respond here after you’ve made the changes we’ve requested and our team will re-open it for further review.
|
||||
|
||||
Please make sure to resolve any conflicts with the main branch before requesting another review.
|
||||
Please make sure to resolve any conflicts with the master branch before requesting another review.
|
||||
|
||||
30
.github/workflows/version-auto-bump.yml
vendored
@@ -1,30 +0,0 @@
|
||||
name: Auto Bump Mobile Version
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v**
|
||||
|
||||
jobs:
|
||||
bump-version:
|
||||
name: Bump Mobile Version
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Login to Azure - CI Subscription
|
||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
||||
with:
|
||||
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
||||
|
||||
- name: Retrieve bot secrets
|
||||
id: retrieve-bot-secrets
|
||||
uses: bitwarden/gh-actions/get-keyvault-secrets@main
|
||||
with:
|
||||
keyvault: bitwarden-ci
|
||||
secrets: "github-pat-bitwarden-devops-bot-repo-scope"
|
||||
|
||||
- name: Trigger Version Bump workflow
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.retrieve-bot-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }}
|
||||
run: |
|
||||
echo '{"cut_rc_branch": "false"}' | \
|
||||
gh workflow run version-bump.yml --json --repo bitwarden/mobile
|
||||
315
.github/workflows/version-bump.yml
vendored
@@ -1,244 +1,79 @@
|
||||
---
|
||||
name: Version Bump
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version_number_override:
|
||||
description: "New version override (leave blank for automatic calculation, example: '2024.1.0')"
|
||||
required: false
|
||||
type: string
|
||||
cut_rc_branch:
|
||||
description: "Cut RC branch?"
|
||||
default: true
|
||||
type: boolean
|
||||
enable_slack_notification:
|
||||
description: "Enable Slack notifications for upcoming release?"
|
||||
default: false
|
||||
type: boolean
|
||||
version_number:
|
||||
description: "New Version"
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
bump_version:
|
||||
name: Bump Version
|
||||
runs-on: ubuntu-22.04
|
||||
outputs:
|
||||
version: ${{ steps.set-final-version-output.outputs.version }}
|
||||
name: "Create version_bump_${{ github.event.inputs.version_number }} branch"
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Validate version input
|
||||
if: ${{ inputs.version_number_override != '' }}
|
||||
uses: bitwarden/gh-actions/version-check@main
|
||||
with:
|
||||
version: ${{ inputs.version_number_override }}
|
||||
|
||||
- name: Slack Notification Check
|
||||
run: |
|
||||
if [[ "${{ inputs.enable_slack_notification }}" == true ]]; then
|
||||
echo "Slack notifications enabled."
|
||||
else
|
||||
echo "Slack notifications disabled."
|
||||
fi
|
||||
|
||||
- name: Checkout Branch
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
with:
|
||||
ref: main
|
||||
|
||||
- name: Check if RC branch exists
|
||||
if: ${{ inputs.cut_rc_branch == true }}
|
||||
run: |
|
||||
remote_rc_branch_check=$(git ls-remote --heads origin rc | wc -l)
|
||||
if [[ "${remote_rc_branch_check}" -gt 0 ]]; then
|
||||
echo "Remote RC branch exists."
|
||||
echo "Please delete current RC branch before running again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Login to Azure - CI Subscription
|
||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
||||
with:
|
||||
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
||||
|
||||
- name: Retrieve secrets
|
||||
id: retrieve-secrets
|
||||
uses: bitwarden/gh-actions/get-keyvault-secrets@main
|
||||
with:
|
||||
keyvault: "bitwarden-ci"
|
||||
secrets: "github-gpg-private-key,
|
||||
github-gpg-private-key-passphrase,
|
||||
github-pat-bitwarden-devops-bot-repo-scope"
|
||||
|
||||
- name: Import GPG key
|
||||
uses: crazy-max/ghaction-import-gpg@01dd5d3ca463c7f10f7f4f7b4f177225ac661ee4 # v6.1.0
|
||||
with:
|
||||
gpg_private_key: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key }}
|
||||
passphrase: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key-passphrase }}
|
||||
git_user_signingkey: true
|
||||
git_commit_gpgsign: true
|
||||
|
||||
- name: Setup git
|
||||
run: |
|
||||
git config --local user.email "106330231+bitwarden-devops-bot@users.noreply.github.com"
|
||||
git config --local user.name "bitwarden-devops-bot"
|
||||
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
|
||||
|
||||
- name: Create Version Branch
|
||||
id: create-branch
|
||||
run: |
|
||||
NAME=version_bump_${{ github.ref_name }}_$(date +"%Y-%m-%d")
|
||||
git switch -c $NAME
|
||||
echo "name=$NAME" >> $GITHUB_OUTPUT
|
||||
git switch -c version_bump_${{ github.event.inputs.version_number }}
|
||||
git push -u origin version_bump_${{ github.event.inputs.version_number }}
|
||||
|
||||
- name: Install xmllint
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libxml2-utils
|
||||
|
||||
- name: Get current version
|
||||
id: current-version
|
||||
run: |
|
||||
CURRENT_VERSION=$(xmllint --xpath '
|
||||
string(/manifest/@*[local-name()="versionName"
|
||||
and namespace-uri()="http://schemas.android.com/apk/res/android"])
|
||||
' src/App/Platforms/Android/AndroidManifest.xml)
|
||||
echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Verify input version
|
||||
if: ${{ inputs.version_number_override != '' }}
|
||||
env:
|
||||
CURRENT_VERSION: ${{ steps.current-version.outputs.version }}
|
||||
NEW_VERSION: ${{ inputs.version_number_override }}
|
||||
run: |
|
||||
# Error if version has not changed.
|
||||
if [[ "$NEW_VERSION" == "$CURRENT_VERSION" ]]; then
|
||||
echo "Version has not changed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if version is newer.
|
||||
printf '%s\n' "${CURRENT_VERSION}" "${NEW_VERSION}" | sort -C -V
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Version check successful."
|
||||
else
|
||||
echo "Version check failed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Calculate next release version
|
||||
if: ${{ inputs.version_number_override == '' }}
|
||||
id: calculate-next-version
|
||||
uses: bitwarden/gh-actions/version-next@main
|
||||
- name: Checkout Version Branch
|
||||
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
|
||||
with:
|
||||
version: ${{ steps.current-version.outputs.version }}
|
||||
ref: version_bump_${{ github.event.inputs.version_number }}
|
||||
|
||||
- name: Bump Version - Android XML - Version Override
|
||||
if: ${{ inputs.version_number_override != '' }}
|
||||
id: bump-version-override
|
||||
uses: bitwarden/gh-actions/version-bump@main
|
||||
- name: Bump Version - Android XML
|
||||
uses: bitwarden/gh-actions/version-bump@03ad9a873c39cdc95dd8d77dbbda67f84db43945
|
||||
with:
|
||||
file_path: "src/App/Platforms/Android/AndroidManifest.xml"
|
||||
version: ${{ inputs.version_number_override }}
|
||||
version: ${{ github.event.inputs.version_number }}
|
||||
file_path: "./src/Android/Properties/AndroidManifest.xml"
|
||||
|
||||
- name: Bump Version - Android XML - Automatic Calculation
|
||||
if: ${{ inputs.version_number_override == '' }}
|
||||
id: bump-version-automatic
|
||||
uses: bitwarden/gh-actions/version-bump@main
|
||||
- name: Bump Version - iOS.Autofill
|
||||
uses: bitwarden/gh-actions/version-bump@03ad9a873c39cdc95dd8d77dbbda67f84db43945
|
||||
with:
|
||||
file_path: "src/App/Platforms/Android/AndroidManifest.xml"
|
||||
version: ${{ steps.calculate-next-version.outputs.version }}
|
||||
version: ${{ github.event.inputs.version_number }}
|
||||
file_path: "./src/iOS.Autofill/Info.plist"
|
||||
|
||||
- name: Bump Version - iOS.Autofill - Version Override
|
||||
if: ${{ inputs.version_number_override != '' }}
|
||||
uses: bitwarden/gh-actions/version-bump@main
|
||||
- name: Bump Version - iOS.Extension
|
||||
uses: bitwarden/gh-actions/version-bump@03ad9a873c39cdc95dd8d77dbbda67f84db43945
|
||||
with:
|
||||
file_path: "src/iOS.Autofill/Info.plist"
|
||||
version: ${{ inputs.version_number_override }}
|
||||
version: ${{ github.event.inputs.version_number }}
|
||||
file_path: "./src/iOS.Extension/Info.plist"
|
||||
|
||||
- name: Bump Version - iOS.Autofill - Automatic Calculation
|
||||
if: ${{ inputs.version_number_override == '' }}
|
||||
uses: bitwarden/gh-actions/version-bump@main
|
||||
- name: Bump Version - iOS.ShareExtension
|
||||
uses: bitwarden/gh-actions/version-bump@03ad9a873c39cdc95dd8d77dbbda67f84db43945
|
||||
with:
|
||||
file_path: "src/iOS.Autofill/Info.plist"
|
||||
version: ${{ steps.calculate-next-version.outputs.version }}
|
||||
version: ${{ github.event.inputs.version_number }}
|
||||
file_path: "./src/iOS.ShareExtension/Info.plist"
|
||||
|
||||
- name: Bump Version - iOS.Extension - Version Override
|
||||
if: ${{ inputs.version_number_override != '' }}
|
||||
uses: bitwarden/gh-actions/version-bump@main
|
||||
- name: Bump Version - iOS
|
||||
uses: bitwarden/gh-actions/version-bump@03ad9a873c39cdc95dd8d77dbbda67f84db43945
|
||||
with:
|
||||
file_path: "src/iOS.Extension/Info.plist"
|
||||
version: ${{ inputs.version_number_override }}
|
||||
|
||||
- name: Bump Version - iOS.Extension - Automatic Calculation
|
||||
if: ${{ inputs.version_number_override == '' }}
|
||||
uses: bitwarden/gh-actions/version-bump@main
|
||||
with:
|
||||
file_path: "src/iOS.Extension/Info.plist"
|
||||
version: ${{ steps.calculate-next-version.outputs.version }}
|
||||
|
||||
- name: Bump Version - iOS.ShareExtension - Version Override
|
||||
if: ${{ inputs.version_number_override != '' }}
|
||||
uses: bitwarden/gh-actions/version-bump@main
|
||||
with:
|
||||
file_path: "src/iOS.ShareExtension/Info.plist"
|
||||
version: ${{ inputs.version_number_override }}
|
||||
|
||||
- name: Bump Version - iOS.ShareExtension - Automatic Calculation
|
||||
if: ${{ inputs.version_number_override == '' }}
|
||||
uses: bitwarden/gh-actions/version-bump@main
|
||||
with:
|
||||
file_path: "src/iOS.ShareExtension/Info.plist"
|
||||
version: ${{ steps.calculate-next-version.outputs.version }}
|
||||
|
||||
- name: Bump Version - iOS - Version Override
|
||||
if: ${{ inputs.version_number_override != '' }}
|
||||
uses: bitwarden/gh-actions/version-bump@main
|
||||
with:
|
||||
file_path: "src/App/Platforms/iOS/Info.plist"
|
||||
version: ${{ inputs.version_number_override }}
|
||||
|
||||
- name: Bump Version - iOS - Automatic Calculation
|
||||
if: ${{ inputs.version_number_override == '' }}
|
||||
uses: bitwarden/gh-actions/version-bump@main
|
||||
with:
|
||||
file_path: "src/App/Platforms/iOS/Info.plist"
|
||||
version: ${{ steps.calculate-next-version.outputs.version }}
|
||||
|
||||
- name: Set Job output
|
||||
id: set-final-version-output
|
||||
run: |
|
||||
if [[ "${{ steps.bump-version-override.outcome }}" == "success" ]]; then
|
||||
echo "version=${{ inputs.version_number_override }}" >> $GITHUB_OUTPUT
|
||||
elif [[ "${{ steps.bump-version-automatic.outcome }}" == "success" ]]; then
|
||||
echo "version=${{ steps.calculate-next-version.outputs.version }}" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Check if version changed
|
||||
id: version-changed
|
||||
run: |
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
echo "changes_to_commit=TRUE" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "changes_to_commit=FALSE" >> $GITHUB_OUTPUT
|
||||
echo "No changes to commit!";
|
||||
fi
|
||||
version: ${{ github.event.inputs.version_number }}
|
||||
file_path: "./src/iOS/Info.plist"
|
||||
|
||||
- name: Commit files
|
||||
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
|
||||
run: git commit -m "Bumped version to ${{ steps.set-final-version-output.outputs.version }}" -a
|
||||
run: |
|
||||
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git config --local user.name "github-actions[bot]"
|
||||
git commit -m "Bumped version to ${{ github.event.inputs.version_number }}" -a
|
||||
|
||||
- name: Push changes
|
||||
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
|
||||
env:
|
||||
PR_BRANCH: ${{ steps.create-branch.outputs.name }}
|
||||
run: git push -u origin $PR_BRANCH
|
||||
run: git push -u origin version_bump_${{ github.event.inputs.version_number }}
|
||||
|
||||
- name: Create Version PR
|
||||
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
|
||||
id: create-pr
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.retrieve-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }}
|
||||
PR_BRANCH: ${{ steps.create-branch.outputs.name }}
|
||||
TITLE: "Bump version to ${{ steps.set-final-version-output.outputs.version }}"
|
||||
PR_BRANCH: "version_bump_${{ github.event.inputs.version_number }}"
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
BASE_BRANCH: master
|
||||
TITLE: "Bump version to ${{ github.event.inputs.version_number }}"
|
||||
run: |
|
||||
PR_URL=$(gh pr create --title "$TITLE" \
|
||||
--base "main" \
|
||||
gh pr create --title "$TITLE" \
|
||||
--base "$BASE" \
|
||||
--head "$PR_BRANCH" \
|
||||
--label "version update" \
|
||||
--label "automated pr" \
|
||||
@@ -251,66 +86,4 @@ jobs:
|
||||
- [X] Other
|
||||
|
||||
## Objective
|
||||
Automated version bump to ${{ steps.set-final-version-output.outputs.version }}")
|
||||
echo "pr_number=${PR_URL##*/}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Approve PR
|
||||
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PR_NUMBER: ${{ steps.create-pr.outputs.pr_number }}
|
||||
run: gh pr review $PR_NUMBER --approve
|
||||
|
||||
- name: Merge PR
|
||||
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.retrieve-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }}
|
||||
PR_NUMBER: ${{ steps.create-pr.outputs.pr_number }}
|
||||
run: gh pr merge $PR_NUMBER --squash --auto --delete-branch
|
||||
|
||||
- name: Report upcoming release version to Slack
|
||||
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' && inputs.enable_slack_notification == true }}
|
||||
uses: bitwarden/gh-actions/report-upcoming-release-version@main
|
||||
with:
|
||||
version: ${{ steps.set-final-version-output.outputs.version }}
|
||||
project: ${{ github.repository }}
|
||||
AZURE_KV_CI_SERVICE_PRINCIPAL: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
||||
|
||||
cut_rc:
|
||||
name: Cut RC branch
|
||||
if: ${{ inputs.cut_rc_branch == true }}
|
||||
needs: bump_version
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout Branch
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
with:
|
||||
ref: main
|
||||
|
||||
- name: Install xmllint
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libxml2-utils
|
||||
|
||||
- name: Verify version has been updated
|
||||
env:
|
||||
NEW_VERSION: ${{ needs.bump_version.outputs.version }}
|
||||
run: |
|
||||
# Wait for version to change.
|
||||
while : ; do
|
||||
echo "Waiting for version to be updated..."
|
||||
git pull --force
|
||||
CURRENT_VERSION=$(xmllint --xpath '
|
||||
string(/manifest/@*[local-name()="versionName"
|
||||
and namespace-uri()="http://schemas.android.com/apk/res/android"])
|
||||
' src/App/Platforms/Android/AndroidManifest.xml)
|
||||
|
||||
# If the versions don't match we continue the loop, otherwise we break out of the loop.
|
||||
[[ "$NEW_VERSION" != "$CURRENT_VERSION" ]] || break
|
||||
sleep 10
|
||||
done
|
||||
|
||||
- name: Cut RC branch
|
||||
run: |
|
||||
git switch --quiet --create rc
|
||||
git push --quiet --set-upstream origin rc
|
||||
Automated version bump to ${{ github.event.inputs.version_number }}"
|
||||
|
||||
11
.github/workflows/workflow-linter.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
name: Workflow Linter
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/workflows/**
|
||||
|
||||
jobs:
|
||||
call-workflow:
|
||||
uses: bitwarden/gh-actions/.github/workflows/workflow-linter.yml@master
|
||||
125
.gitignore
vendored
@@ -30,8 +30,6 @@ Components/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
!src/lib/x86/
|
||||
!src/App/Platforms/Android/lib/x86/
|
||||
build/
|
||||
bld/
|
||||
[Bb]in/
|
||||
@@ -148,7 +146,6 @@ publish/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
!**/Xamarin.AndroidX.Credentials.1.0.0.nupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/packages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
@@ -211,124 +208,4 @@ FakesAssemblies/
|
||||
# Other
|
||||
project.lock.json
|
||||
.DS_Store
|
||||
src/App/Css
|
||||
tools
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/swift,objective-c
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=swift,objective-c
|
||||
|
||||
### Objective-C ###
|
||||
# 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
|
||||
|
||||
# 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/
|
||||
|
||||
# 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/
|
||||
|
||||
### Objective-C Patch ###
|
||||
|
||||
### Swift ###
|
||||
# Xcode
|
||||
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Playgrounds
|
||||
timeline.xctimeline
|
||||
playground.xcworkspace
|
||||
|
||||
# xcode / swift package manager - used by the MessagePack lib
|
||||
/.build
|
||||
/Packages
|
||||
/*.xcodeproj
|
||||
.swiftpm
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
# Code Injection
|
||||
# After new code Injection tools there's a generated folder /iOSInjectionProject
|
||||
# https://github.com/johnno1962/injectionforxcode
|
||||
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/swift,objective-c
|
||||
src/App/Css
|
||||
@@ -1,15 +0,0 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<MauiVersion>8.0.7</MauiVersion>
|
||||
<ReleaseCodesignProvision>Automatic:AppStore</ReleaseCodesignProvision>
|
||||
<ReleaseCodesignKey>iPhone Distribution</ReleaseCodesignKey>
|
||||
<IncludeBitwardeniOSExtensions>True</IncludeBitwardeniOSExtensions>
|
||||
<IncludeBitwardenWatchOSApp>False</IncludeBitwardenWatchOSApp>
|
||||
<Argon2IdLoadMtouchExtraArgs>-gcc_flags "-L$(ProjectDir)../../lib/ios -largon2 -force_load $(ProjectDir)../../lib/ios/libargon2.a"</Argon2IdLoadMtouchExtraArgs>
|
||||
<!-- Uncomment this when Unit Testing-->
|
||||
<!-- <CustomConstants>UT</CustomConstants> -->
|
||||
|
||||
<!-- Uncomment this when building FDROID-->
|
||||
<!-- <CustomConstants>FDROID</CustomConstants> -->
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
26
README.md
@@ -1,22 +1,18 @@
|
||||
[](https://github.com/bitwarden/mobile/actions/workflows/build.yml?query=branch:main)
|
||||
[](https://github.com/bitwarden/mobile/actions/workflows/build.yml?query=branch:master)
|
||||
[](https://crowdin.com/project/bitwarden-mobile)
|
||||
[](https://gitter.im/bitwarden/Lobby)
|
||||
|
||||
# Bitwarden Mobile Application
|
||||
|
||||
> [!TIP]
|
||||
> Looking for the new native apps? Head on over to [bitwarden/android](https://github.com/bitwarden/android) and [bitwarden/ios](https://github.com/bitwarden/ios)
|
||||
<a href="https://play.google.com/store/apps/details?id=com.x8bit.bitwarden" target="_blank"><img alt="Get it on Google Play" src="https://imgur.com/YQzmZi9.png" width="153" height="46"></a> <a href="https://mobileapp.bitwarden.com/fdroid/" target="_blank"><img alt="Get it on Google Play" src="https://i.imgur.com/HDicnzz.png" width="154" height="46"></a> <a href="https://itunes.apple.com/us/app/bitwarden-free-password-manager/id1137397744?mt=8" target="_blank"><img src="https://imgur.com/GdGqPMY.png" width="135" height="40"></a>
|
||||
|
||||
|
||||
<a href="https://play.google.com/store/apps/details?id=com.x8bit.bitwarden" target="_blank"><img alt="Get it on Google Play" src="https://imgur.com/YQzmZi9.png" width="153" height="46"></a> <a href="https://mobileapp.bitwarden.com/fdroid/" target="_blank"><img alt="Get it on F-Droid" src="https://i.imgur.com/HDicnzz.png" width="154" height="46"></a> <a href="https://itunes.apple.com/us/app/bitwarden-free-password-manager/id1137397744?mt=8" target="_blank"><img src="https://imgur.com/GdGqPMY.png" width="135" height="40"></a>
|
||||
|
||||
The Bitwarden mobile application is written in C# using .NET MAUI.
|
||||
The Bitwarden mobile application is written in C# with Xamarin Android, Xamarin iOS, and Xamarin Forms.
|
||||
|
||||
<img src="https://raw.githubusercontent.com/bitwarden/brand/master/screenshots/mobile-android-myvault.png" alt="" width="325" height="650" /> <img src="https://raw.githubusercontent.com/bitwarden/brand/master/screenshots/mobile-ios-myvault.png" alt="" width="300" height="650" />
|
||||
|
||||
# Build/Run
|
||||
|
||||
Please refer to the [Legacy Contributing Documentation](https://github.com/bitwarden/mobile/tree/main/docs/) for build instructions, recommended tooling, code style tips, and lots of other great information to get you started.
|
||||
Please refer to the [Mobile section](https://contributing.bitwarden.com/clients/mobile) of the [Contributing Documentation](https://contributing.bitwarden.com/) for build instructions, recommended tooling, code style tips, and lots of other great information to get you started.
|
||||
|
||||
# We're Hiring!
|
||||
|
||||
@@ -24,6 +20,18 @@ Interested in contributing in a big way? Consider joining our team! We're hiring
|
||||
|
||||
# Contribute
|
||||
|
||||
Code contributions are welcome! Please commit any pull requests against the `main` branch. Learn more about how to contribute by reading the [Contributing Guidelines](https://contributing.bitwarden.com/contributing/). Check out the [Contributing Documentation](https://contributing.bitwarden.com/) for how to get started with your first contribution.
|
||||
Code contributions are welcome! Please commit any pull requests against the `master` branch. Learn more about how to contribute by reading the [Contributing Guidelines](https://contributing.bitwarden.com/contributing/). Check out the [Contributing Documentation](https://contributing.bitwarden.com/) for how to get started with your first contribution.
|
||||
|
||||
Security audits and feedback are welcome. Please open an issue or email us privately if the report is sensitive in nature. You can read our security policy in the [`SECURITY.md`](SECURITY.md) file.
|
||||
|
||||
### Dotnet-format
|
||||
|
||||
We recently migrated to using dotnet-format as code formatter. All previous branches will need to updated to avoid large merge conflicts using the following steps:
|
||||
|
||||
1. Check out your local Branch
|
||||
2. Run `git merge e0efcfbe45b2a27c73e9593bfd7a71fad2aa7a35`
|
||||
3. Resolve any merge conflicts, commit.
|
||||
4. Run `dotnet tool run dotnet-format`
|
||||
5. Commit
|
||||
6. Run `git merge -Xours 04539af2a66668b6e85476d5cf318c9150ec4357`
|
||||
7. Push
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
<svg width="108" height="108" viewBox="0 0 108 108" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_41_29)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M70.214 34C70.7096 34 71.1457 34.1883 71.5124 34.555C71.8791 34.9217 72.0674 35.3479 72.0971 35.8534V50.4336H71.0669V51.9453H72.0971V58.0938C72.0971 59.749 71.7701 61.3942 71.1258 63.0295C70.4816 64.6549 69.6788 66.1019 68.7274 67.3706C67.766 68.6293 66.6262 69.8582 65.308 71.0575C63.98 72.2567 62.7609 73.2478 61.6409 74.0407C60.521 74.8237 59.3515 75.5769 58.1324 76.2806C56.9134 76.9843 56.0511 77.4699 55.5357 77.7177C55.0303 77.9655 54.614 78.1538 54.3068 78.2926C54.0788 78.4115 53.8211 78.471 53.5535 78.471C53.2859 78.471 53.0282 78.4115 52.8003 78.2926C52.5297 78.1791 52.1822 78.0118 51.7511 77.8042C51.6927 77.7761 51.6328 77.7473 51.5713 77.7177C51.0559 77.46 50.1937 76.9843 48.9746 76.2806C47.7555 75.5769 46.586 74.8336 45.4661 74.0407C44.3461 73.2478 43.1172 72.2567 41.799 71.0575C40.4709 69.8682 39.3311 68.6392 38.3797 67.3706C37.4183 66.1119 36.6155 64.6648 35.9713 63.0295C35.3271 61.4041 35 59.749 35 58.0938V35.8534C35 35.3479 35.1883 34.9217 35.555 34.555C35.9217 34.1883 36.3479 34 36.8534 34H70.214ZM67.211 57.5H70.1118V59H67.177C66.4282 66.7468 53.5337 73.2875 53.5337 73.2875V38.7573H67.211V50.4336H70.1118V51.9219H67.211V53.8027H69.895V55.291H67.211V57.5Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M58 46C56.3431 46 55 47.3431 55 49V60C55 61.6569 56.3431 63 58 63H107C108.657 63 110 61.6569 110 60V49C110 47.3431 108.657 46 107 46H58ZM59.7817 50.4336H57.1157V59H60.3208C60.9692 59 61.5278 58.9023 61.9966 58.707C62.4692 58.5078 62.8325 58.2227 63.0864 57.8516C63.3403 57.4805 63.4673 57.0352 63.4673 56.5156C63.4673 56.0664 63.397 55.707 63.2563 55.4375C63.1196 55.1641 62.936 54.957 62.7056 54.8164C62.4751 54.6719 62.2173 54.5703 61.9321 54.5117V54.4531C62.2134 54.4023 62.4517 54.293 62.647 54.125C62.8423 53.957 62.9907 53.7422 63.0923 53.4805C63.1978 53.2188 63.2505 52.9258 63.2505 52.6016C63.2505 51.7969 62.9575 51.2344 62.3716 50.9141C61.7856 50.5938 60.9224 50.4336 59.7817 50.4336ZM59.9868 53.8262H58.9321V51.9219H59.8872C60.4067 51.9219 60.7856 51.9941 61.0239 52.1387C61.2661 52.2793 61.3872 52.5137 61.3872 52.8418C61.3872 53.166 61.2856 53.4121 61.0825 53.5801C60.8794 53.7441 60.5142 53.8262 59.9868 53.8262ZM58.9321 57.5V55.2676H60.0571C60.4438 55.2676 60.7466 55.3125 60.9653 55.4023C61.188 55.4922 61.3462 55.6172 61.4399 55.7773C61.5337 55.9375 61.5806 56.123 61.5806 56.334C61.5806 56.6895 61.4731 56.9727 61.2583 57.1836C61.0435 57.3945 60.6626 57.5 60.1157 57.5H58.9321ZM65.1782 59H70.1118V57.5H66.9946V55.291H69.895V53.8027H66.9946V51.9219H70.1118V50.4336H65.1782V59ZM73.3931 59H75.2095V51.9453H77.5356V50.4336H71.0669V51.9453H73.3931V59ZM83.4771 56.9609L84.0981 59H86.0552L83.02 50.3984H80.7993L77.7759 59H79.7329L80.354 56.9609H83.4771ZM82.4224 53.4453L83.0435 55.4375H80.811L81.4263 53.4453C81.4536 53.3555 81.4985 53.2051 81.561 52.9941C81.6235 52.7832 81.688 52.5605 81.7544 52.3262C81.8247 52.0879 81.8794 51.8887 81.9185 51.7285C81.9575 51.8887 82.0083 52.0781 82.0708 52.2969C82.1372 52.5117 82.2017 52.7246 82.2642 52.9355C82.3306 53.1426 82.3833 53.3125 82.4224 53.4453Z" fill="#6795E8"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_41_29">
|
||||
<rect width="108" height="108" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.3 KiB |
@@ -1,17 +0,0 @@
|
||||
<svg width="108" height="108" viewBox="0 0 108 108" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_36_10)">
|
||||
<path d="M71.5717 34.6343L113 76.0625L89.709 119.077L40.6492 70.0168L53.5336 77.4501L70.8779 60.1057L71.5717 34.6343Z" fill="url(#paint0_linear_36_10)"/>
|
||||
<path d="M71.5124 34.555C71.1457 34.1883 70.7096 34 70.214 34H36.8534C36.3479 34 35.9217 34.1883 35.555 34.555C35.1883 34.9217 35 35.3479 35 35.8534V58.0938C35 59.749 35.3271 61.4041 35.9713 63.0295C36.6155 64.6648 37.4183 66.1119 38.3797 67.3706C39.3311 68.6392 40.4709 69.8682 41.799 71.0575C43.1172 72.2567 44.3461 73.2478 45.4661 74.0407C46.586 74.8336 47.7555 75.5769 48.9746 76.2806C50.1937 76.9843 51.0559 77.46 51.5713 77.7177C52.0867 77.9655 52.493 78.1637 52.8003 78.2926C53.0282 78.4115 53.2859 78.471 53.5535 78.471C53.8211 78.471 54.0788 78.4115 54.3068 78.2926C54.614 78.1538 55.0303 77.9655 55.5357 77.7177C56.0511 77.4699 56.9134 76.9843 58.1324 76.2806C59.3515 75.5769 60.521 74.8237 61.6409 74.0407C62.7609 73.2478 63.98 72.2567 65.308 71.0575C66.6262 69.8582 67.766 68.6293 68.7274 67.3706C69.6788 66.1019 70.4816 64.6549 71.1258 63.0295C71.7701 61.3942 72.0971 59.749 72.0971 58.0938V35.8534C72.0674 35.3479 71.8791 34.9217 71.5124 34.555ZM67.211 58.3019C67.211 66.3497 53.5337 73.2875 53.5337 73.2875V38.7573H67.211C67.211 38.7573 67.211 50.2542 67.211 58.3019Z" fill="white"/>
|
||||
<path d="M55 49C55 47.3431 56.3431 46 58 46H107C108.657 46 110 47.3431 110 49V60C110 61.6569 108.657 63 107 63H58C56.3431 63 55 61.6569 55 60V49Z" fill="#6795E8"/>
|
||||
<path d="M57.116 50.4336H59.782C60.9226 50.4336 61.7859 50.5938 62.3718 50.9141C62.9578 51.2344 63.2507 51.7969 63.2507 52.6016C63.2507 52.9258 63.198 53.2188 63.0925 53.4805C62.991 53.7422 62.8425 53.957 62.6472 54.125C62.4519 54.293 62.2136 54.4023 61.9324 54.4531V54.5117C62.2175 54.5703 62.4753 54.6719 62.7058 54.8164C62.9363 54.957 63.1199 55.1641 63.2566 55.4375C63.3972 55.707 63.4675 56.0664 63.4675 56.5156C63.4675 57.0352 63.3406 57.4805 63.0867 57.8516C62.8328 58.2227 62.4695 58.5078 61.9968 58.707C61.5281 58.9023 60.9695 59 60.321 59H57.116V50.4336ZM58.9324 53.8262H59.9871C60.5144 53.8262 60.8796 53.7441 61.0828 53.5801C61.2859 53.4121 61.3875 53.166 61.3875 52.8418C61.3875 52.5137 61.2664 52.2793 61.0242 52.1387C60.7859 51.9941 60.407 51.9219 59.8875 51.9219H58.9324V53.8262ZM58.9324 55.2676V57.5H60.116C60.6628 57.5 61.0437 57.3945 61.2585 57.1836C61.4734 56.9727 61.5808 56.6895 61.5808 56.334C61.5808 56.123 61.5339 55.9375 61.4402 55.7773C61.3464 55.6172 61.1882 55.4922 60.9656 55.4023C60.7468 55.3125 60.4441 55.2676 60.0574 55.2676H58.9324ZM70.1121 59H65.1785V50.4336H70.1121V51.9219H66.9949V53.8027H69.8953V55.291H66.9949V57.5H70.1121V59ZM75.2097 59H73.3933V51.9453H71.0671V50.4336H77.5359V51.9453H75.2097V59ZM84.0984 59L83.4773 56.9609H80.3542L79.7332 59H77.7761L80.7996 50.3984H83.0203L86.0554 59H84.0984ZM83.0437 55.4375L82.4226 53.4453C82.3835 53.3125 82.3308 53.1426 82.2644 52.9355C82.2019 52.7246 82.1375 52.5117 82.071 52.2969C82.0085 52.0781 81.9578 51.8887 81.9187 51.7285C81.8796 51.8887 81.825 52.0879 81.7546 52.3262C81.6882 52.5605 81.6238 52.7832 81.5613 52.9941C81.4988 53.2051 81.4539 53.3555 81.4265 53.4453L80.8113 55.4375H83.0437Z" fill="#212529"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_36_10" x1="37.8512" y1="38.8122" x2="89.011" y2="89.972" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-opacity="0.247059"/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_36_10">
|
||||
<rect width="108" height="108" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.5 KiB |
@@ -1,11 +0,0 @@
|
||||
<svg width="108" height="108" viewBox="0 0 108 108" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_41_21)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M70.214 34C70.7096 34 71.1457 34.1883 71.5124 34.555C71.8791 34.9217 72.0674 35.3479 72.0971 35.8534V50.4336H71.647L72.0971 51.7604V58.0938C72.0971 59.749 71.7701 61.3942 71.1258 63.0295C70.4816 64.6549 69.6788 66.1019 68.7274 67.3706C67.766 68.6293 66.6262 69.8582 65.308 71.0575C63.98 72.2567 62.7609 73.2478 61.6409 74.0407C60.521 74.8237 59.3515 75.5769 58.1324 76.2806C56.9134 76.9843 56.0511 77.4699 55.5357 77.7177C55.0303 77.9655 54.614 78.1538 54.3068 78.2926C54.0788 78.4115 53.8211 78.471 53.5535 78.471C53.2859 78.471 53.0282 78.4115 52.8003 78.2926C52.5297 78.1791 52.1822 78.0118 51.7511 77.8042C51.6927 77.7761 51.6328 77.7473 51.5713 77.7177C51.0559 77.46 50.1937 76.9843 48.9746 76.2806C47.7555 75.5769 46.586 74.8336 45.4661 74.0407C44.3461 73.2478 43.1172 72.2567 41.799 71.0575C40.4709 69.8682 39.3311 68.6392 38.3797 67.3706C37.4183 66.1119 36.6155 64.6648 35.9713 63.0295C35.3271 61.4041 35 59.749 35 58.0938V35.8534C35 35.3479 35.1883 34.9217 35.555 34.555C35.9217 34.1883 36.3479 34 36.8534 34H70.214ZM67.177 59C66.4282 66.7468 53.5337 73.2875 53.5337 73.2875V38.7573H67.211V50.4336H70.9321V51.9219H67.8149V53.8027H70.7153V55.291H67.8149V57.5H70.9321V59H67.177Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M58 46C56.3431 46 55 47.3431 55 49V60C55 61.6569 56.3431 63 58 63H107C108.657 63 110 61.6569 110 60V49C110 47.3431 108.657 46 107 46H58ZM63.6665 57.0547C64.0376 56.4062 64.2231 55.5996 64.2231 54.6348C64.2231 53.7168 64.0415 52.9473 63.6782 52.3262C63.3149 51.7012 62.8032 51.2305 62.1431 50.9141C61.4829 50.5938 60.7036 50.4336 59.8052 50.4336H57.1157V59H59.5415C60.5259 59 61.3677 58.8379 62.0669 58.5137C62.7661 58.1855 63.2993 57.6992 63.6665 57.0547ZM62.0552 53.123C62.2427 53.5293 62.3364 54.0488 62.3364 54.6816C62.3364 55.6152 62.1196 56.3184 61.686 56.791C61.2563 57.2637 60.5981 57.5 59.7114 57.5H58.9321V51.9219H59.8989C60.4302 51.9219 60.8755 52.0195 61.2349 52.2148C61.5981 52.4102 61.8716 52.7129 62.0552 53.123ZM65.9985 59H70.9321V57.5H67.8149V55.291H70.7153V53.8027H67.8149V51.9219H70.9321V50.4336H65.9985V59ZM76.5337 59L79.4458 50.4336H77.6118L75.9888 55.5312C75.9614 55.6211 75.9165 55.7852 75.854 56.0234C75.7954 56.2578 75.7349 56.5059 75.6724 56.7676C75.6138 57.0293 75.5728 57.2461 75.5493 57.418C75.5259 57.2461 75.481 57.0293 75.4146 56.7676C75.3521 56.502 75.2896 56.252 75.2271 56.0176C75.1646 55.7793 75.1196 55.6172 75.0923 55.5312L73.481 50.4336H71.647L74.5532 59H76.5337Z" fill="#2DA49D"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_41_21">
|
||||
<rect width="108" height="108" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.7 KiB |
@@ -1,17 +0,0 @@
|
||||
<svg width="108" height="108" viewBox="0 0 108 108" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_36_2)">
|
||||
<path d="M71.5717 34.6343L113 76.0625L89.709 119.077L40.6492 70.0168L53.5336 77.4501L70.8779 60.1057L71.5717 34.6343Z" fill="url(#paint0_linear_36_2)"/>
|
||||
<path d="M71.5124 34.555C71.1457 34.1883 70.7096 34 70.214 34H36.8534C36.3479 34 35.9217 34.1883 35.555 34.555C35.1883 34.9217 35 35.3479 35 35.8534V58.0938C35 59.749 35.3271 61.4041 35.9713 63.0295C36.6155 64.6648 37.4183 66.1119 38.3797 67.3706C39.3311 68.6392 40.4709 69.8682 41.799 71.0575C43.1172 72.2567 44.3461 73.2478 45.4661 74.0407C46.586 74.8336 47.7555 75.5769 48.9746 76.2806C50.1937 76.9843 51.0559 77.46 51.5713 77.7177C52.0867 77.9655 52.493 78.1637 52.8003 78.2926C53.0282 78.4115 53.2859 78.471 53.5535 78.471C53.8211 78.471 54.0788 78.4115 54.3068 78.2926C54.614 78.1538 55.0303 77.9655 55.5357 77.7177C56.0511 77.4699 56.9134 76.9843 58.1324 76.2806C59.3515 75.5769 60.521 74.8237 61.6409 74.0407C62.7609 73.2478 63.98 72.2567 65.308 71.0575C66.6262 69.8582 67.766 68.6293 68.7274 67.3706C69.6788 66.1019 70.4816 64.6549 71.1258 63.0295C71.7701 61.3942 72.0971 59.749 72.0971 58.0938V35.8534C72.0674 35.3479 71.8791 34.9217 71.5124 34.555ZM67.211 58.3019C67.211 66.3497 53.5337 73.2875 53.5337 73.2875V38.7573H67.211C67.211 38.7573 67.211 50.2542 67.211 58.3019Z" fill="white"/>
|
||||
<path d="M55 49C55 47.3431 56.3431 46 58 46H107C108.657 46 110 47.3431 110 49V60C110 61.6569 108.657 63 107 63H58C56.3431 63 55 61.6569 55 60V49Z" fill="#2DA49D"/>
|
||||
<path d="M64.2234 54.6348C64.2234 55.5996 64.0378 56.4062 63.6667 57.0547C63.2996 57.6992 62.7664 58.1855 62.0671 58.5137C61.3679 58.8379 60.5261 59 59.5417 59H57.116V50.4336H59.8054C60.7039 50.4336 61.4832 50.5938 62.1433 50.9141C62.8035 51.2305 63.3152 51.7012 63.6785 52.3262C64.0417 52.9473 64.2234 53.7168 64.2234 54.6348ZM62.3367 54.6816C62.3367 54.0488 62.2429 53.5293 62.0554 53.123C61.8718 52.7129 61.5984 52.4102 61.2351 52.2148C60.8757 52.0195 60.4304 51.9219 59.8992 51.9219H58.9324V57.5H59.7117C60.5984 57.5 61.2566 57.2637 61.6863 56.791C62.1199 56.3184 62.3367 55.6152 62.3367 54.6816ZM70.9324 59H65.9988V50.4336H70.9324V51.9219H67.8152V53.8027H70.7156V55.291H67.8152V57.5H70.9324V59ZM79.446 50.4336L76.5339 59H74.5535L71.6472 50.4336H73.4812L75.0925 55.5312C75.1199 55.6172 75.1648 55.7793 75.2273 56.0176C75.2898 56.252 75.3523 56.502 75.4148 56.7676C75.4812 57.0293 75.5261 57.2461 75.5496 57.418C75.573 57.2461 75.614 57.0293 75.6726 56.7676C75.7351 56.5059 75.7957 56.2578 75.8542 56.0234C75.9167 55.7852 75.9617 55.6211 75.989 55.5312L77.6121 50.4336H79.446Z" fill="#212529"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_36_2" x1="37.8512" y1="38.8122" x2="89.011" y2="89.972" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-opacity="0.247059"/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_36_2">
|
||||
<rect width="108" height="108" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.9 KiB |
@@ -1,11 +0,0 @@
|
||||
<svg width="108" height="108" viewBox="0 0 108 108" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_41_13)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M70.214 34C70.7096 34 71.1457 34.1883 71.5124 34.555C71.8791 34.9217 72.0674 35.3479 72.0971 35.8534V52.9823L70.8325 49.3984H68.6118L67.211 53.3838V38.7573H53.5337V73.2875C53.5337 73.2875 67.211 66.3497 67.211 58.3019V58H67.5454L68.1665 55.9609H71.2896L71.9106 58H72.0971V58.0938C72.0971 59.749 71.7701 61.3942 71.1258 63.0295C70.4816 64.6549 69.6788 66.1019 68.7274 67.3706C67.766 68.6293 66.6262 69.8582 65.308 71.0575C63.98 72.2567 62.7609 73.2478 61.6409 74.0407C60.521 74.8237 59.3515 75.5769 58.1324 76.2806C56.9134 76.9843 56.0511 77.4699 55.5357 77.7177C55.0303 77.9655 54.614 78.1538 54.3068 78.2926C54.0788 78.4115 53.8211 78.471 53.5535 78.471C53.2859 78.471 53.0282 78.4115 52.8003 78.2926C52.5297 78.1791 52.1822 78.0118 51.7511 77.8042C51.6927 77.7761 51.6328 77.7473 51.5713 77.7177C51.0559 77.46 50.1937 76.9843 48.9746 76.2806C47.7555 75.5769 46.586 74.8336 45.4661 74.0407C44.3461 73.2478 43.1172 72.2567 41.799 71.0575C40.4709 69.8682 39.3311 68.6392 38.3797 67.3706C37.4183 66.1119 36.6155 64.6648 35.9713 63.0295C35.3271 61.4041 35 59.749 35 58.0938V35.8534C35 35.3479 35.1883 34.9217 35.555 34.555C35.9217 34.1883 36.3479 34 36.8534 34H70.214ZM70.2349 52.4453L70.856 54.4375H68.6235L69.2388 52.4453C69.2661 52.3555 69.311 52.2051 69.3735 51.9941C69.436 51.7832 69.5005 51.5605 69.5669 51.3262C69.6372 51.0879 69.6919 50.8887 69.731 50.7285C69.77 50.8887 69.8208 51.0781 69.8833 51.2969C69.9497 51.5117 70.0142 51.7246 70.0767 51.9355C70.1431 52.1426 70.1958 52.3125 70.2349 52.4453Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M58 46C56.3431 46 55 47.3431 55 49V60C55 61.6569 56.3431 63 58 63H107C108.657 63 110 61.6569 110 60V49C110 47.3431 108.657 46 107 46H58ZM64.6626 55.457C64.8149 54.9258 64.8911 54.3418 64.8911 53.7051C64.8911 52.8145 64.7446 52.0391 64.4517 51.3789C64.1626 50.7188 63.7173 50.207 63.1157 49.8438C62.5181 49.4805 61.7544 49.2988 60.8247 49.2988C59.8911 49.2988 59.1216 49.4805 58.5161 49.8438C57.9106 50.207 57.4614 50.7188 57.1685 51.3789C56.8794 52.0352 56.7349 52.8066 56.7349 53.6934C56.7349 54.3574 56.8169 54.9609 56.981 55.5039C57.145 56.0469 57.3931 56.5137 57.7251 56.9043C58.061 57.2949 58.4849 57.5957 58.9966 57.8066C59.5083 58.0137 60.1138 58.1172 60.813 58.1172H60.8774H60.9478L62.5181 60.0391H64.8442L62.7817 57.7363C63.2622 57.5176 63.6587 57.2148 63.9712 56.8281C64.2837 56.4414 64.5142 55.9844 64.6626 55.457ZM58.8618 55.252C58.7134 54.8184 58.6392 54.3027 58.6392 53.7051C58.6392 53.1035 58.7134 52.5879 58.8618 52.1582C59.0142 51.7246 59.2505 51.3926 59.5708 51.1621C59.895 50.9277 60.313 50.8105 60.8247 50.8105C61.5942 50.8105 62.147 51.0684 62.4829 51.584C62.8188 52.0996 62.9868 52.8066 62.9868 53.7051C62.9868 54.3027 62.9126 54.8184 62.7642 55.252C62.6196 55.6816 62.3872 56.0137 62.0669 56.248C61.7466 56.4785 61.3286 56.5938 60.813 56.5938C60.3052 56.5938 59.8911 56.4785 59.5708 56.248C59.2505 56.0137 59.0142 55.6816 58.8618 55.252ZM71.2896 55.9609L71.9106 58H73.8677L70.8325 49.3984H68.6118L65.5884 58H67.5454L68.1665 55.9609H71.2896ZM70.2349 52.4453L70.856 54.4375H68.6235L69.2388 52.4453C69.2661 52.3555 69.311 52.2051 69.3735 51.9941C69.436 51.7832 69.5005 51.5605 69.5669 51.3262C69.6372 51.0879 69.6919 50.8887 69.731 50.7285C69.77 50.8887 69.8208 51.0781 69.8833 51.2969C69.9497 51.5117 70.0142 51.7246 70.0767 51.9355C70.1431 52.1426 70.1958 52.3125 70.2349 52.4453Z" fill="#C32998"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_41_13">
|
||||
<rect width="108" height="108" rx="34" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.6 KiB |
@@ -1,17 +0,0 @@
|
||||
<svg width="108" height="108" viewBox="0 0 108 108" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_34_2)">
|
||||
<path d="M71.5717 34.6343L113 76.0625L89.709 119.077L40.6492 70.0168L53.5336 77.4501L70.8779 60.1057L71.5717 34.6343Z" fill="url(#paint0_linear_34_2)"/>
|
||||
<path d="M71.5124 34.555C71.1457 34.1883 70.7096 34 70.214 34H36.8534C36.3479 34 35.9217 34.1883 35.555 34.555C35.1883 34.9217 35 35.3479 35 35.8534V58.0938C35 59.749 35.3271 61.4041 35.9713 63.0295C36.6155 64.6648 37.4183 66.1119 38.3797 67.3706C39.3311 68.6392 40.4709 69.8682 41.799 71.0575C43.1172 72.2567 44.3461 73.2478 45.4661 74.0407C46.586 74.8336 47.7555 75.5769 48.9746 76.2806C50.1937 76.9843 51.0559 77.46 51.5713 77.7177C52.0867 77.9655 52.493 78.1637 52.8003 78.2926C53.0282 78.4115 53.2859 78.471 53.5535 78.471C53.8211 78.471 54.0788 78.4115 54.3068 78.2926C54.614 78.1538 55.0303 77.9655 55.5357 77.7177C56.0511 77.4699 56.9134 76.9843 58.1324 76.2806C59.3515 75.5769 60.521 74.8237 61.6409 74.0407C62.7609 73.2478 63.98 72.2567 65.308 71.0575C66.6262 69.8582 67.766 68.6293 68.7274 67.3706C69.6788 66.1019 70.4816 64.6549 71.1258 63.0295C71.7701 61.3942 72.0971 59.749 72.0971 58.0938V35.8534C72.0674 35.3479 71.8791 34.9217 71.5124 34.555ZM67.211 58.3019C67.211 66.3497 53.5337 73.2875 53.5337 73.2875V38.7573H67.211C67.211 38.7573 67.211 50.2542 67.211 58.3019Z" fill="white"/>
|
||||
<path d="M55 49C55 47.3431 56.3431 46 58 46H107C108.657 46 110 47.3431 110 49V60C110 61.6569 108.657 63 107 63H58C56.3431 63 55 61.6569 55 60V49Z" fill="#C32998"/>
|
||||
<path d="M64.8914 53.7051C64.8914 54.3418 64.8152 54.9258 64.6628 55.457C64.5144 55.9844 64.2839 56.4414 63.9714 56.8281C63.6589 57.2148 63.2625 57.5176 62.782 57.7363L64.8445 60.0391H62.5183L60.948 58.1172C60.9207 58.1172 60.8972 58.1172 60.8777 58.1172C60.8582 58.1172 60.8367 58.1172 60.8132 58.1172C60.114 58.1172 59.5085 58.0137 58.9968 57.8066C58.4851 57.5957 58.0613 57.2949 57.7253 56.9043C57.3933 56.5137 57.1453 56.0469 56.9812 55.5039C56.8171 54.9609 56.7351 54.3574 56.7351 53.6934C56.7351 52.8066 56.8796 52.0352 57.1687 51.3789C57.4617 50.7188 57.9109 50.207 58.5164 49.8438C59.1218 49.4805 59.8914 49.2988 60.825 49.2988C61.7546 49.2988 62.5183 49.4805 63.116 49.8438C63.7175 50.207 64.1628 50.7188 64.4519 51.3789C64.7449 52.0391 64.8914 52.8145 64.8914 53.7051ZM58.6394 53.7051C58.6394 54.3027 58.7136 54.8184 58.8621 55.252C59.0144 55.6816 59.2507 56.0137 59.571 56.248C59.8914 56.4785 60.3054 56.5938 60.8132 56.5938C61.3289 56.5938 61.7468 56.4785 62.0671 56.248C62.3875 56.0137 62.6199 55.6816 62.7644 55.252C62.9128 54.8184 62.9871 54.3027 62.9871 53.7051C62.9871 52.8066 62.8191 52.0996 62.4832 51.584C62.1472 51.0684 61.5945 50.8105 60.825 50.8105C60.3132 50.8105 59.8953 50.9277 59.571 51.1621C59.2507 51.3926 59.0144 51.7246 58.8621 52.1582C58.7136 52.5879 58.6394 53.1035 58.6394 53.7051ZM71.9109 58L71.2898 55.9609H68.1667L67.5457 58H65.5886L68.6121 49.3984H70.8328L73.8679 58H71.9109ZM70.8562 54.4375L70.2351 52.4453C70.196 52.3125 70.1433 52.1426 70.0769 51.9355C70.0144 51.7246 69.95 51.5117 69.8835 51.2969C69.821 51.0781 69.7703 50.8887 69.7312 50.7285C69.6921 50.8887 69.6375 51.0879 69.5671 51.3262C69.5007 51.5605 69.4363 51.7832 69.3738 51.9941C69.3113 52.2051 69.2664 52.3555 69.239 52.4453L68.6238 54.4375H70.8562Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_34_2" x1="37.8512" y1="38.8122" x2="89.011" y2="89.972" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-opacity="0.247059"/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_34_2">
|
||||
<rect width="108" height="108" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 17 KiB |
@@ -1,136 +0,0 @@
|
||||
#! /bin/sh
|
||||
|
||||
function print_example() {
|
||||
echo "Example"
|
||||
echo " icons ios ~/AppIcon.pdf ~/Icons/"
|
||||
}
|
||||
|
||||
function print_usage() {
|
||||
echo "Usage"
|
||||
echo " icons <ios|watch|complication|macos> in-file.pdf (out-dir)"
|
||||
}
|
||||
|
||||
function command_exists() {
|
||||
if type "$1" >/dev/null 2>&1; then
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
if command_exists "sips" == 0 ; then
|
||||
echo "sips tool not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$1" = "--help" ] || [ "$1" = "-h" ] ; then
|
||||
print_usage
|
||||
exit 0
|
||||
fi
|
||||
|
||||
PLATFORM="$1"
|
||||
FILE="$2"
|
||||
if [ -z "$PLATFORM" ] || [ -z "$FILE" ] ; then
|
||||
echo "Error: missing arguments"
|
||||
echo ""
|
||||
print_usage
|
||||
echo ""
|
||||
print_example
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DIR="$3"
|
||||
if [ -z "$DIR" ] ; then
|
||||
DIR=$(dirname $FILE)
|
||||
fi
|
||||
|
||||
# Create directory if needed
|
||||
mkdir -p "$DIR"
|
||||
|
||||
if [[ "$PLATFORM" == *"ios"* ]] ; then # iOS
|
||||
sips -s format png -Z '180' "${FILE}" --out "${DIR}"/Icon-180.png
|
||||
sips -s format png -Z '29' "${FILE}" --out "${DIR}"/Icon-29.png
|
||||
sips -s format png -Z '58' "${FILE}" --out "${DIR}"/Icon-58.png
|
||||
sips -s format png -Z '120' "${FILE}" --out "${DIR}"/Icon-120.png
|
||||
sips -s format png -Z '87' "${FILE}" --out "${DIR}"/Icon-87.png
|
||||
sips -s format png -Z '40' "${FILE}" --out "${DIR}"/Icon-40.png
|
||||
sips -s format png -Z '80' "${FILE}" --out "${DIR}"/Icon-80.png
|
||||
sips -s format png -Z '76' "${FILE}" --out "${DIR}"/Icon-76.png
|
||||
sips -s format png -Z '152' "${FILE}" --out "${DIR}"/Icon-152.png
|
||||
sips -s format png -Z '167' "${FILE}" --out "${DIR}"/Icon-167.png
|
||||
sips -s format png -Z '60' "${FILE}" --out "${DIR}"/Icon-60.png
|
||||
sips -s format png -Z '20' "${FILE}" --out "${DIR}"/Icon-20.png
|
||||
sips -s format png -Z '1024' "${FILE}" --out "${DIR}"/Icon-1024.png
|
||||
|
||||
# https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_ref-Asset_Catalog_Format/AppIconType.html
|
||||
contents_json='{"images":[{"size":"20x20","idiom":"iphone","filename":"iPhoneNotification@2x.png","scale":"2x"},{"size":"20x20","idiom":"iphone","filename":"iPhoneNotification@3x.png","scale":"3x"},{"size":"29x29","idiom":"iphone","filename":"iPhoneSettings@2x.png","scale":"2x"},{"size":"29x29","idiom":"iphone","filename":"iPhoneSettings@3x.png","scale":"3x"},{"size":"40x40","idiom":"iphone","filename":"iPhoneSpotlight@2x.png","scale":"2x"},{"size":"40x40","idiom":"iphone","filename":"iPhoneSpotlight@3x.png","scale":"3x"},{"size":"60x60","idiom":"iphone","filename":"iPhone@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"iPhone@3x.png","scale":"3x"},{"size":"20x20","idiom":"ipad","filename":"iPadNotification.png","scale":"1x"},{"size":"20x20","idiom":"ipad","filename":"iPadNotification@2x.png","scale":"2x"},{"size":"29x29","idiom":"ipad","filename":"iPadSettings.png","scale":"1x"},{"size":"29x29","idiom":"ipad","filename":"iPadSettings@2x.png","scale":"2x"},{"size":"40x40","idiom":"ipad","filename":"iPadSpotlight.png","scale":"1x"},{"size":"40x40","idiom":"ipad","filename":"iPadSpotlight@2x.png","scale":"2x"},{"size":"76x76","idiom":"ipad","filename":"iPad.png","scale":"1x"},{"size":"76x76","idiom":"ipad","filename":"iPad@2x.png","scale":"2x"},{"size":"83.5x83.5","idiom":"ipad","filename":"iPadPro@2x.png","scale":"2x"},{"size":"1024x1024","idiom":"ios-marketing","filename":"AppStoreMarketing.png","scale":"1x"}],"info":{"version":1,"author":"xcode"}}'
|
||||
echo $contents_json > "${DIR}"/Contents.json
|
||||
fi
|
||||
|
||||
if [[ "$PLATFORM" == *"watch"* ]] ; then # Apple Watch
|
||||
sips -s format png -Z '48' "${FILE}" --out "${DIR}"/Watch38mmNotificationCenter.png
|
||||
sips -s format png -Z '55' "${FILE}" --out "${DIR}"/Watch42mmNotificationCenter.png
|
||||
sips -s format png -Z '66' "${FILE}" --out "${DIR}"/Watch66NotificationCenter.png
|
||||
sips -s format png -Z '58' "${FILE}" --out "${DIR}"/WatchCompanionSettings@2x.png
|
||||
sips -s format png -Z '87' "${FILE}" --out "${DIR}"/WatchCompanionSettings@3x.png
|
||||
sips -s format png -Z '80' "${FILE}" --out "${DIR}"/Watch38MM42MMHomeScreen.png
|
||||
sips -s format png -Z '88' "${FILE}" --out "${DIR}"/Watch40MMHomeScreen.png
|
||||
sips -s format png -Z '92' "${FILE}" --out "${DIR}"/Watch41MMHomeScreen.png
|
||||
sips -s format png -Z '100' "${FILE}" --out "${DIR}"/Watch44MMHomeScreen.png
|
||||
sips -s format png -Z '102' "${FILE}" --out "${DIR}"/Watch45MMHomeScreen.png
|
||||
sips -s format png -Z '108' "${FILE}" --out "${DIR}"/Watch49MMHomeScreen.png
|
||||
sips -s format png -Z '172' "${FILE}" --out "${DIR}"/Watch38MMShortLook.png
|
||||
sips -s format png -Z '196' "${FILE}" --out "${DIR}"/Watch40MM42MMShortLook.png
|
||||
sips -s format png -Z '216' "${FILE}" --out "${DIR}"/Watch44MMShortLook.png
|
||||
sips -s format png -Z '234' "${FILE}" --out "${DIR}"/Watch234ShortLook.png
|
||||
sips -s format png -Z '258' "${FILE}" --out "${DIR}"/Watch258ShortLook.png
|
||||
sips -s format png -Z '1024' "${FILE}" --out "${DIR}"/WatchAppStore.png
|
||||
|
||||
# https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_ref-Asset_Catalog_Format/AppIconType.html
|
||||
contents_json='{"images":[{"size":"24x24","idiom":"watch","scale":"2x","filename":"Watch38mmNotificationCenter.png","role":"notificationCenter","subtype":"38mm"},{"size":"27.5x27.5","idiom":"watch","scale":"2x","filename":"Watch42mmNotificationCenter.png","role":"notificationCenter","subtype":"42mm"},{"size":"29x29","idiom":"watch","filename":"WatchCompanionSettings@2x.png","role":"companionSettings","scale":"2x"},{"size":"29x29","idiom":"watch","filename":"WatchCompanionSettings@3x.png","role":"companionSettings","scale":"3x"},{"size":"40x40","idiom":"watch","filename":"Watch38MM42MMHomeScreen.png","scale":"2x","role":"appLauncher","subtype":"38mm"},{"size":"44x44","idiom":"watch","scale":"2x","filename":"Watch40MMHomeScreen.png","role":"appLauncher","subtype":"40mm"},{"size":"50x50","idiom":"watch","scale":"2x","filename":"Watch44MMHomeScreen.png","role":"appLauncher","subtype":"44mm"},{"size":"86x86","idiom":"watch","scale":"2x","filename":"Watch38MMShortLook.png","role":"quickLook","subtype":"38mm"},{"size":"98x98","idiom":"watch","scale":"2x","filename":"Watch40MM42MMShortLook.png","role":"quickLook","subtype":"42mm"},{"size":"108x108","idiom":"watch","scale":"2x","filename":"Watch44MMShortLook.png","role":"quickLook","subtype":"44mm"},{"idiom":"watch-marketing","filename":"WatchAppStore.png","size":"1024x1024","scale":"1x"}],"info":{"version":1,"author":"xcode"}}'
|
||||
echo $contents_json > "${DIR}"/Contents.json
|
||||
fi
|
||||
|
||||
if [[ "$PLATFORM" == *"complication"* ]] ; then # Apple Watch
|
||||
sips -s format png -Z '32' "${FILE}" --out "${DIR}"/Circular38mm2x.png
|
||||
sips -s format png -Z '36' "${FILE}" --out "${DIR}"/Circular40mm2x.png
|
||||
sips -s format png -Z '36' "${FILE}" --out "${DIR}"/Circular42mm2x.png
|
||||
sips -s format png -Z '40' "${FILE}" --out "${DIR}"/Circular44mm2x.png
|
||||
sips -s format png -Z '182' "${FILE}" --out "${DIR}"/ExtraLarge38mm2x.png
|
||||
sips -s format png -Z '203' "${FILE}" --out "${DIR}"/ExtraLarge40mm2x.png
|
||||
sips -s format png -Z '203' "${FILE}" --out "${DIR}"/ExtraLarge42mm2x.png
|
||||
sips -s format png -Z '224' "${FILE}" --out "${DIR}"/ExtraLarge44mm2x.png
|
||||
sips -s format png -Z '84' "${FILE}" --out "${DIR}"/GraphicBezel40mm2x.png
|
||||
sips -s format png -Z '84' "${FILE}" --out "${DIR}"/GraphicBezel42mm2x.png
|
||||
sips -s format png -Z '94' "${FILE}" --out "${DIR}"/GraphicBezel44mm2x.png
|
||||
sips -s format png -Z '84' "${FILE}" --out "${DIR}"/GraphicCircular40mm2x.png
|
||||
sips -s format png -Z '84' "${FILE}" --out "${DIR}"/GraphicCircular42mm2x.png
|
||||
sips -s format png -Z '94' "${FILE}" --out "${DIR}"/GraphicCircular44mm2x.png
|
||||
sips -s format png -Z '40' "${FILE}" --out "${DIR}"/GraphicCorner40mm2x.png
|
||||
sips -s format png -Z '40' "${FILE}" --out "${DIR}"/GraphicCorner42mm2x.png
|
||||
sips -s format png -Z '44' "${FILE}" --out "${DIR}"/GraphicCorner44mm2x.png
|
||||
sips -s format png -Z '52' "${FILE}" --out "${DIR}"/GraphicModular38mm2x.png
|
||||
sips -s format png -Z '58' "${FILE}" --out "${DIR}"/GraphicModular40mm2x.png
|
||||
sips -s format png -Z '58' "${FILE}" --out "${DIR}"/GraphicModular42mm2x.png
|
||||
sips -s format png -Z '64' "${FILE}" --out "${DIR}"/GraphicModular44mm2x.png
|
||||
sips -s format png -Z '40' "${FILE}" --out "${DIR}"/GraphicUtilitarian38mm2x.png
|
||||
sips -s format png -Z '44' "${FILE}" --out "${DIR}"/GraphicUtilitarian40mm2x.png
|
||||
sips -s format png -Z '44' "${FILE}" --out "${DIR}"/GraphicUtilitarian42mm2x.png
|
||||
sips -s format png -Z '50' "${FILE}" --out "${DIR}"/GraphicUtilitarian44mm2x.png
|
||||
sips -s format png -Z '206' "${FILE}" --out "${DIR}"/GraphicExtraLarge38mm2x.png
|
||||
sips -s format png -Z '264' "${FILE}" --out "${DIR}"/GraphicExtraLarge44mm2x.png
|
||||
echo "NOTE: Graphic Extra Large is not generated since that is not rectangular"
|
||||
fi
|
||||
|
||||
if [[ "$PLATFORM" == *"macos"* ]] ; then # macOS
|
||||
sips -s format png -Z '1024' "${FILE}" --out "${DIR}"/icon_512x512@2x.png
|
||||
sips -s format png -Z '512' "${FILE}" --out "${DIR}"/icon_512x512.png
|
||||
sips -s format png -Z '512' "${FILE}" --out "${DIR}"/icon_256x256@2x.png
|
||||
sips -s format png -Z '256' "${FILE}" --out "${DIR}"/icon_256x256.png
|
||||
sips -s format png -Z '256' "${FILE}" --out "${DIR}"/icon_128x128@2x.png
|
||||
sips -s format png -Z '128' "${FILE}" --out "${DIR}"/icon_128x128.png
|
||||
sips -s format png -Z '64' "${FILE}" --out "${DIR}"/icon_32x32@2x.png
|
||||
sips -s format png -Z '32' "${FILE}" --out "${DIR}"/icon_32x32.png
|
||||
sips -s format png -Z '32' "${FILE}" --out "${DIR}"/icon_16x16@2x.png
|
||||
sips -s format png -Z '16' "${FILE}" --out "${DIR}"/icon_16x16.png
|
||||
fi
|
||||
@@ -1,233 +1,471 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.8.34112.27
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.29009.5
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "App", "src\App\App.csproj", "{971FDF07-E288-4239-B47A-E9E7E912193B}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Android", "src\Android\Android.csproj", "{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "src\Core\Core.csproj", "{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "App", "src\App\App.csproj", "{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "iOS.Core", "src\iOS.Core\iOS.Core.csproj", "{E71F3053-056C-4381-9638-048ED73BDFF6}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core", "src\Core\Core.csproj", "{4B8A8C41-9820-4341-974C-41E65B7F4366}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Playground", "test\Playground\Playground.csproj", "{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D10CA4A9-F866-40E1-B658-F69051236C71}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{8904C536-C67D-420F-9971-51B26574C3AA}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "store", "store", "{92470CBD-9047-4C3C-8EA3-D972D6622D84}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "google", "google", "{2E399654-26A2-46F6-B9CA-1B496A3F370A}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{76690DFB-B7F4-4781-83E4-113FDC450AFE}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.editorconfig = .editorconfig
|
||||
.gitignore = .gitignore
|
||||
.github\workflows\build.yml = .github\workflows\build.yml
|
||||
CONTRIBUTING.md = CONTRIBUTING.md
|
||||
crowdin.yml = crowdin.yml
|
||||
README.md = README.md
|
||||
SECURITY.md = SECURITY.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Publisher", "store\google\Publisher\Publisher.csproj", "{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Core", "src\iOS.Core\iOS.Core.csproj", "{E71F3053-056C-4381-9638-048ED73BDFF6}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS", "src\iOS\iOS.csproj", "{599E0201-420A-4C3E-A7BA-5349F72E0B15}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Extension", "src\iOS.Extension\iOS.Extension.csproj", "{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "test\Common\Common.csproj", "{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Test", "test\Core.Test\Core.Test.csproj", "{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.ShareExtension", "src\iOS.ShareExtension\iOS.ShareExtension.csproj", "{F8C3F648-EA5A-4719-8005-85D1690B1655}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Autofill", "src\iOS.Autofill\iOS.Autofill.csproj", "{83449CC4-1F76-4CFE-92B1-D2E13A62506F}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{BB702EBD-3B79-4ECA-A2A6-1237B07F0AF0}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{B972BBFA-917F-4A10-B07E-B89CFEC6BBDC}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Test", "test\Core.Test\Core.Test.csproj", "{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "test\Common\Common.csproj", "{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Autofill", "src\iOS.Autofill\iOS.Autofill.csproj", "{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Debug|iPhoneSimulator = Debug|iPhoneSimulator
|
||||
Release|iPhoneSimulator = Release|iPhoneSimulator
|
||||
Debug|iPhone = Debug|iPhone
|
||||
Release|iPhone = Release|iPhone
|
||||
AppStore|iPhoneSimulator = AppStore|iPhoneSimulator
|
||||
AppStore|iPhone = AppStore|iPhone
|
||||
Ad-Hoc|iPhoneSimulator = Ad-Hoc|iPhoneSimulator
|
||||
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
|
||||
Ad-Hoc|iPhone = Ad-Hoc|iPhone
|
||||
Ad-Hoc|iPhoneSimulator = Ad-Hoc|iPhoneSimulator
|
||||
AppStore|Any CPU = AppStore|Any CPU
|
||||
AppStore|iPhone = AppStore|iPhone
|
||||
AppStore|iPhoneSimulator = AppStore|iPhoneSimulator
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|iPhone = Debug|iPhone
|
||||
Debug|iPhoneSimulator = Debug|iPhoneSimulator
|
||||
FDroid|Any CPU = FDroid|Any CPU
|
||||
FDroid|iPhone = FDroid|iPhone
|
||||
FDroid|iPhoneSimulator = FDroid|iPhoneSimulator
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|iPhone = Release|iPhone
|
||||
Release|iPhoneSimulator = Release|iPhoneSimulator
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{971FDF07-E288-4239-B47A-E9E7E912193B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{971FDF07-E288-4239-B47A-E9E7E912193B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{971FDF07-E288-4239-B47A-E9E7E912193B}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
||||
{971FDF07-E288-4239-B47A-E9E7E912193B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{971FDF07-E288-4239-B47A-E9E7E912193B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{971FDF07-E288-4239-B47A-E9E7E912193B}.Release|Any CPU.Deploy.0 = Release|Any CPU
|
||||
{971FDF07-E288-4239-B47A-E9E7E912193B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{971FDF07-E288-4239-B47A-E9E7E912193B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{971FDF07-E288-4239-B47A-E9E7E912193B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{971FDF07-E288-4239-B47A-E9E7E912193B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{971FDF07-E288-4239-B47A-E9E7E912193B}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{971FDF07-E288-4239-B47A-E9E7E912193B}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{971FDF07-E288-4239-B47A-E9E7E912193B}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{971FDF07-E288-4239-B47A-E9E7E912193B}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{971FDF07-E288-4239-B47A-E9E7E912193B}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{971FDF07-E288-4239-B47A-E9E7E912193B}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{971FDF07-E288-4239-B47A-E9E7E912193B}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
||||
{971FDF07-E288-4239-B47A-E9E7E912193B}.AppStore|iPhone.Build.0 = Release|Any CPU
|
||||
{971FDF07-E288-4239-B47A-E9E7E912193B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{971FDF07-E288-4239-B47A-E9E7E912193B}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{971FDF07-E288-4239-B47A-E9E7E912193B}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{971FDF07-E288-4239-B47A-E9E7E912193B}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
||||
{971FDF07-E288-4239-B47A-E9E7E912193B}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{971FDF07-E288-4239-B47A-E9E7E912193B}.FDroid|Any CPU.Build.0 = Debug|Any CPU
|
||||
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
||||
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.AppStore|iPhone.Build.0 = Release|Any CPU
|
||||
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
||||
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.FDroid|Any CPU.Build.0 = Debug|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|Any CPU.Deploy.0 = Release|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|iPhone.Deploy.0 = Release|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|iPhoneSimulator.Deploy.0 = Release|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|Any CPU.Build.0 = Release|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|Any CPU.Deploy.0 = Release|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|iPhone.Build.0 = Release|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|iPhone.Deploy.0 = Release|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|iPhoneSimulator.Deploy.0 = Release|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|iPhone.Deploy.0 = Debug|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|iPhoneSimulator.Deploy.0 = Debug|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|Any CPU.ActiveCfg = FDroid|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|Any CPU.Build.0 = FDroid|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|Any CPU.Deploy.0 = FDroid|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|iPhone.ActiveCfg = FDroid|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|iPhone.Build.0 = FDroid|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|iPhone.Deploy.0 = FDroid|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|iPhoneSimulator.ActiveCfg = FDroid|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|iPhoneSimulator.Build.0 = FDroid|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|iPhoneSimulator.Deploy.0 = FDroid|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|Any CPU.Deploy.0 = Release|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|iPhone.Deploy.0 = Release|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|iPhoneSimulator.Deploy.0 = Release|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|Any CPU.Deploy.0 = Release|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhone.Deploy.0 = Release|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhoneSimulator.Deploy.0 = Release|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|Any CPU.Build.0 = Release|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|Any CPU.Deploy.0 = Release|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhone.Build.0 = Release|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhone.Deploy.0 = Release|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhoneSimulator.Deploy.0 = Release|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Debug|iPhone.Deploy.0 = Debug|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Debug|iPhoneSimulator.Deploy.0 = Debug|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.FDroid|Any CPU.ActiveCfg = FDroid|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.FDroid|Any CPU.Build.0 = FDroid|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.FDroid|iPhone.ActiveCfg = FDroid|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.FDroid|iPhone.Build.0 = FDroid|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.FDroid|iPhoneSimulator.ActiveCfg = FDroid|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.FDroid|iPhoneSimulator.Build.0 = FDroid|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Release|Any CPU.Deploy.0 = Release|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Release|iPhone.Deploy.0 = Release|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Release|iPhoneSimulator.Deploy.0 = Release|Any CPU
|
||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
|
||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
|
||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
|
||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|Any CPU.Build.0 = Release|Any CPU
|
||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|iPhone.Build.0 = Release|Any CPU
|
||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.FDroid|Any CPU.ActiveCfg = FDroid|Any CPU
|
||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.FDroid|Any CPU.Build.0 = FDroid|Any CPU
|
||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.FDroid|iPhone.ActiveCfg = FDroid|Any CPU
|
||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.FDroid|iPhone.Build.0 = FDroid|Any CPU
|
||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.FDroid|iPhoneSimulator.ActiveCfg = FDroid|Any CPU
|
||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.FDroid|iPhoneSimulator.Build.0 = FDroid|Any CPU
|
||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{4B8A8C41-9820-4341-974C-41E65B7F4366}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
||||
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.AppStore|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.AppStore|iPhone.Build.0 = Debug|Any CPU
|
||||
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.FDroid|Any CPU.ActiveCfg = FDroid|Any CPU
|
||||
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.FDroid|Any CPU.Build.0 = FDroid|Any CPU
|
||||
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.FDroid|iPhone.ActiveCfg = FDroid|Any CPU
|
||||
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.FDroid|iPhone.Build.0 = FDroid|Any CPU
|
||||
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.FDroid|iPhoneSimulator.ActiveCfg = FDroid|Any CPU
|
||||
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.FDroid|iPhoneSimulator.Build.0 = FDroid|Any CPU
|
||||
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
|
||||
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
||||
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.AppStore|Any CPU.Build.0 = Debug|Any CPU
|
||||
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.AppStore|iPhone.Build.0 = Debug|Any CPU
|
||||
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.FDroid|Any CPU.ActiveCfg = FDroid|Any CPU
|
||||
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.FDroid|Any CPU.Build.0 = FDroid|Any CPU
|
||||
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.FDroid|iPhone.ActiveCfg = FDroid|Any CPU
|
||||
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.FDroid|iPhone.Build.0 = FDroid|Any CPU
|
||||
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.FDroid|iPhoneSimulator.ActiveCfg = FDroid|Any CPU
|
||||
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.FDroid|iPhoneSimulator.Build.0 = FDroid|Any CPU
|
||||
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|Any CPU.ActiveCfg = Ad-Hoc|Any CPU
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|Any CPU.Build.0 = Ad-Hoc|Any CPU
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|iPhone
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Ad-Hoc|iPhoneSimulator
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhoneSimulator.Build.0 = Ad-Hoc|iPhoneSimulator
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|Any CPU.ActiveCfg = AppStore|Any CPU
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|Any CPU.Build.0 = AppStore|Any CPU
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhone.ActiveCfg = AppStore|iPhone
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhone.Build.0 = AppStore|iPhone
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhone.ActiveCfg = Debug|iPhone
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhone.Build.0 = Debug|iPhone
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.FDroid|Any CPU.ActiveCfg = FDroid|Any CPU
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.FDroid|Any CPU.Build.0 = FDroid|Any CPU
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.FDroid|iPhone.ActiveCfg = FDroid|Any CPU
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.FDroid|iPhone.Build.0 = FDroid|Any CPU
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.FDroid|iPhoneSimulator.ActiveCfg = FDroid|Any CPU
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.FDroid|iPhoneSimulator.Build.0 = FDroid|Any CPU
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhone.Build.0 = Release|Any CPU
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.FDroid|Any CPU.Build.0 = Debug|Any CPU
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhone.Build.0 = Release|Any CPU
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|iPhone.Build.0 = Release|Any CPU
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.FDroid|Any CPU.Build.0 = Debug|Any CPU
|
||||
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
||||
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.AppStore|iPhone.Build.0 = Release|Any CPU
|
||||
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.FDroid|Any CPU.Build.0 = Debug|Any CPU
|
||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.AppStore|iPhone.Build.0 = Release|Any CPU
|
||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.FDroid|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.AppStore|iPhone.Build.0 = Release|Any CPU
|
||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.FDroid|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Ad-Hoc|Any CPU.ActiveCfg = Ad-Hoc|iPhone
|
||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone
|
||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|iPhone
|
||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Ad-Hoc|iPhoneSimulator
|
||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Ad-Hoc|iPhoneSimulator.Build.0 = Ad-Hoc|iPhoneSimulator
|
||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.AppStore|Any CPU.ActiveCfg = AppStore|iPhone
|
||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.AppStore|iPhone.ActiveCfg = AppStore|iPhone
|
||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.AppStore|iPhone.Build.0 = AppStore|iPhone
|
||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator
|
||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator
|
||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|Any CPU.ActiveCfg = Debug|iPhone
|
||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhone.ActiveCfg = Debug|iPhone
|
||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhone.Build.0 = Debug|iPhone
|
||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhone.Deploy.0 = Debug|iPhone
|
||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
|
||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
|
||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.FDroid|Any CPU.ActiveCfg = FDroid|iPhone
|
||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.FDroid|iPhone.ActiveCfg = FDroid|iPhone
|
||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.FDroid|iPhone.Build.0 = FDroid|iPhone
|
||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.FDroid|iPhoneSimulator.ActiveCfg = FDroid|iPhoneSimulator
|
||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.FDroid|iPhoneSimulator.Build.0 = FDroid|iPhoneSimulator
|
||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Release|Any CPU.ActiveCfg = Release|iPhone
|
||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Release|iPhone.ActiveCfg = Release|iPhone
|
||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Release|iPhone.Build.0 = Release|iPhone
|
||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
|
||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|Any CPU.ActiveCfg = Ad-Hoc|iPhone
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|Any CPU.Build.0 = Ad-Hoc|iPhone
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|iPhone
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Ad-Hoc|iPhoneSimulator
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhoneSimulator.Build.0 = Ad-Hoc|iPhoneSimulator
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|Any CPU.ActiveCfg = AppStore|iPhone
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|Any CPU.Build.0 = AppStore|iPhone
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhone.ActiveCfg = AppStore|iPhone
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhone.Build.0 = AppStore|iPhone
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|Any CPU.ActiveCfg = Debug|iPhone
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhone.ActiveCfg = Debug|iPhone
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhone.Build.0 = Debug|iPhone
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhone.Deploy.0 = Debug|iPhone
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|Any CPU.ActiveCfg = Release|iPhone
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|Any CPU.Build.0 = Release|iPhone
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|iPhone.ActiveCfg = Release|iPhone
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|iPhone.Build.0 = Release|iPhone
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|Any CPU.ActiveCfg = Release|iPhone
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhone.ActiveCfg = Release|iPhone
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhone.Build.0 = Release|iPhone
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
|
||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
|
||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
|
||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
|
||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.AppStore|Any CPU.Build.0 = Release|Any CPU
|
||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.AppStore|iPhone.Build.0 = Release|Any CPU
|
||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.FDroid|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.FDroid|Any CPU.Build.0 = Release|Any CPU
|
||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.FDroid|iPhone.ActiveCfg = Release|Any CPU
|
||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.FDroid|iPhone.Build.0 = Release|Any CPU
|
||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.FDroid|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.FDroid|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
|
||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
|
||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
|
||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.AppStore|Any CPU.Build.0 = Release|Any CPU
|
||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.AppStore|iPhone.Build.0 = Release|Any CPU
|
||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.FDroid|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.FDroid|Any CPU.Build.0 = Release|Any CPU
|
||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.FDroid|iPhone.ActiveCfg = Release|Any CPU
|
||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.FDroid|iPhone.Build.0 = Release|Any CPU
|
||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.FDroid|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.FDroid|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|Any CPU.ActiveCfg = Debug|iPhoneSimulator
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|Any CPU.Build.0 = Debug|iPhoneSimulator
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhone.ActiveCfg = Release|iPhone
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhone.Build.0 = Release|iPhone
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|Any CPU.ActiveCfg = Debug|iPhoneSimulator
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|Any CPU.Build.0 = Debug|iPhoneSimulator
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|iPhone.ActiveCfg = Release|iPhone
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|iPhone.Build.0 = Release|iPhone
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|Any CPU.ActiveCfg = Debug|iPhoneSimulator
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|Any CPU.Build.0 = Debug|iPhoneSimulator
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|iPhone.ActiveCfg = Debug|iPhone
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|iPhone.Build.0 = Debug|iPhone
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.FDroid|Any CPU.ActiveCfg = Debug|iPhoneSimulator
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.FDroid|Any CPU.Build.0 = Debug|iPhoneSimulator
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.FDroid|iPhone.ActiveCfg = Release|iPhone
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.FDroid|iPhone.Build.0 = Release|iPhone
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.FDroid|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.FDroid|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|Any CPU.ActiveCfg = Release|iPhone
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|Any CPU.Build.0 = Release|iPhone
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|iPhone.ActiveCfg = Release|iPhone
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|iPhone.Build.0 = Release|iPhone
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|Any CPU.ActiveCfg = Ad-Hoc|iPhone
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|Any CPU.Build.0 = Ad-Hoc|iPhone
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|iPhone
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Ad-Hoc|iPhoneSimulator
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|iPhoneSimulator.Build.0 = Ad-Hoc|iPhoneSimulator
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|Any CPU.ActiveCfg = AppStore|iPhone
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|Any CPU.Build.0 = AppStore|iPhone
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|iPhone.ActiveCfg = AppStore|iPhone
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|iPhone.Build.0 = AppStore|iPhone
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|Any CPU.ActiveCfg = Debug|iPhone
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhone.ActiveCfg = Debug|iPhone
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhone.Build.0 = Debug|iPhone
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhone.Deploy.0 = Debug|iPhone
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|Any CPU.ActiveCfg = Release|iPhone
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|Any CPU.Build.0 = Release|iPhone
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|iPhone.ActiveCfg = Release|iPhone
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|iPhone.Build.0 = Release|iPhone
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|Any CPU.ActiveCfg = Release|iPhone
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhone.ActiveCfg = Release|iPhone
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhone.Build.0 = Release|iPhone
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {3B3A9B6C-D325-4BB3-97D3-8070630C5D3B}
|
||||
EndGlobalSection
|
||||
GlobalSection(MonoDevelopProperties) = preSolution
|
||||
Policies = $0
|
||||
$0.DotNetNamingPolicy = $1
|
||||
$1.DirectoryNamespaceAssociation = PrefixedHierarchical
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{971FDF07-E288-4239-B47A-E9E7E912193B} = {B972BBFA-917F-4A10-B07E-B89CFEC6BBDC}
|
||||
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C} = {B972BBFA-917F-4A10-B07E-B89CFEC6BBDC}
|
||||
{83449CC4-1F76-4CFE-92B1-D2E13A62506F} = {B972BBFA-917F-4A10-B07E-B89CFEC6BBDC}
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6} = {B972BBFA-917F-4A10-B07E-B89CFEC6BBDC}
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545} = {B972BBFA-917F-4A10-B07E-B89CFEC6BBDC}
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655} = {B972BBFA-917F-4A10-B07E-B89CFEC6BBDC}
|
||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6} = {BB702EBD-3B79-4ECA-A2A6-1237B07F0AF0}
|
||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44} = {BB702EBD-3B79-4ECA-A2A6-1237B07F0AF0}
|
||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F} = {D10CA4A9-F866-40E1-B658-F69051236C71}
|
||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C} = {D10CA4A9-F866-40E1-B658-F69051236C71}
|
||||
{4B8A8C41-9820-4341-974C-41E65B7F4366} = {D10CA4A9-F866-40E1-B658-F69051236C71}
|
||||
{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3} = {8904C536-C67D-420F-9971-51B26574C3AA}
|
||||
{2E399654-26A2-46F6-B9CA-1B496A3F370A} = {92470CBD-9047-4C3C-8EA3-D972D6622D84}
|
||||
{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D} = {2E399654-26A2-46F6-B9CA-1B496A3F370A}
|
||||
{E71F3053-056C-4381-9638-048ED73BDFF6} = {D10CA4A9-F866-40E1-B658-F69051236C71}
|
||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15} = {D10CA4A9-F866-40E1-B658-F69051236C71}
|
||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545} = {D10CA4A9-F866-40E1-B658-F69051236C71}
|
||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39} = {8904C536-C67D-420F-9971-51B26574C3AA}
|
||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0} = {8904C536-C67D-420F-9971-51B26574C3AA}
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655} = {D10CA4A9-F866-40E1-B658-F69051236C71}
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A} = {D10CA4A9-F866-40E1-B658-F69051236C71}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {7D436EA3-8B7E-45D2-8D14-0730BD2E0410}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
469
build.cake
@@ -1,469 +0,0 @@
|
||||
#addin nuget:?package=Cake.FileHelpers&version=5.0.0
|
||||
#addin nuget:?package=Cake.AndroidAppManifest&version=1.1.2
|
||||
#addin nuget:?package=Cake.Plist&version=0.7.0
|
||||
#addin nuget:?package=Cake.Incubator&version=7.0.0
|
||||
#tool dotnet:?package=GitVersion.Tool&version=5.10.3
|
||||
using Path = System.IO.Path;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
var debugScript = Argument<bool>("debugScript", false);
|
||||
var target = Argument("target", "Default");
|
||||
var configuration = Argument("configuration", "Release");
|
||||
var variant = Argument("variant", "dev");
|
||||
|
||||
abstract record VariantConfig(
|
||||
string AppName,
|
||||
string AndroidPackageName,
|
||||
string iOSBundleId,
|
||||
string ApsEnvironment,
|
||||
string DistProvisioningProfilePrefix
|
||||
);
|
||||
|
||||
const string BASE_BUNDLE_ID_DROID = "com.x8bit.bitwarden";
|
||||
const string BASE_BUNDLE_ID_IOS = "com.8bit.bitwarden";
|
||||
|
||||
//NOTE: Beta iOS variants have a different ITSEncryptionExportComplianceCode
|
||||
record Dev(): VariantConfig("Bitwarden Dev", $"{BASE_BUNDLE_ID_DROID}.dev", $"{BASE_BUNDLE_ID_IOS}.dev", "development", "Dist:");
|
||||
record QA(): VariantConfig("Bitwarden QA", $"{BASE_BUNDLE_ID_DROID}.qa", $"{BASE_BUNDLE_ID_IOS}.qa", "development", "Dist:");
|
||||
record Beta(): VariantConfig("Bitwarden Beta", $"{BASE_BUNDLE_ID_DROID}.beta", $"{BASE_BUNDLE_ID_IOS}.beta", "production", "Dist: Beta");
|
||||
record Prod(): VariantConfig("Bitwarden", $"{BASE_BUNDLE_ID_DROID}", $"{BASE_BUNDLE_ID_IOS}", "production", "Dist:");
|
||||
|
||||
VariantConfig GetVariant() => variant.ToLower() switch{
|
||||
"qa" => new QA(),
|
||||
"beta" => new Beta(),
|
||||
"prod" => new Prod(),
|
||||
_ => new Dev()
|
||||
};
|
||||
|
||||
GitVersion _gitVersion; //will be set by GetGitInfo task
|
||||
var _slnPath = Path.Combine(""); //base path used to access files. If build.cake file is moved, just update this
|
||||
string _androidPackageName = string.Empty; //will be set by UpdateAndroidManifest task
|
||||
string _iOSVersionName = string.Empty; //will be set by UpdateiOSPlist task
|
||||
string CreateFeatureBranch(string prevVersionName, GitVersion git) => $"{prevVersionName}-{git.BranchName.Replace("/","-")}";
|
||||
string GetVersionName(string prevVersionName, VariantConfig buildVariant, GitVersion git) => buildVariant is Prod? prevVersionName : CreateFeatureBranch(prevVersionName, git);
|
||||
int CreateBuildNumber(int previousNumber) => ++previousNumber;
|
||||
|
||||
Task("GetGitInfo")
|
||||
.Does(()=> {
|
||||
_gitVersion = GitVersion(new GitVersionSettings());
|
||||
|
||||
if(debugScript)
|
||||
{
|
||||
Information($"GitVersion Dump:\n{_gitVersion.Dump()}");
|
||||
}
|
||||
|
||||
Information("Git data Load successfully.");
|
||||
});
|
||||
|
||||
#region Android
|
||||
Task("UpdateAndroidAppIcon")
|
||||
.Does(()=>{
|
||||
//TODO we'll implement variant icons later
|
||||
//manifest.ApplicationIcon = "@mipmap/ic_launcher";
|
||||
Information($"Updated Androix App Icon with success");
|
||||
});
|
||||
|
||||
|
||||
Task("UpdateAndroidManifest")
|
||||
.IsDependentOn("GetGitInfo")
|
||||
.Does(()=>
|
||||
{
|
||||
var buildVariant = GetVariant();
|
||||
var manifestPath = Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "AndroidManifest.xml");
|
||||
|
||||
// Cake.AndroidAppManifest doesn't currently enable us to access nested items so, quick (not ideal) fix:
|
||||
var manifestText = FileReadText(manifestPath);
|
||||
manifestText = manifestText.Replace("com.x8bit.bitwarden.", buildVariant.AndroidPackageName + ".");
|
||||
manifestText = manifestText.Replace("android:label=\"Bitwarden\"", $"android:label=\"{buildVariant.AppName}\"");
|
||||
FileWriteText(manifestPath, manifestText);
|
||||
|
||||
var manifest = DeserializeAppManifest(manifestPath);
|
||||
|
||||
var prevVersionCode = manifest.VersionCode;
|
||||
var prevVersionName = manifest.VersionName;
|
||||
_androidPackageName = manifest.PackageName;
|
||||
|
||||
//manifest.VersionCode = CreateBuildNumber(prevVersionCode);
|
||||
manifest.VersionName = GetVersionName(prevVersionName, buildVariant, _gitVersion);
|
||||
manifest.PackageName = buildVariant.AndroidPackageName;
|
||||
manifest.ApplicationLabel = buildVariant.AppName;
|
||||
|
||||
//Information($"AndroidManigest.xml VersionCode from {prevVersionCode} to {manifest.VersionCode}");
|
||||
Information($"AndroidManigest.xml VersionName from {prevVersionName} to {manifest.VersionName}");
|
||||
Information($"AndroidManigest.xml PackageName from {_androidPackageName} to {buildVariant.AndroidPackageName}");
|
||||
Information($"AndroidManigest.xml ApplicationLabel to {buildVariant.AppName}");
|
||||
|
||||
SerializeAppManifest(manifestPath, manifest);
|
||||
|
||||
Information("AndroidManifest updated with success!");
|
||||
});
|
||||
|
||||
void ReplaceInFile(string filePath, string oldtext, string newtext)
|
||||
{
|
||||
var fileText = FileReadText(filePath);
|
||||
|
||||
if(string.IsNullOrEmpty(fileText) || !fileText.Contains(oldtext))
|
||||
{
|
||||
throw new Exception($"Couldn't find {filePath} or it didn't contain: {oldtext}");
|
||||
}
|
||||
|
||||
fileText = fileText.Replace(oldtext, newtext);
|
||||
|
||||
FileWriteText(filePath, fileText);
|
||||
Information($"{filePath} modified successfully.");
|
||||
}
|
||||
|
||||
Task("UpdateAndroidCodeFiles")
|
||||
.IsDependentOn("UpdateAndroidManifest")
|
||||
.Does(()=> {
|
||||
var buildVariant = GetVariant();
|
||||
|
||||
//We're not using _androidPackageName here because the codefile is currently slightly different string than the one in AndroidManifest.xml
|
||||
var keyName = "com.8bit.bitwarden";
|
||||
var fixedPackageName = buildVariant.AndroidPackageName.Replace("x8bit", "8bit");
|
||||
var filePath = Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Services", "BiometricService.cs");
|
||||
ReplaceInFile(filePath, keyName, fixedPackageName);
|
||||
|
||||
var packageFileList = new string[] {
|
||||
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "MainActivity.cs"),
|
||||
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "MainApplication.cs"),
|
||||
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Constants.cs"),
|
||||
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Accessibility", "AccessibilityService.cs"),
|
||||
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Autofill", "AutofillHelpers.cs"),
|
||||
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Autofill", "AutofillService.cs"),
|
||||
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Receivers", "ClearClipboardAlarmReceiver.cs"),
|
||||
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Receivers", "EventUploadReceiver.cs"),
|
||||
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Receivers", "PackageReplacedReceiver.cs"),
|
||||
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Receivers", "RestrictionsChangedReceiver.cs"),
|
||||
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Services", "DeviceActionService.cs"),
|
||||
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Services", "FileService.cs"),
|
||||
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Tiles", "AutofillTileService.cs"),
|
||||
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Tiles", "GeneratorTileService.cs"),
|
||||
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Tiles", "MyVaultTileService.cs"),
|
||||
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "google-services.json"),
|
||||
Path.Combine(_slnPath, "store", "google", "Publisher", "Program.cs"),
|
||||
};
|
||||
|
||||
foreach(string path in packageFileList)
|
||||
{
|
||||
ReplaceInFile(path, "com.x8bit.bitwarden", buildVariant.AndroidPackageName);
|
||||
}
|
||||
|
||||
var labelFileList = new string[] {
|
||||
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Autofill", "AutofillService.cs"),
|
||||
};
|
||||
|
||||
foreach(string path in labelFileList)
|
||||
{
|
||||
ReplaceInFile(path, "Bitwarden\"", $"{buildVariant.AppName}\"");
|
||||
}
|
||||
});
|
||||
#endregion Android
|
||||
|
||||
#region iOS
|
||||
enum iOSProjectType
|
||||
{
|
||||
Null,
|
||||
MainApp,
|
||||
Autofill,
|
||||
Extension,
|
||||
ShareExtension,
|
||||
WatchApp
|
||||
}
|
||||
|
||||
string GetiOSBundleId(VariantConfig buildVariant, iOSProjectType projectType) => projectType switch
|
||||
{
|
||||
iOSProjectType.Autofill => $"{buildVariant.iOSBundleId}.autofill",
|
||||
iOSProjectType.Extension => $"{buildVariant.iOSBundleId}.find-login-action-extension",
|
||||
iOSProjectType.ShareExtension => $"{buildVariant.iOSBundleId}.share-extension",
|
||||
iOSProjectType.WatchApp => $"{buildVariant.iOSBundleId}.watchkitapp",
|
||||
_ => buildVariant.iOSBundleId
|
||||
};
|
||||
|
||||
string GetiOSBundleName(VariantConfig buildVariant, iOSProjectType projectType) => projectType switch
|
||||
{
|
||||
iOSProjectType.Autofill => $"{buildVariant.AppName} Autofill",
|
||||
iOSProjectType.Extension => $"{buildVariant.AppName} Extension",
|
||||
iOSProjectType.ShareExtension => $"{buildVariant.AppName} Share Extension",
|
||||
_ => buildVariant.AppName
|
||||
};
|
||||
|
||||
private void UpdateiOSInfoPlist(string plistPath, VariantConfig buildVariant, GitVersion git, iOSProjectType projectType = iOSProjectType.MainApp)
|
||||
{
|
||||
var plistFile = File(plistPath);
|
||||
dynamic plist = DeserializePlist(plistFile);
|
||||
|
||||
var prevVersionName = plist["CFBundleShortVersionString"];
|
||||
var prevVersionString = plist["CFBundleVersion"];
|
||||
var prevVersion = int.Parse(plist["CFBundleVersion"]);
|
||||
var prevBundleId = plist["CFBundleIdentifier"];
|
||||
var prevBundleName = plist["CFBundleName"];
|
||||
//var newVersion = CreateBuildNumber(prevVersion).ToString();
|
||||
// we need to maintain version formatting here composed of one to three period-separated integers, so we cannot use the GetVersionName method as in Android for non-Prod.
|
||||
var newVersionName = prevVersionName;
|
||||
var newBundleId = GetiOSBundleId(buildVariant, projectType);
|
||||
var newBundleName = GetiOSBundleName(buildVariant, projectType);
|
||||
|
||||
plist["CFBundleName"] = newBundleName;
|
||||
plist["CFBundleDisplayName"] = newBundleName;
|
||||
//plist["CFBundleVersion"] = newVersion;
|
||||
plist["CFBundleShortVersionString"] = newVersionName;
|
||||
plist["CFBundleIdentifier"] = newBundleId;
|
||||
|
||||
if(projectType == iOSProjectType.MainApp)
|
||||
{
|
||||
_iOSVersionName = newVersionName;
|
||||
plist["CFBundleURLTypes"][0]["CFBundleURLName"] = $"{buildVariant.iOSBundleId}.url";
|
||||
}
|
||||
|
||||
if(projectType == iOSProjectType.Extension)
|
||||
{
|
||||
var keyText = plist["NSExtension"]["NSExtensionAttributes"]["NSExtensionActivationRule"];
|
||||
plist["NSExtension"]["NSExtensionAttributes"]["NSExtensionActivationRule"] = keyText.Replace("com.8bit.bitwarden", buildVariant.iOSBundleId);
|
||||
}
|
||||
|
||||
if(buildVariant is Beta)
|
||||
{
|
||||
plist["ITSEncryptionExportComplianceCode"] = "3dd3e32f-efa6-4d99-b410-28aa28b1cb77";
|
||||
}
|
||||
|
||||
SerializePlist(plistFile, plist);
|
||||
|
||||
Information($"Changed app name from {prevBundleName} to {newBundleName}");
|
||||
//Information($"Changed Bundle Version from {prevVersion} to {newVersion}");
|
||||
Information($"Changed Bundle Short Version name from {prevVersionName} to {newVersionName}");
|
||||
Information($"Changed Bundle Identifier from {prevBundleId} to {newBundleId}");
|
||||
Information($"{plistPath} updated with success!");
|
||||
}
|
||||
|
||||
private void UpdateiOSEntitlementsPlist(string entitlementsPath, VariantConfig buildVariant, bool updateApsEnv)
|
||||
{
|
||||
var EntitlementlistFile = File(entitlementsPath);
|
||||
dynamic Entitlements = DeserializePlist(EntitlementlistFile);
|
||||
|
||||
if (updateApsEnv)
|
||||
{
|
||||
Entitlements["aps-environment"] = buildVariant.ApsEnvironment;
|
||||
}
|
||||
Entitlements["keychain-access-groups"] = new List<string>() { "$(AppIdentifierPrefix)" + buildVariant.iOSBundleId };
|
||||
Entitlements["com.apple.security.application-groups"] = new List<string>() { $"group.{buildVariant.iOSBundleId}" };;
|
||||
|
||||
Information($"Changed ApsEnvironment name to {buildVariant.ApsEnvironment}");
|
||||
Information($"Changed keychain-access-groups bundleID to {buildVariant.iOSBundleId}");
|
||||
|
||||
SerializePlist(EntitlementlistFile, Entitlements);
|
||||
|
||||
Information($"{entitlementsPath} updated with success!");
|
||||
}
|
||||
|
||||
private void UpdateWatchKitAppInfoPlist(string plistPath, VariantConfig buildVariant)
|
||||
{
|
||||
var plistFile = File(plistPath);
|
||||
dynamic plist = DeserializePlist(plistFile);
|
||||
|
||||
var prevBundleId = plist["NSExtension"]["NSExtensionAttributes"]["WKAppBundleIdentifier"];
|
||||
var newBundleId = GetiOSBundleId(buildVariant, iOSProjectType.WatchApp);
|
||||
|
||||
plist["NSExtension"]["NSExtensionAttributes"]["WKAppBundleIdentifier"] = newBundleId;
|
||||
|
||||
SerializePlist(plistFile, plist);
|
||||
|
||||
Information($"Changed Bundle Identifier from {prevBundleId} to {newBundleId}");
|
||||
Information($"{plistPath} updated with success!");
|
||||
}
|
||||
|
||||
private void UpdateWatchPbxproj(string pbxprojPath, string newVersion)
|
||||
{
|
||||
var fileText = FileReadText(pbxprojPath);
|
||||
if (string.IsNullOrEmpty(fileText))
|
||||
{
|
||||
throw new Exception($"Couldn't find {pbxprojPath}");
|
||||
}
|
||||
|
||||
const string pattern = @"MARKETING_VERSION = [^;]*;";
|
||||
|
||||
fileText = Regex.Replace(fileText, pattern, $"MARKETING_VERSION = {newVersion};");
|
||||
|
||||
FileWriteText(pbxprojPath, fileText);
|
||||
|
||||
Information($"{pbxprojPath} modified Marketing Version successfully.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the target icons on the given appiconset target
|
||||
/// taking as source the icon in appIcons/iOS folder for the giving variant
|
||||
/// </summary>
|
||||
/// <param name="target">It can be <ios|watch|complication|macos></param>
|
||||
/// <param name="appiconsetTarget">Folder to copy the generated icons to</param>
|
||||
private void UpdateAppleIcons(string target, string appiconsetTarget)
|
||||
{
|
||||
Information($"Updating {target} App Icons");
|
||||
|
||||
var iconsTempDirPath = Path.Combine(_slnPath, "appIcons", "temp");
|
||||
CreateDirectory(iconsTempDirPath);
|
||||
|
||||
var arguments = new ProcessArgumentBuilder();
|
||||
arguments.Append(target);
|
||||
arguments.Append(Path.Combine(_slnPath, "appIcons", "iOS", $"{variant}.png"));
|
||||
arguments.Append(iconsTempDirPath);
|
||||
|
||||
using(var process = StartAndReturnProcess(Path.Combine(_slnPath, "appIcons", "icongen.sh"),
|
||||
new ProcessSettings { Arguments = arguments }))
|
||||
{
|
||||
process.WaitForExit();
|
||||
Information("Exit code: {0}", process.GetExitCode());
|
||||
}
|
||||
|
||||
var generatedIconsPath = Path.Combine(iconsTempDirPath, "*.png");
|
||||
CopyFiles(generatedIconsPath, appiconsetTarget);
|
||||
|
||||
DeleteDirectory(iconsTempDirPath, new DeleteDirectorySettings {
|
||||
Recursive = true,
|
||||
Force = true
|
||||
});
|
||||
|
||||
Information($"{target} App Icons have been updated");
|
||||
}
|
||||
|
||||
Task("UpdateiOSIcons")
|
||||
.Does(()=>{
|
||||
UpdateAppleIcons("ios", Path.Combine(_slnPath, "src", "App", "Platforms", "iOS", "Resources", "Assets.xcassets", "AppIcons.appiconset"));
|
||||
UpdateAppleIcons("watch", Path.Combine(_slnPath, "src", "watchOS", "bitwarden", "bitwarden WatchKit App", "Assets.xcassets", "AppIcon.appiconset"));
|
||||
// TODO: Update complication icons when they start working
|
||||
});
|
||||
|
||||
Task("UpdateiOSPlist")
|
||||
.IsDependentOn("GetGitInfo")
|
||||
.Does(()=> {
|
||||
var buildVariant = GetVariant();
|
||||
var infoPath = Path.Combine(_slnPath, "src", "App", "Platforms", "iOS", "Info.plist");
|
||||
var entitlementsPath = Path.Combine(_slnPath, "src", "App", "Platforms", "iOS", "Entitlements.plist");
|
||||
UpdateiOSInfoPlist(infoPath, buildVariant, _gitVersion, iOSProjectType.MainApp);
|
||||
UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant, true);
|
||||
});
|
||||
|
||||
Task("UpdateiOSAutofillPlist")
|
||||
.IsDependentOn("GetGitInfo")
|
||||
.IsDependentOn("UpdateiOSPlist")
|
||||
.Does(()=> {
|
||||
var buildVariant = GetVariant();
|
||||
var infoPath = Path.Combine(_slnPath, "src", "iOS.Autofill", "Info.plist");
|
||||
var entitlementsPath = Path.Combine(_slnPath, "src", "iOS.Autofill", "Entitlements.plist");
|
||||
UpdateiOSInfoPlist(infoPath, buildVariant, _gitVersion, iOSProjectType.Autofill);
|
||||
UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant, false);
|
||||
});
|
||||
|
||||
Task("UpdateiOSExtensionPlist")
|
||||
.IsDependentOn("GetGitInfo")
|
||||
.IsDependentOn("UpdateiOSPlist")
|
||||
.Does(()=> {
|
||||
var buildVariant = GetVariant();
|
||||
var infoPath = Path.Combine(_slnPath, "src", "iOS.Extension", "Info.plist");
|
||||
var entitlementsPath = Path.Combine(_slnPath, "src", "iOS.Extension", "Entitlements.plist");
|
||||
UpdateiOSInfoPlist(infoPath, buildVariant, _gitVersion, iOSProjectType.Extension);
|
||||
UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant, false);
|
||||
});
|
||||
|
||||
Task("UpdateiOSShareExtensionPlist")
|
||||
.IsDependentOn("GetGitInfo")
|
||||
.IsDependentOn("UpdateiOSPlist")
|
||||
.Does(()=> {
|
||||
var buildVariant = GetVariant();
|
||||
var infoPath = Path.Combine(_slnPath, "src", "iOS.ShareExtension", "Info.plist");
|
||||
var entitlementsPath = Path.Combine(_slnPath, "src", "iOS.ShareExtension", "Entitlements.plist");
|
||||
UpdateiOSInfoPlist(infoPath, buildVariant, _gitVersion, iOSProjectType.ShareExtension);
|
||||
UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant, false);
|
||||
});
|
||||
|
||||
Task("UpdateiOSCodeFiles")
|
||||
.IsDependentOn("UpdateiOSPlist")
|
||||
.Does(()=> {
|
||||
var buildVariant = GetVariant();
|
||||
var fileList = new string[] {
|
||||
Path.Combine(_slnPath, "src", "iOS.Core", "Utilities", "iOSCoreHelpers.cs"),
|
||||
Path.Combine(_slnPath, "src", "iOS.Core", "Constants.cs"),
|
||||
Path.Combine(_slnPath, "src", "watchOS", "bitwarden", "bitwarden.xcodeproj", "project.pbxproj"),
|
||||
Path.Combine(_slnPath, "src", "watchOS", "bitwarden", "bitwarden WatchKit Extension", "Helpers", "KeychainHelper.swift"),
|
||||
Path.Combine(".github", "resources", "export-options-ad-hoc.plist"),
|
||||
Path.Combine(".github", "resources", "export-options-app-store.plist")
|
||||
};
|
||||
|
||||
foreach(string path in fileList)
|
||||
{
|
||||
ReplaceInFile(path, "com.8bit.bitwarden", buildVariant.iOSBundleId);
|
||||
}
|
||||
});
|
||||
|
||||
Task("UpdateWatchProject")
|
||||
.IsDependentOn("UpdateiOSPlist")
|
||||
.WithCriteria(() => !string.IsNullOrEmpty(_iOSVersionName))
|
||||
.Does(()=> {
|
||||
var watchProjectPath = Path.Combine(_slnPath, "src", "watchOS", "bitwarden", "bitwarden.xcodeproj", "project.pbxproj");
|
||||
UpdateWatchPbxproj(watchProjectPath, _iOSVersionName);
|
||||
});
|
||||
|
||||
Task("UpdateWatchKitAppInfoPlist")
|
||||
.Does(()=> {
|
||||
var buildVariant = GetVariant();
|
||||
var infoPath = Path.Combine(_slnPath, "src", "watchOS", "bitwarden", "bitwarden WatchKit Extension", "Info.plist");
|
||||
UpdateWatchKitAppInfoPlist(infoPath, buildVariant);
|
||||
});
|
||||
|
||||
Task("UpdateDistProfiles")
|
||||
.IsDependentOn("UpdateiOSCodeFiles")
|
||||
.Does(()=> {
|
||||
var buildVariant = GetVariant();
|
||||
|
||||
var filesToReplace = new string[] {
|
||||
Path.Combine(".github", "resources", "export-options-app-store.plist"),
|
||||
Path.Combine(_slnPath, "src", "watchOS", "bitwarden", "bitwarden.xcodeproj", "project.pbxproj")
|
||||
};
|
||||
|
||||
foreach(string path in filesToReplace)
|
||||
{
|
||||
ReplaceInFile(path, "Dist:", buildVariant.DistProvisioningProfilePrefix);
|
||||
}
|
||||
});
|
||||
|
||||
#endregion iOS
|
||||
|
||||
#region Main Tasks
|
||||
Task("Android")
|
||||
//.IsDependentOn("UpdateAndroidAppIcon")
|
||||
.IsDependentOn("UpdateAndroidManifest")
|
||||
.IsDependentOn("UpdateAndroidCodeFiles")
|
||||
.Does(()=>
|
||||
{
|
||||
Information("Android app updated");
|
||||
});
|
||||
|
||||
Task("iOS")
|
||||
.IsDependentOn("UpdateiOSIcons")
|
||||
.IsDependentOn("UpdateiOSPlist")
|
||||
.IsDependentOn("UpdateiOSAutofillPlist")
|
||||
.IsDependentOn("UpdateiOSExtensionPlist")
|
||||
.IsDependentOn("UpdateiOSShareExtensionPlist")
|
||||
.IsDependentOn("UpdateiOSCodeFiles")
|
||||
.IsDependentOn("UpdateWatchProject")
|
||||
.IsDependentOn("UpdateWatchKitAppInfoPlist")
|
||||
.IsDependentOn("UpdateDistProfiles")
|
||||
.Does(()=>
|
||||
{
|
||||
Information("iOS app updated");
|
||||
});
|
||||
|
||||
Task("Default")
|
||||
.Does(() => {
|
||||
var usage = @"Missing target.
|
||||
|
||||
Usage:
|
||||
dotnet cake build.cake --target (Android | iOS) --variant (dev | qa | beta | prod)
|
||||
|
||||
Options:
|
||||
--debugScript=<bool> Script debug mode.
|
||||
";
|
||||
Information(usage);
|
||||
});
|
||||
#endregion Main Tasks
|
||||
|
||||
RunTarget(target);
|
||||
18
crowdin.yml
@@ -2,9 +2,9 @@ project_id_env: _CROWDIN_PROJECT_ID
|
||||
api_token_env: CROWDIN_API_TOKEN
|
||||
preserve_hierarchy: true
|
||||
files:
|
||||
- source: /src/Core/Resources/Localization/AppResources.resx
|
||||
dest: /src/Core/Resources/Localization/%original_file_name%
|
||||
translation: /src/Core/Resources/Localization/AppResources.%two_letters_code%.resx
|
||||
- source: /src/App/Resources/AppResources.resx
|
||||
dest: /src/App/Resources/%original_file_name%
|
||||
translation: /src/App/Resources/AppResources.%two_letters_code%.resx
|
||||
update_option: update_as_unapproved
|
||||
languages_mapping:
|
||||
two_letters_code:
|
||||
@@ -38,15 +38,3 @@ files:
|
||||
pt-PT: pt-PT
|
||||
en-GB: en-GB
|
||||
en-IN: en-IN
|
||||
- source: "/src/watchOS/bitwarden/bitwarden WatchKit Extension/Localization/en.lproj/Localizable.strings"
|
||||
dest: "/src/watchOS/bitwarden/bitwarden WatchKit Extension/Localization/en.lproj/%original_file_name%"
|
||||
translation: "/src/watchOS/bitwarden/bitwarden WatchKit Extension/Localization//%two_letters_code%.lproj/%original_file_name%"
|
||||
update_option: update_as_unapproved
|
||||
languages_mapping:
|
||||
two_letters_code:
|
||||
zh-CN: zh-Hans
|
||||
zh-TW: zh-Hant
|
||||
pt-BR: pt-BR
|
||||
pt-PT: pt-PT
|
||||
en-GB: en-GB
|
||||
en-IN: en-IN
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
---
|
||||
sidebar_position: 5
|
||||
---
|
||||
|
||||
# .NET MAUI (legacy)
|
||||
|
||||
:::warning Legacy
|
||||
|
||||
This represents the **legacy** mobile app architecture done in .NET MAUI.
|
||||
|
||||
:::
|
||||
|
||||
The mobile .NET MAUI clients are Android and iOS applications with extensions and watchOS. They are
|
||||
all located at https://github.com/bitwarden/mobile.
|
||||
|
||||
Principal structure is a follows:
|
||||
|
||||
- `App`: Main .NET MAUI project that shares code between both platforms (Android & iOS). One can see
|
||||
specific platform code under the `Platforms` folder.
|
||||
- `Core`: Shared code having both logical and UI parts of the app. Several classes are a port from
|
||||
the Web Clients to C#. Here one can find most of the UI and logic since it's shared between App
|
||||
and the iOS extensions.
|
||||
- `iOS.Core`: Shared code used by the main iOS app and its extensions
|
||||
- `iOS.Autofill`: iOS extension that handles Autofill
|
||||
- `iOS.Extensions`: iOS extension that handles Autofill from the bottom sheet extension
|
||||
- `iOS.ShareExtension`: iOS extension that handles sharing files through Send
|
||||
- `watchOS`: All the code specific to the watchOS platform
|
||||
- `bitwarden`: Stub iOS app so that the watchOS app has a companion app on Xcode
|
||||
- `bitwarden WatchKit App`: Main Watch app where we set assets.
|
||||
- `bitwarden WatchKit Extension`: All the logic and presentation logic for the Watch app is here
|
||||
|
||||
## Dependencies diagram
|
||||
|
||||
Below is a simplified dependencies diagram of the mobile repository.
|
||||
|
||||
```kroki type=plantuml
|
||||
@startuml
|
||||
skinparam BackgroundColor transparent
|
||||
skinparam componentStyle rectangle
|
||||
skinparam linetype ortho
|
||||
|
||||
title Simplified Dependencies Diagram
|
||||
|
||||
component "Core"
|
||||
component "App"
|
||||
component "iOS.Core"
|
||||
component "iOS.Autofill"
|
||||
component "iOS.Extension"
|
||||
component "iOS.ShareExtension"
|
||||
component "watchOS" {
|
||||
component "bitwarden"
|
||||
component "bitwarden WatchKit App"
|
||||
component "bitwarden WatchKit Extension"
|
||||
}
|
||||
|
||||
[App] --> [Core]
|
||||
|
||||
[iOS.Core] --> [App]
|
||||
|
||||
[App] --> [iOS.Core]
|
||||
[App] --> [iOS.Autofill]
|
||||
[App] --> [iOS.Extension]
|
||||
[App] --> [iOS.ShareExtension]
|
||||
[App] --> [bitwarden WatchKit App]
|
||||
|
||||
[iOS.Autofill] --> [Core]
|
||||
[iOS.Autofill] --> [iOS.Core]
|
||||
|
||||
[iOS.Extension] --> [Core]
|
||||
[iOS.Extension] --> [iOS.Core]
|
||||
|
||||
[iOS.ShareExtension] --> [Core]
|
||||
[iOS.ShareExtension] --> [iOS.Core]
|
||||
|
||||
[bitwarden] --> [bitwarden WatchKit App]
|
||||
[bitwarden WatchKit App] --> [bitwarden WatchKit Extension]
|
||||
|
||||
@enduml
|
||||
```
|
||||
@@ -1,26 +0,0 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
# Overview
|
||||
|
||||
:::warning Legacy
|
||||
|
||||
This represents the **legacy** mobile app overview architecture done in .NET MAUI.
|
||||
|
||||
:::
|
||||
|
||||
The overall architecture of the mobile applications is pretty similar to the
|
||||
[web clients](../../clients/overview.md) one following a layered architecture:
|
||||
|
||||
- State
|
||||
- Services
|
||||
- Presentation
|
||||
|
||||
Even though the State and Services layers are pretty similar to the web ones the Presentation layer
|
||||
differs:
|
||||
|
||||
## Presentation
|
||||
|
||||
The presentation layer is implemented using .NET MAUI for the mobile apps, except for the watchOS
|
||||
one which uses SwiftUI [see ADR](../../adr/0017-watchOS-use-swift.md)
|
||||
@@ -1,186 +0,0 @@
|
||||
# watchOS
|
||||
|
||||
:::warning Legacy
|
||||
|
||||
This represents the **legacy** watchOS app architecture done in .NET MAUI.
|
||||
|
||||
:::
|
||||
|
||||
## Overall architecture
|
||||
|
||||
The watchOS application is organized as follows:
|
||||
|
||||
- `src/watchOS`: All the code specific to the watchOS platform
|
||||
- `bitwarden`: Stub iOS app so that the watchOS app has a companion app on Xcode
|
||||
- `bitwarden WatchKit App`: Main Watch app where we set assets.
|
||||
- `bitwarden WatchKit Extension`: All the logic and presentation logic for the Watch app is here
|
||||
|
||||
So almost all the things related to the watch app will be in the **WatchKit Extension**, the
|
||||
WatchKit App one will be only for assets and some configs.
|
||||
|
||||
Then in the Extension we have a layered architecture:
|
||||
|
||||
- State (it's a really simplified version of the iOS state)
|
||||
- Persistence (here we use `CoreData` to interact with the Database)
|
||||
- Services (totp generation, crypto services and business logic)
|
||||
- Presentation (use `SwiftUI` for the UI with an MVVM pattern)
|
||||
|
||||
## Integration with iOS
|
||||
|
||||
The watchOS app is developed using `Xcode` and `Swift` and we need to integrate it to the .NET MAUI
|
||||
iOS application.
|
||||
|
||||
For this, the `iOS.csproj` has been adapted taking a
|
||||
[solution](https://github.com/xamarin/xamarin-macios/issues/10070#issuecomment-1033428823) provided
|
||||
in the `Xamarin.Forms` GitHub repository and modified to our needs:
|
||||
|
||||
```xml
|
||||
<PropertyGroup>
|
||||
<WatchAppBuildPath Condition=" '$(Configuration)' == 'Debug' ">$(Home)/Library/Developer/Xcode/DerivedData/bitwarden-cbtqsueryycvflfzbsoteofskiyr/Build/Products</WatchAppBuildPath>
|
||||
<WatchAppBuildPath Condition=" '$(Configuration)' != 'Debug' ">$([System.IO.Path]::GetFullPath('$(MSBuildProjectDirectory)\..'))/watchOS/bitwarden.xcarchive/Products/Applications/bitwarden.app/Watch</WatchAppBuildPath>
|
||||
<WatchAppBundle>Bitwarden.app</WatchAppBundle>
|
||||
<WatchAppConfiguration Condition=" '$(Platform)' == 'iPhoneSimulator' ">watchsimulator</WatchAppConfiguration>
|
||||
<WatchAppConfiguration Condition=" '$(Platform)' == 'iPhone' ">watchos</WatchAppConfiguration>
|
||||
<WatchAppBundleFullPath Condition=" '$(Configuration)' == 'Debug' ">$(WatchAppBuildPath)/$(Configuration)-$(WatchAppConfiguration)/$(WatchAppBundle)</WatchAppBundleFullPath>
|
||||
<WatchAppBundleFullPath Condition=" '$(Configuration)' != 'Debug' ">$(WatchAppBuildPath)/$(WatchAppBundle)</WatchAppBundleFullPath>
|
||||
</PropertyGroup>
|
||||
|
||||
...
|
||||
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' AND Exists('$(WatchAppBundleFullPath)') ">
|
||||
<_ResolvedWatchAppReferences Include="$(WatchAppBundleFullPath)" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" '$(Configuration)' != 'Debug' ">
|
||||
<_ResolvedWatchAppReferences Include="$(WatchAppBundleFullPath)" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Condition=" '$(_ResolvedWatchAppReferences)' != '' ">
|
||||
<CodesignExtraArgs>--deep</CodesignExtraArgs>
|
||||
</PropertyGroup>
|
||||
<Target Name="PrintWatchAppBundleStatus" BeforeTargets="Build">
|
||||
<Message Text="WatchAppBundleFullPath: '$(WatchAppBundleFullPath)' exists" Condition=" Exists('$(WatchAppBundleFullPath)') " />
|
||||
<Message Text="WatchAppBundleFullPath: '$(WatchAppBundleFullPath)' does NOT exist" Condition=" !Exists('$(WatchAppBundleFullPath)') " />
|
||||
</Target>
|
||||
```
|
||||
|
||||
So on the `PropertyGroup` the `WatchAppBundleFullPath` is assembled together depending on the
|
||||
Configuration and the Platform taking the output of the Xcode watchOS app build. Then there are some
|
||||
`ItemGroup` to include the watch app depending on if it exists and the Configuration. The task
|
||||
`_ResolvedWatchAppReferences` is the one responsible to peek into the `Bitwarden.app` built by Xcode
|
||||
and if it finds a Watch app, it will just bundle it to the Xamarin iOS application. Finally, if the
|
||||
Watch app is bundled, deep signing is enabled and the build path is printed.
|
||||
|
||||
:::caution
|
||||
|
||||
As one can see in the csproj, to bundle the watchOS app into the iOS app one needs to target the
|
||||
correct platform. So if one is going to use a device, target the device on Xcode to build the
|
||||
watchOS app and after the build is done one can go to VS4M to build the iOS app (which will bundle
|
||||
the watchOS one) and run it on the device.
|
||||
|
||||
:::
|
||||
|
||||
## Synchronization between iPhone and Watch
|
||||
|
||||
In order to sync data between the iPhone and the Watch apps the
|
||||
[Watch Connectivity Framework](https://developer.apple.com/documentation/watchconnectivity) is used.
|
||||
|
||||
So there is a Watch Connectivity Manager on each side that is the interface used for the services on
|
||||
each platform to communicate.
|
||||
|
||||
For the sync communication, mainly
|
||||
[updateApplicationContext](https://developer.apple.com/documentation/watchconnectivity/wcsession/1615621-updateapplicationcontext)
|
||||
is used given that it always have the latest data sent available, it's sent in the background and
|
||||
the counterpart device doesn't necessarily needs to be in range (so it's cached until it can be
|
||||
delivered). Additionally,
|
||||
[sendMessage](https://developer.apple.com/documentation/watchconnectivity/wcsession/1615687-sendmessage)
|
||||
is also used to signal the counterpart of some action to take quickly (like triggering a sync from
|
||||
the Watch).
|
||||
|
||||
The `WatchDTO` is the object that is sent in the synchronization that has all the information for
|
||||
the Watch.
|
||||
|
||||
```kroki type=plantuml
|
||||
title= iOS part
|
||||
@startuml
|
||||
|
||||
title iOS
|
||||
|
||||
participant C as "Caller"
|
||||
participant BWDS as "BaseWatchDeviceService"
|
||||
participant WDS as "WatchDeviceService"
|
||||
participant WCSM as "WCSessionManager"
|
||||
boundary WCF as "Watch Connectivity Framework"
|
||||
|
||||
group Sync
|
||||
C->>BWDS: SyncDataToWatchAsync(...)
|
||||
BWDS->BWDS: GetStateAsync(...)
|
||||
BWDS->>WDS: SendDataToWatchAsync(...)
|
||||
WDS->>WCSM: SendBackgroundHighPriorityMessage(...)
|
||||
WCSM->>WCF: UpdateApplicationContext(...)
|
||||
end
|
||||
@enduml
|
||||
```
|
||||
|
||||
```kroki type=plantuml
|
||||
title= iOS part
|
||||
@startuml
|
||||
|
||||
title watchOS
|
||||
|
||||
boundary WCF as "Watch Connectivity Framework"
|
||||
participant WCM as "WatchConnectivityManager"
|
||||
participant SS as "StateService"
|
||||
participant ES as "EnvironmentService"
|
||||
participant CS as "CipherService"
|
||||
participant WCS as "watchConnectivitySubject"
|
||||
|
||||
group Sync
|
||||
WCF->>WCM: didReceiveApplicationContext(...)
|
||||
WCM->>SS: update state
|
||||
WCM->>ES: update environment
|
||||
WCM->>CS: saveCiphers(...)
|
||||
WCM->>WCS: fire notification change to subscribers
|
||||
end
|
||||
@enduml
|
||||
```
|
||||
|
||||
## States
|
||||
|
||||
The next ones are the states in which the Watch application can be at a given time:
|
||||
|
||||
- **Valid:** Everything it's ok and the user can see the vault ciphers with TOTP
|
||||
- **Need Login:** The user needs to log in using the iPhone
|
||||
- **Need Setup:** The user needs to set up an account with "Connect to Watch" enabled on their
|
||||
iPhone
|
||||
- **Need Premium:** The current account is not a premium account
|
||||
- **Need 2FA item:** The current account doesn't have any cipher with TOTP set up
|
||||
- **Syncing:** Displayed when changing accounts and syncing the new vault TOTPs
|
||||
- **Need Device Owner Auth:** The user needs to set up an Apple Watch Passcode in order to use the
|
||||
app
|
||||
|
||||
## Persistence and encryption
|
||||
|
||||
On the Watch [CoreData](https://developer.apple.com/documentation/coredata) is used as persistence
|
||||
for the ciphers. So in order to encrypt the data in them a Value Transformer in each encrypted
|
||||
attribute is used: `StringEncryptionTransformer`.
|
||||
|
||||
Inside the transformer a call to the `CryptoService` is used that ends up using
|
||||
[AES.GCM](https://developer.apple.com/documentation/cryptokit/aes/gcm) to encrypt the data with a
|
||||
256 bits [SymmetricKey](https://developer.apple.com/documentation/cryptokit/symmetrickey). The key
|
||||
is generated/loaded the first time something needs to be encrypted and stored in the device
|
||||
Keychain.
|
||||
|
||||
## Crash reporting
|
||||
|
||||
On all the other mobile applications, [AppCenter](https://appcenter.ms/) is being used as Crash
|
||||
reporting tool. However, it doesn't have support for watchOS (nor its internal library to handle
|
||||
crashes).
|
||||
|
||||
So, on the watchOS app [Firebase Crashlytics](https://firebase.google.com/docs/crashlytics) is used
|
||||
with basic crash reporting enabled (there is no handled error logging here yet). For this to work a
|
||||
`GoogleService-Info.plist` file is needed which is injected on the CI.
|
||||
|
||||
At the moment of writing this document, no plist is configured for dev environment so `Crashlytics`
|
||||
is enabled on **non-DEBUG** configurations.
|
||||
|
||||
There is a `Log` class to log errors happened in the app, but it's only enabled in **DEBUG**
|
||||
configuration.
|
||||
|
Before Width: | Height: | Size: 350 KiB |
@@ -1,177 +0,0 @@
|
||||
# Android
|
||||
|
||||
:::warning Legacy
|
||||
|
||||
Getting started the **legacy** Android app done in .NET MAUI.
|
||||
|
||||
:::
|
||||
|
||||
## Requirements
|
||||
|
||||
Before you start, you should have the recommended [Tools and Libraries](../../../tools/index.md)
|
||||
installed. You will also need to install:
|
||||
|
||||
1. Visual Studio 2022 / VS Code
|
||||
2. [.NET 8 (latest)](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)
|
||||
- Note: Even if you have an ARM64 based Mac (M1, M2, M3, etc.), you can install all x64 SDKs to
|
||||
run Android
|
||||
- On Visual Studio for Mac you may need to turn on the feature for .NET 8 by going to Visual
|
||||
Studio > Preferences > Preview Features > Use the .NET 8 SDK
|
||||
3. .NET MAUI Workload
|
||||
- You can install this by running `dotnet workload install maui`
|
||||
4. Android SDK 34
|
||||
- You can use the SDK manager in [Visual Studio][xamarin-vs], or [Android
|
||||
Studio][android-studio] to install this
|
||||
|
||||
To make sure you have the Android SDK and Emulator installed:
|
||||
|
||||
1. Open Visual Studio
|
||||
2. Click Tools > SDK Manager (under the Android subheading)
|
||||
3. Click the Tools tab
|
||||
4. Make sure the following items are installed:
|
||||
|
||||
- Android SDK tools (at least one version of the command-line tools)
|
||||
- Android SDK Platform-Tools
|
||||
- Android SDK Build Tools (at least one version)
|
||||
- Android Emulator
|
||||
|
||||
5. Click Apply Changes if you've marked anything for installation
|
||||
|
||||
If you've missed anything, Visual Studio should prompt you anyway.
|
||||
|
||||
## Android Development Setup
|
||||
|
||||
To set up a new virtual Android device for debugging:
|
||||
|
||||
1. Click Tools > Device Manager (under the Android subheading)
|
||||
2. Click New Device
|
||||
3. Set up the device you want to emulate - you can just choose the Base Device and leave the
|
||||
default settings if you're unsure
|
||||
4. Visual Studio will then download the image for that device. The download progress is shown in
|
||||
the progress in the Android Device Manager dialog.
|
||||
5. Once this has completed, the emulated Android device will be available as a build target under
|
||||
App > Debug > (name of device)
|
||||
|
||||
### ARM64 Macs
|
||||
|
||||
1. Install and open Android Studio
|
||||
2. In the top navbar, click on Android Studio > Settings > Appearance & Behavior (tab) > System
|
||||
Settings > Android SDK
|
||||
3. In the SDK Platforms tab, ensure the "Show Package Details" checkbox is checked (located in the
|
||||
bottom-right)
|
||||
4. Bellow each Android API you'll see several System Images, pick one of the `ARM 64 v8a` and wait
|
||||
for it to download
|
||||
5. Go to View > Tool Windows > Device Manager
|
||||
6. Inside Device Manager, create a device using the previously downloaded system image
|
||||
|
||||

|
||||
|
||||
## F-Droid
|
||||
|
||||
On `App.csproj` and `Core.csproj` we can now pass `/p:CustomConstants=FDROID` when
|
||||
building/releasing so that the `FDROID` constant is added to the defined ones at the project level
|
||||
and we can use that with precompiler directives, e.g.:
|
||||
|
||||
```c#
|
||||
#if FDROID
|
||||
// perform operation only for FDROID.
|
||||
#endif
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
There are currently a few problems on Visual Studio for Mac for building correctly the projects, so
|
||||
if you encounter some errors build using the CLI (previously removing bin/obj folders):
|
||||
|
||||
```
|
||||
dotnet build -f net8.0-android -c Debug
|
||||
```
|
||||
|
||||
## Testing and Debugging
|
||||
|
||||
### Using the Android Emulator
|
||||
|
||||
In order to access `localhost:<port>` resources in the Android Emulator when debugging using Visual
|
||||
Studio on your Mac natively, you'll need to configure the endpoint addresses using
|
||||
`<http://10.0.2.2:<port>`\> in order to access `localhost`, which maps the Android proxy by design.
|
||||
|
||||
[xamarin-vs]: https://learn.microsoft.com/en-us/xamarin/android/get-started/installation/android-sdk
|
||||
[android-studio]: https://developer.android.com/studio/releases/platforms
|
||||
|
||||
### Using Server Tunneling
|
||||
|
||||
Instead of configuring your device or emulator, you can instead use a
|
||||
[proxy tunnel to your local server](../../../server/tunnel.md) and have your app connect to it
|
||||
directly.
|
||||
|
||||
### Push Notifications
|
||||
|
||||
The default configuration for the Android app is to register itself to the same environment as
|
||||
Bitwarden's QA Cloud. This means that if you try to debug the app using the production endpoints you
|
||||
won't be able to receive Live Sync updates or Passwordless login requests.
|
||||
|
||||
<Bitwarden>
|
||||
|
||||
So, in order to receive notifications while debugging, you have two options:
|
||||
|
||||
- Use QA Cloud endpoints for the Api and Identity, or
|
||||
- Use a local server setup where the Api is connected to QA Azure Notification Hub
|
||||
|
||||
</Bitwarden>
|
||||
|
||||
### Testing Passwordless Locally
|
||||
|
||||
Before you can start testing and debugging passwordless logins, make sure your local server setup is
|
||||
running correctly ([server setup](../../../server/guide.md)). You should also be able to deploy your
|
||||
Android app to your device or emulator.
|
||||
|
||||
:::note
|
||||
|
||||
Debugging and testing passwordless authentication is limited by
|
||||
[push notifications](#push-notifications).
|
||||
|
||||
:::
|
||||
|
||||
Testing passwordless notifications:
|
||||
|
||||
1. Start your local server (`Api`, `Identity`, `Notifications`)
|
||||
2. Make sure your mobile device can [connect to your local server](#using-server-tunneling)
|
||||
3. [Start the web client](../../../clients/web-vault/index.mdx), as you will need it to make login
|
||||
requests
|
||||
4. Deploy the Android app to your device or emulator
|
||||
5. After deployment, open the app, login to your QA account and activate passwordless login requests
|
||||
in settings
|
||||
6. Open the web vault using your preferred browser (ex: http://localhost:8080)
|
||||
7. Enter the email address of an account that has previously authenticated on that device (i.e. is a
|
||||
"known device") and click Continue. When presented with the login options, click click Login with
|
||||
Device.
|
||||
8. Check mobile device for the notification
|
||||
|
||||
<Bitwarden>
|
||||
|
||||
## AndroidX Credentials
|
||||
|
||||
Currently, the
|
||||
[androidx.credentials](https://developer.android.com/jetpack/androidx/releases/credentials) official
|
||||
binding has some bugs and we cannot use it yet. Because of this, we made a binding ourselves which
|
||||
is located in here:
|
||||
[Xamarin.AndroidX.Credentials](https://github.com/bitwarden/xamarin.androidx.credentials).
|
||||
|
||||
As of today, we are using version 1.2.0.
|
||||
|
||||
In the projects, the package is added as a local NuGet package located in
|
||||
`lib/android/Xamarin.AndroidX.Credentials` and this source is already configured in the
|
||||
`nuget.config` file.
|
||||
|
||||
In the case a change is needed on the binding, create a new local NuGet package and replace it in
|
||||
the aforementioned source.
|
||||
|
||||
:::warning
|
||||
|
||||
Do not add the project to the solution and as a project reference to the `App.csproj` /
|
||||
`Core.csproj` this will strangely make the iOS app crash on start because of solution configuration.
|
||||
Even though we couldn't find the root cause, this is the effect caused by this action.
|
||||
|
||||
:::
|
||||
|
||||
</Bitwarden>
|
||||
@@ -1,107 +0,0 @@
|
||||
---
|
||||
sidebar_position: 4
|
||||
---
|
||||
|
||||
# .NET MAUI (legacy)
|
||||
|
||||
:::warning Legacy
|
||||
|
||||
Getting started the **legacy** mobile app done in .NET MAUI.
|
||||
|
||||
:::
|
||||
|
||||
## Configure Git blame
|
||||
|
||||
We recommend that you configure git to ignore the Prettier revision:
|
||||
|
||||
```bash
|
||||
git config blame.ignoreRevsFile .git-blame-ignore-revs
|
||||
```
|
||||
|
||||
## Android Development
|
||||
|
||||
See the [Android Mobile app](./android/index.md) page to set up an Android development environment.
|
||||
|
||||
## iOS Development
|
||||
|
||||
<Bitwarden>
|
||||
|
||||
See the [iOS Mobile app](./ios/index.mdx) page to set up an iOS development environment.
|
||||
|
||||
</Bitwarden>
|
||||
|
||||
<Community>
|
||||
|
||||
Unfortunately, iOS development requires provisioning profiles and other capabilities only available
|
||||
to internal team members. We do not have any documentation for community developers at this time.
|
||||
|
||||
</Community>
|
||||
|
||||
## watchOS Development
|
||||
|
||||
<Bitwarden>
|
||||
|
||||
See the [watchOS app](./watchos) page to set up an watchOS development environment.
|
||||
|
||||
</Bitwarden>
|
||||
|
||||
<Community>
|
||||
|
||||
Unfortunately, watchOS development requires provisioning profiles and other capabilities only
|
||||
available to internal team members. We do not have any documentation for community developers at
|
||||
this time.
|
||||
|
||||
</Community>
|
||||
|
||||
## Unit tests
|
||||
|
||||
:::info TL;DR;
|
||||
|
||||
In order to run unit tests add the argument `/p:CustomConstants=UT` on the `dotnet` command for
|
||||
building/running. To work on Unit testing or use a Test runner uncomment the `CustomConstants` line
|
||||
on the `Directory.Build.props`
|
||||
|
||||
:::
|
||||
|
||||
Given that the `Core.csproj` is a MAUI project with `net8.0-android;net8.0-ios` target frameworks
|
||||
and we need `net8.0` for the tests we need a way to add that. The `Core.Test.csproj` has `net8.0` as
|
||||
a target so by adding the the argument `/p:CustomConstants=UT` we add `UT` as a constant to use in
|
||||
the projects. With that in place the next things happen:
|
||||
|
||||
- `UT` is added as a constant to use by precompiler directives
|
||||
- `Core.csproj` is changed to add `net8.0` as a target framework for unit tests
|
||||
- `FFImageLoading` is removed as a reference given that it doesn't support `net8.0`. Because of
|
||||
this, now we have a wrapped `CachedImage` that uses the library one if it's not `UT` and a custom
|
||||
one with NOOP implementation for `UT`
|
||||
|
||||
So if one wants to build the test project, one needs to go to `test/Core.Test` and run:
|
||||
|
||||
```bash
|
||||
dotnet build -f net8.0 /p:CustomConstants=UT
|
||||
```
|
||||
|
||||
and to run the tests go to the same folder and run:
|
||||
|
||||
```bash
|
||||
dotnet test -f net8.0 /p:CustomConstants=UT
|
||||
```
|
||||
|
||||
Finally, when working on the `Core.Test` project or when wanting to use a Test runner, go to the
|
||||
`Directory.Build.props` (located in the root) and uncomment the line referencing `CustomConstants`
|
||||
so that everything is loaded accordingly in the project. Because of some issues, the referenced
|
||||
projects, e.g. `Core`, are only included when the `UT` constant is in place. By uncommenting this
|
||||
line the projects will be referenced and one can work on that project or run the tests from a Test
|
||||
runner.
|
||||
|
||||
## Custom constants
|
||||
|
||||
There are custom constants to be used by the parameter `/p:CustomConstants={Value}` when
|
||||
building/running/releasing:
|
||||
|
||||
- `FDROID`: This is used to indicate that it's and F-Droid build/release
|
||||
([want to know more?](./android/index.md#f-droid))
|
||||
- `UT`: This is used when building/running the test projects or when working on one of them
|
||||
([want to know more?](#unit-tests))
|
||||
|
||||
These constants are added to the defined ones, so anyone can use them in the code with precompiler
|
||||
directives.
|
||||
@@ -1,440 +0,0 @@
|
||||
---
|
||||
sidebar_custom_props:
|
||||
access: bitwarden
|
||||
---
|
||||
|
||||
import Tabs from "@theme/Tabs";
|
||||
import TabItem from "@theme/TabItem";
|
||||
|
||||
# iOS
|
||||
|
||||
:::warning Legacy
|
||||
|
||||
Getting started the **legacy** iOS app done in .NET MAUI.
|
||||
|
||||
:::
|
||||
|
||||
## Requirements
|
||||
|
||||
1. Visual Studio 2022 / VS Code
|
||||
2. [.NET 8 (latest)](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)
|
||||
- On Visual Studio for Mac you may need to turn on the feature for .NET 8 by going to Visual
|
||||
Studio > Preferences > Preview Features > Use the .NET 8 SDK
|
||||
3. .NET MAUI Workload
|
||||
- You can install this by running `dotnet workload install maui`
|
||||
4. A Mac with Xcode 15.0 installed
|
||||
|
||||
## Apple Developer Account Setup
|
||||
|
||||
1. Accept your invite to the Bitwarden Apple Developer team. You should get a request in your email
|
||||
with the subject "You're invited to join a development team." Click the link, "Accept Invitation"
|
||||
and you'll be prompted to create an Apple ID for your Bitwarden email address. If you didn't
|
||||
receive this email, contact the IT department (@IT in slack). Accept the terms and conditions and
|
||||
complete the sign up flow
|
||||
|
||||
2. Go to [Apple ID Online](https://appleid.apple.com/) and log in with your new Apple ID. Set up
|
||||
2-factor authentication (using mobile phone and/or trusted device) - this is critical because
|
||||
Apple no longer allows "developer" accounts without MFA, but it won't tell you that when your
|
||||
build fails locally
|
||||
|
||||
3. Go to [App Store Connect](https://appstoreconnect.apple.com/) and accept the terms and conditions
|
||||
|
||||
4. Ensure you have access to the Bitwarden team and team app profiles
|
||||
|
||||
5. Go to [Apple Developer Account](https://developer.apple.com/account/) and go to the
|
||||
"Certificates, IDs & Profiles" menu item. Check that you can see the 8bit Solutions LLC
|
||||
Certificates in the Certificates section, and the Bitwarden profiles in the Profiles section. If
|
||||
any of this is missing, ask the IT department (@IT #tech-support in slack) for the additional
|
||||
roles / permissions
|
||||
|
||||
## macOS Setup
|
||||
|
||||
Next, you need to get your Mac environment set up for building and running the Bitwarden iOS mobile
|
||||
project. This requires creating the necessary developer provisioning profiles for code signing and
|
||||
execution on your Mac through Xcode. Visual Studio has a simple process to get all of the
|
||||
provisioning profiles, however this is prone to fail without much feedback. Try the Visual Studio
|
||||
instructions ("The Easy Way") first, and fallback to the Xcode instructions (“The Hard Way”) if
|
||||
required.
|
||||
|
||||
### Visual Studio: The Easy Way
|
||||
|
||||
1. Open Visual Studio for Mac
|
||||
|
||||
2. Go to Preferences > Publishing > Apple Developer Accounts
|
||||
|
||||
3. Click “Add”, choose "Enterprise Account", and sign in with your previously configured Apple
|
||||
Developer account
|
||||
|
||||
:::note
|
||||
|
||||
If you receive a "Failed to synchronize with Apple Developer Portal" error, you’re missing
|
||||
additional roles / permissions.
|
||||
|
||||
:::
|
||||
|
||||
After signing in successfully, you should see your account in the list and “Bitwarden Inc” in
|
||||
the account teams list
|
||||
|
||||
4. Click “View Details…”
|
||||
|
||||
5. If you don’t have a valid Apple Development certificate, click Create certificate > Apple
|
||||
Development
|
||||
|
||||
6. Click “Download All Profiles”
|
||||
|
||||
7. You should now be able to run the app by setting
|
||||
`iOS > Debug | iPhone Simulator > [pick any iOS Simulator]` in the top left corner and pressing
|
||||
Play
|
||||
|
||||

|
||||
|
||||
If this worked, you can skip the next section.
|
||||
|
||||
If you only have the option "Generic Simulator", with a message to lower the 'Deployment Target',
|
||||
your version of MAUI may not yet support the version of Xcode that you are using (as discussed
|
||||
[here](https://github.com/xamarin/xamarin-macios/issues/15954#issuecomment-1246025735)).
|
||||
|
||||

|
||||
|
||||
To work around this issue, try [downloading](https://developer.apple.com/download/all/) and
|
||||
installing an older version of Xcode from Apple (you can look for guidance on which Xcode version to
|
||||
use from the Xamarin.iOS [release notes](https://github.com/xamarin/xamarin-macios/releases) (this
|
||||
applies to MAUI as well). After installing the new version of Xcode, restart Visual Studio and load
|
||||
your project to verify your available simulator options.
|
||||
|
||||
:::note
|
||||
|
||||
If you need multiple versions of Xcode installed on your development machine, you can rename the
|
||||
`Xcode.app` file extracted from your download to something else (e.g. "Xcode_14_2.app") before
|
||||
placing it in your Applications folder. You can then switch between Xcode versions by using
|
||||
`xcode-select` from the command line. e.g.:
|
||||
|
||||
```shell
|
||||
sudo xcode-select -s /Applications/Xcode_14_2.app
|
||||
```
|
||||
|
||||
You may achieve similar results with tooling such as
|
||||
[Xcodes.app](https://github.com/XcodesOrg/XcodesApp)
|
||||
|
||||
:::
|
||||
|
||||
### Xcode: The Hard Way
|
||||
|
||||
:::note
|
||||
|
||||
If you're the next person to follow these instructions, please commit and upload the Xcode project
|
||||
files you create so we can streamline this process.
|
||||
|
||||
:::
|
||||
|
||||
Only try these instructions if the Visual Studio instructions above didn't work for you.
|
||||
|
||||
1. Open Xcode
|
||||
|
||||
2. Accept any defaults, ensure any extensions/add-ons have been installed, etc.
|
||||
|
||||
3. Create new project... > iOS > App
|
||||
|
||||
4. Use the following options for your new project:
|
||||
|
||||
- Product Name: "bitwarden"
|
||||
|
||||
- Team: Bitwarden Inc (if this is missing, double check your Apple Developer Account setup
|
||||
above)
|
||||
|
||||
- Organization Identifier: "com.8bit"
|
||||
|
||||
- Bundle Identifier (automatically generated): "com.8bit.bitwarden"
|
||||
|
||||
- Language: Objective-C
|
||||
|
||||
- User Interface: Storyboard
|
||||
|
||||
- Leave all other checkboxes unchecked (or uncheck them)
|
||||
|
||||

|
||||
|
||||
5. Click Next, save to the default location and then click "Create"
|
||||
|
||||
6. On the project configuration page, click the "Signing & Capabilities" tab
|
||||
|
||||
7. Make sure you have the following defaults:
|
||||
|
||||
- Automatically manage signing: (checked)
|
||||
|
||||
- Team: Bitwarden Inc
|
||||
|
||||
- Provisioning Profile: Xcode Managed Profile
|
||||
|
||||
- Signing Certificate: your Apple ID/Name
|
||||
|
||||

|
||||
|
||||
8. From the menu bar, click Product > Build
|
||||
|
||||
9. Repeat Steps 3-8, with the following changes in step 4:
|
||||
|
||||
- Product Name: "find-login-action-extension"
|
||||
|
||||
- Organization Identifier: "com.8bit.bitwarden"
|
||||
|
||||
- Bundle Identifier (automatically generated): "com.8bit.bitwarden.find-login-action-extension"
|
||||
|
||||
10. Repeat Steps 3-8, with the following changes in step 4:
|
||||
|
||||
- Product Name: "autofill"
|
||||
|
||||
- Organization Identifier: "com.8bit.bitwarden"
|
||||
|
||||
- Bundle Identifier (automatically generated): "com.8bit.bitwarden.autofill"
|
||||
|
||||
11. Repeat Steps 3-8, with the following changes in step 4:
|
||||
|
||||
- Product Name: "share-extension"
|
||||
|
||||
- Organization Identifier: "com.8bit.bitwarden"
|
||||
|
||||
- Bundle Identifier (automatically generated): "com.8bit.bitwarden.share-extension"
|
||||
|
||||
12. If you have a physical device (e.g. iPhone or iPad) that you want to use for testing, you will
|
||||
also need to do the following for each of the Xcode projects you just created:
|
||||
|
||||
- connect the device with a cable
|
||||
|
||||
- select your device as as the build target in Xcode
|
||||
|
||||
- from the menu bar, click Product > Build
|
||||
|
||||
- agree to register your device if asked
|
||||
|
||||
:::note
|
||||
|
||||
Sometimes these profiles can mess up. If you have issues running on your physical device (or
|
||||
simulator) try running `rm -r ~/Library/MobileDevice/Provisioning\ Profiles` to clear them out.
|
||||
Build each Xcode project again to regenerate them.
|
||||
|
||||
:::
|
||||
|
||||
## Visual Studio
|
||||
|
||||
Next, we need to configure your Visual Studio environment for development.
|
||||
|
||||
<Tabs groupId="os">
|
||||
<TabItem value="win" label="Windows" default>
|
||||
|
||||
1. Connect to the Mac that you just completed the above steps on
|
||||
|
||||
2. Open Visual Studio and click Tools > iOS > Pair to Mac
|
||||
|
||||
3. Scan for and select your machine. If you don't see it, click the "Add Mac..." button and put in
|
||||
the Mac name or IP address. If you don't know your Mac name (or you're in a Windows VM on your
|
||||
Mac), go to your Mac and open System Preferences > Sharing and look for the ".local" address of
|
||||
your machine
|
||||
|
||||
4. Provide your Username and Password for macOS when prompted
|
||||
|
||||
5. Once paired, close the Pair Mac window
|
||||
|
||||
6. Change your active build profile to Debug > iPhoneSimulator > iOS
|
||||
|
||||
7. Rebuild the iOS project from Solution Explorer
|
||||
|
||||
8. You can now debug using the iOS Simulator
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="mac" label="macOS">
|
||||
|
||||
1. Check that command line tools are installed:
|
||||
|
||||
1. Open Xcode
|
||||
|
||||
2. From the menu bar, click Xcode > Preferences > Locations
|
||||
|
||||
3. Make sure an Xcode version is selected under "Command Line Tools"
|
||||
|
||||
2. Open Visual Studio for Mac
|
||||
|
||||
3. Open the mobile solution file (`bitwarden-mobile.sln`) in the root of your local mobile
|
||||
repository
|
||||
|
||||
4. In the top bar, you should be able to select App > Debug > select your model and click run (or
|
||||
your physical device if you set one up)
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Building
|
||||
|
||||
To build from the CLI, navigate to the application directory:
|
||||
|
||||
For device:
|
||||
|
||||
```
|
||||
cd src/App
|
||||
dotnet build -f net8.0-ios -c Debug -r ios-arm64
|
||||
```
|
||||
|
||||
For simulator:
|
||||
|
||||
```
|
||||
cd src/App
|
||||
dotnet build -f net8.0-ios -c Debug -r iossimulator-x64
|
||||
```
|
||||
|
||||
You can also use the IDE but keep in mind:
|
||||
|
||||
:::tip Visual Studio for Mac
|
||||
|
||||
There are currently a few problems on Visual Studio for Mac for building correctly the projects, so
|
||||
if you encounter some errors, build using the CLI being into the `src/App` folder (previously
|
||||
removing bin/obj folders).
|
||||
|
||||
:::
|
||||
|
||||
:::tip Argon2Id
|
||||
|
||||
If you find any errors regarding argon2Id library when building for simulator, please be sure that
|
||||
you are building for runtime identifier `iossimulator-x64` as currently the library doesn't support
|
||||
`iossimulator-arm64`.
|
||||
|
||||
:::
|
||||
|
||||
:::tip Troubleshooting common mistakes
|
||||
|
||||
If you find the next error:
|
||||
|
||||
> `error NETSDK1134`: Building a solution with a specific RuntimeIdentifier is not supported. If you
|
||||
> would like to publish for a single RID, specify the RID at the individual project level instead
|
||||
|
||||
you almost surely are trying to build the app from the root folder. Instead go to `src/App` and try
|
||||
building again.
|
||||
|
||||
:::
|
||||
|
||||
### Argon2Id library loading
|
||||
|
||||
The Argon2Id library (`libargon2.a`) is loaded using `MTouchExtraArgs` in almost all projects of the
|
||||
solution. In order to make this simpler a property was added into **Directory.Build.props** called
|
||||
`Argon2IdLoadMtouchExtraArgs` which has the code to fill in the extra args parameter. Each project
|
||||
is configured with this property so this is only added on the correct runtime identifiers and we can
|
||||
build the app successfully on each case.
|
||||
|
||||
### Ignoring extensions / watchOS app
|
||||
|
||||
Sometimes we need to quickly build the app or maybe some configuration on the iOS extensions or the
|
||||
watchOS app gets in the way. In order to have a fast way to only care about the main app two
|
||||
properties were added to the **Directory.Build.Props** to help with this:
|
||||
|
||||
- `IncludeBitwardeniOSExtensions`: If `True` then all the iOS extensions will be included on the
|
||||
building of the main app, otherwise they will be skipped.
|
||||
- `IncludeBitwardenWatchOSApp`: If `True` then the watchOS app will be included on the building of
|
||||
the main app, otherwise it will be skipped.
|
||||
|
||||
:::warning Shared code
|
||||
|
||||
Toggling these off can provide a faster developer experience which is really useful in a lot of
|
||||
scenarios, but always bear in mind that a lot of things are shared between the main app and the
|
||||
extensions so before pushing your work, test again with everything enabled just in case.
|
||||
|
||||
:::
|
||||
|
||||
### Release mode locally
|
||||
|
||||
There are some issues that require us to build the app on **Release** configuration but locally
|
||||
without going through the CI/CD pipeline. The problem is that we don't have the code signing details
|
||||
for Distribution locally. To overcome this we can use the same `CodesignProvision` and `CodesignKey`
|
||||
we use for **Debug** but on the **Release** config. The thing is that it's a bit cumbersome to
|
||||
change that on every project so two properties were added to the **Directory.Build.Props** to help
|
||||
with this:
|
||||
|
||||
- `ReleaseCodesignProvision`: `CodesignProvision` for Release config on all projects
|
||||
- `ReleaseCodesignKey`: `CodesignKey` for Release config on all projects
|
||||
|
||||
By replacing their values, all projects will have their values applied so it's easier to build the
|
||||
app in **Release** mode locally.
|
||||
|
||||
## Debugging
|
||||
|
||||
### iPhone Simulator
|
||||
|
||||
The iPhone Simulator has access to localhost and you can point the client at your local dev server
|
||||
as usual. However, the app will require https by default. To allow http for testing purposes, follow
|
||||
these steps.
|
||||
|
||||
1. Open `src/App/Platforms/iOS/Info.plist` in Visual Studio Code or another editor so that you can
|
||||
edit the raw XML. (Don't use the Property List Editor in Visual Studio.)
|
||||
|
||||
2. Add the following code in the top-level `<dict>` element:
|
||||
|
||||
```xml
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<false/>
|
||||
<key>NSExceptionDomains</key>
|
||||
<dict>
|
||||
<key>localhost</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
<key>NSIncludesSubdomains</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
```
|
||||
|
||||
3. Save and exit `Info.plist`
|
||||
|
||||
4. Press <kbd>Command</kbd> + <kbd>B</kbd> to force a new build before launching
|
||||
|
||||
5. Don't push these changes :)
|
||||
|
||||
### iPhone device
|
||||
|
||||
The device doesn’t have direct access to your Mac’s localhost, so you can follow
|
||||
[this guide to connect them](https://ymoondhra.medium.com/how-to-run-localhost-on-your-iphone-4110a54d1896).
|
||||
|
||||
After you do that, you’ll have to also modify the `Info.plist` to allow http for testing purposes as
|
||||
explained before on the simulator testing.
|
||||
|
||||
It’s also highly likely that you need to change the `launchSettings.json` on Server, on `Properties`
|
||||
of each project. There you need to change the `applicationUrl` of `iisSettings -> iisExpress` and of
|
||||
`profiles -> Identify` so that instead of `localhost` it says `name.local` where `name` is the
|
||||
computer name you set on Mac’s Sharing config.
|
||||
|
||||
Before you actually test on the app, open a browser and try to connect to the `Api` by going to
|
||||
`http://name.local:4000/alive` . If this doesn’t work then review the steps on the guide or the
|
||||
server configuration. Make sure you have your `User secrets` up to date as well.
|
||||
|
||||
Finally, you’ll have to configure the `Api` and `Identity` urls on the phone to use
|
||||
`http://name.local:4000` and `http://name.local:33656` where `name` is the computer name you set on
|
||||
Mac’s Sharing config.
|
||||
|
||||
### iOS Extensions
|
||||
|
||||
1. Set the iOS Extension project as Startup project
|
||||
|
||||
2. Press Run
|
||||
|
||||
3. You will receive a popup saying "Waiting for the debugger to connect..."
|
||||
|
||||
4. Don’t open the Bitwarden app (otherwise the debugger will connect to it instead of the
|
||||
extension). Instead trigger the extension
|
||||
|
||||
5. Your extension breakpoints should now be hit
|
||||
|
||||
For example: if you want to debug the **iOS.Autofill** extension, you would complete steps 1 - 3,
|
||||
then go to your iOS device, open a browser, go to a login, tap the key icon and open Bitwarden from
|
||||
the bottom popup.
|
||||
|
||||
### Using Server Tunneling
|
||||
|
||||
Instead of configuring your device or emulator to ignore SSL certificates, you can instead use a
|
||||
[proxy tunnel to your local server](../../../server/tunnel.md) and have your app connect to it
|
||||
directly.
|
||||
|
||||
### Push Notifications (Live Sync & Passwordless)
|
||||
|
||||
Push notifications are not currently available for debug deployments. They are only supported on
|
||||
TestFlight and production builds.
|
||||
|
Before Width: | Height: | Size: 95 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 307 KiB |
|
Before Width: | Height: | Size: 28 KiB |
@@ -1,101 +0,0 @@
|
||||
---
|
||||
sidebar_custom_props:
|
||||
access: bitwarden
|
||||
---
|
||||
|
||||
# watchOS
|
||||
|
||||
:::warning Legacy
|
||||
|
||||
Getting started the **legacy** watchOS app done in .NET MAUI.
|
||||
|
||||
:::
|
||||
|
||||
## Requirements
|
||||
|
||||
Follow the [iOS Setup](../ios/index.mdx).
|
||||
|
||||
In order for everything to work properly **devices** are needed. On simulators the synchronization
|
||||
won't work (and some other parts may not work as well). Also, have Bluetooth enabled if possible to
|
||||
ease the sync between devices and the debugging communication.
|
||||
|
||||
It's recommended to read the
|
||||
[Watch Architecture](../../../../../architecture/mobile-clients/net-maui-legacy/watchOS) as well.
|
||||
|
||||
## macOS Setup
|
||||
|
||||
Having followed the macOS setup of iOS, no additional configuration is needed given that when the
|
||||
project is opened in Xcode it will automatically have the provisioning profiles set up.
|
||||
|
||||
## Debugging
|
||||
|
||||
There are two parts from where to debug:
|
||||
|
||||
- From Visual Studio for Mac (the iOS app)
|
||||
- From Xcode (the watchOS app)
|
||||
|
||||
For now, there is no way to debug both apps (iOS and watchOS) at the same time given that from MAUI
|
||||
there is no access to debug information of the watchOS app and from Xcode an iOS stub app is
|
||||
installed on the iPhone to debug it. So, at the moment of debugging one needs to choose which part
|
||||
to have information about, therefore whether to debug from VS4M or from Xcode.
|
||||
|
||||
:::caution
|
||||
|
||||
When debugging from Xcode the MAUI iOS app will be replaced with the stub one from Xcode. So any
|
||||
configuration on the iOS app will be lost (like server urls)
|
||||
|
||||
When debugging from VS4M, uninstall the previous watchOS app (if any) from the Apple Watch in
|
||||
between builds to have it always up to date (there are times that if one doesn't uninstall the
|
||||
previous watchOS app it doesn't get updated)
|
||||
|
||||
:::
|
||||
|
||||
:::tip
|
||||
|
||||
If one needs to get the logs or use the _Console_ app to see the logs from the watch then one needs
|
||||
to install the `sysdiagnose` profile for watchOS from Apple Developer site
|
||||
[here](https://developer.apple.com/bug-reporting/profiles-and-logs/?name=sysdiagnose) into the
|
||||
paired iPhone and after that restart both devices in order for the logs to work.
|
||||
|
||||
:::
|
||||
|
||||
### Building
|
||||
|
||||
Given that the MAUI iOS app needs the output of the build of Xcode, one needs to build the watchOS
|
||||
app from Xcode first and then from VS4M build the iOS app to run it on the device.
|
||||
|
||||
The output of Xcode build is stored in a location pretty similar to the next one that is configured
|
||||
in the `iOS.csproj`:
|
||||
|
||||
```xml
|
||||
<PropertyGroup>
|
||||
<WatchAppBuildPath Condition=" '$(Configuration)' == 'Debug' ">$(Home)/Library/Developer/Xcode/DerivedData/bitwarden-cbtqsueryycvflfzbsoteofskiyr/Build/Products</WatchAppBuildPath>
|
||||
```
|
||||
|
||||
It's highly likely that the folder `bitwarden-cbtqsueryycvflfzbsoteofskiyr` won't be the same on
|
||||
every Mac. So one needs to change that part in `iOS.csproj` to the one created automatically by
|
||||
Xcode locally.
|
||||
|
||||
To know exactly which is the path: Open the Project in Xcode -> Go to Product -> Show Build Folder
|
||||
in Finder.
|
||||
|
||||
_This needs to be improved to have a fixed location or an easier way to get it automatically._
|
||||
|
||||
:::caution
|
||||
|
||||
One needs to take special attention to target the same platform on both IDEs. Therefore when running
|
||||
on a device, target the device both in Xcode and on VS4M when building so that the watchOS app is
|
||||
bundled correctly. Also one needs to make sure that "bitwarden WatchKit app" scheme is selected.
|
||||
|
||||
:::
|
||||
|
||||
### Synchronization
|
||||
|
||||
There is no way to debug the synchronization completely at the same time for the reasons
|
||||
aforementioned.
|
||||
|
||||
So one can debug one end (iOS) or the other (watchOS).
|
||||
|
||||
If needed to check something on both ends "at the same time" (like to check why a message is not
|
||||
sent/arrived), one needs to use console logging or adapt part of the MAUI code to the iOS stub app
|
||||
on Xcode and debug the synchronization from Xcode.
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "8.0.402",
|
||||
"rollForward": "disable"
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
Copyright 2018 Read Evaluate Press, LLC
|
||||
|
||||
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.
|
||||
@@ -1,32 +0,0 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'MessagePack-FlightSchool'
|
||||
s.module_name = 'MessagePack'
|
||||
s.version = '1.2.4'
|
||||
s.summary = 'A MessagePack encoder and decoder for Codable types.'
|
||||
|
||||
s.description = <<-DESC
|
||||
This functionality is discussed in Chapter 7 of
|
||||
Flight School Guide to Swift Codable.
|
||||
DESC
|
||||
|
||||
s.homepage = 'https://flight.school/books/codable/'
|
||||
|
||||
s.license = { type: 'MIT', file: 'LICENSE.md' }
|
||||
|
||||
s.author = { 'Mattt' => 'mattt@flight.school' }
|
||||
|
||||
s.social_media_url = 'https://twitter.com/mattt'
|
||||
|
||||
s.ios.deployment_target = '8.0'
|
||||
s.osx.deployment_target = '10.10'
|
||||
s.watchos.deployment_target = '2.0'
|
||||
s.tvos.deployment_target = '9.0'
|
||||
|
||||
s.source = { git: 'https://github.com/Flight-School/MessagePack.git',
|
||||
tag: s.version.to_s }
|
||||
|
||||
s.source_files = 'Sources/**/*.swift'
|
||||
|
||||
s.swift_version = '4.2'
|
||||
s.static_framework = true
|
||||
end
|
||||
@@ -1,13 +0,0 @@
|
||||
import MessagePack
|
||||
|
||||
let encoder = MessagePackEncoder()
|
||||
|
||||
let value: String = "hello"
|
||||
let encodedData = try encoder.encode(value)
|
||||
|
||||
print("Bytes: ", encodedData.map{ String($0, radix: 16, uppercase: true) })
|
||||
|
||||
let decoder = MessagePackDecoder()
|
||||
let decodedValue = try decoder.decode(String.self, from: encodedData)
|
||||
|
||||
decodedValue == value
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<playground version='5.0' target-platform='macos' executeOnSourceChanges='false'>
|
||||
<timeline fileName='timeline.xctimeline'/>
|
||||
</playground>
|
||||
@@ -1,25 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>BNDL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,25 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,543 +0,0 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXAggregateTarget section */
|
||||
"MessagePack::MessagePackPackageTests::ProductTarget" /* MessagePackPackageTests */ = {
|
||||
isa = PBXAggregateTarget;
|
||||
buildConfigurationList = OBJ_57 /* Build configuration list for PBXAggregateTarget "MessagePackPackageTests" */;
|
||||
buildPhases = (
|
||||
);
|
||||
dependencies = (
|
||||
OBJ_60 /* PBXTargetDependency */,
|
||||
);
|
||||
name = MessagePackPackageTests;
|
||||
productName = MessagePackPackageTests;
|
||||
};
|
||||
/* End PBXAggregateTarget section */
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
1BC312FF2992DE9C00177F2A /* DataSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BC312FE2992DE9C00177F2A /* DataSpec.swift */; };
|
||||
OBJ_38 /* AnyCodingKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* AnyCodingKey.swift */; };
|
||||
OBJ_39 /* Box.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_10 /* Box.swift */; };
|
||||
OBJ_40 /* KeyedDecodingContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_12 /* KeyedDecodingContainer.swift */; };
|
||||
OBJ_41 /* MessagePackDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_13 /* MessagePackDecoder.swift */; };
|
||||
OBJ_42 /* SingleValueDecodingContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_14 /* SingleValueDecodingContainer.swift */; };
|
||||
OBJ_43 /* UnkeyedDecodingContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_15 /* UnkeyedDecodingContainer.swift */; };
|
||||
OBJ_44 /* KeyedEncodingContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_17 /* KeyedEncodingContainer.swift */; };
|
||||
OBJ_45 /* MessagePackEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_18 /* MessagePackEncoder.swift */; };
|
||||
OBJ_46 /* SingleValueEncodingContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_19 /* SingleValueEncodingContainer.swift */; };
|
||||
OBJ_47 /* UnkeyedEncodingContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_20 /* UnkeyedEncodingContainer.swift */; };
|
||||
OBJ_48 /* FixedWidthInteger+Bytes.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_21 /* FixedWidthInteger+Bytes.swift */; };
|
||||
OBJ_55 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_6 /* Package.swift */; };
|
||||
OBJ_66 /* Airport.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_24 /* Airport.swift */; };
|
||||
OBJ_67 /* MessagePackDecodingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_25 /* MessagePackDecodingTests.swift */; };
|
||||
OBJ_68 /* MessagePackEncodingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_26 /* MessagePackEncodingTests.swift */; };
|
||||
OBJ_69 /* MessagePackPerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_27 /* MessagePackPerformanceTests.swift */; };
|
||||
OBJ_70 /* MessagePackRoundTripTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_28 /* MessagePackRoundTripTests.swift */; };
|
||||
OBJ_72 /* MessagePack.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "MessagePack::MessagePack::Product" /* MessagePack.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
1BC312FC2989A1AD00177F2A /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = OBJ_1 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = "MessagePack::MessagePack";
|
||||
remoteInfo = MessagePack;
|
||||
};
|
||||
1BC312FD2989A1B200177F2A /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = OBJ_1 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = "MessagePack::MessagePackTests";
|
||||
remoteInfo = MessagePackTests;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
1BC312FE2992DE9C00177F2A /* DataSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataSpec.swift; sourceTree = "<group>"; };
|
||||
"MessagePack::MessagePack::Product" /* MessagePack.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = MessagePack.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
"MessagePack::MessagePackTests::Product" /* MessagePackTests.xctest */ = {isa = PBXFileReference; lastKnownFileType = file; path = MessagePackTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
OBJ_10 /* Box.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Box.swift; sourceTree = "<group>"; };
|
||||
OBJ_12 /* KeyedDecodingContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyedDecodingContainer.swift; sourceTree = "<group>"; };
|
||||
OBJ_13 /* MessagePackDecoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagePackDecoder.swift; sourceTree = "<group>"; };
|
||||
OBJ_14 /* SingleValueDecodingContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleValueDecodingContainer.swift; sourceTree = "<group>"; };
|
||||
OBJ_15 /* UnkeyedDecodingContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnkeyedDecodingContainer.swift; sourceTree = "<group>"; };
|
||||
OBJ_17 /* KeyedEncodingContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyedEncodingContainer.swift; sourceTree = "<group>"; };
|
||||
OBJ_18 /* MessagePackEncoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagePackEncoder.swift; sourceTree = "<group>"; };
|
||||
OBJ_19 /* SingleValueEncodingContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleValueEncodingContainer.swift; sourceTree = "<group>"; };
|
||||
OBJ_20 /* UnkeyedEncodingContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnkeyedEncodingContainer.swift; sourceTree = "<group>"; };
|
||||
OBJ_21 /* FixedWidthInteger+Bytes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FixedWidthInteger+Bytes.swift"; sourceTree = "<group>"; };
|
||||
OBJ_24 /* Airport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Airport.swift; sourceTree = "<group>"; };
|
||||
OBJ_25 /* MessagePackDecodingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagePackDecodingTests.swift; sourceTree = "<group>"; };
|
||||
OBJ_26 /* MessagePackEncodingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagePackEncodingTests.swift; sourceTree = "<group>"; };
|
||||
OBJ_27 /* MessagePackPerformanceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagePackPerformanceTests.swift; sourceTree = "<group>"; };
|
||||
OBJ_28 /* MessagePackRoundTripTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagePackRoundTripTests.swift; sourceTree = "<group>"; };
|
||||
OBJ_29 /* MessagePack.xcworkspace */ = {isa = PBXFileReference; lastKnownFileType = wrapper.workspace; path = MessagePack.xcworkspace; sourceTree = SOURCE_ROOT; };
|
||||
OBJ_6 /* Package.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = Package.swift; sourceTree = "<group>"; };
|
||||
OBJ_9 /* AnyCodingKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyCodingKey.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
OBJ_49 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 0;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
OBJ_71 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 0;
|
||||
files = (
|
||||
OBJ_72 /* MessagePack.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
OBJ_11 /* Decoder */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
OBJ_12 /* KeyedDecodingContainer.swift */,
|
||||
OBJ_13 /* MessagePackDecoder.swift */,
|
||||
OBJ_14 /* SingleValueDecodingContainer.swift */,
|
||||
OBJ_15 /* UnkeyedDecodingContainer.swift */,
|
||||
);
|
||||
path = Decoder;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
OBJ_16 /* Encoder */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
OBJ_17 /* KeyedEncodingContainer.swift */,
|
||||
OBJ_18 /* MessagePackEncoder.swift */,
|
||||
OBJ_19 /* SingleValueEncodingContainer.swift */,
|
||||
OBJ_20 /* UnkeyedEncodingContainer.swift */,
|
||||
);
|
||||
path = Encoder;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
OBJ_22 /* Tests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
OBJ_23 /* MessagePackTests */,
|
||||
);
|
||||
name = Tests;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
OBJ_23 /* MessagePackTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
OBJ_24 /* Airport.swift */,
|
||||
OBJ_25 /* MessagePackDecodingTests.swift */,
|
||||
OBJ_26 /* MessagePackEncodingTests.swift */,
|
||||
OBJ_27 /* MessagePackPerformanceTests.swift */,
|
||||
OBJ_28 /* MessagePackRoundTripTests.swift */,
|
||||
);
|
||||
name = MessagePackTests;
|
||||
path = Tests/MessagePackTests;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
OBJ_30 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
"MessagePack::MessagePackTests::Product" /* MessagePackTests.xctest */,
|
||||
"MessagePack::MessagePack::Product" /* MessagePack.framework */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
OBJ_5 /* */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
OBJ_6 /* Package.swift */,
|
||||
OBJ_7 /* Sources */,
|
||||
OBJ_22 /* Tests */,
|
||||
OBJ_29 /* MessagePack.xcworkspace */,
|
||||
OBJ_30 /* Products */,
|
||||
);
|
||||
name = "";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
OBJ_7 /* Sources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
OBJ_8 /* MessagePack */,
|
||||
);
|
||||
name = Sources;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
OBJ_8 /* MessagePack */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
OBJ_9 /* AnyCodingKey.swift */,
|
||||
OBJ_10 /* Box.swift */,
|
||||
OBJ_11 /* Decoder */,
|
||||
OBJ_16 /* Encoder */,
|
||||
OBJ_21 /* FixedWidthInteger+Bytes.swift */,
|
||||
1BC312FE2992DE9C00177F2A /* DataSpec.swift */,
|
||||
);
|
||||
name = MessagePack;
|
||||
path = Sources/MessagePack;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
"MessagePack::MessagePack" /* MessagePack */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = OBJ_34 /* Build configuration list for PBXNativeTarget "MessagePack" */;
|
||||
buildPhases = (
|
||||
OBJ_37 /* Sources */,
|
||||
OBJ_49 /* Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = MessagePack;
|
||||
productName = MessagePack;
|
||||
productReference = "MessagePack::MessagePack::Product" /* MessagePack.framework */;
|
||||
productType = "com.apple.product-type.framework";
|
||||
};
|
||||
"MessagePack::MessagePackTests" /* MessagePackTests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = OBJ_62 /* Build configuration list for PBXNativeTarget "MessagePackTests" */;
|
||||
buildPhases = (
|
||||
OBJ_65 /* Sources */,
|
||||
OBJ_71 /* Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
OBJ_73 /* PBXTargetDependency */,
|
||||
);
|
||||
name = MessagePackTests;
|
||||
productName = MessagePackTests;
|
||||
productReference = "MessagePack::MessagePackTests::Product" /* MessagePackTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
"MessagePack::SwiftPMPackageDescription" /* MessagePackPackageDescription */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = OBJ_51 /* Build configuration list for PBXNativeTarget "MessagePackPackageDescription" */;
|
||||
buildPhases = (
|
||||
OBJ_54 /* Sources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = MessagePackPackageDescription;
|
||||
productName = MessagePackPackageDescription;
|
||||
productType = "com.apple.product-type.framework";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
OBJ_1 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 9999;
|
||||
};
|
||||
buildConfigurationList = OBJ_2 /* Build configuration list for PBXProject "MessagePack" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
English,
|
||||
en,
|
||||
);
|
||||
mainGroup = OBJ_5 /* */;
|
||||
productRefGroup = OBJ_30 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
"MessagePack::MessagePack" /* MessagePack */,
|
||||
"MessagePack::SwiftPMPackageDescription" /* MessagePackPackageDescription */,
|
||||
"MessagePack::MessagePackPackageTests::ProductTarget" /* MessagePackPackageTests */,
|
||||
"MessagePack::MessagePackTests" /* MessagePackTests */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
OBJ_37 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 0;
|
||||
files = (
|
||||
OBJ_38 /* AnyCodingKey.swift in Sources */,
|
||||
OBJ_39 /* Box.swift in Sources */,
|
||||
OBJ_40 /* KeyedDecodingContainer.swift in Sources */,
|
||||
OBJ_41 /* MessagePackDecoder.swift in Sources */,
|
||||
OBJ_42 /* SingleValueDecodingContainer.swift in Sources */,
|
||||
OBJ_43 /* UnkeyedDecodingContainer.swift in Sources */,
|
||||
OBJ_44 /* KeyedEncodingContainer.swift in Sources */,
|
||||
OBJ_45 /* MessagePackEncoder.swift in Sources */,
|
||||
OBJ_46 /* SingleValueEncodingContainer.swift in Sources */,
|
||||
OBJ_47 /* UnkeyedEncodingContainer.swift in Sources */,
|
||||
1BC312FF2992DE9C00177F2A /* DataSpec.swift in Sources */,
|
||||
OBJ_48 /* FixedWidthInteger+Bytes.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
OBJ_54 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 0;
|
||||
files = (
|
||||
OBJ_55 /* Package.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
OBJ_65 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 0;
|
||||
files = (
|
||||
OBJ_66 /* Airport.swift in Sources */,
|
||||
OBJ_67 /* MessagePackDecodingTests.swift in Sources */,
|
||||
OBJ_68 /* MessagePackEncodingTests.swift in Sources */,
|
||||
OBJ_69 /* MessagePackPerformanceTests.swift in Sources */,
|
||||
OBJ_70 /* MessagePackRoundTripTests.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
OBJ_60 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = "MessagePack::MessagePackTests" /* MessagePackTests */;
|
||||
targetProxy = 1BC312FD2989A1B200177F2A /* PBXContainerItemProxy */;
|
||||
};
|
||||
OBJ_73 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = "MessagePack::MessagePack" /* MessagePack */;
|
||||
targetProxy = 1BC312FC2989A1AD00177F2A /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
OBJ_3 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
ENABLE_NS_ASSERTIONS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_SWIFT_FLAGS = "-DXcode";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = macosx;
|
||||
SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator";
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "SWIFT_PACKAGE DEBUG";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
USE_HEADERMAP = NO;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
OBJ_35 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ENABLE_TESTABILITY = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PLATFORM_DIR)/Developer/Library/Frameworks",
|
||||
);
|
||||
HEADER_SEARCH_PATHS = "$(inherited)";
|
||||
INFOPLIST_FILE = MessagePack.xcodeproj/MessagePack_Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx";
|
||||
OTHER_CFLAGS = "$(inherited)";
|
||||
OTHER_LDFLAGS = "$(inherited)";
|
||||
OTHER_SWIFT_FLAGS = "$(inherited)";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = MessagePack;
|
||||
PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)";
|
||||
SWIFT_VERSION = 4.0;
|
||||
TARGET_NAME = MessagePack;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
OBJ_36 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ENABLE_TESTABILITY = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PLATFORM_DIR)/Developer/Library/Frameworks",
|
||||
);
|
||||
HEADER_SEARCH_PATHS = "$(inherited)";
|
||||
INFOPLIST_FILE = MessagePack.xcodeproj/MessagePack_Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx";
|
||||
OTHER_CFLAGS = "$(inherited)";
|
||||
OTHER_LDFLAGS = "$(inherited)";
|
||||
OTHER_SWIFT_FLAGS = "$(inherited)";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = MessagePack;
|
||||
PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)";
|
||||
SWIFT_VERSION = 4.0;
|
||||
TARGET_NAME = MessagePack;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
OBJ_4 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
COPY_PHASE_STRIP = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
GCC_OPTIMIZATION_LEVEL = s;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||
OTHER_SWIFT_FLAGS = "-DXcode";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = macosx;
|
||||
SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator";
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = SWIFT_PACKAGE;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
USE_HEADERMAP = NO;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
OBJ_52 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
LD = /usr/bin/true;
|
||||
OTHER_SWIFT_FLAGS = "-swift-version 4 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/4 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode-beta.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk";
|
||||
SWIFT_VERSION = 4.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
OBJ_53 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
LD = /usr/bin/true;
|
||||
OTHER_SWIFT_FLAGS = "-swift-version 4 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/4 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode-beta.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk";
|
||||
SWIFT_VERSION = 4.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
OBJ_58 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
OBJ_59 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
OBJ_63 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PLATFORM_DIR)/Developer/Library/Frameworks",
|
||||
);
|
||||
HEADER_SEARCH_PATHS = "$(inherited)";
|
||||
INFOPLIST_FILE = MessagePack.xcodeproj/MessagePackTests_Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @loader_path/../Frameworks @loader_path/Frameworks";
|
||||
OTHER_CFLAGS = "$(inherited)";
|
||||
OTHER_LDFLAGS = "$(inherited)";
|
||||
OTHER_SWIFT_FLAGS = "$(inherited)";
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)";
|
||||
SWIFT_VERSION = 4.0;
|
||||
TARGET_NAME = MessagePackTests;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
OBJ_64 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PLATFORM_DIR)/Developer/Library/Frameworks",
|
||||
);
|
||||
HEADER_SEARCH_PATHS = "$(inherited)";
|
||||
INFOPLIST_FILE = MessagePack.xcodeproj/MessagePackTests_Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @loader_path/../Frameworks @loader_path/Frameworks";
|
||||
OTHER_CFLAGS = "$(inherited)";
|
||||
OTHER_LDFLAGS = "$(inherited)";
|
||||
OTHER_SWIFT_FLAGS = "$(inherited)";
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)";
|
||||
SWIFT_VERSION = 4.0;
|
||||
TARGET_NAME = MessagePackTests;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
OBJ_2 /* Build configuration list for PBXProject "MessagePack" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
OBJ_3 /* Debug */,
|
||||
OBJ_4 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
OBJ_34 /* Build configuration list for PBXNativeTarget "MessagePack" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
OBJ_35 /* Debug */,
|
||||
OBJ_36 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
OBJ_51 /* Build configuration list for PBXNativeTarget "MessagePackPackageDescription" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
OBJ_52 /* Debug */,
|
||||
OBJ_53 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
OBJ_57 /* Build configuration list for PBXAggregateTarget "MessagePackPackageTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
OBJ_58 /* Debug */,
|
||||
OBJ_59 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
OBJ_62 /* Build configuration list for PBXNativeTarget "MessagePackTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
OBJ_63 /* Debug */,
|
||||
OBJ_64 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = OBJ_1 /* Project object */;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,99 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1010"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "MessagePack::MessagePack"
|
||||
BuildableName = "MessagePack.framework"
|
||||
BlueprintName = "MessagePack"
|
||||
ReferencedContainer = "container:MessagePack.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "MessagePack::MessagePackTests"
|
||||
BuildableName = "MessagePackTests.xctest"
|
||||
BlueprintName = "MessagePackTests"
|
||||
ReferencedContainer = "container:MessagePack.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "MessagePack::MessagePack"
|
||||
BuildableName = "MessagePack.framework"
|
||||
BlueprintName = "MessagePack"
|
||||
ReferencedContainer = "container:MessagePack.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "MessagePack::MessagePack"
|
||||
BuildableName = "MessagePack.framework"
|
||||
BlueprintName = "MessagePack"
|
||||
ReferencedContainer = "container:MessagePack.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "MessagePack::MessagePack"
|
||||
BuildableName = "MessagePack.framework"
|
||||
BlueprintName = "MessagePack"
|
||||
ReferencedContainer = "container:MessagePack.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:MessagePack.playground">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:MessagePack.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,28 +0,0 @@
|
||||
// swift-tools-version:4.0
|
||||
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "MessagePack",
|
||||
products: [
|
||||
// Products define the executables and libraries produced by a package, and make them visible to other packages.
|
||||
.library(
|
||||
name: "MessagePack",
|
||||
targets: ["MessagePack"]),
|
||||
],
|
||||
dependencies: [
|
||||
// Dependencies declare other packages that this package depends on.
|
||||
// .package(url: /* package url */, 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 which this package depends on.
|
||||
.target(
|
||||
name: "MessagePack",
|
||||
dependencies: []),
|
||||
.testTarget(
|
||||
name: "MessagePackTests",
|
||||
dependencies: ["MessagePack"]),
|
||||
]
|
||||
)
|
||||
@@ -1,87 +0,0 @@
|
||||
# MessagePack
|
||||
|
||||
[![Build Status][build status badge]][build status]
|
||||
|
||||
A [MessagePack](https://msgpack.org/) encoder and decoder for `Codable` types.
|
||||
|
||||
This functionality is discussed in Chapter 7 of
|
||||
[Flight School Guide to Swift Codable](https://flight.school/books/codable).
|
||||
|
||||
## Requirements
|
||||
|
||||
- Swift 4.2+
|
||||
|
||||
## Usage
|
||||
|
||||
### Encoding Messages
|
||||
|
||||
```swift
|
||||
import MessagePack
|
||||
|
||||
let encoder = MessagePackEncoder()
|
||||
let value = try! encoder.encode(["a": 1, "b": 2, "c": 3])
|
||||
// [0x83, 0xA1, 0x62, 0x02, 0xA1, 0x61, 0x01, 0xA1, 0x63, 0x03]
|
||||
```
|
||||
|
||||
### Decoding Messages
|
||||
|
||||
```swift
|
||||
import MessagePack
|
||||
|
||||
let decoder = MessagePackDecoder()
|
||||
let data = Data(bytes: [0xCB, 0x40, 0x09, 0x21, 0xF9, 0xF0, 0x1B, 0x86, 0x6E])
|
||||
let value = try! decoder.decode(Double.self, from: data)
|
||||
// 3.14159
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
### Swift Package Manager
|
||||
|
||||
Add the MessagePack package to your target dependencies in `Package.swift`:
|
||||
|
||||
```swift
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "YourProject",
|
||||
dependencies: [
|
||||
.package(
|
||||
url: "https://github.com/Flight-School/MessagePack",
|
||||
from: "1.2.3"
|
||||
),
|
||||
]
|
||||
)
|
||||
```
|
||||
|
||||
Then run the `swift build` command to build your project.
|
||||
|
||||
### CocoaPods
|
||||
|
||||
You can install `MessagePack` via CocoaPods,
|
||||
by adding the following line to your `Podfile`:
|
||||
|
||||
```ruby
|
||||
pod 'MessagePack-FlightSchool', '~> 1.2.4'
|
||||
```
|
||||
|
||||
Run the `pod install` command to download the library
|
||||
and integrate it into your Xcode project.
|
||||
|
||||
> **Note**
|
||||
> The module name for this library is "MessagePack" ---
|
||||
> that is, to use it, you add `import MessagePack` to the top of your Swift code
|
||||
> just as you would by any other installation method.
|
||||
> The pod is called "MessagePack-FlightSchool"
|
||||
> because there's an existing pod with the name "MessagePack".
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
## Contact
|
||||
|
||||
Mattt ([@mattt](https://twitter.com/mattt))
|
||||
|
||||
[build status]: https://github.com/Flight-School/MessagePack/actions?query=workflow%3ACI
|
||||
[build status badge]: https://github.com/Flight-School/MessagePack/workflows/CI/badge.svg
|
||||
@@ -1,28 +0,0 @@
|
||||
struct AnyCodingKey: CodingKey, Equatable {
|
||||
var stringValue: String
|
||||
var intValue: Int?
|
||||
|
||||
init?(stringValue: String) {
|
||||
self.stringValue = stringValue
|
||||
self.intValue = nil
|
||||
}
|
||||
|
||||
init?(intValue: Int) {
|
||||
self.stringValue = "\(intValue)"
|
||||
self.intValue = intValue
|
||||
}
|
||||
|
||||
init<Key>(_ base: Key) where Key : CodingKey {
|
||||
if let intValue = base.intValue {
|
||||
self.init(intValue: intValue)!
|
||||
} else {
|
||||
self.init(stringValue: base.stringValue)!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension AnyCodingKey: Hashable {
|
||||
var hashValue: Int {
|
||||
return self.intValue?.hashValue ?? self.stringValue.hashValue
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
import Foundation
|
||||
|
||||
struct Box<Value> {
|
||||
let value: Value
|
||||
init(_ value: Value) {
|
||||
self.value = value
|
||||
}
|
||||
}
|
||||
|
||||
extension Box: Encodable where Value: Encodable {
|
||||
func encode(to encoder: Encoder) throws {
|
||||
try self.value.encode(to: encoder)
|
||||
}
|
||||
}
|
||||
|
||||
extension Box: Decodable where Value: Decodable {
|
||||
init(from decoder: Decoder) throws {
|
||||
self.init(try Value(from: decoder))
|
||||
}
|
||||
}
|
||||
|
||||
extension Box where Value == Data {
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.singleValueContainer()
|
||||
self.init(try container.decode(Value.self))
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.singleValueContainer()
|
||||
try container.encode(self.value)
|
||||
}
|
||||
}
|
||||
|
||||
extension Box where Value == Date {
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.singleValueContainer()
|
||||
self.init(try container.decode(Value.self))
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.singleValueContainer()
|
||||
try container.encode(self.value)
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
import Foundation
|
||||
|
||||
public struct DataSpec {
|
||||
let name: String
|
||||
let isObj: Bool
|
||||
let isArray: Bool
|
||||
let dataSpecBuilder: DataSpecBuilder?
|
||||
|
||||
init(_ name: String, _ isObj: Bool, _ isArray: Bool, _ dataSpecBuilder: DataSpecBuilder?) {
|
||||
self.name = name
|
||||
self.isObj = isObj
|
||||
self.isArray = isArray
|
||||
self.dataSpecBuilder = dataSpecBuilder
|
||||
}
|
||||
}
|
||||
|
||||
public class DataSpecBuilder : NSCopying {
|
||||
var specs: [DataSpec] = []
|
||||
var specsIterator: IndexingIterator<[DataSpec]>
|
||||
|
||||
init() {
|
||||
specsIterator = IndexingIterator(_elements: [])
|
||||
}
|
||||
|
||||
func append(_ name: String) -> DataSpecBuilder {
|
||||
return append(DataSpec(name, false, false, nil))
|
||||
}
|
||||
|
||||
func appendObj(_ name: String, _ dataSpecBuilder: DataSpecBuilder) -> DataSpecBuilder {
|
||||
return append(DataSpec(name, true, false, dataSpecBuilder))
|
||||
}
|
||||
|
||||
func appendArray(_ name: String) -> DataSpecBuilder {
|
||||
return append(DataSpec(name, false, true, nil))
|
||||
}
|
||||
|
||||
func appendArray(_ name: String, _ dataSpecBuilder: DataSpecBuilder) -> DataSpecBuilder {
|
||||
return append(DataSpec(name, false, true, dataSpecBuilder))
|
||||
}
|
||||
|
||||
func append(_ spec: DataSpec) -> DataSpecBuilder {
|
||||
specs.append(spec)
|
||||
return self
|
||||
}
|
||||
|
||||
func build() -> DataSpecBuilder {
|
||||
specsIterator = specs.makeIterator()
|
||||
return self
|
||||
}
|
||||
|
||||
func next() -> DataSpec {
|
||||
return specsIterator.next()!
|
||||
}
|
||||
|
||||
public func copy(with zone: NSZone? = nil) -> Any {
|
||||
let b = DataSpecBuilder()
|
||||
b.specs = specs
|
||||
return b.build()
|
||||
}
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
import Foundation
|
||||
|
||||
extension _MessagePackDecoder {
|
||||
final class KeyedContainer<Key> where Key: CodingKey {
|
||||
lazy var nestedContainers: [String: MessagePackDecodingContainer] = {
|
||||
guard let count = self.count else {
|
||||
return [:]
|
||||
}
|
||||
|
||||
var nestedContainers: [String: MessagePackDecodingContainer] = [:]
|
||||
|
||||
let unkeyedContainer = UnkeyedContainer(data: self.data.suffix(from: self.index), codingPath: self.codingPath, userInfo: self.userInfo)
|
||||
if currentSpec != nil && currentSpec!.isObj {
|
||||
unkeyedContainer.count = count
|
||||
} else {
|
||||
unkeyedContainer.count = count * 2
|
||||
}
|
||||
|
||||
do {
|
||||
var iterator = unkeyedContainer.nestedContainers.makeIterator()
|
||||
|
||||
for _ in 0..<count {
|
||||
var key: String = ""
|
||||
if currentSpec == nil || !currentSpec!.isObj {
|
||||
guard let keyContainer = iterator.next() as? _MessagePackDecoder.SingleValueContainer else {
|
||||
fatalError() // FIXME
|
||||
}
|
||||
|
||||
key = try keyContainer.decode(String.self)
|
||||
}
|
||||
|
||||
guard let container = iterator.next() else {
|
||||
fatalError() // FIXME
|
||||
}
|
||||
|
||||
|
||||
if currentSpec != nil && currentSpec!.isObj {
|
||||
key = container.currentSpec!.name
|
||||
}
|
||||
|
||||
container.codingPath += [AnyCodingKey(stringValue: key)!]
|
||||
nestedContainers[key] = container
|
||||
}
|
||||
} catch {
|
||||
fatalError("\(error)") // FIXME
|
||||
}
|
||||
|
||||
self.index = unkeyedContainer.index
|
||||
|
||||
return nestedContainers
|
||||
}()
|
||||
|
||||
lazy var count: Int? = {
|
||||
do {
|
||||
let format = try self.readByte()
|
||||
|
||||
if currentSpec != nil && currentSpec!.isObj && 0x90...0x9f ~= format {
|
||||
return Int(format & 0x0F)
|
||||
}
|
||||
|
||||
switch format {
|
||||
case 0x80...0x8f:
|
||||
return Int(format & 0x0F)
|
||||
case 0xde:
|
||||
return Int(try read(UInt16.self))
|
||||
case 0xdf:
|
||||
return Int(try read(UInt32.self))
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
} catch {
|
||||
return nil
|
||||
}
|
||||
}()
|
||||
|
||||
var data: Data
|
||||
var index: Data.Index
|
||||
var codingPath: [CodingKey]
|
||||
var userInfo: [CodingUserInfoKey: Any]
|
||||
var currentSpec: DataSpec?
|
||||
|
||||
func nestedCodingPath(forKey key: CodingKey) -> [CodingKey] {
|
||||
return self.codingPath + [key]
|
||||
}
|
||||
|
||||
init(data: Data, codingPath: [CodingKey], userInfo: [CodingUserInfoKey : Any]) {
|
||||
self.codingPath = codingPath
|
||||
self.userInfo = userInfo
|
||||
self.data = data
|
||||
self.index = self.data.startIndex
|
||||
}
|
||||
|
||||
func checkCanDecodeValue(forKey key: Key) throws {
|
||||
guard self.contains(key) else {
|
||||
let context = DecodingError.Context(codingPath: self.codingPath, debugDescription: "key not found: \(key)")
|
||||
throw DecodingError.keyNotFound(key, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension _MessagePackDecoder.KeyedContainer: KeyedDecodingContainerProtocol {
|
||||
var allKeys: [Key] {
|
||||
return self.nestedContainers.keys.map{ Key(stringValue: $0)! }
|
||||
}
|
||||
|
||||
func contains(_ key: Key) -> Bool {
|
||||
return self.nestedContainers.keys.contains(key.stringValue)
|
||||
}
|
||||
|
||||
func decodeNil(forKey key: Key) throws -> Bool {
|
||||
try checkCanDecodeValue(forKey: key)
|
||||
|
||||
let nestedContainer = self.nestedContainers[key.stringValue]
|
||||
|
||||
switch nestedContainer {
|
||||
case let singleValueContainer as _MessagePackDecoder.SingleValueContainer:
|
||||
return singleValueContainer.decodeNil()
|
||||
case is _MessagePackDecoder.UnkeyedContainer,
|
||||
is _MessagePackDecoder.KeyedContainer<AnyCodingKey>:
|
||||
return false
|
||||
default:
|
||||
let context = DecodingError.Context(codingPath: self.codingPath, debugDescription: "cannot decode nil for key: \(key)")
|
||||
throw DecodingError.typeMismatch(Any?.self, context)
|
||||
}
|
||||
}
|
||||
|
||||
func decode<T>(_ type: T.Type, forKey key: Key) throws -> T where T : Decodable {
|
||||
try checkCanDecodeValue(forKey: key)
|
||||
|
||||
let container = self.nestedContainers[key.stringValue]!
|
||||
let decoder = MessagePackDecoder()
|
||||
|
||||
if userInfo.keys.contains(MessagePackDecoder.dataSpecKey) {
|
||||
decoder.userInfo[MessagePackDecoder.dataSpecKey] = container.currentSpec!.dataSpecBuilder?.copy() as? DataSpecBuilder
|
||||
if container.currentSpec!.isArray {
|
||||
decoder.userInfo[MessagePackDecoder.isArrayDataSpecKey] = true
|
||||
}
|
||||
}
|
||||
|
||||
let value = try decoder.decode(T.self, from: container.data)
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer {
|
||||
try checkCanDecodeValue(forKey: key)
|
||||
|
||||
guard let unkeyedContainer = self.nestedContainers[key.stringValue] as? _MessagePackDecoder.UnkeyedContainer else {
|
||||
throw DecodingError.dataCorruptedError(forKey: key, in: self, debugDescription: "cannot decode nested container for key: \(key)")
|
||||
}
|
||||
|
||||
return unkeyedContainer
|
||||
}
|
||||
|
||||
func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer<NestedKey> where NestedKey : CodingKey {
|
||||
try checkCanDecodeValue(forKey: key)
|
||||
|
||||
guard let keyedContainer = self.nestedContainers[key.stringValue] as? _MessagePackDecoder.KeyedContainer<NestedKey> else {
|
||||
throw DecodingError.dataCorruptedError(forKey: key, in: self, debugDescription: "cannot decode nested container for key: \(key)")
|
||||
}
|
||||
|
||||
return KeyedDecodingContainer(keyedContainer)
|
||||
}
|
||||
|
||||
func superDecoder() throws -> Decoder {
|
||||
return _MessagePackDecoder(data: self.data)
|
||||
}
|
||||
|
||||
func superDecoder(forKey key: Key) throws -> Decoder {
|
||||
let decoder = _MessagePackDecoder(data: self.data)
|
||||
decoder.codingPath = [key]
|
||||
|
||||
return decoder
|
||||
}
|
||||
}
|
||||
|
||||
extension _MessagePackDecoder.KeyedContainer: MessagePackDecodingContainer {}
|
||||
@@ -1,168 +0,0 @@
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
An object that decodes instances of a data type from MessagePack objects.
|
||||
*/
|
||||
final public class MessagePackDecoder {
|
||||
public init() {}
|
||||
|
||||
/**
|
||||
A dictionary you use to customize the decoding process
|
||||
by providing contextual information.
|
||||
*/
|
||||
public var userInfo: [CodingUserInfoKey : Any] = [:]
|
||||
|
||||
/**
|
||||
Returns a value of the type you specify,
|
||||
decoded from a MessagePack object.
|
||||
|
||||
- Parameters:
|
||||
- type: The type of the value to decode
|
||||
from the supplied MessagePack object.
|
||||
- data: The MessagePack object to decode.
|
||||
- Throws: `DecodingError.dataCorrupted(_:)`
|
||||
if the data is not valid MessagePack.
|
||||
*/
|
||||
public func decode<T>(_ type: T.Type, from data: Data) throws -> T where T : Decodable {
|
||||
let decoder = _MessagePackDecoder(data: data)
|
||||
decoder.userInfo = self.userInfo
|
||||
decoder.userInfo[MessagePackDecoder.nonMatchingFloatDecodingStrategyKey] = nonMatchingFloatDecodingStrategy
|
||||
|
||||
switch type {
|
||||
case is Data.Type:
|
||||
let box = try Box<Data>(from: decoder)
|
||||
return box.value as! T
|
||||
case is Date.Type:
|
||||
let box = try Box<Date>(from: decoder)
|
||||
return box.value as! T
|
||||
default:
|
||||
return try T(from: decoder)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
The strategy used by a decoder when it encounters format mismatches for floating point values.
|
||||
*/
|
||||
public var nonMatchingFloatDecodingStrategy: NonMatchingFloatDecodingStrategy = .strict
|
||||
|
||||
/**
|
||||
The strategies for decoding floating point values when their format doesn't match.
|
||||
*/
|
||||
public enum NonMatchingFloatDecodingStrategy {
|
||||
|
||||
/// Throws a DecodingError.typeMismatch
|
||||
case strict
|
||||
|
||||
/// Performs a cast
|
||||
case cast
|
||||
}
|
||||
|
||||
internal static var nonMatchingFloatDecodingStrategyKey: CodingUserInfoKey {
|
||||
return CodingUserInfoKey(rawValue: "nonMatchingFloatDecodingStrategyKey")!
|
||||
}
|
||||
|
||||
static var dataSpecKey : CodingUserInfoKey {
|
||||
return CodingUserInfoKey(rawValue: "dataSpecKey")!
|
||||
}
|
||||
|
||||
static var isArrayDataSpecKey : CodingUserInfoKey {
|
||||
return CodingUserInfoKey(rawValue: "isArrayDataSpecKey")!
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - TopLevelDecoder
|
||||
|
||||
#if canImport(Combine)
|
||||
import Combine
|
||||
|
||||
extension MessagePackDecoder: TopLevelDecoder {
|
||||
public typealias Input = Data
|
||||
}
|
||||
#endif
|
||||
|
||||
// MARK: -
|
||||
|
||||
final class _MessagePackDecoder {
|
||||
var codingPath: [CodingKey] = []
|
||||
|
||||
var userInfo: [CodingUserInfoKey : Any] = [:]
|
||||
|
||||
var container: MessagePackDecodingContainer?
|
||||
fileprivate var data: Data
|
||||
|
||||
init(data: Data) {
|
||||
self.data = data
|
||||
}
|
||||
}
|
||||
|
||||
extension _MessagePackDecoder: Decoder {
|
||||
fileprivate func assertCanCreateContainer() {
|
||||
precondition(self.container == nil)
|
||||
}
|
||||
|
||||
func container<Key>(keyedBy type: Key.Type) -> KeyedDecodingContainer<Key> where Key : CodingKey {
|
||||
assertCanCreateContainer()
|
||||
|
||||
let container = KeyedContainer<Key>(data: self.data, codingPath: self.codingPath, userInfo: self.userInfo)
|
||||
|
||||
if userInfo.keys.contains(MessagePackDecoder.dataSpecKey) {
|
||||
container.currentSpec = DataSpec("", true, false, nil)
|
||||
}
|
||||
|
||||
self.container = container
|
||||
|
||||
return KeyedDecodingContainer(container)
|
||||
}
|
||||
|
||||
func unkeyedContainer() -> UnkeyedDecodingContainer {
|
||||
assertCanCreateContainer()
|
||||
|
||||
let container = UnkeyedContainer(data: self.data, codingPath: self.codingPath, userInfo: self.userInfo)
|
||||
self.container = container
|
||||
|
||||
return container
|
||||
}
|
||||
|
||||
func singleValueContainer() -> SingleValueDecodingContainer {
|
||||
assertCanCreateContainer()
|
||||
|
||||
let container = SingleValueContainer(data: self.data, codingPath: self.codingPath, userInfo: self.userInfo)
|
||||
self.container = container
|
||||
|
||||
return container
|
||||
}
|
||||
}
|
||||
|
||||
protocol MessagePackDecodingContainer: class {
|
||||
var codingPath: [CodingKey] { get set }
|
||||
|
||||
var userInfo: [CodingUserInfoKey : Any] { get }
|
||||
|
||||
var data: Data { get set }
|
||||
var index: Data.Index { get set }
|
||||
|
||||
var currentSpec: DataSpec? { get set }
|
||||
}
|
||||
|
||||
extension MessagePackDecodingContainer {
|
||||
func readByte() throws -> UInt8 {
|
||||
return try read(1).first!
|
||||
}
|
||||
|
||||
func read(_ length: Int) throws -> Data {
|
||||
let nextIndex = self.index.advanced(by: length)
|
||||
guard nextIndex <= self.data.endIndex else {
|
||||
let context = DecodingError.Context(codingPath: self.codingPath, debugDescription: "Unexpected end of data")
|
||||
throw DecodingError.dataCorrupted(context)
|
||||
}
|
||||
defer { self.index = nextIndex }
|
||||
|
||||
return self.data.subdata(in: self.index..<nextIndex)
|
||||
}
|
||||
|
||||
func read<T>(_ type: T.Type) throws -> T where T : FixedWidthInteger {
|
||||
let stride = MemoryLayout<T>.stride
|
||||
let bytes = [UInt8](try read(stride))
|
||||
return T(bytes: bytes)
|
||||
}
|
||||
}
|
||||
@@ -1,226 +0,0 @@
|
||||
import Foundation
|
||||
|
||||
#if os(Linux)
|
||||
let NSEC_PER_SEC: UInt64 = 1000000000
|
||||
#endif
|
||||
|
||||
extension _MessagePackDecoder {
|
||||
final class SingleValueContainer {
|
||||
var codingPath: [CodingKey]
|
||||
var userInfo: [CodingUserInfoKey: Any]
|
||||
var data: Data
|
||||
var index: Data.Index
|
||||
var currentSpec: DataSpec?
|
||||
|
||||
init(data: Data, codingPath: [CodingKey], userInfo: [CodingUserInfoKey : Any]) {
|
||||
self.codingPath = codingPath
|
||||
self.userInfo = userInfo
|
||||
self.data = data
|
||||
self.index = self.data.startIndex
|
||||
}
|
||||
|
||||
func checkCanDecode<T>(_ type: T.Type, format: UInt8) throws {
|
||||
guard self.index <= self.data.endIndex else {
|
||||
throw DecodingError.dataCorruptedError(in: self, debugDescription: "Unexpected end of data")
|
||||
}
|
||||
|
||||
guard self.data[self.index] == format else {
|
||||
let context = DecodingError.Context(codingPath: self.codingPath, debugDescription: "Invalid format: \(format)")
|
||||
throw DecodingError.typeMismatch(type, context)
|
||||
}
|
||||
}
|
||||
|
||||
var nonMatchingFloatDecodingStrategy: MessagePackDecoder.NonMatchingFloatDecodingStrategy {
|
||||
return userInfo[MessagePackDecoder.nonMatchingFloatDecodingStrategyKey] as? MessagePackDecoder.NonMatchingFloatDecodingStrategy ?? .strict
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension _MessagePackDecoder.SingleValueContainer: SingleValueDecodingContainer {
|
||||
func decodeNil() -> Bool {
|
||||
let format = try? readByte()
|
||||
return format == 0xc0
|
||||
}
|
||||
|
||||
func decode(_ type: Bool.Type) throws -> Bool {
|
||||
let format = try readByte()
|
||||
switch format {
|
||||
case 0xc2: return false
|
||||
case 0xc3: return true
|
||||
default:
|
||||
let context = DecodingError.Context(codingPath: self.codingPath, debugDescription: "Invalid format: \(format)")
|
||||
throw DecodingError.typeMismatch(Bool.self, context)
|
||||
}
|
||||
}
|
||||
|
||||
func decode(_ type: String.Type) throws -> String {
|
||||
let length: Int
|
||||
let format = try readByte()
|
||||
switch format {
|
||||
case 0xa0...0xbf:
|
||||
length = Int(format - 0xa0)
|
||||
case 0xd9:
|
||||
length = Int(try read(UInt8.self))
|
||||
case 0xda:
|
||||
length = Int(try read(UInt16.self))
|
||||
case 0xdb:
|
||||
length = Int(try read(UInt32.self))
|
||||
default:
|
||||
throw DecodingError.dataCorruptedError(in: self, debugDescription: "Invalid format for String length: \(format)")
|
||||
}
|
||||
|
||||
let data = try read(length)
|
||||
guard let string = String(data: data, encoding: .utf8) else {
|
||||
let context = DecodingError.Context(codingPath: self.codingPath, debugDescription: "Couldn't decode string with UTF-8 encoding")
|
||||
throw DecodingError.dataCorrupted(context)
|
||||
}
|
||||
|
||||
return string
|
||||
}
|
||||
|
||||
func decode(_ type: Double.Type) throws -> Double {
|
||||
let format = try readByte()
|
||||
switch format {
|
||||
case 0xca:
|
||||
switch nonMatchingFloatDecodingStrategy {
|
||||
case .strict:
|
||||
break
|
||||
case .cast:
|
||||
let bitPattern = try read(UInt32.self)
|
||||
return Double(Float(bitPattern: bitPattern))
|
||||
}
|
||||
case 0xcb:
|
||||
let bitPattern = try read(UInt64.self)
|
||||
return Double(bitPattern: bitPattern)
|
||||
default:
|
||||
break
|
||||
}
|
||||
let context = DecodingError.Context(codingPath: self.codingPath, debugDescription: "Invalid format: \(format)")
|
||||
throw DecodingError.typeMismatch(Double.self, context)
|
||||
}
|
||||
|
||||
func decode(_ type: Float.Type) throws -> Float {
|
||||
let format = try readByte()
|
||||
switch format {
|
||||
case 0xca:
|
||||
let bitPattern = try read(UInt32.self)
|
||||
return Float(bitPattern: bitPattern)
|
||||
case 0xcb:
|
||||
switch nonMatchingFloatDecodingStrategy {
|
||||
case .strict:
|
||||
break
|
||||
case .cast:
|
||||
let bitPattern = try read(UInt64.self)
|
||||
return Float(Double(bitPattern: bitPattern))
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
let context = DecodingError.Context(codingPath: self.codingPath, debugDescription: "Invalid format: \(format)")
|
||||
throw DecodingError.typeMismatch(Float.self, context)
|
||||
}
|
||||
|
||||
func decode<T>(_ type: T.Type) throws -> T where T : BinaryInteger & Decodable {
|
||||
let format = try readByte()
|
||||
var t: T?
|
||||
|
||||
switch format {
|
||||
case 0x00...0x7f:
|
||||
t = T(format)
|
||||
case 0xcc:
|
||||
t = T(exactly: try read(UInt8.self))
|
||||
case 0xcd:
|
||||
t = T(exactly: try read(UInt16.self))
|
||||
case 0xce:
|
||||
t = T(exactly: try read(UInt32.self))
|
||||
case 0xcf:
|
||||
t = T(exactly: try read(UInt64.self))
|
||||
case 0xd0:
|
||||
t = T(exactly: try read(Int8.self))
|
||||
case 0xd1:
|
||||
t = T(exactly: try read(Int16.self))
|
||||
case 0xd2:
|
||||
t = T(exactly: try read(Int32.self))
|
||||
case 0xd3:
|
||||
t = T(exactly: try read(Int64.self))
|
||||
case 0xe0...0xff:
|
||||
t = T(exactly: Int8(bitPattern: format))
|
||||
default:
|
||||
t = nil
|
||||
}
|
||||
|
||||
guard let value = t else {
|
||||
let context = DecodingError.Context(codingPath: self.codingPath, debugDescription: "Invalid format: \(format)")
|
||||
throw DecodingError.typeMismatch(T.self, context)
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
func decode(_ type: Date.Type) throws -> Date {
|
||||
let format = try readByte()
|
||||
|
||||
var seconds: TimeInterval
|
||||
var nanoseconds: TimeInterval
|
||||
|
||||
switch format {
|
||||
case 0xd6:
|
||||
_ = try read(Int8.self) // -1
|
||||
nanoseconds = 0
|
||||
seconds = TimeInterval(try read(UInt32.self))
|
||||
case 0xd7:
|
||||
_ = try read(Int8.self) // -1
|
||||
let bitPattern = try read(UInt64.self)
|
||||
nanoseconds = TimeInterval(UInt32(bitPattern >> 34))
|
||||
seconds = TimeInterval(UInt32(bitPattern & 0x03_FF_FF_FF_FF))
|
||||
case 0xc7:
|
||||
_ = try read(Int8.self) // 12
|
||||
_ = try read(Int8.self) // -1
|
||||
nanoseconds = TimeInterval(try read(UInt32.self))
|
||||
seconds = TimeInterval(try read(Int64.self))
|
||||
default:
|
||||
let context = DecodingError.Context(codingPath: self.codingPath, debugDescription: "Invalid format: \(format)")
|
||||
throw DecodingError.typeMismatch(Date.self, context)
|
||||
}
|
||||
|
||||
let timeInterval = TimeInterval(seconds) + nanoseconds / Double(NSEC_PER_SEC)
|
||||
|
||||
return Date(timeIntervalSince1970: timeInterval)
|
||||
}
|
||||
|
||||
func decode(_ type: Data.Type) throws -> Data {
|
||||
let length: Int
|
||||
let format = try readByte()
|
||||
switch format {
|
||||
case 0xc4:
|
||||
length = Int(try read(UInt8.self))
|
||||
case 0xc5:
|
||||
length = Int(try read(UInt16.self))
|
||||
case 0xc6:
|
||||
length = Int(try read(UInt32.self))
|
||||
default:
|
||||
throw DecodingError.dataCorruptedError(in: self, debugDescription: "Invalid format for Data length: \(format)")
|
||||
}
|
||||
|
||||
return self.data.subdata(in: self.index..<self.index.advanced(by: length))
|
||||
}
|
||||
|
||||
func decode<T>(_ type: T.Type) throws -> T where T : Decodable {
|
||||
switch type {
|
||||
case is Data.Type:
|
||||
return try decode(Data.self) as! T
|
||||
case is Date.Type:
|
||||
return try decode(Date.self) as! T
|
||||
default:
|
||||
let decoder = _MessagePackDecoder(data: self.data)
|
||||
let value = try T(from: decoder)
|
||||
if let nextIndex = decoder.container?.index {
|
||||
self.index = nextIndex
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension _MessagePackDecoder.SingleValueContainer: MessagePackDecodingContainer {}
|
||||
@@ -1,235 +0,0 @@
|
||||
import Foundation
|
||||
|
||||
extension _MessagePackDecoder {
|
||||
final class UnkeyedContainer {
|
||||
var codingPath: [CodingKey]
|
||||
|
||||
var nestedCodingPath: [CodingKey] {
|
||||
return self.codingPath + [AnyCodingKey(intValue: self.count ?? 0)!]
|
||||
}
|
||||
|
||||
var userInfo: [CodingUserInfoKey: Any]
|
||||
|
||||
var data: Data
|
||||
var index: Data.Index
|
||||
var currentSpec: DataSpec?
|
||||
|
||||
lazy var count: Int? = {
|
||||
do {
|
||||
let format = try self.readByte()
|
||||
switch format {
|
||||
case 0x90...0x9f:
|
||||
return Int(format & 0x0F)
|
||||
case 0xdc:
|
||||
return Int(try read(UInt16.self))
|
||||
case 0xdd:
|
||||
return Int(try read(UInt32.self))
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
} catch {
|
||||
return nil
|
||||
}
|
||||
}()
|
||||
|
||||
var currentIndex: Int = 0
|
||||
|
||||
lazy var nestedContainers: [MessagePackDecodingContainer] = {
|
||||
guard let count = self.count else {
|
||||
return []
|
||||
}
|
||||
|
||||
var nestedContainers: [MessagePackDecodingContainer] = []
|
||||
|
||||
do {
|
||||
for _ in 0..<count {
|
||||
let container = try self.decodeContainer()
|
||||
nestedContainers.append(container)
|
||||
}
|
||||
} catch {
|
||||
fatalError("\(error)") // FIXME
|
||||
}
|
||||
|
||||
self.currentIndex = 0
|
||||
|
||||
return nestedContainers
|
||||
}()
|
||||
|
||||
init(data: Data, codingPath: [CodingKey], userInfo: [CodingUserInfoKey : Any]) {
|
||||
self.codingPath = codingPath
|
||||
self.userInfo = userInfo
|
||||
self.data = data
|
||||
self.index = self.data.startIndex
|
||||
}
|
||||
|
||||
var isAtEnd: Bool {
|
||||
guard let count = self.count else {
|
||||
return true
|
||||
}
|
||||
|
||||
return currentIndex >= count
|
||||
}
|
||||
|
||||
func checkCanDecodeValue() throws {
|
||||
guard !self.isAtEnd else {
|
||||
throw DecodingError.dataCorruptedError(in: self, debugDescription: "Unexpected end of data")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension _MessagePackDecoder.UnkeyedContainer: UnkeyedDecodingContainer {
|
||||
func decodeNil() throws -> Bool {
|
||||
try checkCanDecodeValue()
|
||||
defer { self.currentIndex += 1 }
|
||||
|
||||
let nestedContainer = self.nestedContainers[self.currentIndex]
|
||||
|
||||
switch nestedContainer {
|
||||
case let singleValueContainer as _MessagePackDecoder.SingleValueContainer:
|
||||
return singleValueContainer.decodeNil()
|
||||
case is _MessagePackDecoder.UnkeyedContainer,
|
||||
is _MessagePackDecoder.KeyedContainer<AnyCodingKey>:
|
||||
return false
|
||||
default:
|
||||
let context = DecodingError.Context(codingPath: self.codingPath, debugDescription: "cannot decode nil for index: \(self.currentIndex)")
|
||||
throw DecodingError.typeMismatch(Any?.self, context)
|
||||
}
|
||||
}
|
||||
|
||||
func decode<T>(_ type: T.Type) throws -> T where T : Decodable {
|
||||
try checkCanDecodeValue()
|
||||
defer { self.currentIndex += 1 }
|
||||
|
||||
if userInfo.keys.contains(MessagePackDecoder.isArrayDataSpecKey) {
|
||||
currentSpec = DataSpec("", false, true, (userInfo[MessagePackDecoder.dataSpecKey] as? DataSpecBuilder)?.copy() as? DataSpecBuilder)
|
||||
}
|
||||
|
||||
let container = self.nestedContainers[self.currentIndex]
|
||||
let decoder = MessagePackDecoder()
|
||||
|
||||
if userInfo.keys.contains(MessagePackDecoder.dataSpecKey) {
|
||||
decoder.userInfo[MessagePackDecoder.dataSpecKey] = (userInfo[MessagePackDecoder.dataSpecKey] as? DataSpecBuilder)?.copy() as? DataSpecBuilder
|
||||
}
|
||||
|
||||
let value = try decoder.decode(T.self, from: container.data)
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer {
|
||||
try checkCanDecodeValue()
|
||||
defer { self.currentIndex += 1 }
|
||||
|
||||
let container = self.nestedContainers[self.currentIndex] as! _MessagePackDecoder.UnkeyedContainer
|
||||
|
||||
return container
|
||||
}
|
||||
|
||||
func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer<NestedKey> where NestedKey : CodingKey {
|
||||
try checkCanDecodeValue()
|
||||
defer { self.currentIndex += 1 }
|
||||
|
||||
let container = self.nestedContainers[self.currentIndex] as! _MessagePackDecoder.KeyedContainer<NestedKey>
|
||||
|
||||
return KeyedDecodingContainer(container)
|
||||
}
|
||||
|
||||
func superDecoder() throws -> Decoder {
|
||||
return _MessagePackDecoder(data: self.data)
|
||||
}
|
||||
}
|
||||
|
||||
extension _MessagePackDecoder.UnkeyedContainer {
|
||||
func decodeContainer() throws -> MessagePackDecodingContainer {
|
||||
try checkCanDecodeValue()
|
||||
defer { self.currentIndex += 1 }
|
||||
|
||||
let startIndex = self.index
|
||||
|
||||
var currDataSpec: DataSpec? = nil
|
||||
if currentSpec != nil && currentSpec!.isArray && currentSpec!.dataSpecBuilder != nil {
|
||||
currDataSpec = DataSpec("", true, false, currentSpec!.dataSpecBuilder!.copy() as? DataSpecBuilder)
|
||||
} else {
|
||||
let dataSpec = self.userInfo[MessagePackDecoder.dataSpecKey] as? DataSpecBuilder
|
||||
if let currDS = dataSpec?.next() {
|
||||
currDataSpec = DataSpec(currDS.name, currDS.isObj, currDS.isArray, currDS.dataSpecBuilder?.copy() as? DataSpecBuilder)
|
||||
}
|
||||
}
|
||||
|
||||
let length: Int
|
||||
let format = try self.readByte()
|
||||
switch format {
|
||||
case 0x00...0x7f,
|
||||
0xc0, 0xc2, 0xc3,
|
||||
0xe0...0xff:
|
||||
length = 0
|
||||
case 0xcc, 0xd0, 0xd4:
|
||||
length = 1
|
||||
case 0xcd, 0xd1, 0xd5:
|
||||
length = 2
|
||||
case 0xca, 0xce, 0xd2:
|
||||
length = 4
|
||||
case 0xcb, 0xcf, 0xd3:
|
||||
length = 8
|
||||
case 0xd6:
|
||||
length = 5
|
||||
case 0xd7:
|
||||
length = 9
|
||||
case 0xd8:
|
||||
length = 16
|
||||
case 0xa0...0xbf:
|
||||
length = Int(format - 0xa0)
|
||||
case 0xc4, 0xc7, 0xd9:
|
||||
length = Int(try read(UInt8.self))
|
||||
case 0xc5, 0xc8, 0xda:
|
||||
length = Int(try read(UInt16.self))
|
||||
case 0xc6, 0xc9, 0xdb:
|
||||
length = Int(try read(UInt32.self))
|
||||
case 0x80...0x8f, 0xde, 0xdf:
|
||||
let container = _MessagePackDecoder.KeyedContainer<AnyCodingKey>(data: self.data.suffix(from: startIndex), codingPath: self.nestedCodingPath, userInfo: self.userInfo)
|
||||
container.currentSpec = currDataSpec
|
||||
_ = container.nestedContainers // FIXME
|
||||
self.index = container.index
|
||||
|
||||
return container
|
||||
case 0x90...0x9f, 0xdc, 0xdd:
|
||||
if currDataSpec != nil && currDataSpec!.isObj {
|
||||
var objUserInfo = self.userInfo
|
||||
objUserInfo[MessagePackDecoder.dataSpecKey] = currDataSpec!.dataSpecBuilder!
|
||||
|
||||
let container = _MessagePackDecoder.KeyedContainer<AnyCodingKey>(data: self.data.suffix(from: startIndex), codingPath: self.nestedCodingPath, userInfo: objUserInfo)
|
||||
container.currentSpec = currDataSpec
|
||||
_ = container.nestedContainers // FIXME
|
||||
self.index = container.index
|
||||
|
||||
return container
|
||||
}
|
||||
|
||||
var arrUserInfo = self.userInfo
|
||||
if currDataSpec != nil && currDataSpec!.isArray {
|
||||
arrUserInfo[MessagePackDecoder.dataSpecKey] = currDataSpec!.dataSpecBuilder!
|
||||
}
|
||||
|
||||
let container = _MessagePackDecoder.UnkeyedContainer(data: self.data.suffix(from: startIndex), codingPath: self.nestedCodingPath, userInfo: arrUserInfo)
|
||||
container.currentSpec = currDataSpec
|
||||
_ = container.nestedContainers // FIXME
|
||||
|
||||
self.index = container.index
|
||||
|
||||
return container
|
||||
default:
|
||||
throw DecodingError.dataCorruptedError(in: self, debugDescription: "Invalid format: \(format)")
|
||||
}
|
||||
|
||||
let range: Range<Data.Index> = startIndex..<self.index.advanced(by: length)
|
||||
self.index = range.upperBound
|
||||
|
||||
let container = _MessagePackDecoder.SingleValueContainer(data: self.data.subdata(in: range), codingPath: self.codingPath, userInfo: self.userInfo)
|
||||
container.currentSpec = currDataSpec
|
||||
|
||||
return container
|
||||
}
|
||||
}
|
||||
|
||||
extension _MessagePackDecoder.UnkeyedContainer: MessagePackDecodingContainer {}
|
||||
@@ -1,90 +0,0 @@
|
||||
import Foundation
|
||||
|
||||
extension _MessagePackEncoder {
|
||||
final class KeyedContainer<Key> where Key: CodingKey {
|
||||
private var storage: [AnyCodingKey: _MessagePackEncodingContainer] = [:]
|
||||
|
||||
var codingPath: [CodingKey]
|
||||
var userInfo: [CodingUserInfoKey: Any]
|
||||
|
||||
func nestedCodingPath(forKey key: CodingKey) -> [CodingKey] {
|
||||
return self.codingPath + [key]
|
||||
}
|
||||
|
||||
init(codingPath: [CodingKey], userInfo: [CodingUserInfoKey : Any]) {
|
||||
self.codingPath = codingPath
|
||||
self.userInfo = userInfo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension _MessagePackEncoder.KeyedContainer: KeyedEncodingContainerProtocol {
|
||||
func encodeNil(forKey key: Key) throws {
|
||||
var container = self.nestedSingleValueContainer(forKey: key)
|
||||
try container.encodeNil()
|
||||
}
|
||||
|
||||
func encode<T>(_ value: T, forKey key: Key) throws where T : Encodable {
|
||||
var container = self.nestedSingleValueContainer(forKey: key)
|
||||
try container.encode(value)
|
||||
}
|
||||
|
||||
private func nestedSingleValueContainer(forKey key: Key) -> SingleValueEncodingContainer {
|
||||
let container = _MessagePackEncoder.SingleValueContainer(codingPath: self.nestedCodingPath(forKey: key), userInfo: self.userInfo)
|
||||
self.storage[AnyCodingKey(key)] = container
|
||||
return container
|
||||
}
|
||||
|
||||
func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer {
|
||||
let container = _MessagePackEncoder.UnkeyedContainer(codingPath: self.nestedCodingPath(forKey: key), userInfo: self.userInfo)
|
||||
self.storage[AnyCodingKey(key)] = container
|
||||
|
||||
return container
|
||||
}
|
||||
|
||||
func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer<NestedKey> where NestedKey : CodingKey {
|
||||
let container = _MessagePackEncoder.KeyedContainer<NestedKey>(codingPath: self.nestedCodingPath(forKey: key), userInfo: self.userInfo)
|
||||
self.storage[AnyCodingKey(key)] = container
|
||||
|
||||
return KeyedEncodingContainer(container)
|
||||
}
|
||||
|
||||
func superEncoder() -> Encoder {
|
||||
fatalError("Unimplemented") // FIXME
|
||||
}
|
||||
|
||||
func superEncoder(forKey key: Key) -> Encoder {
|
||||
fatalError("Unimplemented") // FIXME
|
||||
}
|
||||
}
|
||||
|
||||
extension _MessagePackEncoder.KeyedContainer: _MessagePackEncodingContainer {
|
||||
var data: Data {
|
||||
var data = Data()
|
||||
|
||||
let length = storage.count
|
||||
if let uint16 = UInt16(exactly: length) {
|
||||
if length <= 15 {
|
||||
data.append(0x80 + UInt8(length))
|
||||
} else {
|
||||
data.append(0xde)
|
||||
data.append(contentsOf: uint16.bytes)
|
||||
}
|
||||
} else if let uint32 = UInt32(exactly: length) {
|
||||
data.append(0xdf)
|
||||
data.append(contentsOf: uint32.bytes)
|
||||
} else {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
for (key, container) in self.storage {
|
||||
let keyContainer = _MessagePackEncoder.SingleValueContainer(codingPath: self.codingPath, userInfo: self.userInfo)
|
||||
try! keyContainer.encode(key.stringValue)
|
||||
data.append(keyContainer.data)
|
||||
|
||||
data.append(container.data)
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
An object that encodes instances of a data type as MessagePack objects.
|
||||
*/
|
||||
final public class MessagePackEncoder {
|
||||
public init() {}
|
||||
|
||||
/**
|
||||
A dictionary you use to customize the encoding process
|
||||
by providing contextual information.
|
||||
*/
|
||||
public var userInfo: [CodingUserInfoKey : Any] = [:]
|
||||
|
||||
/**
|
||||
Returns a MessagePack-encoded representation of the value you supply.
|
||||
|
||||
- Parameters:
|
||||
- value: The value to encode as MessagePack.
|
||||
- Throws: `EncodingError.invalidValue(_:_:)`
|
||||
if the value can't be encoded as a MessagePack object.
|
||||
*/
|
||||
public func encode<T>(_ value: T) throws -> Data where T : Encodable {
|
||||
let encoder = _MessagePackEncoder()
|
||||
encoder.userInfo = self.userInfo
|
||||
|
||||
switch value {
|
||||
case let data as Data:
|
||||
try Box<Data>(data).encode(to: encoder)
|
||||
case let date as Date:
|
||||
try Box<Date>(date).encode(to: encoder)
|
||||
default:
|
||||
try value.encode(to: encoder)
|
||||
}
|
||||
|
||||
return encoder.data
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - TopLevelEncoder
|
||||
|
||||
#if canImport(Combine)
|
||||
import Combine
|
||||
|
||||
extension MessagePackEncoder: TopLevelEncoder {
|
||||
public typealias Input = Data
|
||||
}
|
||||
#endif
|
||||
|
||||
// MARK: -
|
||||
|
||||
protocol _MessagePackEncodingContainer {
|
||||
var data: Data { get }
|
||||
}
|
||||
|
||||
class _MessagePackEncoder {
|
||||
var codingPath: [CodingKey] = []
|
||||
|
||||
var userInfo: [CodingUserInfoKey : Any] = [:]
|
||||
|
||||
fileprivate var container: _MessagePackEncodingContainer?
|
||||
|
||||
var data: Data {
|
||||
return container?.data ?? Data()
|
||||
}
|
||||
}
|
||||
|
||||
extension _MessagePackEncoder: Encoder {
|
||||
fileprivate func assertCanCreateContainer() {
|
||||
precondition(self.container == nil)
|
||||
}
|
||||
|
||||
func container<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> where Key : CodingKey {
|
||||
assertCanCreateContainer()
|
||||
|
||||
let container = KeyedContainer<Key>(codingPath: self.codingPath, userInfo: self.userInfo)
|
||||
self.container = container
|
||||
|
||||
return KeyedEncodingContainer(container)
|
||||
}
|
||||
|
||||
func unkeyedContainer() -> UnkeyedEncodingContainer {
|
||||
assertCanCreateContainer()
|
||||
|
||||
let container = UnkeyedContainer(codingPath: self.codingPath, userInfo: self.userInfo)
|
||||
self.container = container
|
||||
|
||||
return container
|
||||
}
|
||||
|
||||
func singleValueContainer() -> SingleValueEncodingContainer {
|
||||
assertCanCreateContainer()
|
||||
|
||||
let container = SingleValueContainer(codingPath: self.codingPath, userInfo: self.userInfo)
|
||||
self.container = container
|
||||
|
||||
return container
|
||||
}
|
||||
}
|
||||
@@ -1,264 +0,0 @@
|
||||
import Foundation
|
||||
|
||||
extension _MessagePackEncoder {
|
||||
final class SingleValueContainer {
|
||||
private var storage: Data = Data()
|
||||
|
||||
fileprivate var canEncodeNewValue = true
|
||||
fileprivate func checkCanEncode(value: Any?) throws {
|
||||
guard self.canEncodeNewValue else {
|
||||
let context = EncodingError.Context(codingPath: self.codingPath, debugDescription: "Attempt to encode value through single value container when previously value already encoded.")
|
||||
throw EncodingError.invalidValue(value as Any, context)
|
||||
}
|
||||
}
|
||||
|
||||
var codingPath: [CodingKey]
|
||||
var userInfo: [CodingUserInfoKey: Any]
|
||||
|
||||
init(codingPath: [CodingKey], userInfo: [CodingUserInfoKey : Any]) {
|
||||
self.codingPath = codingPath
|
||||
self.userInfo = userInfo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension _MessagePackEncoder.SingleValueContainer: SingleValueEncodingContainer {
|
||||
func encodeNil() throws {
|
||||
try checkCanEncode(value: nil)
|
||||
defer { self.canEncodeNewValue = false }
|
||||
|
||||
self.storage.append(0xc0)
|
||||
}
|
||||
|
||||
func encode(_ value: Bool) throws {
|
||||
try checkCanEncode(value: nil)
|
||||
defer { self.canEncodeNewValue = false }
|
||||
|
||||
switch value {
|
||||
case false:
|
||||
self.storage.append(0xc2)
|
||||
case true:
|
||||
self.storage.append(0xc3)
|
||||
}
|
||||
}
|
||||
|
||||
func encode(_ value: String) throws {
|
||||
try checkCanEncode(value: value)
|
||||
defer { self.canEncodeNewValue = false }
|
||||
|
||||
guard let data = value.data(using: .utf8) else {
|
||||
let context = EncodingError.Context(codingPath: self.codingPath, debugDescription: "Cannot encode string using UTF-8 encoding.")
|
||||
throw EncodingError.invalidValue(value, context)
|
||||
}
|
||||
|
||||
let length = data.count
|
||||
if let uint8 = UInt8(exactly: length) {
|
||||
if (uint8 <= 31) {
|
||||
self.storage.append(0xa0 + uint8)
|
||||
} else {
|
||||
self.storage.append(0xd9)
|
||||
self.storage.append(contentsOf: uint8.bytes)
|
||||
}
|
||||
} else if let uint16 = UInt16(exactly: length) {
|
||||
self.storage.append(0xda)
|
||||
self.storage.append(contentsOf: uint16.bytes)
|
||||
} else if let uint32 = UInt32(exactly: length) {
|
||||
self.storage.append(0xdb)
|
||||
self.storage.append(contentsOf: uint32.bytes)
|
||||
} else {
|
||||
let context = EncodingError.Context(codingPath: self.codingPath, debugDescription: "Cannot encode string with length \(length).")
|
||||
throw EncodingError.invalidValue(value, context)
|
||||
}
|
||||
|
||||
self.storage.append(data)
|
||||
}
|
||||
|
||||
func encode(_ value: Double) throws {
|
||||
try checkCanEncode(value: value)
|
||||
defer { self.canEncodeNewValue = false }
|
||||
|
||||
self.storage.append(0xcb)
|
||||
self.storage.append(contentsOf: value.bitPattern.bytes)
|
||||
}
|
||||
|
||||
func encode(_ value: Float) throws {
|
||||
try checkCanEncode(value: value)
|
||||
defer { self.canEncodeNewValue = false }
|
||||
|
||||
self.storage.append(0xca)
|
||||
self.storage.append(contentsOf: value.bitPattern.bytes)
|
||||
}
|
||||
|
||||
func encode<T>(_ value: T) throws where T : BinaryInteger & Encodable {
|
||||
try checkCanEncode(value: value)
|
||||
defer { self.canEncodeNewValue = false }
|
||||
|
||||
if value < 0 {
|
||||
if let int8 = Int8(exactly: value) {
|
||||
return try encode(int8)
|
||||
} else if let int16 = Int16(exactly: value) {
|
||||
return try encode(int16)
|
||||
} else if let int32 = Int32(exactly: value) {
|
||||
return try encode(int32)
|
||||
} else if let int64 = Int64(exactly: value) {
|
||||
return try encode(int64)
|
||||
}
|
||||
} else {
|
||||
if let uint8 = UInt8(exactly: value) {
|
||||
return try encode(uint8)
|
||||
} else if let uint16 = UInt16(exactly: value) {
|
||||
return try encode(uint16)
|
||||
} else if let uint32 = UInt32(exactly: value) {
|
||||
return try encode(uint32)
|
||||
} else if let uint64 = UInt64(exactly: value) {
|
||||
return try encode(uint64)
|
||||
}
|
||||
}
|
||||
|
||||
let context = EncodingError.Context(codingPath: self.codingPath, debugDescription: "Cannot encode integer \(value).")
|
||||
throw EncodingError.invalidValue(value, context)
|
||||
}
|
||||
|
||||
func encode(_ value: Int8) throws {
|
||||
try checkCanEncode(value: value)
|
||||
defer { self.canEncodeNewValue = false }
|
||||
|
||||
if (value >= 0 && value <= 127) {
|
||||
self.storage.append(UInt8(value))
|
||||
} else if (value < 0 && value >= -31) {
|
||||
self.storage.append(0xe0 + (0x1f & UInt8(truncatingIfNeeded: value)))
|
||||
} else {
|
||||
self.storage.append(0xd0)
|
||||
self.storage.append(contentsOf: value.bytes)
|
||||
}
|
||||
}
|
||||
|
||||
func encode(_ value: Int16) throws {
|
||||
try checkCanEncode(value: value)
|
||||
defer { self.canEncodeNewValue = false }
|
||||
|
||||
self.storage.append(0xd1)
|
||||
self.storage.append(contentsOf: value.bytes)
|
||||
}
|
||||
|
||||
func encode(_ value: Int32) throws {
|
||||
try checkCanEncode(value: value)
|
||||
defer { self.canEncodeNewValue = false }
|
||||
|
||||
self.storage.append(0xd2)
|
||||
self.storage.append(contentsOf: value.bytes)
|
||||
}
|
||||
|
||||
func encode(_ value: Int64) throws {
|
||||
try checkCanEncode(value: value)
|
||||
defer { self.canEncodeNewValue = false }
|
||||
|
||||
self.storage.append(0xd3)
|
||||
self.storage.append(contentsOf: value.bytes)
|
||||
}
|
||||
|
||||
func encode(_ value: UInt8) throws {
|
||||
try checkCanEncode(value: value)
|
||||
defer { self.canEncodeNewValue = false }
|
||||
|
||||
if (value <= 127) {
|
||||
self.storage.append(value)
|
||||
} else {
|
||||
self.storage.append(0xcc)
|
||||
self.storage.append(contentsOf: value.bytes)
|
||||
}
|
||||
}
|
||||
|
||||
func encode(_ value: UInt16) throws {
|
||||
try checkCanEncode(value: value)
|
||||
defer { self.canEncodeNewValue = false }
|
||||
|
||||
self.storage.append(0xcd)
|
||||
self.storage.append(contentsOf: value.bytes)
|
||||
}
|
||||
|
||||
func encode(_ value: UInt32) throws {
|
||||
try checkCanEncode(value: value)
|
||||
defer { self.canEncodeNewValue = false }
|
||||
|
||||
self.storage.append(0xce)
|
||||
self.storage.append(contentsOf: value.bytes)
|
||||
}
|
||||
|
||||
func encode(_ value: UInt64) throws {
|
||||
try checkCanEncode(value: value)
|
||||
defer { self.canEncodeNewValue = false }
|
||||
|
||||
self.storage.append(0xcf)
|
||||
self.storage.append(contentsOf: value.bytes)
|
||||
}
|
||||
|
||||
func encode(_ value: Date) throws {
|
||||
try checkCanEncode(value: value)
|
||||
defer { self.canEncodeNewValue = false }
|
||||
|
||||
let timeInterval = value.timeIntervalSince1970
|
||||
let (integral, fractional) = modf(timeInterval)
|
||||
|
||||
let seconds = Int64(integral)
|
||||
let nanoseconds = UInt32(fractional * Double(NSEC_PER_SEC))
|
||||
|
||||
if seconds < 0 || seconds > UInt32.max {
|
||||
self.storage.append(0xc7)
|
||||
self.storage.append(0x0C)
|
||||
self.storage.append(0xFF)
|
||||
self.storage.append(contentsOf: nanoseconds.bytes)
|
||||
self.storage.append(contentsOf: seconds.bytes)
|
||||
} else if nanoseconds > 0 {
|
||||
self.storage.append(0xd7)
|
||||
self.storage.append(0xFF)
|
||||
self.storage.append(contentsOf: ((UInt64(nanoseconds) << 34) + UInt64(seconds)).bytes)
|
||||
} else {
|
||||
self.storage.append(0xd6)
|
||||
self.storage.append(0xFF)
|
||||
self.storage.append(contentsOf: UInt32(seconds).bytes)
|
||||
}
|
||||
}
|
||||
|
||||
func encode(_ value: Data) throws {
|
||||
let length = value.count
|
||||
if let uint8 = UInt8(exactly: length) {
|
||||
self.storage.append(0xc4)
|
||||
self.storage.append(uint8)
|
||||
self.storage.append(value)
|
||||
} else if let uint16 = UInt16(exactly: length) {
|
||||
self.storage.append(0xc5)
|
||||
self.storage.append(contentsOf: uint16.bytes)
|
||||
self.storage.append(value)
|
||||
} else if let uint32 = UInt32(exactly: length) {
|
||||
self.storage.append(0xc6)
|
||||
self.storage.append(contentsOf: uint32.bytes)
|
||||
self.storage.append(value)
|
||||
} else {
|
||||
let context = EncodingError.Context(codingPath: self.codingPath, debugDescription: "Cannot encode data of length \(value.count).")
|
||||
throw EncodingError.invalidValue(value, context)
|
||||
}
|
||||
}
|
||||
|
||||
func encode<T>(_ value: T) throws where T : Encodable {
|
||||
try checkCanEncode(value: value)
|
||||
defer { self.canEncodeNewValue = false }
|
||||
|
||||
switch value {
|
||||
case let data as Data:
|
||||
try self.encode(data)
|
||||
case let date as Date:
|
||||
try self.encode(date)
|
||||
default:
|
||||
let encoder = _MessagePackEncoder()
|
||||
try value.encode(to: encoder)
|
||||
self.storage.append(encoder.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension _MessagePackEncoder.SingleValueContainer: _MessagePackEncodingContainer {
|
||||
var data: Data {
|
||||
return storage
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
import Foundation
|
||||
|
||||
extension _MessagePackEncoder {
|
||||
final class UnkeyedContainer {
|
||||
private var storage: [_MessagePackEncodingContainer] = []
|
||||
|
||||
var count: Int {
|
||||
return storage.count
|
||||
}
|
||||
|
||||
var codingPath: [CodingKey]
|
||||
|
||||
var nestedCodingPath: [CodingKey] {
|
||||
return self.codingPath + [AnyCodingKey(intValue: self.count)!]
|
||||
}
|
||||
|
||||
var userInfo: [CodingUserInfoKey: Any]
|
||||
|
||||
init(codingPath: [CodingKey], userInfo: [CodingUserInfoKey : Any]) {
|
||||
self.codingPath = codingPath
|
||||
self.userInfo = userInfo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension _MessagePackEncoder.UnkeyedContainer: UnkeyedEncodingContainer {
|
||||
func encodeNil() throws {
|
||||
var container = self.nestedSingleValueContainer()
|
||||
try container.encodeNil()
|
||||
}
|
||||
|
||||
func encode<T>(_ value: T) throws where T : Encodable {
|
||||
var container = self.nestedSingleValueContainer()
|
||||
try container.encode(value)
|
||||
}
|
||||
|
||||
private func nestedSingleValueContainer() -> SingleValueEncodingContainer {
|
||||
let container = _MessagePackEncoder.SingleValueContainer(codingPath: self.nestedCodingPath, userInfo: self.userInfo)
|
||||
self.storage.append(container)
|
||||
|
||||
return container
|
||||
}
|
||||
|
||||
func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer<NestedKey> where NestedKey : CodingKey {
|
||||
let container = _MessagePackEncoder.KeyedContainer<NestedKey>(codingPath: self.nestedCodingPath, userInfo: self.userInfo)
|
||||
self.storage.append(container)
|
||||
|
||||
return KeyedEncodingContainer(container)
|
||||
}
|
||||
|
||||
func nestedUnkeyedContainer() -> UnkeyedEncodingContainer {
|
||||
let container = _MessagePackEncoder.UnkeyedContainer(codingPath: self.nestedCodingPath, userInfo: self.userInfo)
|
||||
self.storage.append(container)
|
||||
|
||||
return container
|
||||
}
|
||||
|
||||
func superEncoder() -> Encoder {
|
||||
fatalError("Unimplemented") // FIXME
|
||||
}
|
||||
}
|
||||
|
||||
extension _MessagePackEncoder.UnkeyedContainer: _MessagePackEncodingContainer {
|
||||
var data: Data {
|
||||
var data = Data()
|
||||
|
||||
let length = storage.count
|
||||
if let uint16 = UInt16(exactly: length) {
|
||||
if uint16 <= 15 {
|
||||
data.append(UInt8(0x90 + uint16))
|
||||
} else {
|
||||
data.append(0xdc)
|
||||
data.append(contentsOf: uint16.bytes)
|
||||
}
|
||||
} else if let uint32 = UInt32(exactly: length) {
|
||||
data.append(0xdd)
|
||||
data.append(contentsOf: uint32.bytes)
|
||||
} else {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
for container in storage {
|
||||
data.append(container.data)
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
extension FixedWidthInteger {
|
||||
init(bytes: [UInt8]) {
|
||||
self = bytes.withUnsafeBufferPointer {
|
||||
$0.baseAddress!.withMemoryRebound(to: Self.self, capacity: 1) {
|
||||
$0.pointee
|
||||
}
|
||||
}.bigEndian
|
||||
}
|
||||
|
||||
var bytes: [UInt8] {
|
||||
let capacity = MemoryLayout<Self>.size
|
||||
var mutableValue = self.bigEndian
|
||||
return withUnsafePointer(to: &mutableValue) {
|
||||
return $0.withMemoryRebound(to: UInt8.self, capacity: capacity) {
|
||||
return Array(UnsafeBufferPointer(start: $0, count: capacity))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
import XCTest
|
||||
@testable import MessagePackTests
|
||||
|
||||
XCTMain([
|
||||
testCase(MessagePackDecodingTests.allTests),
|
||||
testCase(MessagePackEncodingTests.allTests),
|
||||
testCase(MessagePackRoundTripTests.allTests),
|
||||
])
|
||||
@@ -1,63 +0,0 @@
|
||||
struct Airport: Codable, Equatable {
|
||||
let name: String
|
||||
let iata: String
|
||||
let icao: String
|
||||
let coordinates: [Double]
|
||||
|
||||
struct Runway: Codable, Equatable {
|
||||
enum Surface: String, Codable, Equatable {
|
||||
case rigid, flexible, gravel, sealed, unpaved, other
|
||||
}
|
||||
|
||||
let direction: String
|
||||
let distance: Int
|
||||
let surface: Surface
|
||||
}
|
||||
|
||||
let runways: [Runway]
|
||||
|
||||
let instrumentApproachProcedures: [String]
|
||||
|
||||
static var example: Airport {
|
||||
return Airport(
|
||||
name: "Portland International Airport",
|
||||
iata: "PDX",
|
||||
icao: "KPDX",
|
||||
coordinates: [-122.5975,
|
||||
45.5886111111111],
|
||||
runways: [
|
||||
Airport.Runway(
|
||||
direction: "3/21",
|
||||
distance: 1829,
|
||||
surface: .flexible
|
||||
)
|
||||
],
|
||||
instrumentApproachProcedures: [
|
||||
"HI-ILS OR LOC RWY 28",
|
||||
"HI-ILS OR LOC/DME RWY 10",
|
||||
"ILS OR LOC RWY 10L",
|
||||
"ILS OR LOC RWY 10R",
|
||||
"ILS OR LOC RWY 28L",
|
||||
"ILS OR LOC RWY 28R",
|
||||
"ILS RWY 10R (SA CAT I)",
|
||||
"ILS RWY 10R (CAT II - III)",
|
||||
"RNAV (RNP) Y RWY 28L",
|
||||
"RNAV (RNP) Y RWY 28R",
|
||||
"RNAV (RNP) Z RWY 10L",
|
||||
"RNAV (RNP) Z RWY 10R",
|
||||
"RNAV (RNP) Z RWY 28L",
|
||||
"RNAV (RNP) Z RWY 28R",
|
||||
"RNAV (GPS) X RWY 28L",
|
||||
"RNAV (GPS) X RWY 28R",
|
||||
"RNAV (GPS) Y RWY 10L",
|
||||
"RNAV (GPS) Y RWY 10R",
|
||||
"LOC/DME RWY 21",
|
||||
"VOR-A",
|
||||
"HI-TACAN RWY 10",
|
||||
"TACAN RWY 28",
|
||||
"COLUMBIA VISUAL RWY 10L/",
|
||||
"MILL VISUAL RWY 28L/R"
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,301 +0,0 @@
|
||||
import XCTest
|
||||
@testable import MessagePack
|
||||
|
||||
class MessagePackDecodingTests: XCTestCase {
|
||||
var decoder: MessagePackDecoder!
|
||||
|
||||
override func setUp() {
|
||||
self.decoder = MessagePackDecoder()
|
||||
}
|
||||
|
||||
func assertTypeMismatch<T>(_ expression: @autoclosure () throws -> T,
|
||||
_ message: @autoclosure () -> String = "",
|
||||
file: StaticString = #file,
|
||||
line: UInt = #line) -> Any.Type? {
|
||||
var error: Error?
|
||||
XCTAssertThrowsError(expression, message,
|
||||
file: file, line: line) {
|
||||
error = $0
|
||||
}
|
||||
guard case .typeMismatch(let type, _) = error as? DecodingError else {
|
||||
XCTFail(file: file, line: line)
|
||||
return nil
|
||||
}
|
||||
return type
|
||||
}
|
||||
|
||||
func testDecodeNil() {
|
||||
let data = Data(bytes: [0xC0])
|
||||
let value = try! decoder.decode(Int?.self, from: data)
|
||||
XCTAssertNil(value)
|
||||
}
|
||||
|
||||
func testDecodeFalse() {
|
||||
let data = Data(bytes: [0xc2])
|
||||
let value = try! decoder.decode(Bool.self, from: data)
|
||||
XCTAssertEqual(value, false)
|
||||
}
|
||||
|
||||
func testDecodeTrue() {
|
||||
let data = Data(bytes: [0xc3])
|
||||
let value = try! decoder.decode(Bool.self, from: data)
|
||||
XCTAssertEqual(value, true)
|
||||
}
|
||||
|
||||
func testDecodeInt() {
|
||||
let data = Data(bytes: [0x2A])
|
||||
let value = try! decoder.decode(Int.self, from: data)
|
||||
XCTAssertEqual(value, 42)
|
||||
}
|
||||
|
||||
func testDecodeNegativeInt() {
|
||||
let data = Data(bytes: [0xFF])
|
||||
let value = try! decoder.decode(Int.self, from: data)
|
||||
XCTAssertEqual(value, -1)
|
||||
}
|
||||
|
||||
func testDecodeUInt() {
|
||||
let data = Data(bytes: [0xCC, 0x80])
|
||||
let value = try! decoder.decode(Int.self, from: data)
|
||||
XCTAssertEqual(value, 128)
|
||||
}
|
||||
|
||||
func testDecodeFloat() {
|
||||
let data = Data(bytes: [0xCA, 0x40, 0x48, 0xF5, 0xC3])
|
||||
let value = try! decoder.decode(Float.self, from: data)
|
||||
XCTAssertEqual(value, 3.14)
|
||||
}
|
||||
|
||||
func testDecodeFloatToDouble() {
|
||||
let data = Data(bytes: [0xCA, 0x40, 0x48, 0xF5, 0xC3])
|
||||
let type = assertTypeMismatch(try decoder.decode(Double.self, from: data))
|
||||
XCTAssertTrue(type is Double.Type)
|
||||
decoder.nonMatchingFloatDecodingStrategy = .cast
|
||||
let value = try! decoder.decode(Double.self, from: data)
|
||||
XCTAssertEqual(value, 3.14, accuracy: 1e-6)
|
||||
}
|
||||
|
||||
func testDecodeDouble() {
|
||||
let data = Data(bytes: [0xCB, 0x40, 0x09, 0x21, 0xF9, 0xF0, 0x1B, 0x86, 0x6E])
|
||||
let value = try! decoder.decode(Double.self, from: data)
|
||||
XCTAssertEqual(value, 3.14159)
|
||||
}
|
||||
|
||||
func testDecodeDoubleToFloat() {
|
||||
let data = Data(bytes: [0xCB, 0x40, 0x09, 0x21, 0xF9, 0xF0, 0x1B, 0x86, 0x6E])
|
||||
let type = assertTypeMismatch(try decoder.decode(Float.self, from: data))
|
||||
XCTAssertTrue(type is Float.Type)
|
||||
decoder.nonMatchingFloatDecodingStrategy = .cast
|
||||
let value = try! decoder.decode(Float.self, from: data)
|
||||
XCTAssertEqual(value, 3.14159)
|
||||
}
|
||||
|
||||
func testDecodeFixedArray() {
|
||||
let data = Data(bytes: [0x93, 0x01, 0x02, 0x03])
|
||||
let value = try! decoder.decode([Int].self, from: data)
|
||||
XCTAssertEqual(value, [1, 2, 3])
|
||||
}
|
||||
|
||||
func testDecodeVariableArray() {
|
||||
let data = Data(bytes: [0xdc] + [0x00, 0x10] + Array(0x01...0x10))
|
||||
let value = try! decoder.decode([Int].self, from: data)
|
||||
XCTAssertEqual(value, Array(1...16))
|
||||
}
|
||||
|
||||
func testDecodeFixedDictionary() {
|
||||
let data = Data(bytes: [0x83, 0xA1, 0x62, 0x02, 0xA1, 0x61, 0x01, 0xA1, 0x63, 0x03])
|
||||
let value = try! decoder.decode([String: Int].self, from: data)
|
||||
XCTAssertEqual(value, ["a": 1, "b": 2, "c": 3])
|
||||
}
|
||||
|
||||
func testDecodeData() {
|
||||
let data = Data(bytes: [0xC4, 0x05, 0x68, 0x65, 0x6C, 0x6C, 0x6F])
|
||||
let value = try! decoder.decode(Data.self, from: data)
|
||||
XCTAssertEqual(value, "hello".data(using: .utf8))
|
||||
}
|
||||
|
||||
func testDecodeDate() {
|
||||
let data = Data(bytes: [0xD6, 0xFF, 0x00, 0x00, 0x00, 0x01])
|
||||
let date = Date(timeIntervalSince1970: 1)
|
||||
let value = try! decoder.decode(Date.self, from: data)
|
||||
XCTAssertEqual(value, date)
|
||||
}
|
||||
|
||||
func testDecodeDistantPast() {
|
||||
let data = Data(bytes: [0xC7, 0x0C, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xF1, 0x88, 0x6B, 0x66, 0x00])
|
||||
let date = Date.distantPast
|
||||
let value = try! decoder.decode(Date.self, from: data)
|
||||
XCTAssertEqual(value, date)
|
||||
}
|
||||
|
||||
func testDecodeDistantFuture() {
|
||||
let data = Data(bytes: [0xC7, 0x0C, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0xEC, 0x31, 0x88, 0x00])
|
||||
let date = Date.distantFuture
|
||||
let value = try! decoder.decode(Date.self, from: data)
|
||||
XCTAssertEqual(value, date)
|
||||
}
|
||||
|
||||
func testDecodeArrayWithDate() {
|
||||
let data = Data(bytes: [0x91, 0xD6, 0xFF, 0x00, 0x00, 0x00, 0x01])
|
||||
let date = Date(timeIntervalSince1970: 1)
|
||||
let value = try! decoder.decode([Date].self, from: data)
|
||||
XCTAssertEqual(value, [date])
|
||||
}
|
||||
|
||||
func testDecodeDictionaryWithDate() {
|
||||
let data = Data(bytes: [0x81, 0xA1, 0x31, 0xD6, 0xFF, 0x00, 0x00, 0x00, 0x01])
|
||||
let date = Date(timeIntervalSince1970: 1)
|
||||
let value = try! decoder.decode([String: Date].self, from: data)
|
||||
XCTAssertEqual(value, ["1": date])
|
||||
}
|
||||
|
||||
func testDecodeBv() {
|
||||
let b64 = "lK50ZXN0aW5nIHN0cmluZyeSpnF3ZXF3ZagxMjNpY29uc5OU2SRlMTZkYTYwMi0zMjE1LTRiZDYtYjY5MC00Y2Q4NmEwZmU3NjSoQ2lwaGVyIDEBk61jaXBodXNlcm5hbWUxrWFkZmFmZHcyMzQxMzGSkblodHRwczovL3d3dy5nb29nbGUuY29tLmFykbVodHRwczovL3d3dy5hcHBsZS5jb22U2SRhNjExMWU2Ny1hMTMwLTRiM2ItODM5NS0xZjIzMDFjNjk3ZjeoQ2lwaGVyIDIBk6g0MzEzMjEzMatqbGpsbHl1bHVpecCU2SRiOGIwODM3MC0xNGU0LTQzZmUtYjBkOS04ZjJlMDlmODJkYzWoQ2lwaGVyIDMBk6twaW9waW9waXBpb6x6eGN6eHZ6eHZ4enaSkbdodHRwczovL3d3dy52aXNhLmNvbS5hcpG1aHR0cHM6Ly93d3cuZG9ja3MuY29t" // array mode with envData and ciphers
|
||||
|
||||
// let b64 = "hKFirnRlc3Rpbmcgc3RyaW5noWMnp2VudkRhdGGCpGJhc2WmcXdlcXdlpWljb25zqDEyM2ljb25zp2NpcGhlcnOThKJpZNkkMDA4YmE0NDctZjU0Mi00OWVjLWJjYTktMDMzZTQ2OTU0YTBipG5hbWWoQ2lwaGVyIDGkdHlwZQGlbG9naW6DqHVzZXJuYW1lrWNpcGh1c2VybmFtZTGkdG90cK1hZGZhZmR3MjM0MTMxpHVyaXOSgaN1cmm5aHR0cHM6Ly93d3cuZ29vZ2xlLmNvbS5hcoGjdXJptWh0dHBzOi8vd3d3LmFwcGxlLmNvbYSiaWTZJDQ1ZTBhODJiLTgyZGQtNDJiZi05ODhhLTAyYTkyNGM4Yzg5M6RuYW1lqENpcGhlciAypHR5cGUBpWxvZ2lug6h1c2VybmFtZag0MzEzMjEzMaR0b3Rwq2psamxseXVsdWl5pHVyaXPAhKJpZNkkZTBjZWU5NDEtZDI1Ni00MjdiLWJkNWUtNDMxMmMwN2U1NDI5pG5hbWWoQ2lwaGVyIDOkdHlwZQGlbG9naW6DqHVzZXJuYW1lq3Bpb3Bpb3BpcGlvpHRvdHCsenhjenh2enh2eHp2pHVyaXOSgaN1cmm3aHR0cHM6Ly93d3cudmlzYS5jb20uYXKBo3VyabVodHRwczovL3d3dy5kb2Nrcy5jb20=" // dict mode with envData and ciphers
|
||||
|
||||
do {
|
||||
if let d = Data(base64Encoded: b64) {
|
||||
let decoder = MessagePackDecoder()
|
||||
decoder.userInfo[MessagePackDecoder.dataSpecKey] = DataSpecBuilder()
|
||||
.append("b")
|
||||
.append("c")
|
||||
.appendObj("envData", DataSpecBuilder()
|
||||
.append("base")
|
||||
.append("icons")
|
||||
.build())
|
||||
.appendArray("ciphers", DataSpecBuilder()
|
||||
.append("id")
|
||||
.append("name")
|
||||
.append("type")
|
||||
.appendObj("login", DataSpecBuilder()
|
||||
.append("username")
|
||||
.append("totp")
|
||||
.appendArray("uris", DataSpecBuilder()
|
||||
.append("uri")
|
||||
.build())
|
||||
.build())
|
||||
.build())
|
||||
.build()
|
||||
|
||||
let codTest = try decoder.decode(CodableTest.self, from: d)
|
||||
|
||||
XCTAssertEqual(codTest.b, "testing string")
|
||||
XCTAssertEqual(codTest.envData.base, "qweqwe")
|
||||
XCTAssertEqual(codTest.envData.icons, "123icons")
|
||||
XCTAssertTrue(codTest.ciphers!.count > 1)
|
||||
} else {
|
||||
XCTAssertEqual(1, 0)
|
||||
}
|
||||
} catch let error {
|
||||
XCTFail("E: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
static var allTests = [
|
||||
("testDecodeNil", testDecodeNil),
|
||||
("testDecodeFalse", testDecodeFalse),
|
||||
("testDecodeTrue", testDecodeTrue),
|
||||
("testDecodeInt", testDecodeInt),
|
||||
("testDecodeUInt", testDecodeUInt),
|
||||
("testDecodeFloat", testDecodeFloat),
|
||||
("testDecodeFloatToDouble", testDecodeFloatToDouble),
|
||||
("testDecodeDouble", testDecodeDouble),
|
||||
("testDecodeDoubleToFloat", testDecodeDoubleToFloat),
|
||||
("testDecodeFixedArray", testDecodeFixedArray),
|
||||
("testDecodeFixedDictionary", testDecodeFixedDictionary),
|
||||
("testDecodeData", testDecodeData),
|
||||
("testDecodeDistantPast", testDecodeDistantPast),
|
||||
("testDecodeDistantFuture", testDecodeDistantFuture),
|
||||
("testDecodeArrayWithDate", testDecodeArrayWithDate),
|
||||
("testDecodeDictionaryWithDate", testDecodeDictionaryWithDate),
|
||||
("testDecodeBv", testDecodeBv)
|
||||
]
|
||||
}
|
||||
|
||||
struct CodableTest : Codable {
|
||||
enum CodingKeys: Int, CodingKey {
|
||||
case b
|
||||
case c
|
||||
case envData
|
||||
case ciphers
|
||||
}
|
||||
|
||||
var b: String
|
||||
var c: Int
|
||||
var envData: EnvironmentUrlDataDto
|
||||
var ciphers: [Cipher]?
|
||||
|
||||
func printt() {
|
||||
print("B: \(b)")
|
||||
print("C: \(c)")
|
||||
print("ENVDATA")
|
||||
envData.printt()
|
||||
|
||||
if let cs = ciphers {
|
||||
print("CIPHERS")
|
||||
for c in cs {
|
||||
c.printt()
|
||||
print("----------------------------")
|
||||
}
|
||||
}
|
||||
|
||||
print("###########################")
|
||||
}
|
||||
}
|
||||
|
||||
struct EnvironmentUrlDataDto : Codable {
|
||||
var base: String?
|
||||
var icons: String?
|
||||
|
||||
func printt() {
|
||||
print("Base: \(base ?? "")")
|
||||
print("Icons: \(icons ?? "")")
|
||||
}
|
||||
}
|
||||
|
||||
struct Cipher:Identifiable,Codable{
|
||||
enum CodingKeys: Int, CodingKey {
|
||||
case id
|
||||
case name
|
||||
case login
|
||||
}
|
||||
|
||||
var id:String
|
||||
var name:String?
|
||||
var userId:String?
|
||||
var login:Login
|
||||
|
||||
func printt() {
|
||||
print("id: \(id)")
|
||||
print("name: \(name ?? "")")
|
||||
print("LOGIN")
|
||||
login.printt()
|
||||
}
|
||||
}
|
||||
|
||||
struct Login:Codable{
|
||||
var username:String?
|
||||
var totp:String?
|
||||
var uris:[LoginUri]?
|
||||
|
||||
func printt() {
|
||||
print("username: \(username ?? "")")
|
||||
print("totp: \(totp ?? "")")
|
||||
print("URIS")
|
||||
if let us = uris {
|
||||
for u in us {
|
||||
u.printt()
|
||||
print("----------------------------")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct LoginUri:Codable{
|
||||
var uri:String?
|
||||
|
||||
func printt() {
|
||||
print("Uri: \(uri ?? "")")
|
||||
}
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
import XCTest
|
||||
@testable import MessagePack
|
||||
|
||||
class MessagePackEncodingTests: XCTestCase {
|
||||
var encoder: MessagePackEncoder!
|
||||
|
||||
override func setUp() {
|
||||
self.encoder = MessagePackEncoder()
|
||||
}
|
||||
|
||||
func testEncodeNil() {
|
||||
let value = try! encoder.encode(nil as Int?)
|
||||
XCTAssertEqual(value, Data(bytes: [0xc0]))
|
||||
}
|
||||
|
||||
func testEncodeFalse() {
|
||||
let value = try! encoder.encode(false)
|
||||
XCTAssertEqual(value, Data(bytes: [0xc2]))
|
||||
}
|
||||
|
||||
func testEncodeTrue() {
|
||||
let value = try! encoder.encode(true)
|
||||
XCTAssertEqual(value, Data(bytes: [0xc3]))
|
||||
}
|
||||
|
||||
func testEncodeInt() {
|
||||
let value = try! encoder.encode(42 as Int)
|
||||
XCTAssertEqual(value, Data(bytes: [0x2A]))
|
||||
}
|
||||
|
||||
func testEncodeUInt() {
|
||||
let value = try! encoder.encode(128 as UInt)
|
||||
XCTAssertEqual(value, Data(bytes: [0xCC, 0x80]))
|
||||
}
|
||||
|
||||
func testEncodeFloat() {
|
||||
let value = try! encoder.encode(3.14 as Float)
|
||||
XCTAssertEqual(value, Data(bytes: [0xCA, 0x40, 0x48, 0xF5, 0xC3]))
|
||||
}
|
||||
|
||||
func testEncodeDouble() {
|
||||
let value = try! encoder.encode(3.14159 as Double)
|
||||
XCTAssertEqual(value, Data(bytes: [0xCB, 0x40, 0x09, 0x21, 0xF9, 0xF0, 0x1B, 0x86, 0x6E]))
|
||||
}
|
||||
|
||||
func testEncodeString() {
|
||||
let value = try! encoder.encode("hello")
|
||||
XCTAssertEqual(value, Data(bytes: [0xA5, 0x68, 0x65, 0x6C, 0x6C, 0x6F]))
|
||||
}
|
||||
|
||||
func testEncodeFixedArray() {
|
||||
let value = try! encoder.encode([1, 2, 3])
|
||||
XCTAssertEqual(value, Data(bytes: [0x93, 0x01, 0x02, 0x03]))
|
||||
}
|
||||
|
||||
func testEncodeVariableArray() {
|
||||
let value = try! encoder.encode(Array(1...16))
|
||||
XCTAssertEqual(value, Data(bytes: [0xdc] + [0x00, 0x10] + Array(0x01...0x10)))
|
||||
}
|
||||
|
||||
func testEncodeFixedDictionary() {
|
||||
let value = try! encoder.encode(["a": 1])
|
||||
XCTAssertEqual(value, Data(bytes: [0x81, 0xA1, 0x61, 0x01]))
|
||||
}
|
||||
|
||||
func testEncodeVariableDictionary() {
|
||||
let letters = "abcdefghijklmnopqrstuvwxyz".unicodeScalars
|
||||
let dictionary = Dictionary(uniqueKeysWithValues: zip(letters.map { String($0) }, 1...26))
|
||||
let value = try! encoder.encode(dictionary)
|
||||
XCTAssertEqual(value.count, 81)
|
||||
XCTAssert(value.starts(with: [0xde] + [0x00, 0x1A]))
|
||||
}
|
||||
|
||||
func testEncodeData() {
|
||||
let data = "hello".data(using: .utf8)
|
||||
let value = try! encoder.encode(data)
|
||||
XCTAssertEqual(value, Data(bytes: [0xC4, 0x05, 0x68, 0x65, 0x6C, 0x6C, 0x6F]))
|
||||
}
|
||||
|
||||
func testEncodeDate() {
|
||||
let date = Date(timeIntervalSince1970: 1)
|
||||
let value = try! encoder.encode(date)
|
||||
XCTAssertEqual(value, Data(bytes: [0xD6, 0xFF, 0x00, 0x00, 0x00, 0x01]))
|
||||
}
|
||||
|
||||
func testEncodeDistantPast() {
|
||||
let date = Date.distantPast
|
||||
let value = try! encoder.encode(date)
|
||||
XCTAssertEqual(value, Data(bytes: [0xC7, 0x0C, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xF1, 0x88, 0x6B, 0x66, 0x00]))
|
||||
}
|
||||
|
||||
func testEncodeDistantFuture() {
|
||||
let date = Date.distantFuture
|
||||
let value = try! encoder.encode(date)
|
||||
XCTAssertEqual(value, Data(bytes: [0xC7, 0x0C, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0xEC, 0x31, 0x88, 0x00]))
|
||||
}
|
||||
|
||||
func testEncodeArrayWithDate() {
|
||||
let date = Date(timeIntervalSince1970: 1)
|
||||
let value = try! encoder.encode([date])
|
||||
XCTAssertEqual(value, Data(bytes: [0x91, 0xD6, 0xFF, 0x00, 0x00, 0x00, 0x01]))
|
||||
}
|
||||
|
||||
func testEncodeDictionaryWithDate() {
|
||||
let date = Date(timeIntervalSince1970: 1)
|
||||
let value = try! encoder.encode(["1": date])
|
||||
XCTAssertEqual(value, Data(bytes: [0x81, 0xA1, 0x31, 0xD6, 0xFF, 0x00, 0x00, 0x00, 0x01]))
|
||||
}
|
||||
|
||||
static var allTests = [
|
||||
("testEncodeFalse", testEncodeFalse),
|
||||
("testEncodeTrue", testEncodeTrue),
|
||||
("testEncodeInt", testEncodeInt),
|
||||
("testEncodeUInt", testEncodeUInt),
|
||||
("testEncodeFloat", testEncodeFloat),
|
||||
("testEncodeDouble", testEncodeDouble),
|
||||
("testEncodeFixedArray", testEncodeFixedArray),
|
||||
("testEncodeVariableArray", testEncodeVariableArray),
|
||||
("testEncodeFixedDictionary", testEncodeFixedDictionary),
|
||||
("testEncodeVariableDictionary", testEncodeVariableDictionary),
|
||||
("testEncodeDate", testEncodeDate),
|
||||
("testEncodeDistantPast", testEncodeDistantPast),
|
||||
("testEncodeDistantFuture", testEncodeDistantFuture),
|
||||
("testEncodeArrayWithDate", testEncodeArrayWithDate),
|
||||
("testEncodeDictionaryWithDate", testEncodeDictionaryWithDate)
|
||||
]
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import XCTest
|
||||
@testable import MessagePack
|
||||
|
||||
class MessagePackPerformanceTests: XCTestCase {
|
||||
var encoder: MessagePackEncoder!
|
||||
var decoder: MessagePackDecoder!
|
||||
|
||||
override func setUp() {
|
||||
self.encoder = MessagePackEncoder()
|
||||
self.decoder = MessagePackDecoder()
|
||||
}
|
||||
|
||||
func testPerformance() {
|
||||
let count = 100
|
||||
let values = [Airport](repeating: .example, count: count)
|
||||
|
||||
self.measure {
|
||||
let encoded = try! encoder.encode(values)
|
||||
let decoded = try! decoder.decode([Airport].self, from: encoded)
|
||||
XCTAssertEqual(decoded.count, count)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
import XCTest
|
||||
@testable import MessagePack
|
||||
|
||||
class MessagePackRoundTripTests: XCTestCase {
|
||||
var encoder: MessagePackEncoder!
|
||||
var decoder: MessagePackDecoder!
|
||||
|
||||
override func setUp() {
|
||||
self.encoder = MessagePackEncoder()
|
||||
self.decoder = MessagePackDecoder()
|
||||
}
|
||||
|
||||
func testRoundTripAirport() {
|
||||
let value = Airport.example
|
||||
let encoded = try! encoder.encode(value)
|
||||
let decoded = try! decoder.decode(Airport.self, from: encoded)
|
||||
|
||||
XCTAssertEqual(value.name, decoded.name)
|
||||
XCTAssertEqual(value.iata, decoded.iata)
|
||||
XCTAssertEqual(value.icao, decoded.icao)
|
||||
XCTAssertEqual(value.coordinates[0], decoded.coordinates[0], accuracy: 0.01)
|
||||
XCTAssertEqual(value.coordinates[1], decoded.coordinates[1], accuracy: 0.01)
|
||||
XCTAssertEqual(value.runways[0].direction, decoded.runways[0].direction)
|
||||
XCTAssertEqual(value.runways[0].distance, decoded.runways[0].distance)
|
||||
XCTAssertEqual(value.runways[0].surface, decoded.runways[0].surface)
|
||||
}
|
||||
|
||||
func testRoundTripParachutePack() {
|
||||
struct Parachute: Codable, Equatable {
|
||||
enum Canopy: String, Codable, Equatable {
|
||||
case round, cruciform, rogalloWing, annular, ramAir
|
||||
}
|
||||
|
||||
let canpoy: Canopy
|
||||
let surfaceArea: Double
|
||||
}
|
||||
|
||||
struct ParachutePack: Codable, Equatable {
|
||||
let main: Parachute?
|
||||
let reserve: Parachute?
|
||||
}
|
||||
|
||||
let value = ParachutePack(main: Parachute(canpoy: .ramAir, surfaceArea: 200), reserve: nil)
|
||||
let encoded = try! encoder.encode(value)
|
||||
let decoded = try! decoder.decode(ParachutePack.self, from: encoded)
|
||||
|
||||
XCTAssertEqual(value, decoded)
|
||||
}
|
||||
|
||||
func testRoundTripArray() {
|
||||
let count: UInt8 = 100
|
||||
var bytes: [UInt8] = [0xdc, 0x00, count]
|
||||
var encoded: [Int] = []
|
||||
for n in 1...count {
|
||||
bytes.append(n)
|
||||
encoded.append(Int(n))
|
||||
}
|
||||
|
||||
let data = Data(bytes: bytes)
|
||||
let decoded = try! decoder.decode([Int].self, from: data)
|
||||
XCTAssertEqual(encoded, decoded)
|
||||
}
|
||||
|
||||
func testRoundTripDictionary() {
|
||||
let (a, z): (UInt8, UInt8) = (0x61, 0x7a)
|
||||
var bytes: [UInt8] = [0xde, 0x00, 0x1A]
|
||||
var encoded: [String: Int] = [:]
|
||||
for n in a...z {
|
||||
bytes.append(contentsOf: [0xA1, n, n])
|
||||
encoded[String(Unicode.Scalar(n))] = Int(n)
|
||||
}
|
||||
|
||||
let data = Data(bytes: bytes)
|
||||
let decoded = try! decoder.decode([String: Int].self, from: data)
|
||||
XCTAssertEqual(encoded, decoded)
|
||||
}
|
||||
|
||||
func testRoundTripDate() {
|
||||
var bytes: [UInt8] = [0xD6, 0xFF]
|
||||
|
||||
let dateComponents = DateComponents(year: 2018, month: 4, day: 20)
|
||||
let encoded = Calendar.current.date(from: dateComponents)!
|
||||
|
||||
let secondsSince1970 = UInt32(encoded.timeIntervalSince1970)
|
||||
bytes.append(contentsOf: secondsSince1970.bytes)
|
||||
|
||||
let data = Data(bytes: bytes)
|
||||
let decoded = try! decoder.decode(Date.self, from: data)
|
||||
XCTAssertEqual(encoded, decoded)
|
||||
}
|
||||
|
||||
func testRoundTripDateWithNanoseconds() {
|
||||
let encoded = Date()
|
||||
let data = try! self.encoder.encode(encoded)
|
||||
let decoded = try! self.decoder.decode(Date.self, from: data)
|
||||
XCTAssertEqual(encoded.timeIntervalSinceReferenceDate, decoded.timeIntervalSinceReferenceDate, accuracy: 0.0001)
|
||||
}
|
||||
|
||||
static var allTests = [
|
||||
("testRoundTripAirport", testRoundTripAirport),
|
||||
("testRoundTripArray", testRoundTripArray),
|
||||
("testRoundTripDictionary", testRoundTripDictionary),
|
||||
("testRoundTripDate", testRoundTripDate),
|
||||
("testRoundTripDateWithNanoseconds", testRoundTripDateWithNanoseconds)
|
||||
]
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<doc>
|
||||
<assembly>
|
||||
<name>Xamarin.AndroidX.Credentials</name>
|
||||
</assembly>
|
||||
<members>
|
||||
</members>
|
||||
</doc>
|
||||