1
0
mirror of https://github.com/bitwarden/mobile synced 2025-12-05 23:53:33 +00:00

Compare commits

...

2 Commits

Author SHA1 Message Date
mpbw2
b543459f8d Link to legacy docs in readme (#3472) 2025-05-14 19:06:43 +01:00
mpbw2
d0609024df Adding legacy contributing docs (#3471)
Co-authored-by: Matt Portune <mportune@macbook-work.lan>
2025-05-14 12:01:24 -04:00
13 changed files with 1117 additions and 1 deletions

View File

@@ -16,7 +16,7 @@ The Bitwarden mobile application is written in C# using .NET MAUI.
# Build/Run
Please refer to the [Mobile section](https://contributing.bitwarden.com/getting-started/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!

View 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
```

View 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)

View 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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 KiB

View 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
![Android SDK configuration](android-sdk.png)
## 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>

View 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.

View 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, youre 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 dont 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
![](./run-debug.png)
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)).
![](./troubleshoot-generic-simulator.png)
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)
![Example configuration for new Xcode project](./new-project-options.png)
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
![Example configuration for Signing & Capabilities screen](./signing-and-capabilities.png)
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 doesnt have direct access to your Macs 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, youll have to also modify the `Info.plist` to allow http for testing purposes as
explained before on the simulator testing.
Its 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 Macs 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 doesnt 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, youll 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
Macs 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. Dont 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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View 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.