如何实现iOS App在MySQL数据库新增记录时接收推送通知?
嗨,我来帮你理清这个问题——直接用MySQL触发器给iOS发推送确实走不通,得搭个中间链路。下面我一步步给你拆解可行的方案:
整体思路概述
核心流程是:MySQL新增记录 → 触发器触发并记录事件 → 后端服务监听/轮询事件 → 后端调用苹果推送服务(APNs)发送通知 → iOS端接收并处理。触发器的作用是把新增事件留存下来,而非直接发推送,因为MySQL本身没法直接调用外部推送服务。
1. MySQL侧:用触发器记录新增事件
首先创建一个专门的事件记录表,用来存放待处理的新增记录通知请求;然后给目标业务表写INSERT触发器,每次有新记录插入时,就往事件表里存一条待处理的消息。
示例SQL代码:
-- 1. 创建事件记录表,用于存放待推送的新增事件 CREATE TABLE record_events ( id INT AUTO_INCREMENT PRIMARY KEY, target_table VARCHAR(50) NOT NULL, -- 触发事件的业务表名 record_id INT NOT NULL, -- 新增记录的ID created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, is_processed BOOLEAN DEFAULT FALSE -- 标记是否已处理推送 ); -- 2. 给目标业务表(比如user_data)创建INSERT触发器 DELIMITER // CREATE TRIGGER after_user_data_insert AFTER INSERT ON user_data FOR EACH ROW BEGIN -- 每次user_data表新增记录,就往事件表里插一条待处理消息 INSERT INTO record_events (target_table, record_id) VALUES ('user_data', NEW.id); END // DELIMITER ;
2. 后端服务:监听事件并调用APNs
你需要一个后端服务(可以用Node.js、Python、Java等任意语言),核心逻辑是:
- 定时轮询
record_events表,抓取未处理的事件; - 根据事件里的
record_id,从业务表或用户设备表获取对应的iOS设备Token; - 调用APNs接口发送推送通知;
- 标记事件为已处理,避免重复推送。
示例后端核心逻辑(Node.js)
const mysql = require('mysql2'); const apn = require('apn'); // 1. 配置MySQL连接 const db = mysql.createConnection({ host: '你的数据库地址', user: '数据库用户名', password: '数据库密码', database: '你的数据库名' }); // 2. 配置APNs(用Token认证方式,苹果推荐) const apnProvider = new apn.Provider({ token: { key: 'APNsAuthKey_XXXXXXXXXX.p8', // 从苹果开发者后台下载的密钥文件 keyId: 'XXXXXXXXXX', // 密钥ID teamId: 'XXXXXXXXXX' // 开发者团队ID }, production: false // 开发环境用false,生产环境改为true }); // 3. 轮询处理事件的函数 async function processPendingEvents() { // 批量获取未处理的事件(限制数量避免性能问题) const [events] = await db.promise().query('SELECT * FROM record_events WHERE is_processed = FALSE LIMIT 10'); for (const event of events) { // 获取该记录对应的用户设备Token(假设你有user_devices表存储用户和设备的关联) const [devices] = await db.promise().query( 'SELECT device_token FROM user_devices WHERE user_id = ?', [event.record_id] ); if (devices.length === 0) { // 没有设备Token,直接标记为已处理 await db.promise().query('UPDATE record_events SET is_processed = TRUE WHERE id = ?', [event.id]); continue; } // 构造推送消息 const notification = new apn.Notification(); notification.topic = 'com.yourapp.bundleid'; // 你的App Bundle ID notification.alert = { title: '新记录提醒', body: `ID为${event.record_id}的记录已新增` }; notification.sound = 'default'; // 可以自定义额外参数,方便iOS端跳转 notification.payload = { record_id: event.record_id }; // 发送推送 await apnProvider.send(notification, devices.map(d => d.device_token)); // 标记事件为已处理 await db.promise().query('UPDATE record_events SET is_processed = TRUE WHERE id = ?', [event.id]); } } // 每隔30秒轮询一次(小流量场景够用,大流量建议用消息队列替代轮询) setInterval(processPendingEvents, 30000);
3. iOS端(Swift):接收推送通知
这部分需要完成两个核心操作:获取设备Token并上传到后端,以及接收和处理推送消息。
3.1 获取设备Token并上传
在AppDelegate或SceneDelegate中实现:
import UIKit import UserNotifications @main class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // 请求推送权限 UNUserNotificationCenter.current().delegate = self let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound] UNUserNotificationCenter.current().requestAuthorization(options: authOptions) { granted, error in if granted { DispatchQueue.main.async { application.registerForRemoteNotifications() } } } return true } // 获取设备Token func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { let tokenString = deviceToken.map { String(format: "%02.2hhx", $0) }.joined() print("设备Token: \(tokenString)") // 上传Token到后端服务器 guard let url = URL(string: "https://你的后端地址/api/save-device-token") else { return } var request = URLRequest(url: url) request.httpMethod = "POST" request.setValue("application/json", forHTTPHeaderField: "Content-Type") // 替换成当前用户的ID(比如从登录态获取) let body = ["device_token": tokenString, "user_id": 123] request.httpBody = try? JSONSerialization.data(withJSONObject: body) URLSession.shared.dataTask(with: request).resume() } // 获取Token失败的处理 func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { print("推送注册失败: \(error.localizedDescription)") } }
3.2 接收和处理推送
继续在AppDelegate中实现UNUserNotificationCenterDelegate的方法:
// 前台接收推送时的处理(控制是否显示弹窗、声音、角标) func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { // 前台显示通知的方式 completionHandler([.alert, .sound, .badge]) // 处理推送内容 let userInfo = notification.request.content.userInfo print("前台收到推送: \(userInfo)") // 这里可以更新UI或执行业务逻辑 } // 点击推送通知后的处理(无论前台/后台) func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { let userInfo = response.notification.request.content.userInfo print("点击推送: \(userInfo)") // 示例:根据推送里的record_id跳转到详情页 if let recordId = userInfo["record_id"] as? Int { // 这里实现页面跳转逻辑,比如获取当前导航控制器并push详情页 if let window = UIApplication.shared.windows.first, let nav = window.rootViewController as? UINavigationController { let detailVC = RecordDetailViewController(recordId: recordId) nav.pushViewController(detailVC, animated: true) } } completionHandler() }
关键注意事项
- APNs环境匹配:开发环境用sandbox,生产环境用production,后端配置要和iOS打包环境一致,否则推送会失败;
- Token更新:用户卸载重装App或更换设备时,Token会变化,建议每次App启动都重新上传Token到后端;
- 性能优化:如果业务表插入量极大,轮询可能有性能瓶颈,建议用消息队列(比如RabbitMQ)替代中间表,触发器直接把事件发送到队列,后端消费队列消息;
- 错误重试:后端发送推送失败时,要标记事件为处理失败,稍后重试,避免丢失通知。
内容的提问来源于stack exchange,提问作者user11349745




