MagicBell 2.0.0

MagicBell 2.0.0

Ullrich SchäferHana Mohan 维护。



 
依赖项
Harmony= 2.0.0
Ably= 1.2.27
 

MagicBell 2.0.0

MagicBell iOS SDK

这是官方的 MagicBell iOS SDK。

此 SDK 提供

  • 实时更新
  • 对 MagicBell API 的低级别封装
  • 支持 Combine 框架

它需要

  • iOS 12.0+
  • Swift 5.3+
  • Xcode 12+

快速入门

首先,从您的 MagicBell 仪表板 中获取您的 API 密钥。然后,初始化客户端并设置当前用户

import MagicBell

// Create the MagicBell client with your project's API key
let client = MagicBellClient(apiKey: "[MAGICBELL_API_KEY]")

// Set the MagicBell user
let user = client.connectUser(email: "[email protected]")

// Create a store of notifications
let store = user.store.build()

// Fetch the first page of notifications
store.fetch { result in
    if let notifications = try? result.get() {
        // Print the unread count
        print("Count: \(store.unreadCount)")

        // Print the fetched notifications
        print("notifications: \(notifications)")
    }
}

此存储库还包含一个完整的示例。要运行项目

  • 克隆存储库
  • Example 目录运行 pod install
  • 在 Xcode 中打开 Example 项目
  • 运行 Example 项目目标

目录

安装

CocoaPods

要使用 CocoaPods 安装 MagicBell,请将以下条目添加到您的 Podfile

pod 'MagicBell', '>=2.0.0'

重要:请确保您在 Podfile 中指定 use_frameworks!

然后,运行 pod install

Swift 包管理器

要使用 Swift 包管理器 安装 MagicBell,只需将依赖项按如下方式添加到您的项目中

dependencies: [
    .package(url: "https://github.com/magicbell-io/magicbell-swift", .upToNextMajor(from: "2.0.0"))
]

Carthage

要使用 Carthage 安装 MagicBell,请将以下依赖项添加到 Carfile 中

github "magicbell-io/magicbell-swift" "2.0.0"

然后,运行 carthage update --use-xcframeworks --platform [iOS|macOS] --no-use-binaries(选择所需平台)以解决依赖关系。

MagicBell.xcframework 添加到您的项目关联的框架中,连同 Carthage 解决的其他依赖项。

MagicBell 客户端

第一步是创建一个 MagicBellClient 实例。它将为您管理用户和其他功能。需要您的 MagicBell 项目的 API 密钥来初始化它。

let magicbell = MagicBellClient(apiKey: "[MAGICBELL_API_KEY]")

初始化客户端时,您可以提供额外的选项

let magicbell = MagicBellClient(
    apiKey: "[MAGICBELL_API_KEY]"
    logLevel: .debug
)
参数 默认值 描述
apiKey - 您的 MagicBell API 密钥
apiSecret nil 您的 MagicBell API 秘密
logLevel .none 将其设置为 .debug 以启用日志

尽管 API 键旨在公开发布,但您不应分发 API 密钥。相反,为您的项目启用 HMAC,并在分发您的应用程序之前在您的后端生成用户密钥。

集成到您的应用中

应在应用程序中尽早创建客户端实例,并确保在整个应用程序中只有一个实例。

import MagicBell

// Store the instance at a place of your convenience
let magicbell = MagicBellClient(apiKey: "[MAGICBELL_API_KEY]")

可选地,您可以将唯一的实例分配给 MagicBellClient 内部的静态共享实例。

import MagicBell

extension MagicBellClient {
    static var shared = MagicBellClient(apiKey: "[MAGICBELL_API_KEY]")
}

用户

请求MagicBell API需要您识别MagicBell用户。这可以通过在MagicBellClient实例上调用connectUser(...)方法,并使用用户的电子邮件或外部ID来实现

// Identify the user by its email
let user = magicbell.connectUser(email: "[email protected]")

// Identify the user by its external id
let user = magicbell.connectUser(externalId: "001")

// Identify the user by both, email and external id
let user = magicbell.connectUser(email: "[email protected]", externalId: "001")

每个connectUser变体都支持一个可选的hmac参数,当项目启用了HMAC安全时应该发送该参数。

您可以连接所需数量的用户。

重要: User实例是单例。因此,使用相同参数调用connectUser方法将返回相同用户

let userOne = magicbell.connectUser(email: "[email protected]")
let userTwo = magicbell.connectUser(email: "[email protected]")

assert(userOne === userTwo, "Both users reference to the same instance")

多用户支持

如果您的应用支持多个登录,您可能希望同时显示所有已登录用户的推送通知状态。MagicBell SDK允许您这样做。

您可以根据需要多次使用电子邮件或外部ID调用connectUser(:)方法。

let userOne = magicbell.connectUser(email: "[email protected]")
let userTwo = magicbell.connectUser(email: "[email protected]")
let userThree = magicbell.connectUser(externalId: "001")

登出用户

当用户从您的应用程序注销时,您想要

  • 从内存中移除用户的通知
  • 停止与MagicBell API的实时连接
  • 注销设备以接收推送通知

这可以通过MagicBell客户端实例的disconnectUser方法实现

// Remove by email
magicbell.disconnectUser(email: "[email protected]")

// Remove by external id
magicbell.disconnectUser(externalId: "001")

// Remove by email and external id
magicbell.disconnectUser(email: "[email protected]", externalId: "001")

集成到您的应用中

MagicBell User实例需要在您的整个应用中可用。您有以下几种选择

  • 扩展您的用户对象
  • 定义一个全局属性
  • 使用您自己的依赖注入图

扩展您的用户对象

如果您的整个应用中已经有一个用户对象,这种方法很有帮助。MagicBell将保证对于给定的电子邮件/外部ID的User实例是唯一的,您只需要提供对该实例的访问权限。例如

import MagicBell

// Your own user
struct User {
    let name: String
    let email: String
}

extension User {
    /// Returns the logged in MagicBell user
    func magicBell() -> MagicBell.User {
        return magicbell.connectUser(email: email)
    }
}

定义全局属性

这是如何定义一个可空的全局变量,该变量代表您的MagicBell用户

import MagicBell

let magicbell = MagicBellClient(apiKey: "[MAGICBELL_API_KEY]")
var magicbellUser: MagicBell.User? = nil

一旦执行登录,将值赋给此变量。请注意,在您的代码中访问它之前,您必须检查magicbellUser变量实际上已经设置。

使用您自己的依赖注入图

您还可以将MagicBell User实例注入到您自己的图中,并使用您首选的模型跟踪它。

NotificationStore

NotificationStore类代表一组MagicBell通知。您可以通过在用户存储对象上调用.build(...)方法来创建此类的实例。

例如

let allNotifications = user.store.build()

let readNotifications = user.store.build(read: true)

let unreadNotifications = user.store.build(read: false)

let archviedNotifications = user.store.build(archived: true)

let billingNotifications = user.store.build(categories: ["billing"])

let firstOrderNotifications = user.store.build(topics: ["order:001"])

以下是通知存储的属性

属性 类型 描述
totalCount Int 通知总数
unreadCount Int 未读通知数量
unseenCount Int 未查看通知数量
hasNextPage Bool 当向前分页时,是否有更多项目
count Int 存储中当前通知的数量
predicate StorePredicate 用于过滤通知的谓词

以下是可用的方法

方法 描述
refresh 重置存储并获取通知的第一页
fetch 获取通知的下一页
subscript(index:) 子脚本以访问通知:store[index]
delete 删除通知
delete 删除通知
markAsRead 将通知标记为已读
markAsUnread 将通知标记为未读
archive 存档通知
unarchive 取消存档通知
markAllRead 将所有通知标记为已读
markAllUnseen 将所有通知标记为已查看

大多数方法有两种实现

  • 使用完成块(返回一个Result对象)
  • 返回Combine Future(适用于iOS 13+)
// Delete notification
store.delete(notification) { result in
    switch result {
    case .success:
        print("Notification deleted")
    case .failure(error):
        print("Failed: \(error)")
    }
}

// Read a notification
store.markAsRead(notification)
    .sink { error in
        print("Failed: \(error)")
    } receiveValue: { notification in
        print("Notification marked as read")
    }

这些方法确保通知更改时存储的状态是一致的。例如,当通知被阅读时,具有谓词read: .unread的存储将删除该通知,并通知所有通知存储的观察者。

高级过滤

您还可以创建具有更高级过滤器的商店。为此,请使用 .build(...) 方法通过 StorePredicate 获取商店。

let predicate = StorePredicate()
let notifications = user.store.build(predicate: predicate)

以下是可用选项:

参数 选项: 默认值: 描述
读取: truefalsenil nil 通过 read 状态过滤(nil 表示未指定):
已读: truefalsenil nil 通过 seen 状态过滤(nil 表示未指定):
存档: truefalse false 通过 archived 状态过滤:
分类: [String] [] 通过分类过滤:
主题: [String] [] 通过主题过滤:

例如,使用这个断言来获取 "important" 类别的未读通知:

let predicate = StorePredicate(read: .unread, categories: ["important"])
let store = user.store.build(predicate: predicate)

通知存储是单例。使用相同的断言两次创建商店将得到相同的实例。

注意:获取商店后,它将在内存中保持活跃,以便可以实时更新。您可以使用 .dispose 方法强制删除商店。

let predicate = StorePredicate()
user.store.dispose(with: predicate)

当您删除用户实例时,这会自动为您完成。

观察更改

当调用 fetchrefresh 时,商店将通知内容观察者新添加的通知(有关观察者的信息,请参阅此处)。

// Obtaining a new notification store (first time)
let store = user.store.build()

// First loading
store.fetch { result in
    if let notifications = try? result.get() {
        print("Notifications: \(notifications))")

        // If store has next page available
        if store.hasNextPage {
            // Load next page
            store.fetch { result in
                if let notifications = try? result.get() {
                    print("Notifications: \(notifications))")
                }
            }
        }
    }
}

要重置和获取商店:

store.refresh { result in
    if let notifications = try? result.get() {
        print("Notifications: \(notifications))")
    }
}

访问通知

NotificationStore 是一个可迭代的集合。因此,可以像预期的那样访问通知。

for i in 0..<store.count {
    let notification = store[i]
    print("notification: \(notification)")
}

// forEach
store.forEach { notification in
    print("notification: \(notification)")
}

// for in
for notification in store {
    print("notification: \(notification)")
}

// As an array
let notifications = store.notifications()

枚举也是可用的:

// forEach
store.enumerated().forEach { idx, notification in
    print("notification[\(idx)] = \(notification)")
}

// for in
for (idx, notification) in store.enumerated() {
    print("notification[\(idx)] = \(notification)")
}

观察通知存储变化:

经典观察者方法:

当新通知到达或通知状态改变(标记为已读、存档等)时,NotificationStore 的实例会自动更新。

要观察通知存储上的变化,您的观察者必须实现以下协议:

// Get notified when the list of notifications of a notification store changes
protocol NotificationStoreContentObserver: AnyObject {
    func didReloadStore(_ store: NotificationStore)
    func store(_ store: NotificationStore, didInsertNotificationsAt indexes: [Int])
    func store(_ store: NotificationStore, didChangeNotificationAt indexes: [Int])
    func store(_ store: NotificationStore, didDeleteNotificationAt indexes: [Int])
    func store(_ store: NotificationStore, didChangeHasNextPage hasNextPage: Bool)
}

// Get notified when the counters of a notification store change
protocol NotificationStoreCountObserver: AnyObject {
    func store(_ store: NotificationStore, didChangeTotalCount count: Int)
    func store(_ store: NotificationStore, didChangeUnreadCount count: Int)
    func store(_ store: NotificationStore, didChangeUnseenCount count: Int)
}

要观察变化,实现这些协议(或其中一个),并将观察者注册到通知存储中。

let store = user.store.build()
let observer = myObserverClassInstance

store.addContentObserver(observer)
store.addCountObserver(observer)

响应式方法(iOS 13):

使用 NotificationStorePublisher 类创建一个用于发布 NotificaitonStore 主属性变化的 ObservableObject

此对象必须在需要时由用户创建和保留。

属性: 类型 描述
totalCount @Published Int: 总数:
unreadCount @Published Int: 未读数:
unseenCount @Published Int: 未看见数:
hasNextPage @Published Bool: 表示是否可以获取更多内容的布尔值:
通知: @Published [Notification] 通知数组。

典型用法是在 SwiftUI 的 View 中,作为一个可以直接在视图中引用的视图模型:

import SwiftUI
import MagicBell

class Notifications: View {
    let store: NotificationStore
    @ObservedObject var bell: NotificationStorePublisher

    init(store: NotificationStore) {
        self.store = store
        self.bell = NotificationStorePublisher(store)
    }

    var body: some View {
        List(bell.notifications, id: \.id) { notification in
            VStack(alignment: .leading) {
                Text(notification.title)
                Text(notification.content ?? "-")
            }
        }
        .navigationBarTitle("Notifications - \(bell.totalCount)")
    }
}

通知偏好:

您可以为 MagicBell 通道和分类获取并设置通知偏好。

public struct Channel {
    public let label: String
    public let slug: String
    public let enabled: Bool
}

public struct Category {
    public let channels: [Channel]
    public let label: String
    public let slug: String
}

public struct NotificationPreferences {
    public let categories: [Category]
}

要获取通知偏好,请使用以下格式的 fetch 方法:

user.preferences.fetch { result in
    if let preferences = try? result.get() {
        print("Notification Preferences: \(preferences)")
    }
}

要更新偏好,请使用 update

// Updating notification preferences.
// The update can be partial and only will affect the categories included in the object being sent
user.preferences.update(preferences) { result in }

要更新单个通道,您可以使用提供的便利函数 updateChannel

user.preferences.update(categorySlug: "new_comment", channelSlug: "in_app", enabled: true) { result in }

推送通知:

您可以为移动推送通知将设备令牌注册到 MagicBell。为此,一旦 iOS 提供了它,就立即设置设备令牌

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    // Storing device token when refreshed
    magicbell.setDeviceToken(deviceToken: deviceToken)
}

MagicBell 会将其临时存储在内存中,并在通过 MagicBellClient.connectUser 声明新用户时立即发送。

当用户断开连接(MagicBellClient.disconnectUser)时,自动取消注册该用户的设备令牌。

提交

我们欢迎各种形式的贡献。要这样做,克隆存储库,通过在根目录中运行命令 carthage update --use-xcframeworks --no-use-binaries 解析依赖关系,并打开 MagicBell.xcodeproj