You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何实现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并上传

AppDelegateSceneDelegate中实现:

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

火山引擎 最新活动