Swift新手求助:实现网站访问时iPhone接收通知的简易App方案
Hey Dave!作为有PHP和JS基础的开发者,你其实已经握有一半的优势了,咱们一步步拆解这个需求,绝对能轻松搞定~
整体实现核心逻辑
说白了就是三个关键环节:网站访问触发后端事件 → 后端调用苹果推送服务(APNs) → 你的iPhone接收通知,咱们拆分每个环节来落地:
1. Swift端:搞定iPhone的推送基础配置(新手友好)
这部分是入门级Swift操作,不用怕,跟着步骤来:
- 先在Xcode新建一个iOS项目(选「Single View App」就足够,不用复杂模板)
- 打开项目的
Signing & Capabilities,添加Push Notifications能力,同时开启Background Modes里的Remote notifications(保证App后台也能收通知) - 复制这段带注释的基础代码到
AppDelegate.swift,核心是获取你的iPhone在APNs中的唯一标识(设备令牌):
运行项目后,控制台会打印出你的设备令牌,把它复制下来给后端用。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 } // 获取设备令牌(一定要把这个令牌传给你的网站后端存起来!) func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) } let token = tokenParts.joined() print("你的设备令牌:\(token)") // 这里可以写个简单的网络请求,把token发送到你的网站后端接口存储 } // 处理前台收到通知的提示逻辑 func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { completionHandler([.alert, .sound]) } }
2. 后端端:用你熟悉的PHP/JS处理访问触发与推送
这部分是你的强项,分两个小步骤:
- 检测网站访问:在网站入口文件(比如PHP的
index.php,或JS后端的路由逻辑里)添加代码,每次有用户访问时触发推送逻辑(可以加个简单去重,比如同一IP 5分钟内重复访问不推送,避免被通知轰炸) - 调用APNs发送通知:苹果提供了HTTP/2接口,用PHP或JS都能轻松实现,给你两个示例:
PHP推送示例
<?php // 替换成你自己的苹果开发者信息 $apnsKey = file_get_contents('你的APNs密钥文件.p8'); // 从苹果开发者后台下载的.p8文件 $apnsKeyId = '你的密钥ID'; // 苹果后台生成密钥时的ID $teamId = '你的开发者团队ID'; // 苹果开发者账号的团队ID $deviceToken = '刚才从Swift端拿到的设备令牌'; $topic = '你的App的Bundle ID'; // 比如 com.yourname.VisitorAlert // 生成APNs需要的JWT身份令牌 $header = json_encode(['alg' => 'ES256', 'kid' => $apnsKeyId]); $payload = json_encode(['iss' => $teamId, 'iat' => time()]); $base64Header = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($header)); $base64Payload = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($payload)); $signature = ''; openssl_sign("$base64Header.$base64Payload", $signature, $apnsKey, 'SHA256'); $jwt = "$base64Header.$base64Payload." . str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($signature)); // 构建通知内容 $notification = json_encode([ 'aps' => [ 'alert' => [ 'title' => '网站有新访问!', 'body' => '有人刚刚浏览了你的网站~' ], 'sound' => 'default' ] ]); // 发送请求到APNs $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "https://api.push.apple.com/3/device/$deviceToken"); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $notification); curl_setopt($ch, CURLOPT_HTTPHEADER, [ "Authorization: Bearer $jwt", "apns-topic: $topic", "Content-Type: application/json" ]); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); curl_close($ch); // 可选:记录推送日志方便排查问题 file_put_contents('push_log.txt', date('Y-m-d H:i:s') . ' - ' . $response . PHP_EOL, FILE_APPEND); ?>
Node.js推送示例
const jwt = require('jsonwebtoken'); const https = require('https'); const fs = require('fs'); // 替换成你自己的信息 const apnsKey = fs.readFileSync('你的APNs密钥文件.p8'); const apnsKeyId = '你的密钥ID'; const teamId = '你的开发者团队ID'; const deviceToken = '刚才从Swift端拿到的设备令牌'; const topic = '你的App的Bundle ID'; // 生成JWT令牌 const token = jwt.sign({}, apnsKey, { algorithm: 'ES256', expiresIn: '1h', issuer: teamId, header: { alg: 'ES256', kid: apnsKeyId } }); // 构建通知内容 const notification = JSON.stringify({ aps: { alert: { title: '网站有新访问!', body: '有人刚刚浏览了你的网站~' }, sound: 'default' } }); // 发送请求到APNs const options = { hostname: 'api.push.apple.com', port: 443, path: `/3/device/${deviceToken}`, method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'apns-topic': topic, 'Content-Type': 'application/json', 'Content-Length': notification.length } }; const req = https.request(options, (res) => { res.on('data', (d) => { // 可选:记录推送结果 fs.appendFileSync('push_log.txt', `${new Date().toISOString()} - ${d}\n`); }); }); req.on('error', (e) => { console.error('推送失败:', e); }); req.write(notification); req.end();
3. 测试与优化小建议
- 优先用真机测试推送,模拟器偶尔会有推送延迟或接收不到的情况
- 后端可以加个冷却机制,比如同一IP 5分钟内重复访问不触发推送,避免通知刷屏
- 把设备令牌存在数据库里,以后换手机直接更新令牌就行,不用改后端代码
- 苹果开发者后台的APNs密钥要妥善保存,别泄露出去
内容的提问来源于stack exchange,提问作者David Stančík




