Our codebase is still very much a hybrid codebase, but I've been trying to do more work in Swift. Unfortunately I'm running into a weird situation where a command that works fine in Obj-C absolutely refuses to run in Swift.
I'm trying to send a file from one of our desktop products to another of our products. All of the URL schemes are set up correctly. This command in Obj-C works perfectly:
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:#"ourotherapp://v1/import?filePath=/Users/gi.joe/Documents/test.wav"]];
However, in trying to use the Swift version, this one absolutely refuses to execute. No exception, no clue, no nothing:
NSWorkspace.shared.open(URL(fileURLWithPath: "ourotherapp://v1/import?filePath=/Users/gi.joe/Documents/test.wav"))
Is there some difference in the way NSWorkspace works in Swift as opposed to Obj-C? I've looked over the Apple docs but maybe I missed something?
I see that you tried two different calls in Obj C and Swift.
I haven't tried the below code, maybe this example helps you:
if let checkURL = NSURL(string: "https://google.com") {
if NSWorkspace.shared().open(checkURL as URL) {
print("URL Successfully Opened")
}
} else {
print("Invalid URL")
}
Try this:
guard let url = URL(string: "someURL") else { return }
UIApplication.shared.open(url)
Related
I have a watch app with complications. Updating the complication on a watch face did work for a long time, but stopped recently, maybe due to a watchOS update.
The reason is that the activeComplications property of the CLKComplicationServer.sharedInstance() is nil, although my complication placeholder is shown on the watch face (device & simulator).
The code could not be simpler:
final class ComplicationController: NSObject, CLKComplicationDataSource {
// …
func updateComplications() {
//…
let complicationServer = CLKComplicationServer.sharedInstance()
if let activeComplications = complicationServer.activeComplications {
for complication in activeComplications {
complicationServer.reloadTimeline(for: complication)
}
}
//…
}
//…
}
If I stop at a breakpoint at the if let instruction, complicationServer has the following values:
And the following lldb command outputs nil:
What could be the reason?
My bad: I solved the problem 4 years ago, but forgot the solution during refactoring of the app.
Actually I don’t know if this is a solution, a workaround or a hack:
I suspect that the CLKComplicationServer or its CLKComplicationDataSource, i.e. the ComplicationController, is not correctly initialized if ComplicationController.shared is executed anywhere in the code. If not, the ComplicationController is correctly initialized by the CLKComplicationServer.
Therefore, one cannot call any function in the ComplicationController, e.g. to update complications. Instead one can send a notification to the ComplicationController that executes the requested function. Of course, one has to ensure that the ComplicationController is already initialized and registered to receive such a notification before it is posted.
If so, CLKComplicationServer.sharedInstance().activeComplications is no longer nil, and the complication update works.
I have been following this tutorial, which is talking about how to communicated from React Native and objective C. I followed the tutorial, and got a good result. The problem started when I wanted to add a new function called sendData. This would be called in JS and it would pass a string, and in objective C, it would receive the string.
But, the bridging wasn't working.
Here is my code:
RCT_REMAP_METHOD(sendData : (NSString*)str,
sendData_resolver:(RCTPromiseResolveBlock)resolve
sendData_rejecter:(RCTPromiseRejectBlock)reject)
{
BOOL response = [_cppApi sendData];
resolve(response);
}
This code gives the error No visible #interface for 'CCCppCom' declares the selector 'sendData' in Xcode. (CCCppCom is a header file)
another function I have, which is getData seems to work fine.
RCT_REMAP_METHOD(getData,
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
NSString *response = [_cppApi getData];
resolve(response);
}
I'm not entirely sure what is going on here. I checked CCCppCom, and sendData is indeed there.
Can someone help me? Thanks in advance.
I solved the problem by using RCT_EXPORT_METHOD instead of RCT_REMAP_METHOD.
I've been working on a new Document-based app, and was super glad about the new UIDocumentBrowserController...trying to roll my own solution for the document browser UI was tricky!
I'm having some trouble getting the browser to open documents after they've been created.
What happens now is that when I choose to create a new document in the document browser, the document is created and opened as expected, although an error message is logged. However, after the doc is closed, I cannot reopen the file, either immediately or upon subsequent launches, even though the document is displayed. However, a weird clue here is that if I stop running the app after creating the document, but without adding new information to it (triggering the save cycle), and run the project again, I can open the file correctly. Whuch makes me think that there's something in the way the files are being saved that is the issue.
(Note: At this phase, I'm working on getting the local, non/icloud implentation working, before I move on to the icloud implementation.)
Here is the error message at any point in the code whenthe document is saved to disk (or at least most of the time!):
2017-06-20 13:21:58.254938-0500 Sermon Design 2 iOS[22454:5000138] [default] [ERROR] Could not get attribute values for item file:///Users/stevenhovater/Library/Developer/CoreSimulator/Devices/9A4364F2-B3A1-4AD9-B680-FB4BC876C707/data/Containers/Data/Application/DD534ED8-C4A3-40FE-9777-AED961976878/Documents/Untitled-9.sermon. Error: Error Domain=NSFileProviderInternalErrorDomain Code=1 "The reader is not permitted to access the URL." UserInfo={NSLocalizedDescription=The reader is not permitted to access the URL.}
I suspect that the issue lies somewher in my document types plists, which I've tried to set up by imitating the setup in the video for wwdc 2017 session 229.
My docs are encapuslated by an NSData object, using what I take to be a pretty standard subclass implentation of UIDocument. (I'm omitting the code to generate the thumbnails)
override func contents(forType typeName: String) throws -> Any {
print("Saving Document Changes")
if sermon != nil {
let newData = NSKeyedArchiver.archivedData(withRootObject: sermon!)
return newData
} else {
let newData = NSKeyedArchiver.archivedData(withRootObject: Sermon())
return newData
}
}
override func fileAttributesToWrite(to url: URL, for saveOperation: UIDocumentSaveOperation) throws -> [AnyHashable : Any] {
let thumbnail:UIImage = self.createThumbnail()
let thumbnaildict = [URLThumbnailDictionaryItem.NSThumbnail1024x1024SizeKey : thumbnail]
let dict = [URLResourceKey.thumbnailDictionaryKey:thumbnaildict]
return dict
}
override func load(fromContents contents: Any, ofType typeName: String?) throws {
guard let newSermon:Sermon = NSKeyedUnarchiver.unarchiveObject(with: contents as! Data) as? Sermon else{
throw documentErrors.invalidFile
}
self.sermon = newSermon
}
In my subclass of UIDocumentBrowserViewController, Here is my code for getting a local filename and for creating the new document.
func documentBrowser(_ controller: UIDocumentBrowserViewController, didRequestDocumentCreationWithHandler importHandler: #escaping (URL?, UIDocumentBrowserViewController.ImportMode) -> Void) {
var newDocumentURL: URL? = nil
print("creating new local document")
guard let target = self.newLocalFilename() else {
return
}
let targetSuffix = target.lastPathComponent
let tempURL = URL(fileURLWithPath: NSTemporaryDirectory() + targetSuffix)
let newDocument:SDDocument = SDDocument(fileURL: tempURL)
newDocument.sermon = Sermon()
/
newDocument.save(to: tempURL, for: .forCreating) { (saveSuccess) in
/
guard saveSuccess else {
/
importHandler(nil, .none)
return
}
/
newDocument.close(completionHandler: { (closeSuccess) in
/
guard closeSuccess else {
/
importHandler(nil, .none)
return
}
/
importHandler(tempURL, .move)
})
}
}
func newLocalFilename() -> URL? {
let fileManager = FileManager()
guard let baseURL = self.localDocumentsDirectoryURL.appendingPathComponent("Untitled")
else {return nil}
var target = baseURL.appendingPathExtension(DocumentBrowserViewController.documentExtension)
var nameSuffix = 2
while fileManager.fileExists(atPath: target.path) {
target = URL(fileURLWithPath: baseURL.path + "-\(nameSuffix).\(DocumentBrowserViewController.documentExtension)")
nameSuffix += 1
}
let targetSuffix = target.lastPathComponent
print("Target name: \(targetSuffix)")
print("new url: \(target)")
return target
}
After four or five hours of work banging my head against this problem, I discovered a simple solution: don't test in the Simulator. I switched to testing on my device and instantly everything started working as advertised.
[I can't speak from experience here, but it may be that the "doesn't work in the Simulator" problem is confined to Sierra, but that the Simulator does work in High Sierra. This would explain why some users see this issue and others don't, and especially why Apple seems blissfully unaware of it in the WWDC video.]
I had exactly the same issue when I was trying to save to NSTemporaryDirectory().
If you instead save to the documents directory ([[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] firstObject]), it appears to work fine!
Update: it looks like this issue is fixed in iOS 11 beta 3, and you can now save newly created documents to NSTemporaryDirectory() correctly.
Here is my current theory.
This error
Error Domain=NSFileProviderInternalErrorDomain Code=1 "The reader is not permitted to access the URL."
shows up when first creating a UIDocument at a new URL using -initWithFileURL. It's basically saying "this URL doesn't exist yet," but in a way that makes it sound more like a permissions issue.
As far as I can tell, it doesn't prevent you from saving, opening, editing, or closing the file. So I think it's just a superfluous error that Apple should have taken out.
I found that the error happens on simulator when LSSupportsOpeningDocumentsInPlace property is set to YES in the info.plist.
Turn this property to NO, then it starts working, in my case.
On the real device, it works anyway without having error.
Had similar problem, and tried the method for providing a default Core Location in the Schemes settings and it works now. The method was mentioned in this answer: IOS 9 Error Domain=kCLErrorDomain Code=0 "(null)"
I am having some issues migrating from Swift 1.2 to 2.0 particularly understanding error handling when calling methods from objective-c API. For example, I can't implement the method checkResourceIsReachableAndReturnError correctly. According to the Swift documentation for this method it will return a Bool. I want to check if a folder (self.baseURL) exists and tried the two following approaches:
let folderExists: Bool
do{
try folderExists = self.baseURL!.checkResourceIsReachableAndReturnError(){
} catch let error as NSError {
}
}
And:
let folderExists: Bool
var error: NSError?
do{
try folderExists = self.baseURL!.checkResourceIsReachableAndReturnError(&error){
} catch error as NSError {
}
}
But neither approaches work. I also thought that the Swift implementation of error throwing functions with the word AndReturnError would be shortened to checkResourceIsReachable but that does not seem to be the case.
I am sure I am doing something fundamentally wrong here and if someone has a suggestion for where to look I would appreciate that.
This method indeed returns a bool, it hasn't been modified to follow the new do try catch mechanism of Swift 2: it doesn't throw.
So you just use it like this, without do try catch:
var error:NSError?
let folderExists = self.baseURL!.checkResourceIsReachableAndReturnError(&error)
I've been working on a BIG project (there's no point of showing any actual code anyway) and I've notice that the following message appears in the logs:
CoreText CopyFontsForRequest received mig IPC error (FFFFFFFFFFFFFECC) from font server
The error pops up as soon as a WebView has finished loading. And I kinda believe it's the culprit behind a tiny lag.
Why is that happening? What can I do to fix this?
P.S. Tried the suggested solution here to check whether it was something system-specific, but it didn't work.
More details:
The error appears when using the AMEditorAppearance.car NSAppearance file, from the Appearance Maker project. Disabling it (= not loading it all) makes the error go away.
I don't really care about the error message, other than that it creates some weird issues with fonts. E.g. NSAlert panels, with input fiels, show a noticeable flicker and the font/text seems rather messed up, in a way I'm not sure I can accurately describe. (I could post a video with that if that'd help)
This is probably related to system font conflicts and can easily be fixed:
Open Font book
Select all fonts
Go to the file menu and select "Validate fonts"
Resolve all font conflicts (by removing duplets).
Source: Andreas Wacker
Answer by #Abrax5 is excellent. I just wanted to add my experience with this problem and could not fit it into a comment:
As far as I can tell, this error is raised only on the first failed attempt to initialise an NSFont with a font name that is not available. NSFont initialisers are failable and will return nil in such a case at which time you have an opportunity to do something about it.
You can check whether a font by a given name is available using:
NSFontDescriptor(fontAttributes: [NSFontNameAttribute: "<font name>"]).matchingFontDescriptorWithMandatoryKeys([NSFontNameAttribute]) != nil
Unfortunately, this also raises the error! The following method does not, but is deprecated:
let fontDescr = NSFontDescriptor(fontAttributes: [NSFontNameAttribute: "<font name>"])
let isAvailable = NSFontManager.sharedFontManager().availableFontNamesMatchingFontDescriptor(fontDescr)?.count ?? 0 > 0
So the only way I found of checking the availability of a font of a given name without raising that error is as follows:
public extension NSFont {
private static let availableFonts = (NSFontManager.sharedFontManager().availableFonts as? [String]).map { Set($0) }
public class func available(fontName: String) -> Bool {
return NSFont.availableFonts?.contains(fontName) ?? false
}
}
For example:
NSFont.available("Georgia") //--> true
NSFont.available("WTF?") //--> false
(I'm probably overly cautious with that optional constant there and if you are so inclined you can convert the returned [AnyObject] using as! [String]...)
Note that for the sake of efficiency this will not update until the app is started again, i.e. any fonts installed during the app's run will not be matched. If this is an important issue for your particular app, just turn the constant into a computed property:
public extension NSFont {
private static var allAvailable: Set<String>? {
return (NSFontManager.sharedFontManager().availableFonts as? [String]).map { Set($0) }
}
private static let allAvailableAtStart = allAvailable
public class func available(fontName: String) -> Bool {
return NSFont.allAvailable?.contains(fontName) ?? false
}
public class func availableAtStart(fontName: String) -> Bool {
return NSFont.allAvailableAtStart?.contains(fontName) ?? false
}
}
On my machine available(:) takes 0.006s. Of course, availableAtStart(:) takes virtually no time on all but the first call...
This is caused by calling NSFont fontWithFamily: with a family name argument which is not available on the system from within Chromium's renderer process. When Chromium's sandbox is active this call triggers the CoreText error that you're observing.
It happens during matching CSS font family names against locally installed system fonts.
Probably you were working on a Chromium-derived project. More info can be found in Chromium Bug 452849.