NSSpeechSynthesizer get Siri voices on macOS - swift

Is there a way to get the Siri voices for NSSpeechSynthesizer? NSSpeechSynthesizer.availableVoices() does not list them, but maybe there is an undocumented trick or something?
I've also tried to use AVSpeech​Synthesizer, even tough it should be available on macOS 10.14+, I couldn't get it to read out loud …
I've used a Playground to test this with the following code from NSHipster:
import Cocoa
import AVFoundation
let string = "Hello, World!"
let utterance = AVSpeechUtterance(string: string)
let synthesizer = AVSpeechSynthesizer()
synthesizer.speak(utterance)

Siri voice
I'm not aware of any trick to make the Siri voice available and I'm afraid it's impossible to make it working via public API.
Both NSSpeechSynthesizer and AVSpeechSynthesizer do utilize SpeechSynthesis.framework (included in the ApplicationServices.framework).
Quickly checked it and the function BuildDefaultVoiceList(bool) checks bundle identifier (via PermitAllVoices which compares it with com.apple.VoiceOverUtility, com.apple.VoiceOver, com.apple.VoiceOverUtilityCacheBuilder).
There're other checks probably.
All the voices are stored in the /System/Library/Speech/Voices folder. Tried to copy the voice, change Info.plist values to make it look like another voice, but still not available/working.
How far do you want to go? Do you want to disable SIP, modify framework, preload your stuff, ... Is it worth it?
AVSpeech​Synthesizer on macOS
It works, but it's buggy and you have to kill the service from time to time.
List of available voices
AVSpeechSynthesisVoice.speechVoices().forEach { voice in
print("Name: \(voice.name) Language: \(voice.language) Identifier: \(voice.identifier)")
}
Speak
let utterance = AVSpeechUtterance(string: "Ahoj, jak se máš?")
utterance.voice = AVSpeechSynthesisVoice(identifier: "com.apple.speech.synthesis.voice.zuzana.premium")
let synthesizer = AVSpeechSynthesizer()
synthesizer.speak(utterance)
Doesn't speak?
All the following methods (delegate) are called when you call speak(utterance) and it works as it should:
speechSynthesizer(_:didStart:)
speechSynthesizer(_:willSpeakRangeOfSpeechString:utterance:)
speechSynthesizer(_:didFinish:)
Something's wrong if you get just the speechSynthesizer(_:didCancel:) method call and you have to kill the speechsynthesisd process:
kill `pgrep speechsynthesisd`
Then try again, it fixes the issue for me.
Additional voices installation
System Preferences - Accessibility - Speech
System Voice - click & scroll down & select Customize...
Select additional voice to install

Related

Global keyboard shortcuts for a Mac tray app

I am developing a Mac app using Swift 4 which only runs as an Agent (it only has a menu in the tray). I'm completely new to Swift.
I want it to respond to keyboard shortcuts, but the basic shortcuts (see picture) only work when the app has focus (in my case, when the menu is being clicked).
So what I need is for my app to respond to system-wide keyboard shortcuts.
I've tried:
HotKey but it doesn't seem to be compatible with Swift 4, or perhaps I can't use Swift Package Manager
this answer but again Swift 4 raises several errors that I couldn't solve
Has anyone done it?
So I ended up getting it to work with HotKey:
by using Carthage as a package manager instead of SPM
and by not using HotKey's example code from their README, which for some reason didn't work for me, but the one in their example app.
It works great!
I made a Swift package that makes this very easy:
import KeyboardShortcuts
extension KeyboardShortcuts.Name {
static let startFiveRounds = Self("startFiveRounds", default: .init(.t, modifiers: [.command, .option]))
}
#main
final class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ notification: Notification) {
KeyboardShortcuts.onKeyUp(for: .startFiveRounds) {
// …
}
}
}
Even though you already solved your problem, I think this could be useful for other people having the same problem.
I would also recommend letting the user pick their own keyboard shortcut instead of hardcoding it. My package comes with a control where the user can set their preferred shortcut.

How to enable Bluetooth with a button OSX

Is it possible to enable Bluetooth (with Discoverability on), using a button on macOS? Or enable it when an application opens? I've looked around online for a solution but everything directs towards iPhone and iOS Development rather than anything on the Mac side. Everything that is geared towards iOS also states that it is not possible on the mobile devices, but it is possible to display an alert notifying the user to turn on their Bluetooth to use their accessories. Is any alert type possible?
Edit: The closest I could get to this was opening the Bluetooth pane of the System Preferences programmatically.
It's a private API.
Add the following to your bridging header:
void IOBluetoothPreferenceSetControllerPowerState(int);
And call it with 1 to enable or 0 to disable:
func setBluetooth(on: Bool) {
IOBluetoothPreferenceSetControllerPowerState(on ? 1 : 0)
}
setBluetooth(on: true)
Don't forget to import IOBluetooth in your Swift file.
To get the current status use int IOBluetoothPreferenceGetControllerPowerState();

Programmatically minimize all windows or besides my Cocoa application?

I haven't been able to find anything about this in Swift. Is there a way to programmatically make my application minimize all other windows open in the background, or even just minimize Safari? I basically want my application to run against the desktop, without any clutter in the background. Is there a way to programmatically do this for a Cocoa app? I'm pretty new to swift, so any help would be appreciated.
You can use api on NSWorkspace which allows you to hide all visible app in the background.
You can find more about NSWorkspace here: link
Hides all applications other than the sender. This method must be called from your app’s main thread.
NSWorkspace.shared().hideOtherApplications()
If you only want to hide Safari,
let appPath = NSWorkspace.shared().fullPath(forApplication: "Safari")
let identifier = Bundle.init(path: appPath!)?.bundleIdentifier
if let bundleID = identifier {
let runningApps = NSRunningApplication.runningApplications(withBundleIdentifier:bundleID )
if !runningApps.isEmpty {
runningApps.first?.hide()
}
}

RevMob Integration for Swift

**I have read the RevMob instructions for Swift and I have read an answer to an Swift-RevMob question on here, but it didn't resolve my particular issue. **
I am currently trying to get a fullscreen ad to show.
This is my GameViewController.swift:
verride func viewDidLoad() {
super.viewDidLoad()
//Start RevMob code
let completionBlock: () -> Void = {
// do something when it successfully starts the session
RevMobAds.session().showFullscreen();
}
let errorBlock: (NSError!) -> Void = {error in
// check the error
println(error);
}
RevMobAds.startSessionWithAppID("55770fcc17dd7840727aa5e8",
withSuccessHandler: completionBlock, andFailHandler: errorBlock);
//End of RevMob Code
I have defined the modules in my package as "Yes":
This is my Bridge-Header:
// Use this file to import your target's public headers that you would like to expose to Swift.
#import <RevMobAds/RevMobAds.h>
Lastly, this is the error that I am currently getting when I try to build my code:
Thanks if you can help!
CLBeaconRegion is part of Core Location from iOS 7 on, so you need to make sure the framework is linked in your project.
Note: in my experience, CLBeaconRegion is picked up by Apple as part of app review and they often want to know how you're using beacons – even if the code is just sitting there and you're not planning to use it.
Twice now I have been asked by app review to provide a video of my app detecting a beacon so they can see how it works, so either be prepared to explain your usage or see if there's a library version without it built in.
In addition to the required frameworks, you have to add CoreLocation.framework
I had the same issue. With CoreLocation.framework, also add AVFoundation.framework. Hope that helps

How to exit app and return to home screen in iOS 8 using Swift programming

I'm trying to programmatically return to the home screen in an iOS8 App using Swift. I want to continue the application running in the background though. Any ideas on how to do this?
Thanks in advance for the help.
When an app is launched, the system calls the UIApplicationMain function; among its other tasks, this function creates a singleton UIApplication object. Thereafter you access the object by calling the sharedApplication class method.
To exit gracefully (the iOS developer library explicitly warns you not to use exit(0) because this is logged as a crash ) you can use:
UIControl().sendAction(#selector(URLSessionTask.suspend), to: UIApplication.shared, for: nil)
For example, my app exits when the user shakes the device. So, in ViewController.swift:
override func motionEnded(motion: UIEventSubtype,
withEvent event: UIEvent?) {
if motion == .MotionShake{
//Comment: to terminate app, do not use exit(0) bc that is logged as a crash.
UIControl().sendAction(Selector("suspend"), to: UIApplication.sharedApplication(), forEvent: nil)
}}
Swift 4:
UIControl().sendAction(#selector(NSXPCConnection.suspend),
to: UIApplication.shared, for: nil)
Edit: It's worth mentioning that in iOS 12 there's a bug that will prevent network connectivity if the app is brought back from background after sending the suspend action.
For that you should use following code
import Darwin
exit(0)
To force your app into the background, you can legally launch another app, such as Safari, via a URL, into the foreground.
See: how to open an URL in Swift3
UIApplication.shared.open() (and the older openURL) are a documented public APIs.
If you set the exits-on-suspend plist key, opening another app via URL will also kill your app. The use of this key is a documented legal use of app plist keys, available for any app to "legally" use.
Also, if your app, for some impolite reason, continues to allocate and dirty large amounts of memory in the background, without responding to memory warnings, the OS will very likely kill it.
How about setting of info.plist?
You can set "Application does not run in background" true in info.plist with editor.
Or Add this lines with code editor.
<key>UIApplicationExitsOnSuspend</key>
<true/>
There is no way to "programmatically return to the home screen" except for crashing, exiting your program or calling unofficial API. Neither is welcome by Apple. Plus the Human Interface Guidelines are also covering this.