mirror of
https://github.com/bitwarden/mobile
synced 2025-12-05 23:53:33 +00:00
Compare commits
323 Commits
community/
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b543459f8d | ||
|
|
d0609024df | ||
|
|
a798ae0761 | ||
|
|
7f8b19a0a7 | ||
|
|
e50175abe1 | ||
|
|
8f435b2f26 | ||
|
|
cdbdec8943 | ||
|
|
3d42da2a29 | ||
|
|
16303e581b | ||
|
|
bc4b03b994 | ||
|
|
484eb03eca | ||
|
|
d59f80776c | ||
|
|
44849cb0a2 | ||
|
|
65794c02b1 | ||
|
|
8bc90b9649 | ||
|
|
466bb81cb7 | ||
|
|
c957e46ff8 | ||
|
|
d2d442253b | ||
|
|
f1a2714e8a | ||
|
|
9083bb1ab8 | ||
|
|
a69420257d | ||
|
|
ef81365ae0 | ||
|
|
81ad6b3cc4 | ||
|
|
5ca0167dbb | ||
|
|
e1f3f3dfd9 | ||
|
|
53a5a9caaa | ||
|
|
94381eebf0 | ||
|
|
b43eaf4a0a | ||
|
|
b89d8214d2 | ||
|
|
902ce67867 | ||
|
|
062b15aae9 | ||
|
|
dcf441afd7 | ||
|
|
c987840936 | ||
|
|
6e93983711 | ||
|
|
70de8245fe | ||
|
|
77d9920aab | ||
|
|
b908f1c721 | ||
|
|
46a249de46 | ||
|
|
837628e9bc | ||
|
|
3262fdc9ec | ||
|
|
c763b0072b | ||
|
|
b81ce44cc7 | ||
|
|
8a43bb4655 | ||
|
|
44f8901519 | ||
|
|
463d837fa8 | ||
|
|
ad6ffad5d1 | ||
|
|
9edcc8b4f7 | ||
|
|
cf5c5aa114 | ||
|
|
36c7beb1e6 | ||
|
|
f6d798af94 | ||
|
|
08ed70d5b1 | ||
|
|
33e0187460 | ||
|
|
2b78859d06 | ||
|
|
81205154c4 | ||
|
|
14d2660b61 | ||
|
|
bad5673724 | ||
|
|
67b76a777e | ||
|
|
02f5936fce | ||
|
|
fc208b08d7 | ||
|
|
f165135147 | ||
|
|
d458f17ad6 | ||
|
|
c3bd4b84b1 | ||
|
|
0af78d0e03 | ||
|
|
e86a01a7db | ||
|
|
e16074a73e | ||
|
|
a333e72448 | ||
|
|
ffb7b3b8ac | ||
|
|
c8d0db9f31 | ||
|
|
8566f5c00a | ||
|
|
b65f18d8e2 | ||
|
|
7a3816007b | ||
|
|
477b1cca44 | ||
|
|
dee9524b2c | ||
|
|
f5572511c6 | ||
|
|
9a17da009c | ||
|
|
b5443c79d2 | ||
|
|
9dc620b492 | ||
|
|
1f966e6cbe | ||
|
|
a4fa03656e | ||
|
|
12385d9add | ||
|
|
13ca0fd4cb | ||
|
|
0b28b954fe | ||
|
|
e4841bb322 | ||
|
|
98621341a2 | ||
|
|
2023fe6644 | ||
|
|
567a23e29f | ||
|
|
8b65d99442 | ||
|
|
f80ec1b221 | ||
|
|
ba1183234b | ||
|
|
5946af9eec | ||
|
|
b091051633 | ||
|
|
06488539b0 | ||
|
|
8f77822b1b | ||
|
|
3c1105b35d | ||
|
|
c847449db8 | ||
|
|
c2771eb3c7 | ||
|
|
6e41731dcb | ||
|
|
7c90b35592 | ||
|
|
93f9dc4498 | ||
|
|
e033832261 | ||
|
|
fa5d92fbf7 | ||
|
|
e672cb132f | ||
|
|
e7a7eed7e8 | ||
|
|
43a4915323 | ||
|
|
b1ae3cc325 | ||
|
|
b9dada07ea | ||
|
|
58442389df | ||
|
|
a3378d33ae | ||
|
|
2e1982b08e | ||
|
|
e9e9b6f7bc | ||
|
|
9be8fec219 | ||
|
|
9db32ca019 | ||
|
|
f04ff7777a | ||
|
|
64775694e0 | ||
|
|
3c0007a21a | ||
|
|
35ff235010 | ||
|
|
01bd5a7b8d | ||
|
|
3fce8c76bc | ||
|
|
3b64d7b979 | ||
|
|
f343a2cdbb | ||
|
|
9a9fb85ad8 | ||
|
|
e7f9d64edb | ||
|
|
459d20c019 | ||
|
|
a8529fa4b7 | ||
|
|
d1e82c9f1d | ||
|
|
9bc2901255 | ||
|
|
e3441845cd | ||
|
|
3f463647a0 | ||
|
|
4f169a6fe3 | ||
|
|
82c2e91446 | ||
|
|
7482808857 | ||
|
|
fd233fa27f | ||
|
|
19f238d9bb | ||
|
|
6f6487ccc9 | ||
|
|
dd3dc82595 | ||
|
|
40c80f082d | ||
|
|
bca5b95446 | ||
|
|
602627b5fa | ||
|
|
6f32afb919 | ||
|
|
2ca47a4da4 | ||
|
|
4ff56ba11e | ||
|
|
22d0cc681c | ||
|
|
4e0a18cce5 | ||
|
|
c9fdfa7a15 | ||
|
|
850a7e754a | ||
|
|
67c5f79625 | ||
|
|
04e7cfe06d | ||
|
|
d6c2ebe4c2 | ||
|
|
2a28294f91 | ||
|
|
8584bbaecc | ||
|
|
2f3cded9c5 | ||
|
|
eff0ea7ce7 | ||
|
|
6c3a53dd76 | ||
|
|
cf8d801c55 | ||
|
|
eaa6844742 | ||
|
|
29e2f728e0 | ||
|
|
fe160a570f | ||
|
|
a508bea4b0 | ||
|
|
a73923c4f7 | ||
|
|
11465e8975 | ||
|
|
4c88524f0e | ||
|
|
f1c20e03bc | ||
|
|
920a2273c5 | ||
|
|
96a9978ef8 | ||
|
|
1ae388cb03 | ||
|
|
c6aaf5002f | ||
|
|
75be6504e1 | ||
|
|
cae1825e3f | ||
|
|
c23100d281 | ||
|
|
c9c0d0b4d6 | ||
|
|
fec0743e4d | ||
|
|
118dcf164c | ||
|
|
bd03b6b5aa | ||
|
|
333917c00d | ||
|
|
450101d9e4 | ||
|
|
4e50f1697d | ||
|
|
3c96ae2220 | ||
|
|
fdbd16a6fd | ||
|
|
39a34bd8c4 | ||
|
|
f30158adf5 | ||
|
|
c6a086fe62 | ||
|
|
b217451ea9 | ||
|
|
8cb7d5e1a3 | ||
|
|
82b837ef33 | ||
|
|
ebb2a288a1 | ||
|
|
de7ae27a77 | ||
|
|
d3dd2e9342 | ||
|
|
a5878d3341 | ||
|
|
1dc55f78e7 | ||
|
|
37b62b317f | ||
|
|
528e412458 | ||
|
|
0f22f2750e | ||
|
|
7bbb711175 | ||
|
|
fd80a9ce7c | ||
|
|
3f10a6be24 | ||
|
|
f3537b1a74 | ||
|
|
849a0c24b0 | ||
|
|
f6a58e469f | ||
|
|
983937c9eb | ||
|
|
b2f93d3d4b | ||
|
|
64c694e593 | ||
|
|
56b9e3f615 | ||
|
|
7558f60a44 | ||
|
|
e66ac9dd44 | ||
|
|
d6c139cb8a | ||
|
|
6b7c6eac71 | ||
|
|
9e1d6c7b03 | ||
|
|
e107b893ea | ||
|
|
5de02c863f | ||
|
|
0e95d4d4ca | ||
|
|
a42b88b666 | ||
|
|
af6866cee1 | ||
|
|
0cec49f121 | ||
|
|
d091922017 | ||
|
|
f14be2a3a2 | ||
|
|
8ee744b746 | ||
|
|
15a03ba573 | ||
|
|
82711a0235 | ||
|
|
e6635564aa | ||
|
|
6c078fe343 | ||
|
|
743e71ff92 | ||
|
|
7b579b7aa5 | ||
|
|
fe10fd7766 | ||
|
|
3c0de8aacc | ||
|
|
18d9a77f25 | ||
|
|
9eca82a62b | ||
|
|
b90e030b8f | ||
|
|
9a28419a4e | ||
|
|
f4c468e6a1 | ||
|
|
2c346eb710 | ||
|
|
9c0908f7b7 | ||
|
|
827fbbc9ce | ||
|
|
5b249bed67 | ||
|
|
afbcb212f6 | ||
|
|
a71c28536d | ||
|
|
ba5fa8a518 | ||
|
|
65ea5574de | ||
|
|
f013f69669 | ||
|
|
f98dfa6581 | ||
|
|
0723999652 | ||
|
|
96343eccf7 | ||
|
|
793c5fef6f | ||
|
|
3a13ba4efa | ||
|
|
c5288d3921 | ||
|
|
9506595fdd | ||
|
|
7a65bf7fd7 | ||
|
|
d0ce89fedb | ||
|
|
3c94ea4579 | ||
|
|
658c1eaf64 | ||
|
|
02b0265767 | ||
|
|
bd2481b3e4 | ||
|
|
12c72b2833 | ||
|
|
2e5fb414b5 | ||
|
|
4dda7a6634 | ||
|
|
a1808f64b3 | ||
|
|
142c3145f0 | ||
|
|
72de17bd1d | ||
|
|
ed3467515e | ||
|
|
21fc56457d | ||
|
|
bc2eb212a6 | ||
|
|
a1912526c2 | ||
|
|
9d0209751c | ||
|
|
f2936c95fa | ||
|
|
bb2f1f0f5f | ||
|
|
5a0c2115a1 | ||
|
|
a67f50b145 | ||
|
|
757e5ea647 | ||
|
|
b23f29511c | ||
|
|
71731bb9b7 | ||
|
|
f2be840a7d | ||
|
|
685e0f407a | ||
|
|
bbef0f8c93 | ||
|
|
3cdf5ccd3b | ||
|
|
e97a37222a | ||
|
|
218a30b510 | ||
|
|
828043ec97 | ||
|
|
b25c8b0842 | ||
|
|
a4a0d31fc6 | ||
|
|
6ef6cf5d84 | ||
|
|
597f629920 | ||
|
|
b8cef16711 | ||
|
|
c4f6ae9077 | ||
|
|
8b9658d2c5 | ||
|
|
43bf0fbdb3 | ||
|
|
11922c6f49 | ||
|
|
a6f05338c2 | ||
|
|
b932824b5a | ||
|
|
efd1671f48 | ||
|
|
3e2005e5ed | ||
|
|
382eee2ed3 | ||
|
|
b0f1dd00ee | ||
|
|
5961a001ab | ||
|
|
9026dd10e5 | ||
|
|
355261679d | ||
|
|
7f14ec9b5d | ||
|
|
0c72626916 | ||
|
|
f21fae7fea | ||
|
|
6d4792bc24 | ||
|
|
dbadf8c56f | ||
|
|
4d0f9d1c03 | ||
|
|
68759fc608 | ||
|
|
47be3d6aef | ||
|
|
7ec5c8ccfd | ||
|
|
819aabb330 | ||
|
|
9c7ff853d7 | ||
|
|
e30f9903d1 | ||
|
|
249406e3a8 | ||
|
|
8cae840c68 | ||
|
|
e274c04107 | ||
|
|
7043be67dd | ||
|
|
afb8c515d6 | ||
|
|
bfcfd367dd | ||
|
|
a23454bc53 | ||
|
|
6f7100ae4f | ||
|
|
01ac20e6e4 | ||
|
|
8474f536ff | ||
|
|
f426c0e370 | ||
|
|
420dc09fd1 | ||
|
|
6d4793d592 | ||
|
|
eea7c6b7d7 | ||
|
|
ec93a61275 | ||
|
|
c34d1da6e6 | ||
|
|
c4e64e082b |
51
.github/CODEOWNERS
vendored
Normal file
51
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# 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
18
.github/ISSUE_TEMPLATE/bug.yml
vendored
@@ -7,7 +7,19 @@ body:
|
|||||||
value: |
|
value: |
|
||||||
Thanks for taking the time to fill out this bug report!
|
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.
|
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
|
- type: textarea
|
||||||
id: reproduce
|
id: reproduce
|
||||||
attributes:
|
attributes:
|
||||||
@@ -73,9 +85,3 @@ body:
|
|||||||
description: What version of our software are you running? (go to "Settings" → "About" in the app)
|
description: What version of our software are you running? (go to "Settings" → "About" in the app)
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: checkboxes
|
|
||||||
id: beta
|
|
||||||
attributes:
|
|
||||||
label: Beta
|
|
||||||
options:
|
|
||||||
- label: Using a pre-release version of the application.
|
|
||||||
|
|||||||
6
.github/ISSUE_TEMPLATE/config.yml
vendored
6
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +1,11 @@
|
|||||||
blank_issues_enabled: false
|
blank_issues_enabled: false
|
||||||
contact_links:
|
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
|
- name: Customer Support
|
||||||
url: https://bitwarden.com/contact/
|
url: https://bitwarden.com/contact/
|
||||||
about: Please contact our customer support for account issues and general customer support.
|
about: Please contact our customer support for account issues and general customer support.
|
||||||
|
|||||||
35
.github/labeler.yml
vendored
35
.github/labeler.yml
vendored
@@ -1,19 +1,26 @@
|
|||||||
android:
|
android:
|
||||||
- src/App/*
|
- changed-files:
|
||||||
- src/Core/*
|
- any-glob-to-any-file:
|
||||||
- src/Android/*
|
- src/App/*
|
||||||
|
- src/Core/*
|
||||||
|
- src/Android/*
|
||||||
|
- 'src/Xamarin.AndroidX.Credentials/*'
|
||||||
|
|
||||||
iOS:
|
iOS:
|
||||||
- src/App/*
|
- changed-files:
|
||||||
- src/Core/*
|
- any-glob-to-any-file:
|
||||||
- lib/ios/*
|
- src/App/*
|
||||||
- src/iOS/*
|
- src/Core/*
|
||||||
- 'src/iOS.Autofill/*'
|
- lib/ios/*
|
||||||
- 'src/iOS.Core/*'
|
- src/iOS/*
|
||||||
- 'src/iOS.Extension/*'
|
- 'src/iOS.Autofill/*'
|
||||||
- 'src/iOS.ShareExtension/*'
|
- 'src/iOS.Core/*'
|
||||||
- 'src/iOS.Widget/*'
|
- 'src/iOS.Extension/*'
|
||||||
- src/watchOS/*
|
- 'src/iOS.ShareExtension/*'
|
||||||
|
- 'src/iOS.Widget/*'
|
||||||
|
- src/watchOS/*
|
||||||
|
|
||||||
watchOS:
|
watchOS:
|
||||||
- src/watchOS/*
|
- changed-files:
|
||||||
|
- any-glob-to-any-file:
|
||||||
|
- src/watchOS/*
|
||||||
18
.github/renovate.json
vendored
18
.github/renovate.json
vendored
@@ -1,23 +1,23 @@
|
|||||||
{
|
{
|
||||||
|
"enabled": false,
|
||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
"extends": [
|
"extends": [
|
||||||
"config:base",
|
"config:base",
|
||||||
|
"github>bitwarden/renovate-config:pin-actions",
|
||||||
":combinePatchMinorReleases",
|
":combinePatchMinorReleases",
|
||||||
":dependencyDashboard",
|
":dependencyDashboard",
|
||||||
":maintainLockFilesWeekly",
|
":maintainLockFilesWeekly",
|
||||||
":pinAllExceptPeerDependencies",
|
":pinAllExceptPeerDependencies",
|
||||||
":prConcurrentLimit10",
|
":prConcurrentLimit10",
|
||||||
":rebaseStalePrs",
|
":rebaseStalePrs",
|
||||||
"schedule:weekends",
|
":separateMajorReleases",
|
||||||
":separateMajorReleases"
|
"group:monorepos",
|
||||||
|
"schedule:weekends"
|
||||||
],
|
],
|
||||||
"enabledManagers": ["cargo", "github-actions", "npm", "nuget"],
|
"enabledManagers": ["github-actions", "npm", "nuget"],
|
||||||
|
"commitMessagePrefix": "[deps]:",
|
||||||
|
"commitMessageTopic": "{{depName}}",
|
||||||
"packageRules": [
|
"packageRules": [
|
||||||
{
|
|
||||||
"groupName": "cargo minor",
|
|
||||||
"matchManagers": ["cargo"],
|
|
||||||
"matchUpdateTypes": ["minor", "patch"]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"groupName": "gh minor",
|
"groupName": "gh minor",
|
||||||
"matchManagers": ["github-actions"],
|
"matchManagers": ["github-actions"],
|
||||||
@@ -32,6 +32,6 @@
|
|||||||
"groupName": "nuget minor",
|
"groupName": "nuget minor",
|
||||||
"matchManagers": ["nuget"],
|
"matchManagers": ["nuget"],
|
||||||
"matchUpdateTypes": ["minor", "patch"]
|
"matchUpdateTypes": ["minor", "patch"]
|
||||||
},
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
.github/secrets/GoogleService-Info.plist.gpg
vendored
BIN
.github/secrets/GoogleService-Info.plist.gpg
vendored
Binary file not shown.
BIN
.github/secrets/app_fdroid-keystore.jks.gpg
vendored
BIN
.github/secrets/app_fdroid-keystore.jks.gpg
vendored
Binary file not shown.
BIN
.github/secrets/app_play-keystore.jks.gpg
vendored
BIN
.github/secrets/app_play-keystore.jks.gpg
vendored
Binary file not shown.
BIN
.github/secrets/app_upload-keystore.jks.gpg
vendored
BIN
.github/secrets/app_upload-keystore.jks.gpg
vendored
Binary file not shown.
BIN
.github/secrets/bitwarden-mobile-key.p12.gpg
vendored
BIN
.github/secrets/bitwarden-mobile-key.p12.gpg
vendored
Binary file not shown.
BIN
.github/secrets/dist_autofill.mobileprovision.gpg
vendored
BIN
.github/secrets/dist_autofill.mobileprovision.gpg
vendored
Binary file not shown.
BIN
.github/secrets/dist_bitwarden.mobileprovision.gpg
vendored
BIN
.github/secrets/dist_bitwarden.mobileprovision.gpg
vendored
Binary file not shown.
BIN
.github/secrets/dist_extension.mobileprovision.gpg
vendored
BIN
.github/secrets/dist_extension.mobileprovision.gpg
vendored
Binary file not shown.
Binary file not shown.
BIN
.github/secrets/dist_watch_app.mobileprovision.gpg
vendored
BIN
.github/secrets/dist_watch_app.mobileprovision.gpg
vendored
Binary file not shown.
Binary file not shown.
3
.github/secrets/google-services.json.gpg
vendored
3
.github/secrets/google-services.json.gpg
vendored
@@ -1,3 +0,0 @@
|
|||||||
<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
BIN
.github/secrets/iphone-distribution-cert.p12.gpg
vendored
Binary file not shown.
BIN
.github/secrets/play_creds.json.gpg
vendored
BIN
.github/secrets/play_creds.json.gpg
vendored
Binary file not shown.
BIN
.github/secrets/store_fdroid-keystore.jks.gpg
vendored
BIN
.github/secrets/store_fdroid-keystore.jks.gpg
vendored
Binary file not shown.
@@ -1,4 +1,3 @@
|
|||||||
---
|
|
||||||
name: Automatic responses
|
name: Automatic responses
|
||||||
on:
|
on:
|
||||||
issues:
|
issues:
|
||||||
@@ -7,7 +6,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
close-issue:
|
close-issue:
|
||||||
name: 'Close issue with automatic response'
|
name: 'Close issue with automatic response'
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-22.04
|
||||||
permissions:
|
permissions:
|
||||||
issues: write
|
issues: write
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
349
.github/workflows/build-beta.yml
vendored
Normal file
349
.github/workflows/build-beta.yml
vendored
Normal file
@@ -0,0 +1,349 @@
|
|||||||
|
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 }}
|
||||||
737
.github/workflows/build.yml
vendored
737
.github/workflows/build.yml
vendored
File diff suppressed because it is too large
Load Diff
52
.github/workflows/cleanup-rc-branch.yml
vendored
Normal file
52
.github/workflows/cleanup-rc-branch.yml
vendored
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
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
|
||||||
26
.github/workflows/crowdin-pull.yml
vendored
26
.github/workflows/crowdin-pull.yml
vendored
@@ -1,42 +1,48 @@
|
|||||||
---
|
|
||||||
name: Crowdin Sync
|
name: Crowdin Sync
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs: {}
|
inputs: {}
|
||||||
schedule:
|
|
||||||
- cron: '0 0 * * 5'
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
crowdin-sync:
|
crowdin-sync:
|
||||||
name: Autosync
|
name: Autosync
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-24.04
|
||||||
env:
|
env:
|
||||||
_CROWDIN_PROJECT_ID: "269690"
|
_CROWDIN_PROJECT_ID: "269690"
|
||||||
steps:
|
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
|
- name: Checkout repo
|
||||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
token: ${{ steps.app-token.outputs.token }}
|
||||||
|
|
||||||
- name: Login to Azure - CI Subscription
|
- name: Login to Azure - CI Subscription
|
||||||
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf # v1.4.3
|
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
||||||
with:
|
with:
|
||||||
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
||||||
|
|
||||||
- name: Retrieve secrets
|
- name: Retrieve secrets
|
||||||
id: retrieve-secrets
|
id: retrieve-secrets
|
||||||
uses: bitwarden/gh-actions/get-keyvault-secrets@34ecb67b2a357795dc893549df0795e7383ff50f
|
uses: bitwarden/gh-actions/get-keyvault-secrets@main
|
||||||
with:
|
with:
|
||||||
keyvault: "bitwarden-ci"
|
keyvault: "bitwarden-ci"
|
||||||
secrets: "crowdin-api-token, github-gpg-private-key, github-gpg-private-key-passphrase"
|
secrets: "crowdin-api-token, github-gpg-private-key, github-gpg-private-key-passphrase"
|
||||||
|
|
||||||
- name: Download translations
|
- name: Download translations
|
||||||
uses: crowdin/github-action@965d501f160af7b1f88aed4c29154b0caf1e94b9 # v1.9.0
|
uses: crowdin/github-action@61ac8b980551f674046220c3e104bddae2916ac5 # v2.0.0
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||||
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
|
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
|
||||||
with:
|
with:
|
||||||
config: crowdin.yml
|
config: crowdin.yml
|
||||||
crowdin_branch_name: master
|
crowdin_branch_name: main
|
||||||
upload_sources: false
|
upload_sources: false
|
||||||
upload_translations: false
|
upload_translations: false
|
||||||
download_translations: true
|
download_translations: true
|
||||||
|
|||||||
3
.github/workflows/enforce-labels.yml
vendored
3
.github/workflows/enforce-labels.yml
vendored
@@ -1,4 +1,3 @@
|
|||||||
---
|
|
||||||
name: Enforce PR labels
|
name: Enforce PR labels
|
||||||
|
|
||||||
on:
|
on:
|
||||||
@@ -7,7 +6,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
enforce-label:
|
enforce-label:
|
||||||
name: EnforceLabel
|
name: EnforceLabel
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Enforce Label
|
- name: Enforce Label
|
||||||
uses: yogevbd/enforce-label-action@a3c219da6b8fa73f6ba62b68ff09c469b3a1c024 # 2.2.2
|
uses: yogevbd/enforce-label-action@a3c219da6b8fa73f6ba62b68ff09c469b3a1c024 # 2.2.2
|
||||||
|
|||||||
6
.github/workflows/pr-labeler.yml
vendored
6
.github/workflows/pr-labeler.yml
vendored
@@ -1,4 +1,3 @@
|
|||||||
---
|
|
||||||
name: "Pull Request Labeler"
|
name: "Pull Request Labeler"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
@@ -10,8 +9,9 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/labeler@ba790c862c380240c6d5e7427be5ace9a05c754b # v4.0.3
|
- name: Label PR
|
||||||
|
uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5.0.0
|
||||||
with:
|
with:
|
||||||
sync-labels: true
|
sync-labels: true
|
||||||
|
|||||||
169
.github/workflows/release.yml
vendored
169
.github/workflows/release.yml
vendored
@@ -1,4 +1,3 @@
|
|||||||
---
|
|
||||||
name: Release
|
name: Release
|
||||||
run-name: Release ${{ inputs.release_type }}
|
run-name: Release ${{ inputs.release_type }}
|
||||||
|
|
||||||
@@ -23,12 +22,12 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
name: Create Release
|
name: Create Release
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-22.04
|
||||||
outputs:
|
outputs:
|
||||||
branch-name: ${{ steps.branch.outputs.branch-name }}
|
branch-name: ${{ steps.branch.outputs.branch-name }}
|
||||||
steps:
|
steps:
|
||||||
- name: Branch check
|
- name: Branch check
|
||||||
if: github.event.inputs.release_type != 'Dry Run'
|
if: inputs.release_type != 'Dry Run'
|
||||||
run: |
|
run: |
|
||||||
if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix-rc" ]]; then
|
if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix-rc" ]]; then
|
||||||
echo "==================================="
|
echo "==================================="
|
||||||
@@ -38,15 +37,15 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||||
|
|
||||||
- name: Check Release Version
|
- name: Check Release Version
|
||||||
id: version
|
id: version
|
||||||
uses: bitwarden/gh-actions/release-version-check@34ecb67b2a357795dc893549df0795e7383ff50f
|
uses: bitwarden/gh-actions/release-version-check@main
|
||||||
with:
|
with:
|
||||||
release-type: ${{ github.event.inputs.release_type }}
|
release-type: ${{ inputs.release_type }}
|
||||||
project-type: xamarin
|
project-type: xamarin
|
||||||
file: src/Android/Properties/AndroidManifest.xml
|
file: src/App/Platforms/Android/AndroidManifest.xml
|
||||||
|
|
||||||
- name: Get branch name
|
- name: Get branch name
|
||||||
id: branch
|
id: branch
|
||||||
@@ -55,8 +54,8 @@ jobs:
|
|||||||
echo "branch-name=$BRANCH_NAME" >> $GITHUB_OUTPUT
|
echo "branch-name=$BRANCH_NAME" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Create GitHub deployment
|
- name: Create GitHub deployment
|
||||||
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
if: ${{ inputs.release_type != 'Dry Run' }}
|
||||||
uses: chrnorm/deployment-action@d42cde7132fcec920de534fffc3be83794335c00 # v2.0.5
|
uses: chrnorm/deployment-action@55729fcebec3d284f60f5bcabbd8376437d696b1 # v2.0.7
|
||||||
id: deployment
|
id: deployment
|
||||||
with:
|
with:
|
||||||
token: '${{ secrets.GITHUB_TOKEN }}'
|
token: '${{ secrets.GITHUB_TOKEN }}'
|
||||||
@@ -65,29 +64,35 @@ jobs:
|
|||||||
description: 'Deployment ${{ steps.version.outputs.version }} from branch ${{ steps.branch.outputs.branch-name }}'
|
description: 'Deployment ${{ steps.version.outputs.version }} from branch ${{ steps.branch.outputs.branch-name }}'
|
||||||
task: release
|
task: release
|
||||||
|
|
||||||
|
|
||||||
- name: Download all artifacts
|
- name: Download all artifacts
|
||||||
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
if: ${{ inputs.release_type != 'Dry Run' }}
|
||||||
uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # v2.27.0
|
uses: bitwarden/gh-actions/download-artifacts@main
|
||||||
with:
|
with:
|
||||||
workflow: build.yml
|
workflow: build.yml
|
||||||
workflow_conclusion: success
|
workflow_conclusion: success
|
||||||
branch: ${{ steps.branch.outputs.branch-name }}
|
branch: ${{ steps.branch.outputs.branch-name }}
|
||||||
|
skip_unpack: true
|
||||||
|
|
||||||
- name: Dry Run - Download all artifacts
|
- name: Dry Run - Download all artifacts
|
||||||
if: ${{ github.event.inputs.release_type == 'Dry Run' }}
|
if: ${{ inputs.release_type == 'Dry Run' }}
|
||||||
uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # v2.27.0
|
uses: bitwarden/gh-actions/download-artifacts@main
|
||||||
with:
|
with:
|
||||||
workflow: build.yml
|
workflow: build.yml
|
||||||
workflow_conclusion: success
|
workflow_conclusion: success
|
||||||
branch: master
|
branch: main
|
||||||
|
skip_unpack: true
|
||||||
|
|
||||||
- name: Prep Bitwarden iOS release asset
|
- name: Unzip release assets
|
||||||
run: zip -r Bitwarden\ iOS.zip Bitwarden\ iOS
|
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: Create release
|
- name: Create release
|
||||||
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
if: ${{ inputs.release_type != 'Dry Run' }}
|
||||||
uses: ncipollo/release-action@40bb172bd05f266cf9ba4ff965cb61e9ee5f6d01 # v1.9.0
|
uses: ncipollo/release-action@2c591bcc8ecdcd2db72b97d6147f871fcd833ba5 # v1.14.0
|
||||||
with:
|
with:
|
||||||
artifacts: "./com.x8bit.bitwarden.aab/com.x8bit.bitwarden.aab,
|
artifacts: "./com.x8bit.bitwarden.aab/com.x8bit.bitwarden.aab,
|
||||||
./com.x8bit.bitwarden.apk/com.x8bit.bitwarden.apk,
|
./com.x8bit.bitwarden.apk/com.x8bit.bitwarden.apk,
|
||||||
@@ -103,16 +108,16 @@ jobs:
|
|||||||
draft: true
|
draft: true
|
||||||
|
|
||||||
- name: Update deployment status to Success
|
- name: Update deployment status to Success
|
||||||
if: ${{ github.event.inputs.release_type != 'Dry Run' && success() }}
|
if: ${{ inputs.release_type != 'Dry Run' && success() }}
|
||||||
uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1
|
uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
|
||||||
with:
|
with:
|
||||||
token: '${{ secrets.GITHUB_TOKEN }}'
|
token: '${{ secrets.GITHUB_TOKEN }}'
|
||||||
state: 'success'
|
state: 'success'
|
||||||
deployment-id: ${{ steps.deployment.outputs.deployment_id }}
|
deployment-id: ${{ steps.deployment.outputs.deployment_id }}
|
||||||
|
|
||||||
- name: Update deployment status to Failure
|
- name: Update deployment status to Failure
|
||||||
if: ${{ github.event.inputs.release_type != 'Dry Run' && failure() }}
|
if: ${{ inputs.release_type != 'Dry Run' && failure() }}
|
||||||
uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1
|
uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
|
||||||
with:
|
with:
|
||||||
token: '${{ secrets.GITHUB_TOKEN }}'
|
token: '${{ secrets.GITHUB_TOKEN }}'
|
||||||
state: 'failure'
|
state: 'failure'
|
||||||
@@ -121,40 +126,36 @@ jobs:
|
|||||||
|
|
||||||
f-droid:
|
f-droid:
|
||||||
name: F-Droid Release
|
name: F-Droid Release
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-22.04
|
||||||
needs: release
|
needs: release
|
||||||
if: inputs.fdroid_publish
|
if: inputs.fdroid_publish
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||||
|
|
||||||
- name: Download F-Droid .apk artifact
|
- name: Download F-Droid .apk artifact
|
||||||
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
if: ${{ inputs.release_type != 'Dry Run' }}
|
||||||
uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # v2.27.0
|
uses: bitwarden/gh-actions/download-artifacts@main
|
||||||
with:
|
with:
|
||||||
workflow: build.yml
|
workflow: build.yml
|
||||||
workflow_conclusion: success
|
workflow_conclusion: success
|
||||||
branch: ${{ needs.release.outputs.branch-name }}
|
branch: ${{ needs.release.outputs.branch-name }}
|
||||||
name: com.x8bit.bitwarden-fdroid.apk
|
|
||||||
|
|
||||||
- name: Dry Run - Download F-Droid .apk artifact
|
- name: Dry Run - Download F-Droid .apk artifact
|
||||||
if: ${{ github.event.inputs.release_type == 'Dry Run' }}
|
if: ${{ inputs.release_type == 'Dry Run' }}
|
||||||
uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # v2.27.0
|
uses: bitwarden/gh-actions/download-artifacts@main
|
||||||
with:
|
with:
|
||||||
workflow: build.yml
|
workflow: build.yml
|
||||||
workflow_conclusion: success
|
workflow_conclusion: success
|
||||||
branch: master
|
branch: main
|
||||||
name: com.x8bit.bitwarden-fdroid.apk
|
|
||||||
|
|
||||||
- name: Set up Node
|
- name: Set up Node
|
||||||
uses: actions/setup-node@1f8c6b94b26d0feae1e387ca63ccbdc44d27b561 # v2.5.1
|
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
|
||||||
with:
|
with:
|
||||||
node-version: '10.x'
|
node-version: '16.x'
|
||||||
|
|
||||||
- name: Set up F-Droid server
|
- name: Set up F-Droid server
|
||||||
run: |
|
run: pip install git+https://gitlab.com/fdroid/fdroidserver.git
|
||||||
sudo apt-get -qq update
|
|
||||||
sudo apt-get -qqy install --no-install-recommends fdroidserver wget
|
|
||||||
|
|
||||||
- name: Set up Git credentials
|
- name: Set up Git credentials
|
||||||
env:
|
env:
|
||||||
@@ -167,49 +168,85 @@ jobs:
|
|||||||
|
|
||||||
- name: Print environment
|
- name: Print environment
|
||||||
run: |
|
run: |
|
||||||
node --version
|
echo "Node Version: $(node --version)"
|
||||||
npm --version
|
echo "NPM Version: $(npm --version)"
|
||||||
git --version
|
echo "Git Version: $(git --version)"
|
||||||
|
echo "F-Droid Server Version: $(fdroid --version)"
|
||||||
echo "GitHub ref: $GITHUB_REF"
|
echo "GitHub ref: $GITHUB_REF"
|
||||||
echo "GitHub event: $GITHUB_EVENT"
|
echo "GitHub event: $GITHUB_EVENT"
|
||||||
|
|
||||||
- name: Install Node dependencies
|
- name: Install Node dependencies
|
||||||
run: npm install
|
run: npm install
|
||||||
|
|
||||||
- name: Decrypt secrets
|
- name: Login to Azure - CI Subscription
|
||||||
env:
|
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
||||||
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
|
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: |
|
run: |
|
||||||
mkdir -p ~/secrets
|
git config --local user.email "106330231+bitwarden-devops-bot@users.noreply.github.com"
|
||||||
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
git config --local user.name "bitwarden-devops-bot"
|
||||||
--output ./store/fdroid/keystore.jks ./.github/secrets/store_fdroid-keystore.jks.gpg
|
|
||||||
|
- name: Download secrets
|
||||||
|
env:
|
||||||
|
ACCOUNT_NAME: bitwardenci
|
||||||
|
CONTAINER_NAME: mobile
|
||||||
|
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
|
||||||
|
|
||||||
- name: Compile for F-Droid Store
|
- name: Compile for F-Droid Store
|
||||||
env:
|
env:
|
||||||
FDROID_STORE_KEYSTORE_PASSWORD: ${{ secrets.FDROID_STORE_KEYSTORE_PASSWORD }}
|
FDROID_STORE_KEYSTORE_PASSWORD: ${{ secrets.FDROID_STORE_KEYSTORE_PASSWORD }}
|
||||||
run: |
|
run: |
|
||||||
cd $GITHUB_WORKSPACE
|
# Create required directories.
|
||||||
mkdir dist
|
mkdir dist
|
||||||
cp CNAME ./dist
|
mkdir -p store/temp/fdroid
|
||||||
cd store
|
mkdir -p store/fdroid/repo
|
||||||
chmod 600 fdroid/config.py fdroid/keystore.jks
|
|
||||||
mkdir -p temp/fdroid
|
# Configure F-Droid server.
|
||||||
|
cp CNAME dist/
|
||||||
|
chmod 600 store/fdroid/config.yml store/fdroid/keystore.jks
|
||||||
TEMP_DIR="$GITHUB_WORKSPACE/store/temp/fdroid"
|
TEMP_DIR="$GITHUB_WORKSPACE/store/temp/fdroid"
|
||||||
cd fdroid
|
echo "keypass: $FDROID_STORE_KEYSTORE_PASSWORD" >> store/fdroid/config.yml
|
||||||
echo "keypass=\"$FDROID_STORE_KEYSTORE_PASSWORD\"" >>config.py
|
echo "keystorepass: $FDROID_STORE_KEYSTORE_PASSWORD" >> store/fdroid/config.yml
|
||||||
echo "keystorepass=\"$FDROID_STORE_KEYSTORE_PASSWORD\"" >>config.py
|
echo "local_copy_dir: $TEMP_DIR" >> store/fdroid/config.yml
|
||||||
echo "local_copy_dir=\"$TEMP_DIR\"" >>config.py
|
mv $GITHUB_WORKSPACE/com.x8bit.bitwarden-fdroid.apk store/fdroid/repo/
|
||||||
mkdir -p repo
|
|
||||||
mv $GITHUB_WORKSPACE/com.x8bit.bitwarden-fdroid.apk ./repo/
|
# Run update and deploy.
|
||||||
|
cd store/fdroid
|
||||||
fdroid update
|
fdroid update
|
||||||
fdroid server update
|
fdroid deploy
|
||||||
cd ..
|
cd ../..
|
||||||
rm -rf temp/fdroid/archive
|
|
||||||
mv -v temp/fdroid ../dist
|
# Move files for distribution.
|
||||||
cd fdroid
|
rm -rf store/temp/fdroid/archive
|
||||||
cp index.html btn.png qr.png ../../dist/fdroid
|
mv -v store/temp/fdroid dist
|
||||||
cd $GITHUB_WORKSPACE
|
cp store/fdroid/index.html store/fdroid/btn.png store/fdroid/qr.png dist/fdroid
|
||||||
|
|
||||||
- name: Deploy to gh-pages
|
- name: Deploy to gh-pages
|
||||||
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
if: ${{ inputs.release_type != 'Dry Run' }}
|
||||||
run: npm run deploy
|
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>"
|
||||||
|
|||||||
7
.github/workflows/stale-bot.yml
vendored
7
.github/workflows/stale-bot.yml
vendored
@@ -1,4 +1,3 @@
|
|||||||
---
|
|
||||||
name: 'Close stale issues and PRs'
|
name: 'Close stale issues and PRs'
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
@@ -8,10 +7,10 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
stale:
|
stale:
|
||||||
name: 'Check for stale issues and PRs'
|
name: 'Check for stale issues and PRs'
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: 'Run stale action'
|
- name: 'Run stale action'
|
||||||
uses: actions/stale@3cc123766321e9f15a6676375c154ccffb12a358 # v5.0.0
|
uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0
|
||||||
with:
|
with:
|
||||||
stale-issue-label: 'needs-reply'
|
stale-issue-label: 'needs-reply'
|
||||||
stale-pr-label: 'needs-changes'
|
stale-pr-label: 'needs-changes'
|
||||||
@@ -27,4 +26,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.
|
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 master branch before requesting another review.
|
Please make sure to resolve any conflicts with the main branch before requesting another review.
|
||||||
|
|||||||
51
.github/workflows/version-auto-bump.yml
vendored
51
.github/workflows/version-auto-bump.yml
vendored
@@ -1,5 +1,4 @@
|
|||||||
---
|
name: Auto Bump Mobile Version
|
||||||
name: Version Auto Bump
|
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -7,35 +6,25 @@ on:
|
|||||||
- v**
|
- v**
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
setup:
|
bump-version:
|
||||||
name: "Setup"
|
name: Bump Mobile Version
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
outputs:
|
|
||||||
version_number: ${{ steps.version.outputs.new-version }}
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Branch
|
- name: Login to Azure - CI Subscription
|
||||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
||||||
|
|
||||||
- name: Calculate bumped version
|
|
||||||
id: version
|
|
||||||
env:
|
|
||||||
RELEASE_TAG: ${{ github.ref }}
|
|
||||||
run: |
|
|
||||||
CURR_MAJOR=$(echo $RELEASE_TAG | sed -r 's/refs\/tags\/v([0-9]{4}\.[0-9]{1,2})\.([0-9]{1,2})/\1/')
|
|
||||||
CURR_PATCH=$(echo $RELEASE_TAG | sed -r 's/refs\/tags\/v([0-9]{4}\.[0-9]{1,2})\.([0-9]{1,2})/\2/')
|
|
||||||
echo "Current Major: $CURR_MAJOR"
|
|
||||||
echo "Current Patch: $CURR_PATCH"
|
|
||||||
|
|
||||||
NEW_PATCH=$((CURR_PATCH+1))
|
|
||||||
NEW_VER=$CURR_MAJOR.$NEW_PATCH
|
|
||||||
echo "New Version: $NEW_VER"
|
|
||||||
echo "new-version=$NEW_VER" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
trigger_version_bump:
|
|
||||||
name: Bump version to ${{ needs.setup.outputs.version_number }}
|
|
||||||
needs: setup
|
|
||||||
uses: ./.github/workflows/version-bump.yml
|
|
||||||
secrets:
|
|
||||||
AZURE_PROD_KV_CREDENTIALS: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
|
||||||
with:
|
with:
|
||||||
version_number: ${{ needs.setup.outputs.version_number }}
|
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
|
||||||
|
|||||||
311
.github/workflows/version-bump.yml
vendored
311
.github/workflows/version-bump.yml
vendored
@@ -1,87 +1,214 @@
|
|||||||
---
|
|
||||||
name: Version Bump
|
name: Version Bump
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
version_number:
|
version_number_override:
|
||||||
description: "New Version"
|
description: "New version override (leave blank for automatic calculation, example: '2024.1.0')"
|
||||||
required: true
|
required: false
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
version_number:
|
|
||||||
required: true
|
|
||||||
type: string
|
type: string
|
||||||
secrets:
|
cut_rc_branch:
|
||||||
AZURE_PROD_KV_CREDENTIALS:
|
description: "Cut RC branch?"
|
||||||
required: true
|
default: true
|
||||||
|
type: boolean
|
||||||
|
enable_slack_notification:
|
||||||
|
description: "Enable Slack notifications for upcoming release?"
|
||||||
|
default: false
|
||||||
|
type: boolean
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
bump_version:
|
bump_version:
|
||||||
name: "Create version_bump_${{ github.event.inputs.version_number }} branch"
|
name: Bump Version
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-22.04
|
||||||
|
outputs:
|
||||||
|
version: ${{ steps.set-final-version-output.outputs.version }}
|
||||||
steps:
|
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
|
- name: Checkout Branch
|
||||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
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
|
- name: Login to Azure - CI Subscription
|
||||||
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf # v1.4.3
|
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
||||||
with:
|
with:
|
||||||
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
||||||
|
|
||||||
- name: Retrieve secrets
|
- name: Retrieve secrets
|
||||||
id: retrieve-secrets
|
id: retrieve-secrets
|
||||||
uses: bitwarden/gh-actions/get-keyvault-secrets@34ecb67b2a357795dc893549df0795e7383ff50f
|
uses: bitwarden/gh-actions/get-keyvault-secrets@main
|
||||||
with:
|
with:
|
||||||
keyvault: "bitwarden-ci"
|
keyvault: "bitwarden-ci"
|
||||||
secrets: "github-gpg-private-key, github-gpg-private-key-passphrase"
|
secrets: "github-gpg-private-key,
|
||||||
|
github-gpg-private-key-passphrase,
|
||||||
|
github-pat-bitwarden-devops-bot-repo-scope"
|
||||||
|
|
||||||
- name: Import GPG key
|
- name: Import GPG key
|
||||||
uses: crazy-max/ghaction-import-gpg@111c56156bcc6918c056dbef52164cfa583dc549 # v5.2.0
|
uses: crazy-max/ghaction-import-gpg@01dd5d3ca463c7f10f7f4f7b4f177225ac661ee4 # v6.1.0
|
||||||
with:
|
with:
|
||||||
gpg_private_key: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key }}
|
gpg_private_key: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key }}
|
||||||
passphrase: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key-passphrase }}
|
passphrase: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key-passphrase }}
|
||||||
git_user_signingkey: true
|
git_user_signingkey: true
|
||||||
git_commit_gpgsign: true
|
git_commit_gpgsign: true
|
||||||
|
|
||||||
- name: Create Version Branch
|
|
||||||
run: git switch -c version_bump_${{ github.event.inputs.version_number }}
|
|
||||||
|
|
||||||
- name: Bump Version - Android XML
|
|
||||||
uses: bitwarden/gh-actions/version-bump@34ecb67b2a357795dc893549df0795e7383ff50f
|
|
||||||
with:
|
|
||||||
version: ${{ github.event.inputs.version_number }}
|
|
||||||
file_path: "./src/Android/Properties/AndroidManifest.xml"
|
|
||||||
|
|
||||||
- name: Bump Version - iOS.Autofill
|
|
||||||
uses: bitwarden/gh-actions/version-bump@34ecb67b2a357795dc893549df0795e7383ff50f
|
|
||||||
with:
|
|
||||||
version: ${{ github.event.inputs.version_number }}
|
|
||||||
file_path: "./src/iOS.Autofill/Info.plist"
|
|
||||||
|
|
||||||
- name: Bump Version - iOS.Extension
|
|
||||||
uses: bitwarden/gh-actions/version-bump@34ecb67b2a357795dc893549df0795e7383ff50f
|
|
||||||
with:
|
|
||||||
version: ${{ github.event.inputs.version_number }}
|
|
||||||
file_path: "./src/iOS.Extension/Info.plist"
|
|
||||||
|
|
||||||
- name: Bump Version - iOS.ShareExtension
|
|
||||||
uses: bitwarden/gh-actions/version-bump@34ecb67b2a357795dc893549df0795e7383ff50f
|
|
||||||
with:
|
|
||||||
version: ${{ github.event.inputs.version_number }}
|
|
||||||
file_path: "./src/iOS.ShareExtension/Info.plist"
|
|
||||||
|
|
||||||
- name: Bump Version - iOS
|
|
||||||
uses: bitwarden/gh-actions/version-bump@34ecb67b2a357795dc893549df0795e7383ff50f
|
|
||||||
with:
|
|
||||||
version: ${{ github.event.inputs.version_number }}
|
|
||||||
file_path: "./src/iOS/Info.plist"
|
|
||||||
|
|
||||||
- name: Setup git
|
- name: Setup git
|
||||||
run: |
|
run: |
|
||||||
git config --local user.email "106330231+bitwarden-devops-bot@users.noreply.github.com"
|
git config --local user.email "106330231+bitwarden-devops-bot@users.noreply.github.com"
|
||||||
git config --local user.name "bitwarden-devops-bot"
|
git config --local user.name "bitwarden-devops-bot"
|
||||||
|
|
||||||
|
- 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
|
||||||
|
|
||||||
|
- 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
|
||||||
|
with:
|
||||||
|
version: ${{ steps.current-version.outputs.version }}
|
||||||
|
|
||||||
|
- name: Bump Version - Android XML - Version Override
|
||||||
|
if: ${{ inputs.version_number_override != '' }}
|
||||||
|
id: bump-version-override
|
||||||
|
uses: bitwarden/gh-actions/version-bump@main
|
||||||
|
with:
|
||||||
|
file_path: "src/App/Platforms/Android/AndroidManifest.xml"
|
||||||
|
version: ${{ inputs.version_number_override }}
|
||||||
|
|
||||||
|
- name: Bump Version - Android XML - Automatic Calculation
|
||||||
|
if: ${{ inputs.version_number_override == '' }}
|
||||||
|
id: bump-version-automatic
|
||||||
|
uses: bitwarden/gh-actions/version-bump@main
|
||||||
|
with:
|
||||||
|
file_path: "src/App/Platforms/Android/AndroidManifest.xml"
|
||||||
|
version: ${{ steps.calculate-next-version.outputs.version }}
|
||||||
|
|
||||||
|
- name: Bump Version - iOS.Autofill - Version Override
|
||||||
|
if: ${{ inputs.version_number_override != '' }}
|
||||||
|
uses: bitwarden/gh-actions/version-bump@main
|
||||||
|
with:
|
||||||
|
file_path: "src/iOS.Autofill/Info.plist"
|
||||||
|
version: ${{ inputs.version_number_override }}
|
||||||
|
|
||||||
|
- name: Bump Version - iOS.Autofill - Automatic Calculation
|
||||||
|
if: ${{ inputs.version_number_override == '' }}
|
||||||
|
uses: bitwarden/gh-actions/version-bump@main
|
||||||
|
with:
|
||||||
|
file_path: "src/iOS.Autofill/Info.plist"
|
||||||
|
version: ${{ steps.calculate-next-version.outputs.version }}
|
||||||
|
|
||||||
|
- name: Bump Version - iOS.Extension - Version Override
|
||||||
|
if: ${{ inputs.version_number_override != '' }}
|
||||||
|
uses: bitwarden/gh-actions/version-bump@main
|
||||||
|
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
|
- name: Check if version changed
|
||||||
id: version-changed
|
id: version-changed
|
||||||
run: |
|
run: |
|
||||||
@@ -94,22 +221,24 @@ jobs:
|
|||||||
|
|
||||||
- name: Commit files
|
- name: Commit files
|
||||||
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
|
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
|
||||||
run: git commit -m "Bumped version to ${{ github.event.inputs.version_number }}" -a
|
run: git commit -m "Bumped version to ${{ steps.set-final-version-output.outputs.version }}" -a
|
||||||
|
|
||||||
- name: Push changes
|
- name: Push changes
|
||||||
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
|
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
|
||||||
run: git push -u origin version_bump_${{ github.event.inputs.version_number }}
|
env:
|
||||||
|
PR_BRANCH: ${{ steps.create-branch.outputs.name }}
|
||||||
|
run: git push -u origin $PR_BRANCH
|
||||||
|
|
||||||
- name: Create Version PR
|
- name: Create Version PR
|
||||||
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
|
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
|
||||||
|
id: create-pr
|
||||||
env:
|
env:
|
||||||
PR_BRANCH: "version_bump_${{ github.event.inputs.version_number }}"
|
GH_TOKEN: ${{ steps.retrieve-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }}
|
||||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
PR_BRANCH: ${{ steps.create-branch.outputs.name }}
|
||||||
BASE_BRANCH: master
|
TITLE: "Bump version to ${{ steps.set-final-version-output.outputs.version }}"
|
||||||
TITLE: "Bump version to ${{ github.event.inputs.version_number }}"
|
|
||||||
run: |
|
run: |
|
||||||
gh pr create --title "$TITLE" \
|
PR_URL=$(gh pr create --title "$TITLE" \
|
||||||
--base "$BASE" \
|
--base "main" \
|
||||||
--head "$PR_BRANCH" \
|
--head "$PR_BRANCH" \
|
||||||
--label "version update" \
|
--label "version update" \
|
||||||
--label "automated pr" \
|
--label "automated pr" \
|
||||||
@@ -122,4 +251,66 @@ jobs:
|
|||||||
- [X] Other
|
- [X] Other
|
||||||
|
|
||||||
## Objective
|
## Objective
|
||||||
Automated version bump to ${{ github.event.inputs.version_number }}"
|
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
|
||||||
|
|||||||
11
.github/workflows/workflow-linter.yml
vendored
11
.github/workflows/workflow-linter.yml
vendored
@@ -1,11 +0,0 @@
|
|||||||
---
|
|
||||||
name: Workflow Linter
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- .github/workflows/**
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
call-workflow:
|
|
||||||
uses: bitwarden/gh-actions/.github/workflows/workflow-linter.yml@34ecb67b2a357795dc893549df0795e7383ff50f
|
|
||||||
18
.gitignore
vendored
18
.gitignore
vendored
@@ -31,6 +31,7 @@ Components/
|
|||||||
x64/
|
x64/
|
||||||
x86/
|
x86/
|
||||||
!src/lib/x86/
|
!src/lib/x86/
|
||||||
|
!src/App/Platforms/Android/lib/x86/
|
||||||
build/
|
build/
|
||||||
bld/
|
bld/
|
||||||
[Bb]in/
|
[Bb]in/
|
||||||
@@ -147,6 +148,7 @@ publish/
|
|||||||
|
|
||||||
# NuGet Packages
|
# NuGet Packages
|
||||||
*.nupkg
|
*.nupkg
|
||||||
|
!**/Xamarin.AndroidX.Credentials.1.0.0.nupkg
|
||||||
# The packages folder can be ignored because of Package Restore
|
# The packages folder can be ignored because of Package Restore
|
||||||
**/packages/*
|
**/packages/*
|
||||||
# except build/, which is used as an MSBuild target.
|
# except build/, which is used as an MSBuild target.
|
||||||
@@ -294,17 +296,11 @@ iOSInjectionProject/
|
|||||||
timeline.xctimeline
|
timeline.xctimeline
|
||||||
playground.xcworkspace
|
playground.xcworkspace
|
||||||
|
|
||||||
# Swift Package Manager
|
# xcode / swift package manager - used by the MessagePack lib
|
||||||
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
|
/.build
|
||||||
# Packages/
|
/Packages
|
||||||
# Package.pins
|
/*.xcodeproj
|
||||||
# Package.resolved
|
.swiftpm
|
||||||
# *.xcodeproj
|
|
||||||
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
|
|
||||||
# hence it is not needed unless you have added a package configuration file to your project
|
|
||||||
# .swiftpm
|
|
||||||
|
|
||||||
.build/
|
|
||||||
|
|
||||||
# CocoaPods
|
# CocoaPods
|
||||||
# We recommend against adding the Pods directory to your .gitignore. However
|
# We recommend against adding the Pods directory to your .gitignore. However
|
||||||
|
|||||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +0,0 @@
|
|||||||
[submodule "lib/MessagePack"]
|
|
||||||
path = lib/MessagePack
|
|
||||||
url = https://github.com/bitwarden/MessagePack.git
|
|
||||||
15
Directory.Build.props
Normal file
15
Directory.Build.props
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<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>
|
||||||
12
README.md
12
README.md
@@ -1,18 +1,22 @@
|
|||||||
[](https://github.com/bitwarden/mobile/actions/workflows/build.yml?query=branch:master)
|
[](https://github.com/bitwarden/mobile/actions/workflows/build.yml?query=branch:main)
|
||||||
[](https://crowdin.com/project/bitwarden-mobile)
|
[](https://crowdin.com/project/bitwarden-mobile)
|
||||||
[](https://gitter.im/bitwarden/Lobby)
|
[](https://gitter.im/bitwarden/Lobby)
|
||||||
|
|
||||||
# Bitwarden Mobile Application
|
# 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 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>
|
<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# with Xamarin Android, Xamarin iOS, and Xamarin Forms.
|
The Bitwarden mobile application is written in C# using .NET MAUI.
|
||||||
|
|
||||||
<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" />
|
<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
|
# Build/Run
|
||||||
|
|
||||||
Please refer to the [Mobile section](https://contributing.bitwarden.com/getting-started/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.
|
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.
|
||||||
|
|
||||||
# We're Hiring!
|
# We're Hiring!
|
||||||
|
|
||||||
@@ -20,6 +24,6 @@ Interested in contributing in a big way? Consider joining our team! We're hiring
|
|||||||
|
|
||||||
# Contribute
|
# Contribute
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|||||||
@@ -1,471 +1,233 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio Version 16
|
# Visual Studio Version 17
|
||||||
VisualStudioVersion = 16.0.29009.5
|
VisualStudioVersion = 17.8.34112.27
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Android", "src\Android\Android.csproj", "{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "App", "src\App\App.csproj", "{971FDF07-E288-4239-B47A-E9E7E912193B}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "App", "src\App\App.csproj", "{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "src\Core\Core.csproj", "{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core", "src\Core\Core.csproj", "{4B8A8C41-9820-4341-974C-41E65B7F4366}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "iOS.Core", "src\iOS.Core\iOS.Core.csproj", "{E71F3053-056C-4381-9638-048ED73BDFF6}"
|
||||||
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
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Extension", "src\iOS.Extension\iOS.Extension.csproj", "{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Extension", "src\iOS.Extension\iOS.Extension.csproj", "{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}"
|
||||||
EndProject
|
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}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.ShareExtension", "src\iOS.ShareExtension\iOS.ShareExtension.csproj", "{F8C3F648-EA5A-4719-8005-85D1690B1655}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Autofill", "src\iOS.Autofill\iOS.Autofill.csproj", "{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}"
|
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}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
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|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|Any CPU = Release|Any CPU
|
||||||
Release|iPhone = Release|iPhone
|
Debug|iPhoneSimulator = Debug|iPhoneSimulator
|
||||||
Release|iPhoneSimulator = Release|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|iPhone = Ad-Hoc|iPhone
|
||||||
|
FDroid|Any CPU = FDroid|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
|
{971FDF07-E288-4239-B47A-E9E7E912193B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
|
{971FDF07-E288-4239-B47A-E9E7E912193B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|Any CPU.Deploy.0 = Release|Any CPU
|
{971FDF07-E288-4239-B47A-E9E7E912193B}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
|
{971FDF07-E288-4239-B47A-E9E7E912193B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
|
{971FDF07-E288-4239-B47A-E9E7E912193B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|iPhone.Deploy.0 = Release|Any CPU
|
{971FDF07-E288-4239-B47A-E9E7E912193B}.Release|Any CPU.Deploy.0 = Release|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
{971FDF07-E288-4239-B47A-E9E7E912193B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
|
{971FDF07-E288-4239-B47A-E9E7E912193B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|iPhoneSimulator.Deploy.0 = Release|Any CPU
|
{971FDF07-E288-4239-B47A-E9E7E912193B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
|
{971FDF07-E288-4239-B47A-E9E7E912193B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|Any CPU.Build.0 = Release|Any CPU
|
{971FDF07-E288-4239-B47A-E9E7E912193B}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|Any CPU.Deploy.0 = Release|Any CPU
|
{971FDF07-E288-4239-B47A-E9E7E912193B}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
{971FDF07-E288-4239-B47A-E9E7E912193B}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|iPhone.Build.0 = Release|Any CPU
|
{971FDF07-E288-4239-B47A-E9E7E912193B}.Release|iPhone.Build.0 = Release|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|iPhone.Deploy.0 = Release|Any CPU
|
{971FDF07-E288-4239-B47A-E9E7E912193B}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
{971FDF07-E288-4239-B47A-E9E7E912193B}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
|
{971FDF07-E288-4239-B47A-E9E7E912193B}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|iPhoneSimulator.Deploy.0 = Release|Any CPU
|
{971FDF07-E288-4239-B47A-E9E7E912193B}.AppStore|iPhone.Build.0 = Release|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{971FDF07-E288-4239-B47A-E9E7E912193B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{971FDF07-E288-4239-B47A-E9E7E912193B}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
{971FDF07-E288-4239-B47A-E9E7E912193B}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
{971FDF07-E288-4239-B47A-E9E7E912193B}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|iPhone.Build.0 = Debug|Any CPU
|
{971FDF07-E288-4239-B47A-E9E7E912193B}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|iPhone.Deploy.0 = Debug|Any CPU
|
{971FDF07-E288-4239-B47A-E9E7E912193B}.FDroid|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|iPhoneSimulator.Deploy.0 = Debug|Any CPU
|
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|Any CPU.ActiveCfg = FDroid|Any CPU
|
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|Any CPU.Build.0 = FDroid|Any CPU
|
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|Any CPU.Deploy.0 = FDroid|Any CPU
|
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|iPhone.ActiveCfg = FDroid|Any CPU
|
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|iPhone.Build.0 = FDroid|Any CPU
|
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|iPhone.Deploy.0 = FDroid|Any CPU
|
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|iPhoneSimulator.ActiveCfg = FDroid|Any CPU
|
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|iPhoneSimulator.Build.0 = FDroid|Any CPU
|
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|iPhoneSimulator.Deploy.0 = FDroid|Any CPU
|
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Release|iPhone.Build.0 = Release|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|Any CPU.Build.0 = Release|Any CPU
|
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|Any CPU.Deploy.0 = Release|Any CPU
|
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|iPhone.ActiveCfg = Release|Any CPU
|
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.AppStore|iPhone.Build.0 = Release|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|iPhone.Build.0 = Release|Any CPU
|
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|iPhone.Deploy.0 = Release|Any CPU
|
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
||||||
{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|iPhoneSimulator.Deploy.0 = Release|Any CPU
|
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
|
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.FDroid|Any CPU.Build.0 = Debug|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.ActiveCfg = Debug|Any CPU
|
||||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|Any CPU.Build.0 = 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.ActiveCfg = Release|Any CPU
|
||||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Release|Any CPU.Build.0 = Release|Any CPU
|
{E71F3053-056C-4381-9638-048ED73BDFF6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Release|iPhone.ActiveCfg = Release|Any CPU
|
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Release|iPhone.Build.0 = Release|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.ActiveCfg = Release|Any CPU
|
||||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Release|iPhoneSimulator.Build.0 = 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
|
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone
|
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|iPhone
|
{E71F3053-056C-4381-9638-048ED73BDFF6}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Ad-Hoc|iPhoneSimulator
|
{E71F3053-056C-4381-9638-048ED73BDFF6}.Release|iPhone.Build.0 = Release|Any CPU
|
||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Ad-Hoc|iPhoneSimulator.Build.0 = Ad-Hoc|iPhoneSimulator
|
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.AppStore|Any CPU.ActiveCfg = AppStore|iPhone
|
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.AppStore|iPhone.ActiveCfg = AppStore|iPhone
|
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.AppStore|iPhone.Build.0 = AppStore|iPhone
|
{E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhone.Build.0 = Release|Any CPU
|
||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator
|
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator
|
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|Any CPU.ActiveCfg = Debug|iPhone
|
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
|
||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhone.ActiveCfg = Debug|iPhone
|
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhone.Build.0 = Debug|iPhone
|
{E71F3053-056C-4381-9638-048ED73BDFF6}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhone.Deploy.0 = Debug|iPhone
|
{E71F3053-056C-4381-9638-048ED73BDFF6}.FDroid|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
|
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
|
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.FDroid|Any CPU.ActiveCfg = FDroid|iPhone
|
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.FDroid|iPhone.ActiveCfg = FDroid|iPhone
|
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.FDroid|iPhone.Build.0 = FDroid|iPhone
|
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.FDroid|iPhoneSimulator.ActiveCfg = FDroid|iPhoneSimulator
|
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.FDroid|iPhoneSimulator.Build.0 = FDroid|iPhoneSimulator
|
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Release|Any CPU.ActiveCfg = Release|iPhone
|
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Release|iPhone.ActiveCfg = Release|iPhone
|
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Release|iPhone.Build.0 = Release|iPhone
|
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
|
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
|
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhone.Build.0 = Release|Any CPU
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|Any CPU.ActiveCfg = Ad-Hoc|iPhone
|
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|Any CPU.Build.0 = Ad-Hoc|iPhone
|
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone
|
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|iPhone
|
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhone.Build.0 = Release|Any CPU
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Ad-Hoc|iPhoneSimulator
|
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhoneSimulator.Build.0 = Ad-Hoc|iPhoneSimulator
|
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|Any CPU.ActiveCfg = AppStore|iPhone
|
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|Any CPU.Build.0 = AppStore|iPhone
|
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhone.ActiveCfg = AppStore|iPhone
|
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhone.Build.0 = AppStore|iPhone
|
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator
|
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator
|
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|Any CPU.ActiveCfg = Debug|iPhone
|
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhone.ActiveCfg = Debug|iPhone
|
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhone.Build.0 = Debug|iPhone
|
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhone.Deploy.0 = Debug|iPhone
|
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
|
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
|
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|Any CPU.ActiveCfg = Release|iPhone
|
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|Any CPU.Build.0 = Release|iPhone
|
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|iPhone.ActiveCfg = Release|iPhone
|
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|iPhone.Build.0 = Release|iPhone
|
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|iPhone.Build.0 = Release|Any CPU
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
|
{F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
|
{F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|Any CPU.ActiveCfg = Release|iPhone
|
{F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhone.ActiveCfg = Release|iPhone
|
{F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|iPhone.Build.0 = Release|Any CPU
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhone.Build.0 = Release|iPhone
|
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
|
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
|
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
|
||||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
|
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
|
||||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
|
{F8C3F648-EA5A-4719-8005-85D1690B1655}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
|
{F8C3F648-EA5A-4719-8005-85D1690B1655}.FDroid|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
|
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
|
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
|
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.AppStore|Any CPU.Build.0 = Release|Any CPU
|
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Release|iPhone.Build.0 = Release|Any CPU
|
||||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.AppStore|iPhone.Build.0 = Release|Any CPU
|
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
|
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
||||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.AppStore|iPhone.Build.0 = Release|Any CPU
|
||||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Debug|iPhone.Build.0 = Debug|Any CPU
|
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.FDroid|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.FDroid|Any CPU.ActiveCfg = Release|Any CPU
|
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.FDroid|Any CPU.Build.0 = Release|Any CPU
|
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.FDroid|iPhone.ActiveCfg = Release|Any CPU
|
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.FDroid|iPhone.Build.0 = Release|Any CPU
|
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.FDroid|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.FDroid|iPhoneSimulator.Build.0 = Release|Any CPU
|
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Release|Any CPU.Build.0 = Release|Any CPU
|
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Release|iPhone.ActiveCfg = Release|Any CPU
|
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Release|iPhone.Build.0 = Release|Any CPU
|
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Release|iPhone.Build.0 = Release|Any CPU
|
||||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
|
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
|
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
|
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.AppStore|iPhone.Build.0 = Release|Any CPU
|
||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
|
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
|
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
|
||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
|
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.AppStore|Any CPU.Build.0 = Release|Any CPU
|
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.FDroid|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.AppStore|iPhone.Build.0 = Release|Any CPU
|
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
|
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Debug|iPhone.Build.0 = Debug|Any CPU
|
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.FDroid|Any CPU.ActiveCfg = Release|Any CPU
|
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.FDroid|Any CPU.Build.0 = Release|Any CPU
|
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.FDroid|iPhone.ActiveCfg = Release|Any CPU
|
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Release|iPhone.Build.0 = Release|Any CPU
|
||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.FDroid|iPhone.Build.0 = Release|Any CPU
|
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.FDroid|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.FDroid|iPhoneSimulator.Build.0 = Release|Any CPU
|
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.AppStore|iPhone.Build.0 = Release|Any CPU
|
||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|Any CPU.Build.0 = Release|Any CPU
|
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|iPhone.ActiveCfg = Release|Any CPU
|
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|iPhone.Build.0 = Release|Any CPU
|
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
|
||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|Any CPU.ActiveCfg = Debug|iPhoneSimulator
|
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.FDroid|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(NestedProjects) = preSolution
|
|
||||||
{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
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {7D436EA3-8B7E-45D2-8D14-0730BD2E0410}
|
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}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|||||||
97
build.cake
97
build.cake
@@ -15,16 +15,18 @@ abstract record VariantConfig(
|
|||||||
string AppName,
|
string AppName,
|
||||||
string AndroidPackageName,
|
string AndroidPackageName,
|
||||||
string iOSBundleId,
|
string iOSBundleId,
|
||||||
string ApsEnvironment
|
string ApsEnvironment,
|
||||||
|
string DistProvisioningProfilePrefix
|
||||||
);
|
);
|
||||||
|
|
||||||
const string BASE_BUNDLE_ID_DROID = "com.x8bit.bitwarden";
|
const string BASE_BUNDLE_ID_DROID = "com.x8bit.bitwarden";
|
||||||
const string BASE_BUNDLE_ID_IOS = "com.8bit.bitwarden";
|
const string BASE_BUNDLE_ID_IOS = "com.8bit.bitwarden";
|
||||||
|
|
||||||
record Dev(): VariantConfig("Bitwarden Dev", $"{BASE_BUNDLE_ID_DROID}.dev", $"{BASE_BUNDLE_ID_IOS}.dev", "development");
|
//NOTE: Beta iOS variants have a different ITSEncryptionExportComplianceCode
|
||||||
record QA(): VariantConfig("Bitwarden QA", $"{BASE_BUNDLE_ID_DROID}.qa", $"{BASE_BUNDLE_ID_IOS}.qa", "development");
|
record Dev(): VariantConfig("Bitwarden Dev", $"{BASE_BUNDLE_ID_DROID}.dev", $"{BASE_BUNDLE_ID_IOS}.dev", "development", "Dist:");
|
||||||
record Beta(): VariantConfig("Bitwarden Beta", $"{BASE_BUNDLE_ID_DROID}.beta", $"{BASE_BUNDLE_ID_IOS}.beta", "production");
|
record QA(): VariantConfig("Bitwarden QA", $"{BASE_BUNDLE_ID_DROID}.qa", $"{BASE_BUNDLE_ID_IOS}.qa", "development", "Dist:");
|
||||||
record Prod(): VariantConfig("Bitwarden", $"{BASE_BUNDLE_ID_DROID}", $"{BASE_BUNDLE_ID_IOS}", "production");
|
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{
|
VariantConfig GetVariant() => variant.ToLower() switch{
|
||||||
"qa" => new QA(),
|
"qa" => new QA(),
|
||||||
@@ -67,7 +69,7 @@ Task("UpdateAndroidManifest")
|
|||||||
.Does(()=>
|
.Does(()=>
|
||||||
{
|
{
|
||||||
var buildVariant = GetVariant();
|
var buildVariant = GetVariant();
|
||||||
var manifestPath = Path.Combine(_slnPath, "src", "Android", "Properties", "AndroidManifest.xml");
|
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:
|
// Cake.AndroidAppManifest doesn't currently enable us to access nested items so, quick (not ideal) fix:
|
||||||
var manifestText = FileReadText(manifestPath);
|
var manifestText = FileReadText(manifestPath);
|
||||||
@@ -119,26 +121,26 @@ Task("UpdateAndroidCodeFiles")
|
|||||||
//We're not using _androidPackageName here because the codefile is currently slightly different string than the one in AndroidManifest.xml
|
//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 keyName = "com.8bit.bitwarden";
|
||||||
var fixedPackageName = buildVariant.AndroidPackageName.Replace("x8bit", "8bit");
|
var fixedPackageName = buildVariant.AndroidPackageName.Replace("x8bit", "8bit");
|
||||||
var filePath = Path.Combine(_slnPath, "src", "Android", "Services", "BiometricService.cs");
|
var filePath = Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Services", "BiometricService.cs");
|
||||||
ReplaceInFile(filePath, keyName, fixedPackageName);
|
ReplaceInFile(filePath, keyName, fixedPackageName);
|
||||||
|
|
||||||
var packageFileList = new string[] {
|
var packageFileList = new string[] {
|
||||||
Path.Combine(_slnPath, "src", "Android", "MainActivity.cs"),
|
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "MainActivity.cs"),
|
||||||
Path.Combine(_slnPath, "src", "Android", "MainApplication.cs"),
|
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "MainApplication.cs"),
|
||||||
Path.Combine(_slnPath, "src", "Android", "Constants.cs"),
|
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Constants.cs"),
|
||||||
Path.Combine(_slnPath, "src", "Android", "Accessibility", "AccessibilityService.cs"),
|
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Accessibility", "AccessibilityService.cs"),
|
||||||
Path.Combine(_slnPath, "src", "Android", "Autofill", "AutofillHelpers.cs"),
|
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Autofill", "AutofillHelpers.cs"),
|
||||||
Path.Combine(_slnPath, "src", "Android", "Autofill", "AutofillService.cs"),
|
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Autofill", "AutofillService.cs"),
|
||||||
Path.Combine(_slnPath, "src", "Android", "Receivers", "ClearClipboardAlarmReceiver.cs"),
|
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Receivers", "ClearClipboardAlarmReceiver.cs"),
|
||||||
Path.Combine(_slnPath, "src", "Android", "Receivers", "EventUploadReceiver.cs"),
|
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Receivers", "EventUploadReceiver.cs"),
|
||||||
Path.Combine(_slnPath, "src", "Android", "Receivers", "PackageReplacedReceiver.cs"),
|
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Receivers", "PackageReplacedReceiver.cs"),
|
||||||
Path.Combine(_slnPath, "src", "Android", "Receivers", "RestrictionsChangedReceiver.cs"),
|
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Receivers", "RestrictionsChangedReceiver.cs"),
|
||||||
Path.Combine(_slnPath, "src", "Android", "Services", "DeviceActionService.cs"),
|
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Services", "DeviceActionService.cs"),
|
||||||
Path.Combine(_slnPath, "src", "Android", "Services", "FileService.cs"),
|
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Services", "FileService.cs"),
|
||||||
Path.Combine(_slnPath, "src", "Android", "Tiles", "AutofillTileService.cs"),
|
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Tiles", "AutofillTileService.cs"),
|
||||||
Path.Combine(_slnPath, "src", "Android", "Tiles", "GeneratorTileService.cs"),
|
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Tiles", "GeneratorTileService.cs"),
|
||||||
Path.Combine(_slnPath, "src", "Android", "Tiles", "MyVaultTileService.cs"),
|
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Tiles", "MyVaultTileService.cs"),
|
||||||
Path.Combine(_slnPath, "src", "Android", "google-services.json"),
|
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "google-services.json"),
|
||||||
Path.Combine(_slnPath, "store", "google", "Publisher", "Program.cs"),
|
Path.Combine(_slnPath, "store", "google", "Publisher", "Program.cs"),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -148,7 +150,7 @@ Task("UpdateAndroidCodeFiles")
|
|||||||
}
|
}
|
||||||
|
|
||||||
var labelFileList = new string[] {
|
var labelFileList = new string[] {
|
||||||
Path.Combine(_slnPath, "src", "Android", "Autofill", "AutofillService.cs"),
|
Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Autofill", "AutofillService.cs"),
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach(string path in labelFileList)
|
foreach(string path in labelFileList)
|
||||||
@@ -197,7 +199,8 @@ private void UpdateiOSInfoPlist(string plistPath, VariantConfig buildVariant, Gi
|
|||||||
var prevBundleId = plist["CFBundleIdentifier"];
|
var prevBundleId = plist["CFBundleIdentifier"];
|
||||||
var prevBundleName = plist["CFBundleName"];
|
var prevBundleName = plist["CFBundleName"];
|
||||||
//var newVersion = CreateBuildNumber(prevVersion).ToString();
|
//var newVersion = CreateBuildNumber(prevVersion).ToString();
|
||||||
var newVersionName = GetVersionName(prevVersionName, buildVariant, git);
|
// 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 newBundleId = GetiOSBundleId(buildVariant, projectType);
|
||||||
var newBundleName = GetiOSBundleName(buildVariant, projectType);
|
var newBundleName = GetiOSBundleName(buildVariant, projectType);
|
||||||
|
|
||||||
@@ -219,6 +222,11 @@ private void UpdateiOSInfoPlist(string plistPath, VariantConfig buildVariant, Gi
|
|||||||
plist["NSExtension"]["NSExtensionAttributes"]["NSExtensionActivationRule"] = keyText.Replace("com.8bit.bitwarden", buildVariant.iOSBundleId);
|
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);
|
SerializePlist(plistFile, plist);
|
||||||
|
|
||||||
Information($"Changed app name from {prevBundleName} to {newBundleName}");
|
Information($"Changed app name from {prevBundleName} to {newBundleName}");
|
||||||
@@ -228,12 +236,15 @@ private void UpdateiOSInfoPlist(string plistPath, VariantConfig buildVariant, Gi
|
|||||||
Information($"{plistPath} updated with success!");
|
Information($"{plistPath} updated with success!");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateiOSEntitlementsPlist(string entitlementsPath, VariantConfig buildVariant)
|
private void UpdateiOSEntitlementsPlist(string entitlementsPath, VariantConfig buildVariant, bool updateApsEnv)
|
||||||
{
|
{
|
||||||
var EntitlementlistFile = File(entitlementsPath);
|
var EntitlementlistFile = File(entitlementsPath);
|
||||||
dynamic Entitlements = DeserializePlist(EntitlementlistFile);
|
dynamic Entitlements = DeserializePlist(EntitlementlistFile);
|
||||||
|
|
||||||
|
if (updateApsEnv)
|
||||||
|
{
|
||||||
Entitlements["aps-environment"] = buildVariant.ApsEnvironment;
|
Entitlements["aps-environment"] = buildVariant.ApsEnvironment;
|
||||||
|
}
|
||||||
Entitlements["keychain-access-groups"] = new List<string>() { "$(AppIdentifierPrefix)" + buildVariant.iOSBundleId };
|
Entitlements["keychain-access-groups"] = new List<string>() { "$(AppIdentifierPrefix)" + buildVariant.iOSBundleId };
|
||||||
Entitlements["com.apple.security.application-groups"] = new List<string>() { $"group.{buildVariant.iOSBundleId}" };;
|
Entitlements["com.apple.security.application-groups"] = new List<string>() { $"group.{buildVariant.iOSBundleId}" };;
|
||||||
|
|
||||||
@@ -274,7 +285,8 @@ private void UpdateWatchPbxproj(string pbxprojPath, string newVersion)
|
|||||||
fileText = Regex.Replace(fileText, pattern, $"MARKETING_VERSION = {newVersion};");
|
fileText = Regex.Replace(fileText, pattern, $"MARKETING_VERSION = {newVersion};");
|
||||||
|
|
||||||
FileWriteText(pbxprojPath, fileText);
|
FileWriteText(pbxprojPath, fileText);
|
||||||
Information($"{pbxprojPath} modified successfully.");
|
|
||||||
|
Information($"{pbxprojPath} modified Marketing Version successfully.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -315,7 +327,7 @@ private void UpdateAppleIcons(string target, string appiconsetTarget)
|
|||||||
|
|
||||||
Task("UpdateiOSIcons")
|
Task("UpdateiOSIcons")
|
||||||
.Does(()=>{
|
.Does(()=>{
|
||||||
UpdateAppleIcons("ios", Path.Combine(_slnPath, "src", "iOS", "Resources", "Assets.xcassets", "AppIcons.appiconset"));
|
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"));
|
UpdateAppleIcons("watch", Path.Combine(_slnPath, "src", "watchOS", "bitwarden", "bitwarden WatchKit App", "Assets.xcassets", "AppIcon.appiconset"));
|
||||||
// TODO: Update complication icons when they start working
|
// TODO: Update complication icons when they start working
|
||||||
});
|
});
|
||||||
@@ -324,10 +336,10 @@ Task("UpdateiOSPlist")
|
|||||||
.IsDependentOn("GetGitInfo")
|
.IsDependentOn("GetGitInfo")
|
||||||
.Does(()=> {
|
.Does(()=> {
|
||||||
var buildVariant = GetVariant();
|
var buildVariant = GetVariant();
|
||||||
var infoPath = Path.Combine(_slnPath, "src", "iOS", "Info.plist");
|
var infoPath = Path.Combine(_slnPath, "src", "App", "Platforms", "iOS", "Info.plist");
|
||||||
var entitlementsPath = Path.Combine(_slnPath, "src", "iOS", "Entitlements.plist");
|
var entitlementsPath = Path.Combine(_slnPath, "src", "App", "Platforms", "iOS", "Entitlements.plist");
|
||||||
UpdateiOSInfoPlist(infoPath, buildVariant, _gitVersion, iOSProjectType.MainApp);
|
UpdateiOSInfoPlist(infoPath, buildVariant, _gitVersion, iOSProjectType.MainApp);
|
||||||
UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant);
|
UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
Task("UpdateiOSAutofillPlist")
|
Task("UpdateiOSAutofillPlist")
|
||||||
@@ -338,7 +350,7 @@ Task("UpdateiOSAutofillPlist")
|
|||||||
var infoPath = Path.Combine(_slnPath, "src", "iOS.Autofill", "Info.plist");
|
var infoPath = Path.Combine(_slnPath, "src", "iOS.Autofill", "Info.plist");
|
||||||
var entitlementsPath = Path.Combine(_slnPath, "src", "iOS.Autofill", "Entitlements.plist");
|
var entitlementsPath = Path.Combine(_slnPath, "src", "iOS.Autofill", "Entitlements.plist");
|
||||||
UpdateiOSInfoPlist(infoPath, buildVariant, _gitVersion, iOSProjectType.Autofill);
|
UpdateiOSInfoPlist(infoPath, buildVariant, _gitVersion, iOSProjectType.Autofill);
|
||||||
UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant);
|
UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
Task("UpdateiOSExtensionPlist")
|
Task("UpdateiOSExtensionPlist")
|
||||||
@@ -349,7 +361,7 @@ Task("UpdateiOSExtensionPlist")
|
|||||||
var infoPath = Path.Combine(_slnPath, "src", "iOS.Extension", "Info.plist");
|
var infoPath = Path.Combine(_slnPath, "src", "iOS.Extension", "Info.plist");
|
||||||
var entitlementsPath = Path.Combine(_slnPath, "src", "iOS.Extension", "Entitlements.plist");
|
var entitlementsPath = Path.Combine(_slnPath, "src", "iOS.Extension", "Entitlements.plist");
|
||||||
UpdateiOSInfoPlist(infoPath, buildVariant, _gitVersion, iOSProjectType.Extension);
|
UpdateiOSInfoPlist(infoPath, buildVariant, _gitVersion, iOSProjectType.Extension);
|
||||||
UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant);
|
UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
Task("UpdateiOSShareExtensionPlist")
|
Task("UpdateiOSShareExtensionPlist")
|
||||||
@@ -360,7 +372,7 @@ Task("UpdateiOSShareExtensionPlist")
|
|||||||
var infoPath = Path.Combine(_slnPath, "src", "iOS.ShareExtension", "Info.plist");
|
var infoPath = Path.Combine(_slnPath, "src", "iOS.ShareExtension", "Info.plist");
|
||||||
var entitlementsPath = Path.Combine(_slnPath, "src", "iOS.ShareExtension", "Entitlements.plist");
|
var entitlementsPath = Path.Combine(_slnPath, "src", "iOS.ShareExtension", "Entitlements.plist");
|
||||||
UpdateiOSInfoPlist(infoPath, buildVariant, _gitVersion, iOSProjectType.ShareExtension);
|
UpdateiOSInfoPlist(infoPath, buildVariant, _gitVersion, iOSProjectType.ShareExtension);
|
||||||
UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant);
|
UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
Task("UpdateiOSCodeFiles")
|
Task("UpdateiOSCodeFiles")
|
||||||
@@ -397,6 +409,22 @@ Task("UpdateWatchKitAppInfoPlist")
|
|||||||
UpdateWatchKitAppInfoPlist(infoPath, buildVariant);
|
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
|
#endregion iOS
|
||||||
|
|
||||||
#region Main Tasks
|
#region Main Tasks
|
||||||
@@ -418,6 +446,7 @@ Task("iOS")
|
|||||||
.IsDependentOn("UpdateiOSCodeFiles")
|
.IsDependentOn("UpdateiOSCodeFiles")
|
||||||
.IsDependentOn("UpdateWatchProject")
|
.IsDependentOn("UpdateWatchProject")
|
||||||
.IsDependentOn("UpdateWatchKitAppInfoPlist")
|
.IsDependentOn("UpdateWatchKitAppInfoPlist")
|
||||||
|
.IsDependentOn("UpdateDistProfiles")
|
||||||
.Does(()=>
|
.Does(()=>
|
||||||
{
|
{
|
||||||
Information("iOS app updated");
|
Information("iOS app updated");
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ project_id_env: _CROWDIN_PROJECT_ID
|
|||||||
api_token_env: CROWDIN_API_TOKEN
|
api_token_env: CROWDIN_API_TOKEN
|
||||||
preserve_hierarchy: true
|
preserve_hierarchy: true
|
||||||
files:
|
files:
|
||||||
- source: /src/App/Resources/AppResources.resx
|
- source: /src/Core/Resources/Localization/AppResources.resx
|
||||||
dest: /src/App/Resources/%original_file_name%
|
dest: /src/Core/Resources/Localization/%original_file_name%
|
||||||
translation: /src/App/Resources/AppResources.%two_letters_code%.resx
|
translation: /src/Core/Resources/Localization/AppResources.%two_letters_code%.resx
|
||||||
update_option: update_as_unapproved
|
update_option: update_as_unapproved
|
||||||
languages_mapping:
|
languages_mapping:
|
||||||
two_letters_code:
|
two_letters_code:
|
||||||
|
|||||||
79
docs/architecture/index.mdx
Normal file
79
docs/architecture/index.mdx
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
---
|
||||||
|
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
|
||||||
|
```
|
||||||
26
docs/architecture/overview.md
Normal file
26
docs/architecture/overview.md
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
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)
|
||||||
186
docs/architecture/watchOS.md
Normal file
186
docs/architecture/watchOS.md
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
# 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.
|
||||||
BIN
docs/getting-started/android/android-sdk.png
Normal file
BIN
docs/getting-started/android/android-sdk.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 350 KiB |
177
docs/getting-started/android/index.md
Normal file
177
docs/getting-started/android/index.md
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
# 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>
|
||||||
107
docs/getting-started/index.md
Normal file
107
docs/getting-started/index.md
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
---
|
||||||
|
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.
|
||||||
440
docs/getting-started/ios/index.mdx
Normal file
440
docs/getting-started/ios/index.mdx
Normal file
@@ -0,0 +1,440 @@
|
|||||||
|
---
|
||||||
|
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.
|
||||||
BIN
docs/getting-started/ios/new-project-options.png
Normal file
BIN
docs/getting-started/ios/new-project-options.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 95 KiB |
BIN
docs/getting-started/ios/run-debug.png
Normal file
BIN
docs/getting-started/ios/run-debug.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
BIN
docs/getting-started/ios/signing-and-capabilities.png
Normal file
BIN
docs/getting-started/ios/signing-and-capabilities.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 307 KiB |
BIN
docs/getting-started/ios/troubleshoot-generic-simulator.png
Normal file
BIN
docs/getting-started/ios/troubleshoot-generic-simulator.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
101
docs/getting-started/watchos/index.mdx
Normal file
101
docs/getting-started/watchos/index.mdx
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
---
|
||||||
|
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.
|
||||||
6
global.json
Normal file
6
global.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"sdk": {
|
||||||
|
"version": "8.0.402",
|
||||||
|
"rollForward": "disable"
|
||||||
|
}
|
||||||
|
}
|
||||||
Submodule lib/MessagePack deleted from 1ecb15e311
19
lib/MessagePack/LICENSE.md
Normal file
19
lib/MessagePack/LICENSE.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
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.
|
||||||
32
lib/MessagePack/MessagePack-FlightSchool.podspec
Normal file
32
lib/MessagePack/MessagePack-FlightSchool.podspec
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
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
|
||||||
13
lib/MessagePack/MessagePack.playground/Contents.swift
Normal file
13
lib/MessagePack/MessagePack.playground/Contents.swift
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
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
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<playground version='5.0' target-platform='macos' executeOnSourceChanges='false'>
|
||||||
|
<timeline fileName='timeline.xctimeline'/>
|
||||||
|
</playground>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<?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>
|
||||||
25
lib/MessagePack/MessagePack.xcodeproj/MessagePack_Info.plist
Normal file
25
lib/MessagePack/MessagePack.xcodeproj/MessagePack_Info.plist
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?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>
|
||||||
543
lib/MessagePack/MessagePack.xcodeproj/project.pbxproj
Normal file
543
lib/MessagePack/MessagePack.xcodeproj/project.pbxproj
Normal file
@@ -0,0 +1,543 @@
|
|||||||
|
// !$*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 */;
|
||||||
|
}
|
||||||
7
lib/MessagePack/MessagePack.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
7
lib/MessagePack/MessagePack.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "self:">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?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>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?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>
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
<?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>
|
||||||
10
lib/MessagePack/MessagePack.xcworkspace/contents.xcworkspacedata
generated
Normal file
10
lib/MessagePack/MessagePack.xcworkspace/contents.xcworkspacedata
generated
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "group:MessagePack.playground">
|
||||||
|
</FileRef>
|
||||||
|
<FileRef
|
||||||
|
location = "group:MessagePack.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?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>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?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>
|
||||||
28
lib/MessagePack/Package.swift
Normal file
28
lib/MessagePack/Package.swift
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
// 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"]),
|
||||||
|
]
|
||||||
|
)
|
||||||
87
lib/MessagePack/README.md
Normal file
87
lib/MessagePack/README.md
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
# 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
|
||||||
28
lib/MessagePack/Sources/MessagePack/AnyCodingKey.swift
Normal file
28
lib/MessagePack/Sources/MessagePack/AnyCodingKey.swift
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
44
lib/MessagePack/Sources/MessagePack/Box.swift
Normal file
44
lib/MessagePack/Sources/MessagePack/Box.swift
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
60
lib/MessagePack/Sources/MessagePack/DataSpec.swift
Normal file
60
lib/MessagePack/Sources/MessagePack/DataSpec.swift
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
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 {}
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,226 @@
|
|||||||
|
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 {}
|
||||||
@@ -0,0 +1,235 @@
|
|||||||
|
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 {}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,264 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
8
lib/MessagePack/Tests/LinuxMain.swift
Normal file
8
lib/MessagePack/Tests/LinuxMain.swift
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import XCTest
|
||||||
|
@testable import MessagePackTests
|
||||||
|
|
||||||
|
XCTMain([
|
||||||
|
testCase(MessagePackDecodingTests.allTests),
|
||||||
|
testCase(MessagePackEncodingTests.allTests),
|
||||||
|
testCase(MessagePackRoundTripTests.allTests),
|
||||||
|
])
|
||||||
63
lib/MessagePack/Tests/MessagePackTests/Airport.swift
Normal file
63
lib/MessagePack/Tests/MessagePackTests/Airport.swift
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
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"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,301 @@
|
|||||||
|
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 ?? "")")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
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)
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
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)
|
||||||
|
]
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<doc>
|
||||||
|
<assembly>
|
||||||
|
<name>Xamarin.AndroidX.Credentials</name>
|
||||||
|
</assembly>
|
||||||
|
<members>
|
||||||
|
</members>
|
||||||
|
</doc>
|
||||||
Binary file not shown.
7
nuget.config
Normal file
7
nuget.config
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<configuration>
|
||||||
|
<packageSources>
|
||||||
|
<add key="MAUI Nightly builds" value="https://pkgs.dev.azure.com/xamarin/public/_packaging/maui-nightly/nuget/v3/index.json" />
|
||||||
|
<add key="Local AndroidX Credentials" value="lib/android/Xamarin.AndroidX.Credentials" />
|
||||||
|
</packageSources>
|
||||||
|
</configuration>
|
||||||
169
package-lock.json
generated
169
package-lock.json
generated
@@ -8,7 +8,7 @@
|
|||||||
"name": "bitwarden-mobile",
|
"name": "bitwarden-mobile",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"gh-pages": "^3.2.3"
|
"gh-pages": "^6.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/array-union": {
|
"node_modules/array-union": {
|
||||||
@@ -33,13 +33,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/async": {
|
"node_modules/async": {
|
||||||
"version": "2.6.3",
|
"version": "3.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz",
|
||||||
"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
|
"integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"license": "MIT"
|
||||||
"lodash": "^4.17.14"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"node_modules/balanced-match": {
|
"node_modules/balanced-match": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
@@ -58,10 +56,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/commander": {
|
"node_modules/commander": {
|
||||||
"version": "2.20.3",
|
"version": "11.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz",
|
||||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
"integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/commondir": {
|
"node_modules/commondir": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
@@ -76,10 +78,11 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/email-addresses": {
|
"node_modules/email-addresses": {
|
||||||
"version": "3.1.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-5.0.0.tgz",
|
||||||
"integrity": "sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg==",
|
"integrity": "sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/escape-string-regexp": {
|
"node_modules/escape-string-regexp": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
@@ -147,17 +150,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/fs-extra": {
|
"node_modules/fs-extra": {
|
||||||
"version": "8.1.0",
|
"version": "11.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
|
||||||
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
|
"integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"graceful-fs": "^4.2.0",
|
"graceful-fs": "^4.2.0",
|
||||||
"jsonfile": "^4.0.0",
|
"jsonfile": "^6.0.1",
|
||||||
"universalify": "^0.1.0"
|
"universalify": "^2.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6 <7 || >=8"
|
"node": ">=14.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/fs.realpath": {
|
"node_modules/fs.realpath": {
|
||||||
@@ -167,17 +171,18 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/gh-pages": {
|
"node_modules/gh-pages": {
|
||||||
"version": "3.2.3",
|
"version": "6.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-3.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-6.1.1.tgz",
|
||||||
"integrity": "sha512-jA1PbapQ1jqzacECfjUaO9gV8uBgU6XNMV0oXLtfCX3haGLe5Atq8BxlrADhbD6/UdG9j6tZLWAkAybndOXTJg==",
|
"integrity": "sha512-upnohfjBwN5hBP9w2dPE7HO5JJTHzSGMV1JrLrHvNuqmjoYHg6TBrCcnEoorjG/e0ejbuvnwyKMdTyM40PEByw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"async": "^2.6.1",
|
"async": "^3.2.4",
|
||||||
"commander": "^2.18.0",
|
"commander": "^11.0.0",
|
||||||
"email-addresses": "^3.0.1",
|
"email-addresses": "^5.0.0",
|
||||||
"filenamify": "^4.3.0",
|
"filenamify": "^4.3.0",
|
||||||
"find-cache-dir": "^3.3.1",
|
"find-cache-dir": "^3.3.1",
|
||||||
"fs-extra": "^8.1.0",
|
"fs-extra": "^11.1.1",
|
||||||
"globby": "^6.1.0"
|
"globby": "^6.1.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -225,10 +230,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/graceful-fs": {
|
"node_modules/graceful-fs": {
|
||||||
"version": "4.2.8",
|
"version": "4.2.11",
|
||||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz",
|
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||||
"integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==",
|
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/inflight": {
|
"node_modules/inflight": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
@@ -247,10 +253,14 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/jsonfile": {
|
"node_modules/jsonfile": {
|
||||||
"version": "4.0.0",
|
"version": "6.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
|
||||||
"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
|
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"universalify": "^2.0.0"
|
||||||
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"graceful-fs": "^4.1.6"
|
"graceful-fs": "^4.1.6"
|
||||||
}
|
}
|
||||||
@@ -267,12 +277,6 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lodash": {
|
|
||||||
"version": "4.17.21",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
|
||||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/make-dir": {
|
"node_modules/make-dir": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
|
||||||
@@ -448,12 +452,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/universalify": {
|
"node_modules/universalify": {
|
||||||
"version": "0.1.2",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
||||||
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
|
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 4.0.0"
|
"node": ">= 10.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/wrappy": {
|
"node_modules/wrappy": {
|
||||||
@@ -480,13 +485,10 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"async": {
|
"async": {
|
||||||
"version": "2.6.3",
|
"version": "3.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz",
|
||||||
"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
|
"integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==",
|
||||||
"dev": true,
|
"dev": true
|
||||||
"requires": {
|
|
||||||
"lodash": "^4.17.14"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"balanced-match": {
|
"balanced-match": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
@@ -505,9 +507,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"commander": {
|
"commander": {
|
||||||
"version": "2.20.3",
|
"version": "11.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz",
|
||||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
"integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"commondir": {
|
"commondir": {
|
||||||
@@ -523,9 +525,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"email-addresses": {
|
"email-addresses": {
|
||||||
"version": "3.1.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-5.0.0.tgz",
|
||||||
"integrity": "sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg==",
|
"integrity": "sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"escape-string-regexp": {
|
"escape-string-regexp": {
|
||||||
@@ -573,14 +575,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fs-extra": {
|
"fs-extra": {
|
||||||
"version": "8.1.0",
|
"version": "11.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
|
||||||
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
|
"integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"graceful-fs": "^4.2.0",
|
"graceful-fs": "^4.2.0",
|
||||||
"jsonfile": "^4.0.0",
|
"jsonfile": "^6.0.1",
|
||||||
"universalify": "^0.1.0"
|
"universalify": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fs.realpath": {
|
"fs.realpath": {
|
||||||
@@ -590,17 +592,17 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"gh-pages": {
|
"gh-pages": {
|
||||||
"version": "3.2.3",
|
"version": "6.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-3.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-6.1.1.tgz",
|
||||||
"integrity": "sha512-jA1PbapQ1jqzacECfjUaO9gV8uBgU6XNMV0oXLtfCX3haGLe5Atq8BxlrADhbD6/UdG9j6tZLWAkAybndOXTJg==",
|
"integrity": "sha512-upnohfjBwN5hBP9w2dPE7HO5JJTHzSGMV1JrLrHvNuqmjoYHg6TBrCcnEoorjG/e0ejbuvnwyKMdTyM40PEByw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"async": "^2.6.1",
|
"async": "^3.2.4",
|
||||||
"commander": "^2.18.0",
|
"commander": "^11.0.0",
|
||||||
"email-addresses": "^3.0.1",
|
"email-addresses": "^5.0.0",
|
||||||
"filenamify": "^4.3.0",
|
"filenamify": "^4.3.0",
|
||||||
"find-cache-dir": "^3.3.1",
|
"find-cache-dir": "^3.3.1",
|
||||||
"fs-extra": "^8.1.0",
|
"fs-extra": "^11.1.1",
|
||||||
"globby": "^6.1.0"
|
"globby": "^6.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -632,9 +634,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"graceful-fs": {
|
"graceful-fs": {
|
||||||
"version": "4.2.8",
|
"version": "4.2.11",
|
||||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz",
|
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||||
"integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==",
|
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"inflight": {
|
"inflight": {
|
||||||
@@ -654,12 +656,13 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"jsonfile": {
|
"jsonfile": {
|
||||||
"version": "4.0.0",
|
"version": "6.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
|
||||||
"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
|
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"graceful-fs": "^4.1.6"
|
"graceful-fs": "^4.1.6",
|
||||||
|
"universalify": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"locate-path": {
|
"locate-path": {
|
||||||
@@ -671,12 +674,6 @@
|
|||||||
"p-locate": "^4.1.0"
|
"p-locate": "^4.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lodash": {
|
|
||||||
"version": "4.17.21",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
|
||||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"make-dir": {
|
"make-dir": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
|
||||||
@@ -801,9 +798,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"universalify": {
|
"universalify": {
|
||||||
"version": "0.1.2",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
||||||
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
|
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"wrappy": {
|
"wrappy": {
|
||||||
|
|||||||
@@ -6,6 +6,6 @@
|
|||||||
"clean:l10n": "git push origin --delete l10n_master"
|
"clean:l10n": "git push origin --delete l10n_master"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"gh-pages": "^3.2.3"
|
"gh-pages": "^6.1.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,319 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<PropertyGroup>
|
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
|
||||||
<ProjectGuid>{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}</ProjectGuid>
|
|
||||||
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
|
||||||
<TemplateGuid>{c9e5eea5-ca05-42a1-839b-61506e0a37df}</TemplateGuid>
|
|
||||||
<OutputType>Library</OutputType>
|
|
||||||
<RootNamespace>Bit.Droid</RootNamespace>
|
|
||||||
<AssemblyName>BitwardenAndroid</AssemblyName>
|
|
||||||
<AndroidApplication>True</AndroidApplication>
|
|
||||||
<AndroidResgenFile>Resources\Resource.designer.cs</AndroidResgenFile>
|
|
||||||
<AndroidResgenClass>Resource</AndroidResgenClass>
|
|
||||||
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
|
|
||||||
<MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
|
|
||||||
<MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
|
|
||||||
<TargetFrameworkVersion>v13.0</TargetFrameworkVersion>
|
|
||||||
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
|
|
||||||
<NuGetPackageImportStamp>
|
|
||||||
</NuGetPackageImportStamp>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
|
||||||
<DebugSymbols>true</DebugSymbols>
|
|
||||||
<DebugType>portable</DebugType>
|
|
||||||
<Optimize>false</Optimize>
|
|
||||||
<OutputPath>bin\Debug</OutputPath>
|
|
||||||
<DefineConstants>DEBUG;</DefineConstants>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<WarningLevel>3</WarningLevel>
|
|
||||||
<AndroidSupportedAbis />
|
|
||||||
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
|
||||||
<DebugSymbols>false</DebugSymbols>
|
|
||||||
<DebugType>pdbonly</DebugType>
|
|
||||||
<Optimize>true</Optimize>
|
|
||||||
<OutputPath>bin\Release</OutputPath>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<WarningLevel>4</WarningLevel>
|
|
||||||
<AndroidManagedSymbols>true</AndroidManagedSymbols>
|
|
||||||
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
|
|
||||||
<AndroidSupportedAbis>armeabi-v7a;x86;x86_64;arm64-v8a</AndroidSupportedAbis>
|
|
||||||
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
|
|
||||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
|
||||||
<AndroidEnableProfiledAot>true</AndroidEnableProfiledAot>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'FDroid|AnyCPU'">
|
|
||||||
<DebugSymbols>false</DebugSymbols>
|
|
||||||
<OutputPath>bin\FDroid\</OutputPath>
|
|
||||||
<Optimize>true</Optimize>
|
|
||||||
<DebugType>pdbonly</DebugType>
|
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
|
||||||
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
|
||||||
<DefineConstants>FDROID</DefineConstants>
|
|
||||||
<AndroidSupportedAbis>armeabi-v7a;x86;x86_64;arm64-v8a</AndroidSupportedAbis>
|
|
||||||
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
|
|
||||||
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
|
|
||||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
|
||||||
<AndroidEnableProfiledAot>true</AndroidEnableProfiledAot>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="Mono.Android" />
|
|
||||||
<Reference Include="Mono.Android.Export" />
|
|
||||||
<Reference Include="System" />
|
|
||||||
<Reference Include="System.Core" />
|
|
||||||
<Reference Include="System.Xml.Linq" />
|
|
||||||
<Reference Include="System.Xml" />
|
|
||||||
<Reference Include="System.Net.Http" Condition="'$(Configuration)'=='FDroid'" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Plugin.CurrentActivity">
|
|
||||||
<Version>2.1.0.4</Version>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="Portable.BouncyCastle">
|
|
||||||
<Version>1.9.0</Version>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.5.1.1" />
|
|
||||||
<PackageReference Include="Xamarin.AndroidX.AutoFill" Version="1.1.0.16" />
|
|
||||||
<PackageReference Include="Xamarin.AndroidX.CardView" Version="1.0.0.19" />
|
|
||||||
<PackageReference Include="Xamarin.AndroidX.Core" Version="1.10.0" />
|
|
||||||
<PackageReference Include="Xamarin.AndroidX.MediaRouter" Version="1.3.1.1" />
|
|
||||||
<PackageReference Include="Xamarin.Essentials">
|
|
||||||
<Version>1.7.5</Version>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="Xamarin.Firebase.Messaging">
|
|
||||||
<Version>123.1.1.1</Version>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="Xamarin.Google.Android.Material" Version="1.8.0" />
|
|
||||||
<PackageReference Include="Xamarin.Google.Dagger" Version="2.44.2.1" />
|
|
||||||
<PackageReference Include="Xamarin.GooglePlayServices.SafetyNet">
|
|
||||||
<Version>118.0.1.3</Version>
|
|
||||||
</PackageReference>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Compile Include="Accessibility\AccessibilityActivity.cs" />
|
|
||||||
<Compile Include="Accessibility\AccessibilityHelpers.cs" />
|
|
||||||
<Compile Include="Accessibility\Credentials.cs" />
|
|
||||||
<Compile Include="Accessibility\AccessibilityService.cs" />
|
|
||||||
<Compile Include="Accessibility\Browser.cs" />
|
|
||||||
<Compile Include="Accessibility\NodeList.cs" />
|
|
||||||
<Compile Include="Accessibility\KnownUsernameField.cs" />
|
|
||||||
<Compile Include="Autofill\AutofillConstants.cs" />
|
|
||||||
<Compile Include="Autofill\AutofillHelpers.cs" />
|
|
||||||
<Compile Include="Autofill\AutofillService.cs" />
|
|
||||||
<Compile Include="Autofill\AutofillExternalSelectionActivity.cs" />
|
|
||||||
<Compile Include="Autofill\Field.cs" />
|
|
||||||
<Compile Include="Autofill\FieldCollection.cs" />
|
|
||||||
<Compile Include="Autofill\FilledItem.cs" />
|
|
||||||
<Compile Include="Autofill\Parser.cs" />
|
|
||||||
<Compile Include="Autofill\SavedItem.cs" />
|
|
||||||
<Compile Include="Effects\FabShadowEffect.cs" />
|
|
||||||
<Compile Include="Effects\FixedSizeEffect.cs" />
|
|
||||||
<Compile Include="Effects\TabBarEffect.cs" />
|
|
||||||
<Compile Include="Push\FirebaseMessagingService.cs" />
|
|
||||||
<Compile Include="Receivers\ClearClipboardAlarmReceiver.cs" />
|
|
||||||
<Compile Include="Receivers\RestrictionsChangedReceiver.cs" />
|
|
||||||
<Compile Include="Receivers\EventUploadReceiver.cs" />
|
|
||||||
<Compile Include="Receivers\PackageReplacedReceiver.cs" />
|
|
||||||
<Compile Include="Renderers\ExtendedGridRenderer.cs" />
|
|
||||||
<Compile Include="Renderers\ExtendedDatePickerRenderer.cs" />
|
|
||||||
<Compile Include="Renderers\CustomTabbedRenderer.cs" />
|
|
||||||
<Compile Include="Renderers\ExtendedStackLayoutRenderer.cs" />
|
|
||||||
<Compile Include="Renderers\ExtendedStepperRenderer.cs" />
|
|
||||||
<Compile Include="Renderers\CustomSwitchRenderer.cs" />
|
|
||||||
<Compile Include="Renderers\ExtendedTimePickerRenderer.cs" />
|
|
||||||
<Compile Include="Renderers\ExtendedSliderRenderer.cs" />
|
|
||||||
<Compile Include="Renderers\CustomEditorRenderer.cs" />
|
|
||||||
<Compile Include="Renderers\CustomPickerRenderer.cs" />
|
|
||||||
<Compile Include="Renderers\CustomEntryRenderer.cs" />
|
|
||||||
<Compile Include="Renderers\CustomSearchBarRenderer.cs" />
|
|
||||||
<Compile Include="Renderers\HybridWebViewRenderer.cs" />
|
|
||||||
<Compile Include="Services\AndroidPushNotificationService.cs" />
|
|
||||||
<Compile Include="Services\AndroidLogService.cs" />
|
|
||||||
<Compile Include="MainApplication.cs" />
|
|
||||||
<Compile Include="MainActivity.cs" />
|
|
||||||
<Compile Include="Resources\Resource.designer.cs" />
|
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
|
||||||
<Compile Include="Services\BiometricService.cs" />
|
|
||||||
<Compile Include="Services\CryptoPrimitiveService.cs" />
|
|
||||||
<Compile Include="Services\DeviceActionService.cs" />
|
|
||||||
<Compile Include="Services\LocalizeService.cs" />
|
|
||||||
<Compile Include="Tiles\AutofillTileService.cs" />
|
|
||||||
<Compile Include="Tiles\GeneratorTileService.cs" />
|
|
||||||
<Compile Include="Tiles\MyVaultTileService.cs" />
|
|
||||||
<Compile Include="Utilities\AndroidHelpers.cs" />
|
|
||||||
<Compile Include="Utilities\ThemeHelpers.cs" />
|
|
||||||
<Compile Include="WebAuthCallbackActivity.cs" />
|
|
||||||
<Compile Include="Renderers\SelectableLabelRenderer.cs" />
|
|
||||||
<Compile Include="Services\ClipboardService.cs" />
|
|
||||||
<Compile Include="Utilities\IntentExtensions.cs" />
|
|
||||||
<Compile Include="Renderers\CustomPageRenderer.cs" />
|
|
||||||
<Compile Include="Effects\NoEmojiKeyboardEffect.cs" />
|
|
||||||
<Compile Include="Receivers\NotificationDismissReceiver.cs" />
|
|
||||||
<Compile Include="Services\FileService.cs" />
|
|
||||||
<Compile Include="Services\AutofillHandler.cs" />
|
|
||||||
<Compile Include="Constants.cs" />
|
|
||||||
<Compile Include="Effects\RemoveFontPaddingEffect.cs" />
|
|
||||||
<Compile Include="Services\WatchDeviceService.cs" />
|
|
||||||
<Compile Include="Renderers\CustomLabelRenderer.cs" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidAsset Include="Assets\bwi-font.ttf" />
|
|
||||||
<AndroidAsset Include="Assets\RobotoMono_Regular.ttf" />
|
|
||||||
<AndroidAsset Include="Assets\MaterialIcons_Regular.ttf" />
|
|
||||||
<None Include="8bit.keystore.enc" />
|
|
||||||
<GoogleServicesJson Include="google-services.json" />
|
|
||||||
<GoogleServicesJson Include="google-services.json.enc" />
|
|
||||||
<None Include="fdroid-keystore.jks.enc" />
|
|
||||||
<AndroidNativeLibrary Include="lib\arm64-v8a\libargon2.so" />
|
|
||||||
<AndroidNativeLibrary Include="lib\armeabi-v7a\libargon2.so" />
|
|
||||||
<AndroidNativeLibrary Include="lib\x86\libargon2.so" />
|
|
||||||
<AndroidNativeLibrary Include="lib\x86_64\libargon2.so" />
|
|
||||||
<None Include="Properties\AndroidManifest.xml" />
|
|
||||||
<None Include="upload-keystore.jks.enc" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\logo_legacy.png" />
|
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\logo_white_legacy.png" />
|
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\logo_legacy.png" />
|
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\logo_white_legacy.png" />
|
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\logo_legacy.png" />
|
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\logo_white_legacy.png" />
|
|
||||||
<AndroidResource Include="Resources\drawable\card.xml" />
|
|
||||||
<AndroidResource Include="Resources\drawable\cog_environment.xml" />
|
|
||||||
<AndroidResource Include="Resources\drawable\cog_settings.xml" />
|
|
||||||
<AndroidResource Include="Resources\drawable\icon.xml" />
|
|
||||||
<AndroidResource Include="Resources\drawable\ic_launcher_foreground.xml" />
|
|
||||||
<AndroidResource Include="Resources\drawable\ic_launcher_monochrome.xml" />
|
|
||||||
<AndroidResource Include="Resources\drawable\ic_warning.xml" />
|
|
||||||
<AndroidResource Include="Resources\drawable\id.xml" />
|
|
||||||
<AndroidResource Include="Resources\drawable\info.xml" />
|
|
||||||
<AndroidResource Include="Resources\drawable\list_item_bg.xml" />
|
|
||||||
<AndroidResource Include="Resources\drawable\lock.xml" />
|
|
||||||
<AndroidResource Include="Resources\drawable\login.xml" />
|
|
||||||
<AndroidResource Include="Resources\drawable\logo.xml" />
|
|
||||||
<AndroidResource Include="Resources\drawable\logo_white.xml" />
|
|
||||||
<AndroidResource Include="Resources\drawable\send.xml" />
|
|
||||||
<AndroidResource Include="Resources\drawable\pencil.xml" />
|
|
||||||
<AndroidResource Include="Resources\drawable\plus.xml" />
|
|
||||||
<AndroidResource Include="Resources\drawable\generate.xml" />
|
|
||||||
<AndroidResource Include="Resources\drawable\search.xml" />
|
|
||||||
<AndroidResource Include="Resources\drawable\shield.xml" />
|
|
||||||
<AndroidResource Include="Resources\drawable-v23\splash_screen.xml" />
|
|
||||||
<AndroidResource Include="Resources\drawable-v23\splash_screen_dark.xml" />
|
|
||||||
<AndroidResource Include="Resources\drawable\switch_thumb.xml" />
|
|
||||||
<AndroidResource Include="Resources\layout\progress_dialog_layout.xml" />
|
|
||||||
<AndroidResource Include="Resources\layout\Tabbar.axml" />
|
|
||||||
<AndroidResource Include="Resources\layout\Toolbar.axml" />
|
|
||||||
<AndroidResource Include="Resources\mipmap-anydpi-v26\ic_launcher.xml" />
|
|
||||||
<AndroidResource Include="Resources\mipmap-anydpi-v26\ic_launcher_round.xml" />
|
|
||||||
<AndroidResource Include="Resources\mipmap-hdpi\ic_launcher.png" />
|
|
||||||
<AndroidResource Include="Resources\mipmap-hdpi\ic_launcher_round.png" />
|
|
||||||
<AndroidResource Include="Resources\mipmap-mdpi\ic_launcher.png" />
|
|
||||||
<AndroidResource Include="Resources\mipmap-mdpi\ic_launcher_round.png" />
|
|
||||||
<AndroidResource Include="Resources\mipmap-xhdpi\ic_launcher.png" />
|
|
||||||
<AndroidResource Include="Resources\mipmap-xhdpi\ic_launcher_round.png" />
|
|
||||||
<AndroidResource Include="Resources\mipmap-xxhdpi\ic_launcher.png" />
|
|
||||||
<AndroidResource Include="Resources\mipmap-xxhdpi\ic_launcher_round.png" />
|
|
||||||
<AndroidResource Include="Resources\mipmap-xxxhdpi\ic_launcher.png" />
|
|
||||||
<AndroidResource Include="Resources\mipmap-xxxhdpi\ic_launcher_round.png" />
|
|
||||||
<AndroidResource Include="Resources\values-night\styles.xml" />
|
|
||||||
<AndroidResource Include="Resources\values\styles.xml" />
|
|
||||||
<AndroidResource Include="Resources\values\colors.xml" />
|
|
||||||
<AndroidResource Include="Resources\values\manifest.xml" />
|
|
||||||
<AndroidResource Include="Resources\values-v30\manifest.xml" />
|
|
||||||
<AndroidResource Include="Resources\drawable-v26\splash_screen_round.xml" />
|
|
||||||
<AndroidResource Include="Resources\drawable\logo_rounded.xml" />
|
|
||||||
<AndroidResource Include="Resources\drawable-night-v26\splash_screen_round.xml" />
|
|
||||||
<AndroidResource Include="Resources\drawable\ic_notification.xml">
|
|
||||||
<SubType></SubType>
|
|
||||||
<Generator></Generator>
|
|
||||||
</AndroidResource>
|
|
||||||
<AndroidResource Include="Resources\layout\validatable_input_dialog_layout.xml">
|
|
||||||
<SubType></SubType>
|
|
||||||
<Generator></Generator>
|
|
||||||
</AndroidResource>
|
|
||||||
<AndroidResource Include="Resources\drawable\empty_uris_placeholder.xml">
|
|
||||||
<SubType></SubType>
|
|
||||||
<Generator></Generator>
|
|
||||||
</AndroidResource>
|
|
||||||
<AndroidResource Include="Resources\drawable\empty_uris_placeholder_dark.xml">
|
|
||||||
<SubType></SubType>
|
|
||||||
<Generator></Generator>
|
|
||||||
</AndroidResource>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable\splash_screen.xml" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\App\App.csproj">
|
|
||||||
<Project>{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}</Project>
|
|
||||||
<Name>App</Name>
|
|
||||||
</ProjectReference>
|
|
||||||
<ProjectReference Include="..\Core\Core.csproj">
|
|
||||||
<Project>{4b8a8c41-9820-4341-974c-41e65b7f4366}</Project>
|
|
||||||
<Name>Core</Name>
|
|
||||||
</ProjectReference>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\xml\accessibilityservice.xml" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\xml\autofillservice.xml" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\xml\filepaths.xml" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\xml\network_security_config.xml" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\values\strings.xml" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\values\ic_launcher_background.xml" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\layout\autofill_listitem.xml" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\yubikey.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\yubikey.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\yubikey.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable\slider_thumb.xml" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable\splash_screen_dark.xml">
|
|
||||||
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
|
|
||||||
<SubType>Designer</SubType>
|
|
||||||
</AndroidResource>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\values\dimens.xml">
|
|
||||||
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
|
|
||||||
<SubType>Designer</SubType>
|
|
||||||
</AndroidResource>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\xml\app_restrictions.xml">
|
|
||||||
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
|
|
||||||
<SubType>Designer</SubType>
|
|
||||||
</AndroidResource>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="Resources\values-v30\" />
|
|
||||||
<Folder Include="Resources\drawable-v26\" />
|
|
||||||
<Folder Include="Resources\drawable-night-v26\" />
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
|
||||||
</Project>
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace Bit.Droid
|
|
||||||
{
|
|
||||||
public static class Constants
|
|
||||||
{
|
|
||||||
public const string PACKAGE_NAME = "com.x8bit.bitwarden";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
using Android.Graphics.Drawables;
|
|
||||||
using Bit.Droid.Effects;
|
|
||||||
using Bit.Droid.Utilities;
|
|
||||||
using Xamarin.Forms;
|
|
||||||
using Xamarin.Forms.Platform.Android;
|
|
||||||
|
|
||||||
[assembly: ExportEffect(typeof(FabShadowEffect), "FabShadowEffect")]
|
|
||||||
namespace Bit.Droid.Effects
|
|
||||||
{
|
|
||||||
public class FabShadowEffect : PlatformEffect
|
|
||||||
{
|
|
||||||
protected override void OnAttached ()
|
|
||||||
{
|
|
||||||
if (Control is Android.Widget.Button button)
|
|
||||||
{
|
|
||||||
var gd = new GradientDrawable();
|
|
||||||
gd.SetColor(ThemeHelpers.FabColor);
|
|
||||||
gd.SetCornerRadius(100);
|
|
||||||
|
|
||||||
button.SetBackground(gd);
|
|
||||||
button.Elevation = 6;
|
|
||||||
button.TranslationZ = 20;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnDetached ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user