添加slug,改版文章目录命名结构,添加了一个实用的新建文章Shell脚本。
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,117 @@
|
||||
+++
|
||||
date = '2025-12-23T13:55:15+08:00'
|
||||
draft = false
|
||||
title = '开发日志:Swift简易命令行闹钟程序 – 1'
|
||||
tags = ['技术栈']
|
||||
license = 'MIT Licence'
|
||||
description = '可弹窗,可在Touch Bar上关闭。'
|
||||
+++
|
||||
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<AnyCancellable>()
|
||||
_ = 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)(声音播放?),不能生成日志文件,不能以系统服务在后台运行。
|
||||
Reference in New Issue
Block a user