🕊️
[Swift]AT ProtocolでBlueskyにポストする
AT Protocol・Blueskyとは
AT Protocol: 分散型SNSのプロトコル
Bluesky: AT Protocolを使用したサービス
※現在、Blueskyはprivate betaとなっており、招待コードが必要です。
投稿の流れ
1. セッションを作成
func createSession(identifier: String, password: String, completion: @escaping (Bool) -> Void) {
let url = URL(string: "https://bsky.social/xrpc/com.atproto.server.createSession")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let body: [String: Any] = ["identifier": identifier, "password": password]
request.httpBody = try? JSONSerialization.data(withJSONObject: body, options: [])
let task = URLSession.shared.dataTask(with: request) { data, _, error in
guard let data = data, error == nil else {
completion(false)
return
}
if let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
let jwt = json["accessJwt"] as? String
{
self.accessJwt = jwt
completion(true)
} else {
completion(false)
}
}
task.resume()
}
2. 投稿の処理
func createPost(text: String, completion: @escaping (Bool) -> Void) {
guard let jwt = accessJwt else {
completion(false)
return
}
let url = URL(string: "https://bsky.social/xrpc/com.atproto.repo.createRecord")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("Bearer \(jwt)", forHTTPHeaderField: "Authorization")
let now = ISO8601DateFormatter().string(from: Date())
let post: [String: Any] = ["$type": "app.bsky.feed.post", "text": text, "createdAt": now]
let body: [String: Any] = ["repo": identifier, "collection": "app.bsky.feed.post", "record": post]
request.httpBody = try? JSONSerialization.data(withJSONObject: body, options: [])
let task = URLSession.shared.dataTask(with: request) { data, _, error in
guard data != nil, error == nil else {
completion(false)
return
}
completion(true)
}
task.resume()
}
3. 投稿
import SwiftUI
struct ContentView: View {
@EnvironmentObject var atProtocolManager: ATProtocolManager
@State var identifier = "ryodeveloper.bsky.social"
@State var password = "パスワード"
@State var text = ""
var body: some View {
NavigationView {
Form {
Section("Settings") {
TextField("identifier", text: $identifier)
TextField("password", text: $password)
}
Section {
TextEditor(text: $text)
.frame(minHeight: 240)
} header: {
Text("Content")
} footer: {
Text("\(text.count) / 300")
.foregroundColor(text.count > 300 ? .red : Color(.secondaryLabel))
.frame(maxWidth: .infinity, alignment: .trailing)
}
}
.navigationTitle("ATProtocolClient")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("Post") {
atProtocolManager.createSession(identifier: identifier, password: password) { _ in
}
atProtocolManager.createPost(text: text) { _ in
}
}
.disabled(text.isEmpty || text.count > 300)
}
}
}
}
}
Discussion