EffemKit #
Swift client for the Effem AT Protocol AppView. Provides social podcasting features — subscriptions, comments, recommendations, bookmarks, curated lists, and profiles — built on the AT Protocol.
Requirements #
- Swift 6.2+
- iOS 26+ / macOS 26+ / watchOS 26+ / tvOS 26+
Dependencies #
- CoreATProtocol — AT Protocol primitives, OAuth, networking
Installation #
Add EffemKit as a dependency in your Package.swift:
dependencies: [
.package(url: "https://tangled.org/@sparrowtek.com/EffemKit", branch: "main"),
]
Or add it as a local package in Xcode.
Quick Start #
1. Configure the AppView #
import EffemKit
import CoreATProtocol
// At app launch
Task { @APActor in
setup(appViewHost: "https://appview.effem.xyz")
}
2. Read Data #
@APActor
func loadTrending() async throws -> [PodcastResult] {
let service = EffemService()
let response = try await service.getTrending(max: 20)
return response.feeds
}
3. Write Data (requires authentication) #
@APActor
func subscribe(feedId: Int, userDID: String) async throws {
// Resolve the podcast's canonical catalog strongRef first so every
// subscriber points at the same catalog row.
let resolution = try await EffemService().resolvePodcastSubject(feedId: feedId)
_ = try await EffemRepoService().subscribe(to: resolution.subject, repo: userDID)
}
Architecture #
EffemKit uses a two-router architecture:
| Path | Service | Target | Auth Required |
|---|---|---|---|
| Read | EffemService |
Effem AppView | No |
| Write | EffemRepoService |
User's PDS | Yes |
Reads go to the Effem AppView, which indexes social records from the AT Protocol firehose and proxies Podcast Index metadata enriched with social overlay data.
Writes go to the authenticated user's PDS using standard com.atproto.repo.createRecord / deleteRecord calls. The AppView picks up new records asynchronously via the firehose.
All public API is isolated to @APActor for thread safety.
API Overview #
EffemService (Read) #
| Category | Methods |
|---|---|
| Subscriptions | getSubscriptions, getSubscribers |
| Comments | getComments, getCommentThread, subscribeComments (live websocket) |
| Recommendations | getRecommendations, getPopular |
| Lists | getList, getLists |
| Bookmarks | getBookmarks |
| Episode State | getEpisodeStates |
| Inbox | getInbox |
| Profiles | getProfile |
| Blocks | getBlocks |
| Catalog | resolveEpisodeSubject, resolvePodcastSubject, getEpisodeRecord, getPodcastRecord, describeLexicons |
| Notifications | listNotifications, updateNotificationsSeen, getNotificationPreferences, setNotificationPreferences, registerDevice, unregisterDevice |
| Podcast Search | searchPodcasts, searchEpisodes |
| Podcast Metadata | getPodcast, getEpisodes, getEpisode, getTrending, getCategories, getRecentEpisodes, getStats |
EffemRepoService (Write) #
| Category | Methods |
|---|---|
| Subscriptions | subscribe, unsubscribe |
| Comments | postComment, deleteComment, likeComment, unlikeComment, setThreadgate, deleteThreadgate |
| Episode State | putEpisodeState, deleteEpisodeState |
| Boosts | boost |
| Blocks & Reports | blockUser, unblockUser, reportContent |
| Recommendations | recommend, unrecommend |
| Bookmarks | bookmark, removeBookmark |
| Lists | createList, deleteList |
| Profile | updateProfile |
Lexicon Namespace #
All Effem records use the xyz.effem.* namespace:
xyz.effem.feed.subscriptionxyz.effem.feed.comment,xyz.effem.feed.commentLike,xyz.effem.feed.threadgatexyz.effem.feed.episodeStatexyz.effem.feed.boostxyz.effem.feed.recommendationxyz.effem.feed.bookmarkxyz.effem.feed.listxyz.effem.graph.block,xyz.effem.moderation.reportxyz.effem.actor.profile
Documentation #
Build the DocC documentation:
swift package generate-documentation
Or in Xcode: Product > Build Documentation.
Testing #
swift test
38 tests across 12 suites covering model decoding, round-tripping, serialization, websocket event decoding, and record shapes.