The "prefs" URL Scheme is not working in iOS 10 (Beta 1 & 2) - swift

I can't get the "prefs" URL Scheme to work in iOS 10 (Beta 1).
It's set up correctly since the same App works fine on iOS 9.
Is this a bug or did it get renamed / removed?
Code:
let settingsUrl = NSURL(string: "prefs:root=SOMETHING")
if let url = settingsUrl {
UIApplication.sharedApplication().openURL(url)
}
Update: (Beta 2)
Still not working in Beta 2.
It seams to be an bug. For example if you want do invite someone using GameCenter in iOS 10 and you're not logged into iMessage, you'll get a popup asking you to log in. But the "Settings" button does absolutely nothing.

Just replace prefs to App-Prefs for iOS 10
Below code works for iOS 8,9,10
Swift 3.0 and Xcode >= 8.1
if #available(iOS 10.0, *)
{
UIApplication.shared.openURL(URL(string: "App-Prefs:root=SOMETHING")!)
}
else
{
UIApplication.shared.openURL(URL(string: "prefs:root=SOMETHING")!)
}
Swift 2.2
if #available(iOS 10.0, *)
{
UIApplication.sharedApplication().openURL(NSURL(string:"App-Prefs:root=SOMETHING")!)
}
else
{
UIApplication.sharedApplication().openURL(NSURL(string:"prefs:root=SOMETHING")!)
}
Works for me.
Happy Coding 😊

You can use UIApplicationOpenSettingsURLString to open your own app's settings (this has been available since iOS 8) but any other prefs: URL is now considered a private API and use will result in app rejection.

You can use Prefs:root=SOMETHING
iOS 10 updated URL Scheme for Settings, you need to upcase the "p".
Ref: https://github.com/cyanzhong/app-tutorials/blob/master/schemes.md
NOTICE: It only works on Widgets, not works in Apps. (iOS 10.0.2)
#Saumil Shah's solution works in App, is more useful.

For the record, for Location services App-Prefs:root=Privacy&path=LOCATION worked for me. When I tested on a device and not a simulator.
I won't list the things I tried that did not work, it's a long list.
Usage example that assumes either location services are disabled or permission is denied or not determined:
if !CLLocationManager.locationServicesEnabled() {
if let url = URL(string: "App-Prefs:root=Privacy&path=LOCATION") {
// If general location settings are disabled then open general location settings
UIApplication.shared.openURL(url)
}
} else {
if let url = URL(string: UIApplicationOpenSettingsURLString) {
// If general location settings are enabled then open location settings for the app
UIApplication.shared.openURL(url)
}
}

This is not available on iOS 11, we can just open Settings like:
if let url = URL(string:UIApplicationOpenSettingsURLString) {
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}

If anyone is interested in a "gray area" API, you can use:
//url = "prefs:root=SOMETHING"
[[LSApplicationWorkspace defaultWorkspace] openSensitiveURL:url withOptions:nil];
This will give you what you want. Hide it well, and it works in iOS 10.

Related

iOS 13 Custom Fonts download and installation

THIS IS NOT A DUPLICATE QUESTION
I have searched for everything that I can, but I didn't find something meaningful.
Here is the question:
I want to download a font using CoreText API but it works in iOS 12, NOT available on iOS 13.
There is a demo project from Apple in 2013. But The API it used seems to be private since iOS 13.
/*
#param progressBlock
Callback block to indicate the progress.
Return true to continue, and return false to cancel the process.
This block is called on a private serial queue on OS X 10.15, iOS 13, and later.
*/
#available(iOS 6.0, *)
public func CTFontDescriptorMatchFontDescriptorsWithProgressHandler(_ descriptors: CFArray, _ mandatoryAttributes: CFSet?, _ progressBlock: #escaping CTFontDescriptorProgressHandler) -> Bool
As the documentation declared:
This block is called on a private serial queue on OS X 10.15, iOS 13, and later.
Here is my code for font download:
let fontName = "STXingkai-SC-Light"
let attributes = [kCTFontNameAttribute : fontName] as CFDictionary
let fontDescription = CTFontDescriptorCreateWithAttributes(attributes)
// create font font descriptor and add it in an array
let descs = [fontDescription] as CFArray
CTFontDescriptorMatchFontDescriptorsWithProgressHandler(descs, nil) { (state, progressParamater) -> Bool in
let progressValue = (progressParamater as Dictionary)[kCTFontDescriptorMatchingPercentage]?.doubleValue
switch state {
case .didBegin: print("didBegin")
case .didFinish: print("didFinish")
case .willBeginDownloading: print("willBeginDownloading")
case .didFinishDownloading:
print("--> download finish")
DispatchQueue.main.async {
self.fontLabel.font = UIFont(name: self.fontName, size: 20)
}
case .downloading:
print("downloading#####\(progressValue ?? 0.0)")
DispatchQueue.main.async {
self.progressView.progress = Float(progressValue ?? 0.0)
}
case .didFailWithError:
if let error = (progressParamater as Dictionary)[kCTFontDescriptorMatchingError] as? NSError {
print(error.description)
} else {
print("ERROR MESSAGE IS NOT AVAILABLE")
}
default: print(String(reflecting: state))
}
return true
}
I tried this API on iOS 12 and Xcode 10, everything works fine and I can download a font with provided fontName.
But, when I use this one on Xcode 11 Version 11.0 beta 6 (11M392q) and macOS Catalina 10.15 Beta (19A526h), something went wrong and that method is no longer effective. And I got some output:
didBegin
__C.CTFontDescriptorMatchingState
didFinish
done
"Error Domain=com.apple.CoreText.CTFontManagerErrorDomain Code=303 \"0
font descriptors do not have information to specify a font file.\"
UserInfo={NSLocalizedDescription=0
font descriptors do not have information to specify a font file.
0 font descriptors do not have information to specify a font file
The state will only have didBegin and didFinish, not calling downloading.
WWDC19 seeesion 227 has announced that CTFontManagerRegisterFontDescriptors(fontDescriptors:scope:enabled:registrationHandler:) is available and something about font management. But the source code from the session pdf is not clearly and I've tried, got an other error:
done
[Error Domain=com.apple.CoreText.CTFontManagerErrorDomain Code=306 "The file is not in an allowed location. It must be either in the application’s bundle or an on-demand resource." UserInfo={NSLocalizedDescription=The file is not in an allowed location. It must be either in the application’s bundle or an on-demand resource., CTFontManagerErrorFontURLs=(
"http://iweslie.com/fonts/HanziPenSC-W3.ttf"
), NSLocalizedFailureReason=Font registration was unsuccessful.}]
done
Here is my code referencing from WWDC19 session 227:
let urlString = "http://iweslie.com/fonts/HanziPenSC-W3.ttf"
let url = URL(string: urlString)! as CFURL
let fontURLArray = [url] as CFArray
CTFontManagerRegisterFontURLs(fontURLArray, .persistent, true) { (errors, done) -> Bool in
if done {
print("done")
}
print(errors as Array)
return true
}
And got the error:
Error Domain=com.apple.CoreText.CTFontManagerErrorDomain Code=306
"The file is not in an allowed location. It must be either in the application’s bundle or an on-demand resource."
For other people looking for a solution for iOS 13
The easiest way to do it is probably using Apple Bundle and the 'new' CTFontManagerRegisterFontURLs api.
Just gather all your urls in a list by utilizing Bundle.main.url:
let fontUrl = Bundle.main.url(forResource: fileName, withExtension: "ttf")
Then just register your fonts:
CTFontManagerRegisterFontURLs([fontUrl] as CFArray, .persistent, true) { (errors, done) -> Bool in
if(done) {
print("Done")
}
print(errors as Array)
return true
}
If you're using another solution and having problems with the font name it might be xcode which is at fault. A tip I saw in another thread for figuring out what xcode is naming your fonts are:
for family in UIFont.familyNames.sorted() {
let names = UIFont.fontNames(forFamilyName: family)
print("Family: \(family) Font names: \(names)")
}
Also remember to enable Install Fonts capability under the Signing & Capabilities in your project settings.
I have facing the same problem. and I finaly found this.
https://juejin.im/post/5aebd428f265da0ba266d897
the problem is you are using the wrong font postscriptname. the correct font name is "STXingkaiSC-Light".
I have test this on xcode 12, iOS 13 simulator.
you can find the correct font postscript name using "Font BooK.app".

How can I programmatically open 'Location Services' in iOS 12, using Swift 5?

I am developing an app for iOS, in which I have to check the GPS, whether 'Location Services' is off or not. If 'Location Services' is off, then I have to launch the Settings app with url "Settings->Privacy->Location Services", in which user can enable the 'Location Services'. For this I am using the following code
if let url = URL(string: "App-Prefs:root=Privacy&path=LOCATION")
{
if #available(iOS 10.0, *) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
else
{
UIApplication.shared.openURL(url)
// Fallback on earlier versions
}
}
But this will only launch the 'Settings' app with last active session of settings app not in 'Location Services'. I am using Swift 5 with Xcode 11 beta 2. I have listen about custom url scheme. But don't know how to use it properly. Please guide me for this.

Cannot Find Private URL For Opening Files App From Within Another App On iOS

I need to open up the Files app from within my app. This is an internal application not going on the app store so I'm not worried about trying to use private URLs.
I've tried
"App-Prefs:root=files"
"App-Prefs:root=FILES"
"App-Prefs:root=Files"
in
let url = URL.init(string: "App-Prefs:root=Files")!
UIApplication.shared.open(url, options: [:]) { (Bool) in
print("opened")
}
Depending on the string it will either not open anything or open the settings application.
According to this thread, you can use shareddocuments:// as the URLScheme to open the Files app. You can read more about how to use URLSchemes in this document in the section called Using URL Schemes to Communicate with Apps.
For anyone looking for the code:
// give your initial URL as fileUrl
let sharedPath = fileUrl.absoluteString.replacingOccurrences(of: "file://", with: "shareddocuments://")
guard let sharedUrl = URL(string: sharedPath) else {
return
}
UIApplication.shared.open(sharedUrl)

INInteraction donation duplicated in Spotlight IOS 12 Beta 5

I am donating an INInteraction and everything is working ok, however I can't understand why the donation is showing up duplicated in Spotlight.
Is there a property that needs to be set in order to prevent this??
let viewUsageIntent = UsageIntent()
var susbcribers = [INObject]()
for sub in account.subscribers {
let inObject = INObject(identifier: sub.phoneNumber, display: sub.id)
susbcribers.append(inObject)
}
viewUsageIntent.suggestedInvocationPhrase = phrase
viewUsageIntent.ban = account.ban
viewUsageIntent.subs = susbcribers
let interaction = INInteraction(intent: viewUsageIntent, response: nil)
interaction.donate(completion: {
error in
if let err = error {
MyAppServices.Logger.error(tag: "UsageIntentDonation", message: "Donation for ban \(account.ban) could not be completed: \(err.localizedDescription)")
}
})
Anybody dealing with this issue?. Thanks.
This is not a bug. This is the default behaviour when you set your simulator or iPhone to display recent shortcuts in the developer section. I was just confused by it.
Is not a duplication, it is just displaying the newest one over older ones for development sake

Open another Mac app

In my app I would like to open another app that is installed on the User's Mac (such as iPhoto). I am not sure what I should be looking for in the documentation. What is this called and how should I do it?
Thank you
Swift 5 or later
import Cocoa
func openPhotos() -> Bool {
if let photosApp = FileManager.default.urls(
for: .applicationDirectory,
in: .systemDomainMask
).first?.appendingPathComponent("Photos.app") {
return NSWorkspace.shared.open(photosApp)
}
return false
}
Usage:
if openPhotos() {
print(true)
}
Or using launchApplication with the app name parameter in the method:
import Cocoa
func openApp(_ named: String) -> Bool {
NSWorkspace.shared.launchApplication(named)
}
Usage:
if openApp("Photos") {
print(true)
}
XCode 11 • MacOS Catalina 10.15 • Swift 5
NSWorkspace.shared.launchApplication is deprecated and starting from the MacOS 10.15 the new function NSWorkspace.shared.openApplication shall be used.
Example - open terminal application by its bundle id
guard let url = NSWorkspace.shared.urlForApplication(withBundleIdentifier: "com.apple.Terminal") else { return }
let path = "/bin"
let configuration = NSWorkspace.OpenConfiguration()
configuration.arguments = [path]
NSWorkspace.shared.openApplication(at: url,
configuration: configuration,
completionHandler: nil)
Example - open terminal application by its path
let url = NSURL(fileURLWithPath: "/System/Applications/Utilities/Terminal.app", isDirectory: true) as URL
let path = "/bin"
let configuration = NSWorkspace.OpenConfiguration()
configuration.arguments = [path]
NSWorkspace.shared.openApplication(at: url,
configuration: configuration,
completionHandler: nil)
You can use NSWorkspace class written by Swift/Cocoa.
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSWorkspace_Class/index.html#//apple_ref/occ/instm/NSWorkspace/launchApplication:
let task = NSTask.launchedTaskWithLaunchPath(<#path: String#>, arguments: <#[AnyObject]#>) will probably do what you want
I second the answer by vookimedlo - unfortunately, I cannot yet comment (silly reputation limit) so I post this as an extra answer.
This is just one caveat, which might not affect too many: while launchApplication() accepted a path to an executable (e.g. "MyApp.app/Contents/MacOS/MyApp"), this will result in an error (lacking privileges) with openApplication(::). You have to supply the path to the app bundle ("MyApp.app") instead.
Of particular interest when you try to make a helper ("launcher") to add as a login item. See the following and keep my comment in mind:
https://theswiftdev.com/2017/10/27/how-to-launch-a-macos-app-at-login/
(GREAT article by Tibor Bödecs)
BTW, as for vookimedlo's code: in my experience, you don't need to specify the OpenContext.arguments with [path], you can simply pass a default NSWorkspace.OpenContext()...
There are different ways to do that. The most efficient is to use fvork and execve - see man vfork and man execve.
Less efficient but more flexible is to use the system library call. What that actually does is runs a shell - like bash - then passes the string you provide, to bash. So you can set up pipelines, redirection and such.
Or you can send an Apple Event to the Finder: "Tell Finder Open iPhoto".
In the first two cases you want to launch the executable inside the bundle, that is, /Applications/iPhoto.app/Contents/MacOS/iPhoto.
Try the above from the command line, in the Terminal:
$ /Applications/iPhoto.app/Contents/MacOS/iPhoto
You'll see the iPhoto App launch.