Swiftagram 是一个用于 Instagram 私有 API 的封装库,完全使用(现代)Swift 编写。
Instagram 的官方 API,包括 Instagram Basic Display API 和 Instagram Graph API,仅适用于创作者和商业帐户,要么缺乏对最基本功能的支持,要么限制了其在较小范围的用户配置文件中的可用性。
为了绕过这些限制,Swiftagram 依赖于 Android 和 iOS 官方 Instagram 应用内部使用的 API,不需要 API 令牌,并且允许重现用户可以执行的几乎任何操作。请记住,由于这些 API 未经授权用于外部使用,因此您需要自行承担使用风险。
什么是 SwiftagramCrypto?
依赖加密通常需要特定的披露,例如,在提交到 App Store 时。
尽管 Swiftagram,和所有依赖未经授权的第三方 API 的库一样,不能被认为是 App Store 安全的,但我们仍然重视将所有依赖于 加密 的内容分离到它自己的目标库中,我们称之为 SwiftagramCrypto。请记住,像 BasicAuthenticator
这样的功能,一个非可视化的 Authenticator
,或者 KeychainStorage
,存储 Secret
的安全和首选方式,甚至是在你的 feed 上发布内容和上传故事的功能,都是 SwiftagramCrypto 独有的。
请查看 文档 以了解更多信息。
您可以在每个 版本 下直接找到所有变更日志,如果您想收到未来版本的通知,请不要忘记订阅我们的 Telegram 频道。
下一步是什么?
里程碑,问题,以及 WIP 仪表板,是了解当前开发状态的最佳方式。
欢迎通过发送 pull request 来做出贡献。请记住事先参考我们的 指南 和 行为准则。
File
/Swift Packages
/Add Package Dependency…
。https://github.com/sbertix/Swiftagram.git
。为什么不使用 CocoaPods、Carthage 或
空白?
支持多个依赖管理器会使维护一个库变得极其复杂和耗时。
此外,随着 Swift Package Manager 集成到 Xcode 11 及更高版本中,我们预计对替代解决方案的需求会迅速减弱。
Swiftagram 依赖于 ComposableRequest,一个最初集成在 Swiftagram 中的 HTTP 客户端。
它开箱即用地支持 Combine
Publisher
和通过 ComposableStorage 缓存 Secret
。
SwiftagramCrypto 依赖于 Swiftchain 和 SwCrypt 的一个分支,可以与 Swiftagram 一起导入以扩展其功能,访问更安全的 KeychainStorage
和加密的 Endpoint
(例如 Endpoint.Friendship.follow
、Endpoint.Friendship.unfollow
)。
查看我们的 示例 或访问(自动生成)文档,了解 Swiftagram 和 SwiftagramCrypto 的用例。
身份验证通过遵循 Authenticator
协议来提供,该协议在成功后返回一个 Secret
,其中包含签署 Endpoint
请求所需的所有 cookie。
该库带有两个具体的实现。
Authenticator.Group.Visual
是一个基于可视化的 Authenticator
,依靠 WKWebView
来登录用户。由于它基于 WebKit
,因此仅适用于 iOS 11(及以上)和 macOS 10.13(及以上)。
import UIKit
import Swiftagram
/// A `class` defining a view controller capable of displaying the authentication web view.
class LoginViewController: UIViewController {
/// The completion handler.
var completion: ((Secret) -> Void)? {
didSet {
guard oldValue == nil, let completion = completion else { return }
// Authenticate.
DispatchQueue.main.asyncAfter(deadline: .now()) {
// We're using `Authentication.keyhcain`, being encrypted,
// but you can rely on different ones.
Authenticator.keychain
.visual(filling: self.view)
.authenticate()
.sink(receiveCompletion: { _ in }, receiveValue: completion)
.store(in: &self.bin)
}
}
}
/// The dispose bag.
private var bin: Set<AnyCancellable> = []
}
然后,您只需初始化它并分配一个 completion
处理程序即可使用它。
let controller = LoginViewController()
controller.completion = { _ in /* do something */ }
// Present/push the controller.
Authenticator.Group.Basic
是一个基于代码的 Authenticator
,支持 2FA,在 SwiftagramCrypto 中定义:您只需要一个用户名和密码就可以开始了。
import SwiftagramCrypto
/// A retained dispose bag.
/// **You need to retain this.**
private var bin: Set<AnyCancellable> = []
// We're using `Authentication.keyhcain`, being encrypted,
// but you can rely on different ones.
Authenticator.keychain
.basic(username: /* username */,
password: /* password */)
.authenticate()
.sink(receiveCompletion: {
switch $0 {
case .failure(let error):
// Deal with two factor authentication.
switch error {
case Authenticator.Error.twoFactorChallenge(let challenge):
// Once you receive the challenge,
// ask the user for the 2FA code
// then just call:
// `challenge.code(/* the code */).authenticate()`
// and deal with the publisher.
break
default:
break
}
default:
break
}
},
receiveValue: { _ in /* do something */ })
.store(in: &self.bin)
}
Secret
的缓存通过其符合 ComposableStorage 的 Storable
协议来提供。
该库带有几个 Storage
的具体实现。
TransientStorage
,并且在未提供 Storage
时,Authenticator
默认使用它。UserDefaultsStorage
允许更快、开箱即用的测试,但不建议用于生产,因为私有 cookie 未加密。KeychainStorage
,是 ComposableRequestCrypto 的一部分,(首选)将其安全地存储在用户的钥匙串中。如何绕过 Instagram 的“垃圾邮件”过滤器,并让他们相信我实际上不是一个机器人?
在旧版本的 Swiftagram 中,我们允许用户设置发送请求和其实际分发之间的延迟。这最终只会减慢实现速度,而对防止滥用几乎没有任何作用。
从 5.0
开始,我们现在直接将 URLSession
暴露给最终用户,因此您可以构建自己的实现。
Swiftagram 定义了一个 static
URLSession
(URLSession.instagram
) ,一次获取一个资源。依赖于此是处理 Instagram“垃圾邮件”过滤器的首选方法。
// A valid secret.
let secret: Secret = /* the authentication response */
// A **retained** collection of cancellables.
var bin: Set<AnyCancellable> = []
// We're using a random endpoint to demonstrate
// how `URLSession` is exposed in code.
Endpoint.user(secret.identifier)
.unlock(with: secret)
.session(.instagram) // `URLSession.instagram`
.sink(receiveCompletion: { _ in }, receiveValue: { print($0) })
.store(in: &bin)
如何取消正在进行的请求?
一旦您拥有一个流 Cancellable
,只需在其上调用 cancel
或清空 bin
。
// A valid secret.
let secret: Secret = /* the authentication response */
// A **retained** collection of cancellables.
var bin: Set<AnyCancellable> = []
// We're using a random endpoint to demonstrate
// how `Deferrable` is exposed in code.
Endpoint.user(secret.identifier)
.unlock(with: secret)
.session(.instagram)
.sink(receiveCompletion: { _ in }, receiveValue: { print($0) })
.store(in: &bin)
// Cancel it.
bin.removeAll()
如何处理分页和分页偏移量?
很简单。假设您正在获取一个实际上可以分页的资源……
// A valid secret.
let secret: Secret = /* the authentication response */
// A **retained** collection of cancellables.
var bin: Set<AnyCancellable> = []
// We're using a random endpoint to demonstrate
// how `PagerProvider` is exposed in code.
Endpoint.user(secret.identifier)
.posts
.unlock(with: secret)
.session(.instagram)
.pages(.max, delay: 1) // Exhaust all with `.max`
// or pass any `Int` to limit
// pages.
// You can omit `delay`, in that
// case pages will be fetched
// one immediately after the other.
.sink(receiveCompletion: { _ in }, receiveValue: { print($0) })
.store(in: &bin)
PagerProvider
还支持 offset
,即传递给其第一次迭代的值,以及某些情况下的 rank
(令牌),两者都作为 pages(_:offset:)
/pages(_:offset:rank:)
方法中的可选参数。
非常感谢所有为 TheM4hd1/SwiftyInsta, dilame/instagram-private-api 和 ping/instagram_private_api 做出贡献的人,感谢他们的灵感和对开源社区的宝贵服务,没有他们,今天可能就不会有 Swiftagram 了。