+++ date = '2025-12-23T13:55:15+08:00' draft = false title = '开发日志:Swift简易命令行闹钟程序 – 1' tags = ['技术栈'] license = 'MIT Licence' description = '可弹窗,可在Touch Bar上关闭。' slug = '00B' +++ 2025年11月1日,我通过AI协助,开发了一个名为[NewTesuto](NewTesuto.zip)的SwiftUI闹钟程序,但代码臃肿(1000+行),图形界面难用且资源占用大,有时还会神秘地不响,让我错过早八。 11月23日,我进行了简单维护,但这个程序依然不令我满意。 今天,我开始轻量级命令行程序:[Tesuto-Alarm-Dec-2025](Tesuto-Alarm-Dec-2025_12_23.zip)的开发。 ## 完整代码 ```swift import UniformTypeIdentifiers import SwiftUI import Combine class Alarm: Codable, ObservableObject { let name: String let hour: Int let minute: Int var triggered: Bool } let app = NSApplication.shared NSApp.setActivationPolicy(.accessory) let sound = NSSound(contentsOf: URL(fileURLWithPath: ("~/Tesuto-Alarm-Dec-2025/sound.mp3" as NSString).expandingTildeInPath), byReference: false) if sound != nil { print("\(Date()): 读取声音文件成功") print("尝试播放:(2秒后停止)") sound?.play() DispatchQueue.main.asyncAfter(deadline: .now() + 2) { sound?.stop() } } else { print("\(Date()): 读取声音文件失败") } var alarms: [Alarm] = [] func readJSON(url: URL) -> Bool { do { let json = try Data(contentsOf: url) let loadedData = try JSONDecoder().decode([Alarm].self, from: json) alarms = loadedData print("\(Date()): 读取JSON成功") print("闹钟数据:") for alarm in alarms { print("\n名称:\(alarm.name)") print("时间:\(alarm.hour):\(alarm.minute)") } return true } catch { print("\(Date()): 读取JSON失败(\(error))") return false } } func perform(for alarm: Alarm) { let window = NSAlert() window.messageText = "闹钟提醒" window.informativeText = "\(alarm.name) - \(alarm.hour):\(alarm.minute)" window.addButton(withTitle: "关闭\(alarm.name)") print("\(Date()): 触发\(alarm.name) - \(alarm.hour):\(alarm.minute)") sound?.play() Task { try await Task.sleep(nanoseconds: 1 * 60 * 1_000_000_000) sound?.stop() print("\(Date()): 超时自动关闭\(alarm.name) - \(alarm.hour):\(alarm.minute)") return } window.runModal() sound?.stop() print("\(Date()): 手动关闭\(alarm.name) - \(alarm.hour):\(alarm.minute)") } let timer = Timer.publish(every: 1, on: .main, in: .default).autoconnect() var cancellables = Set() _ = readJSON(url: URL(fileURLWithPath: ("~/Tesuto-Alarm-Dec-2025/alarms.json" as NSString).expandingTildeInPath)) timer.sink { _ in let now = Date() let calendar = Calendar.current let currentSecond = calendar.component(.second, from: now) if currentSecond < 3 { let currentHour = calendar.component(.hour, from: now) let currentMinute = calendar.component(.minute, from: now) for alarm in alarms { if alarm.hour == currentHour && alarm.minute == currentMinute && !alarm.triggered { perform(for: alarm) alarm.triggered.toggle() DispatchQueue.main.asyncAfter(deadline: .now() + 5) { alarm.triggered.toggle() print("\(Date()): 清除\(alarm.name)的triggered标识") } } } } } .store(in: &cancellables) RunLoop.current.run() ``` ## 注 正正好好一百行,且代码亲手写成,再也不会看不懂了。 ## 待解决 疑似包含非标准输出(STDOUT)(声音播放?),不能生成日志文件,不能以系统服务在后台运行。