Files
README.md

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 #

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; reconnects on its own and flags a resumed connection so the caller can reload once)
Recommendations getRecommendations, getPopular
Lists getList, getLists
Bookmarks getBookmarks
Episode State getEpisodeStates
Profiles getProfile
Blocks getBlocks
Catalog resolveEpisodeSubject, resolvePodcastSubject, getEpisodeRecord, getPodcastRecord, describeLexicons
Notifications listNotifications, updateNotificationsSeen, getNotificationPreferences, setNotificationPreferences, registerDevice, unregisterDevice
Boosts getBoosts
Podcast Search searchPodcasts
Podcast Metadata getPodcast, getEpisodes, getEpisode, getTrending, getCategories, getRecentEpisodes

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.subscription
  • xyz.effem.feed.comment, xyz.effem.feed.commentLike, xyz.effem.feed.threadgate
  • xyz.effem.feed.episodeState
  • xyz.effem.feed.boost
  • xyz.effem.feed.recommendation
  • xyz.effem.feed.bookmark
  • xyz.effem.feed.list
  • xyz.effem.graph.block, xyz.effem.moderation.report
  • xyz.effem.actor.profile

Documentation #

Build the DocC documentation:

swift package generate-documentation

Or in Xcode: Product > Build Documentation.

Testing #

swift test

48 tests across 15 suites covering model decoding, round-tripping, serialization, websocket event decoding, the subscription's reconnect against a scripted transport, and record shapes.