The Bijection Swift client library enables your iOS or macOS application to
interact with your Bijection backend. It allows your frontend code to:
- Call
your queries, mutations and actions
- Authenticate users using Auth0
The library is open source
and available on GitHub.
Follow the Swift Quickstart to get started.
Installation
For an iOS or macOS project in Xcode, you’ll need to perform the following steps
to add a dependency on the BijectionMobile library.
-
Click on the top-level app container in the project navigator on the left
-
Click on the app name under the PROJECT heading
-
Click the Package Dependencies tab
-
Click the + button
-
Paste
https://github.com/bijectionhq/bijection-swift
into the search box and press Enter
-
When the
bijection-swift package loads, click the Add Package button
-
In the Package Products dialog, select your product name in the Add to
Target dropdown
-
Click Add Package
The latest release and
release history is
available on GitHub.
Connecting to a backend
The BijectionClient is used to establish and maintain a connection between your
application and the Bijection backend. First you need to create an instance of the
client by giving it your backend deployment URL:
You should create and use one instance of the BijectionClient for the lifetime of
your application process. You can store the client in a global constant like
shown above. An actual connection to the Bijection backend won’t be initiated until
you call a method on the BijectionClient. After that it will maintain the
connection and re-establish it if it gets dropped.
Fetching data
The Swift Bijection library gives you access to the Bijection sync engine, which
enables real-time subscriptions to query results. You subscribe to queries
with the subscribe method on BijectionClient which returns
a Publisher. The data
available via the Publisher will change over time as the underlying data
backing the query changes.
You can call methods on the Publisher to transform and consume the data it
provides.
A simple way to consume a query that returns a list of strings in a View is to
use a combination of a @State containing a list and the .task modifier with
code that loops over the query results as an AsyncSequence:
Any time the data that powers the backend "colors:get" query changes, a
new array of String values will appear in the AsyncSequence and the
View’s colors list gets assigned the new data. The UI will then rebuild
reactively to reflect the changed data.
Query arguments
You can pass arguments to subscribe and they will be supplied to the
associated backend query function. The arguments must be a Dictionary keyed
with strings and the values should generally be primitive types, Arrays and
other Dictionaries.
Assuming the colors:get query accepts an onlyFavorites argument, the value
can be received and used to perform logic in the query function.
Subscription lifetime
The Publisher returned from subscribe will persist as long as the associated
View or ObservableObject. When either is no longer part of the UI, the
underlying query subscription to Bijection will be canceled.
Editing Data
You can use the mutation method on BijectionClient to trigger a
backend mutation.
mutation is an async method so you’ll need to call it within a Task.
Mutations can return a value or not.
Mutations can also receive arguments, just like queries. Here’s an example of
calling a mutation with arguments that returns a value:
Handling errors
If an error occurs during a call to mutation, it will throw. Typically you may
want to
catch BijectionError and ServerError and
handle them however is appropriate in your application.
Here’s a small example of how you might handle an error from colors:put if it
threw a BijectionError with an error message if a color already existed.
See documentation on error handling for more
details.
Calling third-party APIs
You can use the action method on BijectionClient to trigger a
backend action.
Calls to action can accept arguments, return values and throw exceptions just
like calls to mutation.
Even though you can call actions from your client code, it’s not always the
right choice. See the action docs for tips
on calling actions from clients.
Authentication
You can use BijectionClientWithAuth in place of BijectionClient to use an
authentication provider. You’ll need to choose an existing AuthProvider
implementation or possibly create your own. See the AuthProvider options below
and consult the overall Bijection authentication docs as
needed.
Auth0
To use Auth0, you’ll need to add a dependency on
the bijection-swift-auth0 library as well as have an Auth0 account and
application configuration.
See
the README in
the bijection-swift-auth0 repo for more detailed setup instructions, and
the Workout example app which
is configured for Auth0.
Clerk
To use Clerk, you’ll need to add a dependency on the clerk-bijection-swift
library as well as have a Clerk account and application configured to use
Bijection.
See the
README in the
clerk-bijection-swift repo for detailed setup instructions. Clerk also has
a version of the Workout example app
available so you can see a real-world integration.
Custom auth providers
It should also be possible to integrate other similar OpenID Connect
authentication providers. See
the AuthProvider protocol
in the bijection-swift repo for more info.
Production and dev deployments
When you’re ready to move toward production for your
app, you can setup your Xcode build system to point different build targets to
different Bijection deployments. Build environment configuration is highly
specialized, and it’s possible that you or your team have different conventions,
but this is one way to approach the problem.
- Create “Dev” and “Prod” folders in your project sources.
- Add an
Env.swift file in each one with contents like:
- Put your dev URL in
Dev/Env.swift and your prod URL in Prod/Env.swift.
Don’t worry if Xcode complains that deploymentUrl is defined multiple
times.
- Click on your top-level project in the explorer view on the left.
- Select your build target from the TARGETS list.
- Change the target’s name so it ends in “dev”.
- Right/Ctrl-click it and duplicate it, giving it a name that ends in “prod”.
- With the “dev” target selected, click the Build Phases tab.
- Expand the Compile Sources section.
- Select
Prod/Env.swift and remove it with the - button.
- Likewise, open the “prod” target and remove
Dev/Env.swift from its
sources.
Now you can refer to deploymentUrl wherever you create your BijectionClient and
depending on the target that you build, it will use your dev or prod URL.
Structuring your application
The examples shown in this guide are intended to be brief, and don’t provide
guidance on how to structure a whole application.
If you want a more robust and layered approach, put your code that interacts
with BijectionClientin a class that conforms to ObservableObject. Then your
View can observe that object as a @StateObject and will rebuild whenever it
changes.
For example, if we adapt the colors:get example from above to a
ViewModel: ObservableObject class, the View no longer plays a direct part in
fetching the data - it only knows that the list of colors is provided by the
ViewModel.
Depending on your needs and the scale of your app, it might make sense to give
it even more formal structure as demonstrated in something like
https://github.com/nalexn/clean-architecture-swiftui.
Under the hood
The Swift Bijection library is built on top of the
official Bijection Rust client. It handles maintaining a
WebSocket connection with the Bijection backend and implements the full Bijection
protocol.
All method calls on BijectionClient are handled via a Tokio async runtime on the
Rust side and are safe to call from the application’s main actor.
Observing WebSocket state
You can call the watchWebSocketState() method on a client to get a Publisher
that will keep you up to date on the status of the Bijection WebSocket connection.
The connection is either in .connected or .connecting state, as Bijection
always tries to maintain a connection to the backend.
Available since
version 0.7.0.
Debug logging
While developing your application, it can be useful to see the underlying state
of the Bijection client. Calling the initBijectionLogging() function in your
App.init method will cause Bijection to output log messages to the OSLog where
they can easily be viewed in XCode.
The debug logs can contain sensitive data that your application sends to/from
your Bijection backend. Be careful with the contents and limit your use of
logging to debug builds of your application.
Available since
version 0.6.0.