语音指令解析与定时器触发方案咨询及代码优化需求
Hey Mike, sorry to hear you’ve been stuck on this for 3 months—let’s break this down step by step to fix your parsing issues, validate commands properly, and outline a solid architecture for your voice analysis system.
Your current regex and parsing code has a few gaps: it only catches numeric minutes, mishandles Swift string ranges, and cleans task content too rigidly. Let’s rewrite and improve it.
1. Support Both Numeric and Text-Based Time Values
First, create a mapping for text-based numbers to integers, then expand your regex to match both numeric and text minute values:
private let textNumberToInt: [String: Int] = [ "one": 1, "two": 2, "three": 3, "four": 4, "five": 5, "six": 6, "seven": 7, "eight": 8, "nine": 9, "ten": 10 ]
2. Fix Range Handling & Regex Matching
NSRegularExpression uses UTF-16 ranges, but your original code used UTF-8 counts—this causes crashes with multi-byte characters. We’ll safely convert NSRange to Swift String ranges and extract time values correctly.
3. Clean Task Content Intelligently
Instead of hardcoding a range to remove "to", we’ll strip the matched time string, trim whitespace, and handle optional leading "to " gracefully.
Revised Parsing Function
var timeValue: Int = 0 var taskValue: String = "" private let textNumberToInt: [String: Int] = [ "one": 1, "two": 2, "three": 3, "four": 4, "five": 5, "six": 6, "seven": 7, "eight": 8, "nine": 9, "ten": 10 ] func parseVoiceCommand(_ inputText: String) -> (Int?, String?) { // Regex to match either numeric (e.g., "20 minutes") or text-based (e.g., "five minutes") time values let pattern = "(\\d+|one|two|three|four|five|six|seven|eight|nine|ten)\\s+minute(s)?" guard let regex = try? NSRegularExpression(pattern: pattern, options: .caseInsensitive) else { print("Invalid regex pattern") return (nil, nil) } let fullRange = NSRange(inputText.startIndex..., in: inputText) guard let match = regex.firstMatch(in: inputText, options: [], range: fullRange) else { print("No valid time match found") return (nil, nil) } // Extract time string and convert to Int let timeMatchRange = Range(match.range(at: 1), in: inputText)! let timeString = String(inputText[timeMatchRange]).lowercased() let extractedTime: Int? if let numericTime = Int(timeString) { extractedTime = numericTime } else { extractedTime = textNumberToInt[timeString] } // Clean task content: remove the matched time string, trim whitespace, remove leading "to " if present let cleanedTask = inputText.replacingCharacters(in: Range(match.range, in: inputText)!, with: "") .trimmingCharacters(in: .whitespacesAndNewlines) .replacingOccurrences(of: "^to\\s+", with: "", options: .regularExpression) // Update global variables (optional—you might prefer to use the returned tuple instead) if let time = extractedTime { timeValue = time } taskValue = cleanedTask return (extractedTime, cleanedTask) }
Before triggering a timer, add checks to ensure the command is valid:
- Ensure
extractedTimeis greater than 0 (no zero or negative timers) - Ensure
cleanedTaskisn’t empty (avoid timers with no task description)
Example validation logic:
func validateAndTriggerTimer(from inputText: String) { guard let (time, task) = parseVoiceCommand(inputText), time > 0, !task.isEmpty else { print("Invalid command: please provide a valid positive time and task") return } // Trigger timer here startTimer(with: time, task: task) }
For reliable timer execution, use either Timer (for UI-related timers) or DispatchQueue (for background tasks). Here’s a safe implementation:
func startTimer(with minutes: Int, task: String) { let seconds = TimeInterval(minutes * 60) // Use DispatchQueue for background-safe execution DispatchQueue.global().asyncAfter(deadline: .now() + seconds) { [weak self] in // Trigger your task completion action (e.g., show a notification) self?.handleTimerCompletion(task: task) } // Optional: Update UI to show active timer DispatchQueue.main.async { print("Timer started: \(minutes) minutes for task: \(task)") } } func handleTimerCompletion(task: String) { DispatchQueue.main.async { // Show local notification or alert print("Timer finished! Task: \(task)") // Example: Trigger a local notification let content = UNMutableNotificationContent() content.title = "Timer Complete" content.body = "Time to \(task)" let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil) UNUserNotificationCenter.current().add(request) } }
For a scalable system beyond basic regex parsing, use a layered architecture:
- Speech-to-Text Layer: Use Apple’s
Speechframework to handle voice transcription (it’s built-in and optimized for iOS/macOS) - Semantic Parsing Layer:
- For simple commands: Use regex as we did, but expand patterns to support more variations (e.g., "set a timer for X minutes to Y", "remind me in X minutes about Y")
- For complex commands: Use Apple’s
NLProcessingframework (iOS 15+) to perform entity recognition (identify time values) and intent classification (determine the user wants a timer)
- Task Scheduler Layer: Manage active timers, persist tasks to
UserDefaultsor Core Data, and handle pause/cancel actions - Execution Layer: Trigger notifications, UI updates, or other actions when timers complete
This layered approach makes it easier to add features (like supporting hours, days, or different command types) without rewriting core logic.
内容的提问来源于stack exchange,提问作者MikeMaus

