Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/tootsdk/tootsdk

Cross-platform Swift library for Mastodon and the fediverse
https://github.com/tootsdk/tootsdk

mastodon pixelfed pleroma swift

Last synced: about 1 month ago
JSON representation

Cross-platform Swift library for Mastodon and the fediverse

Awesome Lists containing this project

README

        

# TootSDK

Cross-platform Swift library for the Mastodon API


Swift 5.7
Platforms
BSD 3-clause
Build Status

TootSDK is a framework for Mastodon and the Fediverse, for iOS. It provides a toolkit for authorizing a user with an instance, and interacting with their posts.

TootSDK is a community developed SDK for Mastodon and the Fediverse.
It is designed to work with all major servers (Mastodon, Pleroma, PixelFed etc).

You can use TootSDK to build a client for Apple operating systems, or Linux with Vapor.

![overview of how TootSDK integrates with fedi platforms](https://raw.githubusercontent.com/TootSDK/TootSDK/main/media/overview.png)

## Why make TootSDK?

When app developers build apps for Mastodon and the Fediverse, every developer ends up having to solve the same set of problems when it comes to the API and data model.

[Konstantin](https://social.headbright.eu/@konstantin) and [Dave](https://social.lightbeamapps.com/@dave) decided to share this effort.

TootSDK is a shared Swift Package that any client app can be built on.

## Key Principles ⚙️

- Async/Await based. All asynchronous functions are defined as Async ones that you can use with Async/Await code
- Internal consistency and standardization of model property names
- Standardization across all supported Fediverse APIs
- Platform agnostic (TootSDK shouldn't care if it's on iOS, macOS or Linux!)

Please don't hesitate to open an issue or create a PR for features you need 🙏

## Quick start 🏁

It's easy to get started with TootSDK.

- Add TootSDK to your project via Swift Package Manager: `https://github.com/TootSDK/TootSDK`

- Instantiate with an instanceURL and accessToken:

```swift
let instanceURL = URL(string: "social.yourinstance.com")
let client = try await TootClient(connect: instanceURL, accessToken: "USERACCESSTOKEN")
```

### Signing in (for macOS and iOS):

Network Sandbox Capability/Entitlement (macOS)

When using TootSDK within a macOS target you will need to enable the `com.apple.security.network.client` entitlement in your entitlements file or within the **Signing & Capabilities** tab in Xcode.

```
com.apple.security.network.client

```

![Xcode target view showing the Signing & Capabilities tab with and arrow pointing to a checked Outgoing Connections (Client) option](media/network_sandbox_capability_entitlement.png)

- Instantiate your client without a token:

```swift
let client = try await TootClient(connect: url)
```

- Use the sign in helper we've created based on [`ASWebAuthenticationSession`](https://developer.apple.com/documentation/authenticationservices/aswebauthenticationsession):

```swift
let client = try await TootClient(connect: url)

guard let accessToken = try await client.presentSignIn(callbackURI: callbackURI) else {
// handle failed sign in
return
}
```

That's it 🎉!

We recommend keeping the accessToken somewhere secure, for example the Keychain.

### Signing in (all platforms):

- Instantiate your client without a token:

```swift
let client = try await TootClient(connect: instanceURL)
```

- Retrieve an authorization URL to present to the user (so they can sign in)

```swift
let authURL = client.createAuthorizeURL(callbackURI: "myapp://someurl")
```

- Present the the authorization URL as a web page
- Let the user sign in, and wait for the callbackURI to be called
- When that callbackURI is called, give it back to the client to collect the token

```swift
let accessToken = client.collectToken(returnUrl: url, callbackURI: callbackURI)
```

We recommend keeping the accessToken somewhere secure, for example the Keychain.

## Usage and key concepts

Once you have your client connected, you're going to want to use it. Our example apps and reference docs will help you get into the nitty gritty of it all, but some key concepts are highlighted here.

Accessing a user's timeline

There are several different types of timeline in TootSDK that you can access, for example their home timeline, the local timeline of their instance, or the federated timeline. These are all enumerated in the `Timeline` enum.

You can retrieve the latest posts (up to 40 on Mastodon) with a call like so:

```swift
let items = try await client.getTimeline(.home)
let posts = items.result
```

TootSDK returns Posts, Accounts, Lists and DomainBblocks as `PagedResult`. In our code, `items` is a PagedResult struct. It contains a property called `result` which will be the type of data request (in this case an array of `Post`).

Paging requests

Some requests in TootSDK allow pagination in order to request more information. TootSDK can request a specific page using the `PagedInfo` struct and handles paginaged server responses using the `PagedResult` struct.

PagedInfo has the following properties:

- maxId (Return results older than ID)
- minId (Return results immediately newer than ID)
- sinceId (Return results newer than ID)

So for example, if we want all posts from the user's home timeline that are newer than post ID 100, we could write:

```swift
let items = try await client.getTimeline(.home, PagedInfo(minId: 100))
let posts = items.result
```

Paged requests also deliver a PagedInfo struct as a property of the `PagedResult` returned, which means you can use that for subsequent requests of the same type.

```swift

var pagedInfo: PagedInfo?
var posts: [Post] = []

func retrievePosts() async {
let items = try await client.getTimeline(.home, pagedInfo)
posts.append(contentsOf: items.result)
self.pagedInfo = items.pagedInfo
}

```

TootSDK implements several facilities to make it easier to iterate over multiple pages using the `hasPrevious`, `hasNext`, `previousPage` and `nextPage` properties of `PagedResult`:

```swift
var pagedInfo: PagedInfo? = nil
var hasMore = true
let query = TootNotificationParams(types: [.mention])

while hasMore {
let page = try await client.getNotifications(params: query, pagedInfo)
for notification in page.result {
print(notification.id)
}
hasMore = page.hasPrevious
pagedInfo = page.previousPage
}
```

⚠️ Different fediverse servers handle pagination differently and so there is no guarantee that `hasPrevious` or `hasNext` can correctly interpret the server response in all cases.

You can learn more about how pagination works for Fediverse servers using a Mastodon compatible API [here](https://docs.joinmastodon.org/api/guidelines/#pagination).

Streaming timelines

In TootSDK 4.0, experimental support for streaming timelines was introduced. It allows an app to subscribe for one or more available timelines in order to receive events as they happen instead of polling the server.

```swift
// open a socket to a specific timeline
let stream = try! await client.streaming.subscribe(to: .userNotification)

do {
// listen for events
for try await event in stream {
print("got event")
switch event {
case .connectionUp:
//...
case .connectionDown:
//...
case .receivedEvent(let eventContent):
//...
}
}
} catch {
print(String(describing: error))
}
```

An example of subscribing to a timeline is available in [StreamEvents](Examples/swiftyadmin/Sources/swiftyadmin/Streams/StreamEvents.swift)

You can learn more about Streaminng event support for Mastodon [here](https://docs.joinmastodon.org/methods/streaming/).

You can learn more about Pleroma's implementation of streaming [here](https://api.pleroma.social/#operation/WebsocketHandler.streaming).

Creating an account

- Register the app with the following scopes `["read", "write:accounts"]`.

- Get instance information and determine the sign up requirements. Some instances may not be open for registration while others may require additional verification.

```swift
let instance = try await client.getInstanceInfo()
if instance.registrations == false {
// instance not open for registration
return
}
// ...
```

- Use the `registerAccount` method to create a user account:

```swift
let params = RegisterAccountParams(
username: name, email: email, password: password, agreement: true, locale: "en")
let token = try await client.registerAccount(params: params)
```

## Further Documentation 📖

- Reference documentation is available [here](https://tootsdk.github.io/TootDocs/?v=2)
- Examples:
- [swiftyadmin](https://github.com/TootSDK/TootSDK/tree/main/Examples/swiftyadmin) - a command line utility to interact with and control a server using TootSDK
- [tootsdk-release](https://github.com/TootSDK/TootSDK/tree/main/Examples/tootsdk-release) - Example GitHub action to publish a post when a new release is published.

## Contributing

### Code of Conduct and Contributing rules 🧑‍⚖️

- Our guide to contributing is available here: [CONTRIBUTING.md](CONTRIBUTING.md).
- All contributions, pull requests, and issues are expected to adhere to our community code of conduct: [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md).

## License 📃

TootSDK is licensed with the BSD-3-Clause license, more information here: [LICENSE.MD](LICENSE.md)

This is a permissive license which allows for any type of use, provided the copyright notice is included.

## Acknowledgements 🙏

- The Mastodon API documentation https://github.com/mastodon/documentation
- We hat-tip top Metatext's source for some guidance on what's where: https://github.com/metabolist/metatext
- [Kris Slazinski](https://mastodon.social/@kslazinski) for our TootSDK logo 🤩

## Built with TootSDK

- [Fedicat](https://fedicat.com/)
- [Pipilo](https://apps.apple.com/pl/app/pipilo/id1584544719)
- [TootyGraph](https://github.com/samscam/tootygraph)
- [Topiary](https://lightbeamapps.com/topiary/)
- [TootLater](https://tootlater.kruschel.dev/)
- [Oxpecker](https://oxpecker.social)