Header


Swift codecov Telegram
iOS macOS tvOS watchOS


Swiftagram 是一个用于 Instagram 私有 API 的封装库,完全使用(现代)Swift 编写。

Instagram 的官方 API,包括 Instagram Basic Display APIInstagram Graph API,仅适用于创作者商业帐户,要么缺乏对最基本功能的支持,要么限制了其在较小范围的用户配置文件中的可用性。

为了绕过这些限制,Swiftagram 依赖于 Android 和 iOS 官方 Instagram 应用内部使用的 API,不需要 API 令牌,并且允许重现用户可以执行的几乎任何操作。请记住,由于这些 API 未经授权用于外部使用,因此您需要自行承担使用风险。


什么是 SwiftagramCrypto

依赖加密通常需要特定的披露,例如,在提交到 App Store 时。

尽管 Swiftagram,和所有依赖未经授权的第三方 API 的库一样,不能被认为是 App Store 安全的,但我们仍然重视将所有依赖于 加密 的内容分离到它自己的目标库中,我们称之为 SwiftagramCrypto。请记住,像 BasicAuthenticator 这样的功能,一个非可视化的 Authenticator,或者 KeychainStorage,存储 Secret 的安全和首选方式,甚至是在你的 feed 上发布内容和上传故事的功能,都是 SwiftagramCrypto 独有的。

请查看 文档 以了解更多信息。

状态

push GitHub release (latest by date)

您可以在每个 版本 下直接找到所有变更日志,如果您想收到未来版本的通知,请不要忘记订阅我们的 Telegram 频道

下一步是什么?

里程碑问题,以及 WIP 仪表板,是了解当前开发状态的最佳方式。

欢迎通过发送 pull request 来做出贡献。请记住事先参考我们的 指南行为准则

安装

Swift Package Manager(Xcode 11 及以上)

  1. 从菜单中选择 File/Swift Packages/Add Package Dependency…
  2. 粘贴 https://github.com/sbertix/Swiftagram.git
  3. 按照步骤操作。
  4. SwiftagramCryptoSwiftagram 一起添加以获得完整的体验。

为什么不使用 CocoaPods、Carthage 或 空白

支持多个依赖管理器会使维护一个库变得极其复杂和耗时。
此外,随着 Swift Package Manager 集成到 Xcode 11 及更高版本中,我们预计对替代解决方案的需求会迅速减弱。

目标

用法

查看我们的 示例 或访问(自动生成)文档,了解 SwiftagramSwiftagramCrypto 的用例。

身份验证

身份验证通过遵循 Authenticator 协议来提供,该协议在成功后返回一个 Secret,其中包含签署 Endpoint 请求所需的所有 cookie。

该库带有两个具体的实现。

基于 WebView

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 的缓存通过其符合 ComposableStorageStorable 协议来提供。

该库带有几个 Storage 的具体实现。

请求

如何绕过 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-apiping/instagram_private_api 做出贡献的人,感谢他们的灵感和对开源社区的宝贵服务,没有他们,今天可能就不会有 Swiftagram 了。