In Swift, how do you loop through a list and hand one item at a time to a function with completion closure?

jl303

I'm trying to process a folder with audio files through speech to text recognition on MacOS.

If I just process one file, it works, but if I feed multiple files, only one file works and throws an error for rest.

I thought I could use DispatchGroup, but it still feeds everything at once instead of waiting for each item to be completed.

Could someone help me to understand what I'm doing wrong?

let recognizer = SFSpeechRecognizer()
recognizer?.supportsOnDeviceRecognition = true
let group = DispatchGroup()
let fd = FileManager.default
fd.enumerator(at: url, includingPropertiesForKeys: nil)?.forEach({ (e) in
    if let url = e as? URL, url.pathExtension == "wav" || url.pathExtension == "aiff" {
        let request = SFSpeechURLRecognitionRequest(url: url)
        group.enter()
        let task =  recognizer?.recognitionTask(with: request) { (result, error) in
            print("Transcribing \(url.lastPathComponent)")
            guard let result = result else {
                print("\(url.lastPathComponent): No message")
                group.leave()
                return
            }
            while  result.isFinal == false {
                sleep(1)
            }
            print("\(url.lastPathComponent): \(result.bestTranscription.formattedString)")
            group.leave()
        }
        group.wait()
    }
}
group.notify(queue: .main) {
    print("Done")
}

Update: I tried DispatchQueue, but it transcribes only one file and hangs.

let recognizer = SFSpeechRecognizer()
recognizer?.supportsOnDeviceRecognition = true
let fd = FileManager.default
let q = DispatchQueue(label: "serial q")
fd.enumerator(at: url, includingPropertiesForKeys: nil)?.forEach({ (e) in
    if let url = e as? URL, url.pathExtension == "wav" {
        let request = SFSpeechURLRecognitionRequest(url: url)
        q.sync {
            let task =  recognizer?.recognitionTask(with: request) { (result, error) in
                guard let result = result else {
                    print("\(url.lastPathComponent): No message")
                    return
                }
                if result.isFinal {
                    print("\(url.lastPathComponent): \(result.bestTranscription.formattedString)")
                }
            }
        }
    }
})
print("Done")
vadian

This is a async/await solution with a Continuation. It runs sequentially.

let recognizer = SFSpeechRecognizer()
recognizer?.supportsOnDeviceRecognition = true

let fd = FileManager.default
let enumerator = fd.enumerator(at: url, includingPropertiesForKeys: nil, options: .skipsHiddenFiles)!
Task {
    for case let fileURL as URL in enumerator where ["wav", "aiff"].contains(fileURL.pathExtension) {
        do {
            try await recognizeText(at: fileURL)
        } catch {
            print(error)
        }
    }
}


func recognizeText(at url: URL) async throws {
    return try await withCheckedThrowingContinuation { (continuation : CheckedContinuation<Void, Error>) in
        let request = SFSpeechURLRecognitionRequest(url: url)
        let task =  recognizer?.recognitionTask(with: request) { (result, error) in
            print("Transcribing \(url.lastPathComponent)")
            if let error = error {
                continuation.resume(throwing: error)
                print("\(url.lastPathComponent): No message")
            } else {
                print("\(url.lastPathComponent): \(result!.bestTranscription.formattedString)")
                if result!.isFinal {
                    continuation.resume(returning: ())
                }
            }
        }
    }
}

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

How to loop through a function for each item in list?

In Python, How do you loop through a list if one value is missing in that list?

How do I append an item in a list to another list one at a time using for loop?

How to loop through an array in angular controller and display one item at a time

How do you loop through api with list of parameters and store resulting calls in one dataframe

how do you create a loop that cycles through more than one List in C#

how do you loop through a List based on List contents

swift - how to return from a within a completion handler closure of a system function?

How do you create this tree data structure one item at a time?

How do you loop through a list of objects and get a specific attribute?

How do you document the parameters of a function's closure parameter in Swift 3?

How do you use <TAB> to cycle through completion suggestions with ALE?

In the function the for loop misses one item on the list

How to loop through all but the last item of a list?

In Swift, How do I use a Completion Handler in a function that's already Using One?

Firebase For Android Studio - How do you loop through each item in a database?

How can I loop through a jquery array and then output each item (starting at index 0) one at a time?

How do you write a completion handler in Swift 3?

How do you add windowSoftInputMode to one item

How do you make an alias or function that retains tab completion?

Trying to send one list item through a search at a time

How to handle two completion handlers in one function swift

Blazor: How to show one list item at a time

Swift: How do I store an array of object by reference from completion handler (closure)?

In JavaScript, when looping through a FOR loop, how do I pass the value of the item in an array to an anonymous function?

Python Tkinter: How do you loop through a list and display items on a new line in a new root

How to loop through each item inside a list of list in python?

How do you remove items from several list boxes by removing one item associated with them all?

How do I loop through a list by twos?