这是官方的 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 安装 MagicBell,请将以下条目添加到您的 Podfile
pod 'MagicBell', '>=2.0.0'
重要:请确保您在 Podfile
中指定 use_frameworks!
。
然后,运行 pod install
。
要使用 Swift 包管理器 安装 MagicBell,只需将依赖项按如下方式添加到您的项目中
dependencies: [
.package(url: "https://github.com/magicbell-io/magicbell-swift", .upToNextMajor(from: "2.0.0"))
]
要使用 Carthage 安装 MagicBell,请将以下依赖项添加到 Carfile 中
github "magicbell-io/magicbell-swift" "2.0.0"
然后,运行 carthage update --use-xcframeworks --platform [iOS|macOS] --no-use-binaries
(选择所需平台)以解决依赖关系。
将 MagicBell.xcframework
添加到您的项目关联的框架中,连同 Carthage 解决的其他依赖项。
第一步是创建一个 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
类代表一组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)
以下是可用选项:
参数 | 选项: | 默认值: | 描述 |
---|---|---|---|
读取: |
true ,false ,nil |
nil |
通过 read 状态过滤(nil 表示未指定): |
已读: |
true ,false ,nil |
nil |
通过 seen 状态过滤(nil 表示未指定): |
存档: |
true ,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)
当您删除用户实例时,这会自动为您完成。
当调用 fetch
或 refresh
时,商店将通知内容观察者新添加的通知(有关观察者的信息,请参阅此处)。
// 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)
使用 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
。