Swift/Cocoa: launchApplication(at:options:configuration:) and catching errors - swift

So... I want to launch an application at a given URL using this function. The problem is, the developer documentation says:
Parameters
url
The application URL.
options
Options to use when launching the application. See
NSWorkspace.LaunchOptions for possible values.
configuration
A dictionary containing the configuration options. This
dictionary can be used to pass additional options to the app. Possible
key-value pairs are described in Workspace Launch Configuration
Options. The configuration dictionary may be empty, in which case
default behavior applies.
error
Returns, by-reference, the error if the application was unable
to be launched. You may specify nil for this parameter if you do not
want the error information.
Return Value
If the app is already running, and newInstance is not
specified in the options dictionary, then a reference to the existing
app is returned; otherwise a new application reference is returned. If
the application could not be launched, nil is returned and the error
is specified in error.
The problem is, that this doesn't match with the function signature at all, which is:
func launchApplication(at url: URL, options: NSWorkspace.LaunchOptions = [], configuration: [NSWorkspace.LaunchConfigurationKey : Any]) throws -> NSRunningApplication
Where the hell am I supposed to specify said error?

Except for the error handling, the parameters mentioned in the documentation exactly match the actual method. Both have url, options, and configuration. Though, thanks to Swift's ability to have argument labels, you use at with the url parameter when actually calling the method.
Most Objective-C APIs that have a last parameter of NSError **error are translated in Swift to an API that has no error parameter. Instead, they declare that they throw.
If you read the rest of the documentation for the method you quoted, you will see:
Handling Errors In Swift:
In Swift, this method returns a nonoptional result and is marked with the throws keyword to indicate that it throws an error in cases of failure.
You call this method in a try expression and handle any errors in the catch clauses of a do statement, as described in Error Handling in The Swift Programming Language and About Imported Cocoa Error Parameters.
You handle the error as follows:
do {
// pass real options and configuration as needed
try someWorkspace.launcApplication(at: someURL, options: [], configuration: [:])
} catch {
// handle error here as needed
print(error)
}

Related

How to make signalEnumerator(for: identifier) call the relevant enumerateChanges method

In macOS FileProvider extension, when I invoke:
manager.signalEnumerator(for: identifier) { error in
print("Signal completed with error: \(error)")
}
where identifier is identifier of the relevant folder which contains remote changes, system does NOT call the enumerateChanges method on relevant FileProviderEnumerator.
On other hand if I invoke
manager.signalEnumerator(for: .workingSet) { error in
print("Signal completed with error: \(error)")
}
there the system properly calls the relevant enumerateChanges method.
Is there a way to make sure that enumerateChanges will be invoked for the provided itemIdentifier? Or is there some explanation as to why the system doesn't call the enumerateChanges method when I provide folder item identifier?
The following note can be found buried in Using push notifications to signal changes
When using an NSFileProviderReplicatedExtension, always set the container identifier to NSFileProviderWorkingSetContainerItemIdentifier. The system automatically propagates any working set changes to the user interface, without requiring you to signal individual containers. It ignores any other identifiers.

The function has return type of 'FutureOr<User>', but doesn't end with a return statement

I am trying to create a repository for use cases of FirebaseAuth and can see warnings!
Should I ignore the warning, as it not impacting the app?
OR should I handle it now, and How?
Dart has analyzed your program and found out that you have not handled all cases how the method can exit. This is a potentially a problem since your method signature specifies that you method are returning a Future<User> object.
In your case you have not handled the case where an exception are throw from the createUserWithEmailAndPassword method. If an exception are catch, you ends up with a execution path without any return statement.
In this case, Dart will at runtime just return null so it is not an error. But it is a potential sign of a code error since it it not unlikely that you have forgotten the handling of the exception by not have any return statement.
If it is your intention to just return null in case of an exception, you should therefore have insert return null; in both of your catch blocks (or just at the bottom of your method outside the catch-blocks).
Alternative, you can rethrow the exception if you just want to add some logging but still let the caller to also handle the exception. You can read more about this in the language tour: https://dart.dev/guides/language/language-tour#catch
In either cases, you should describe the behavior in the documentation of the method.

Swift Error handling best practices

Im used to typical try catch blocks to handle exceptions of any function or block of code like objective C.
However, after making leap of faith to Swift and reading about Swift’s error handling; it’s not much what I expected. You must handle your exceptions by throwing errors and guard and checking if else everything.
I need general Swift error handling tips and best practices, and especially for the following cases:
Im using Alamofire for calling services, which has closure, and calling it through another function with closure too. AFAIK I can’t throw error inside async code, so what is best practice for such case? will try to update with code sample
Is it favourable to have every function in the app throw errors? Just in case ? Like checking every single value or result.
Can I have a Singleton Error handling module?
Thanks in advance
You must handle your exceptions by throwing errors and guard and checking if else everything.
This is an assumption that doesn't have to be true. If your code is structured correctly, you shouldn't have to check everything with if lets and throws.
I need general Swift error handling tips and best practices ...
Before you look at anything else, read the following pages in order. They should give you a good background on best practices.
Error Protocol Apple Developer Documentation
Error Handling - Apple's Swift Programming Language Guide
Magical Error Handling in Swift - Ray Wenderlich
Im using Alamofire for calling services, which has closure, and calling it through another function with closure too. AFAIK I can’t throw error inside async code, so what is best practice for such case?
You can. A good practice is to pass a closure as a parameter to your service-calling function, then call the closure when the asynchronous operation is complete, like so:
functionThatCallsAService(completion: #escaping (Data?, NetworkingErrors?) -> ()) {
session.dataTask(with: request) { data, response, error in
guard error == nil else {
completion(nil, NetworkingErrors.returnedError(error!))
return
}
completion(data, nil)
}.resume()
}
enum NetworkingErrors: Error {
case errorParsingJSON
case noInternetConnection
case dataReturnedNil
case returnedError(Error)
case invalidStatusCode(Int)
case customError(String)
}
Is it favourable to have every function in the app throw errors? Just in case? Like checking every single value or result.
If you know for sure that a function or value won't be nil/cause a runtime error/throw an error, then don't check for it! But generally, according to the web pages above, you should check for nil and/or errors from calling a web service, interacting with the file system, creating a complex object, etc.
Can I have a Singleton Error handling module?
You technically can, but I don't see any reason to do so besides logging.
enum CheckValidAge : Error{
case overrage
case underage
}
func checkValidAgeForGovernmentJob(age:Int)throws -> Bool{
if age < 18{
throw CheckValidAge.underage
}else if age > 25{
throw CheckValidAge.overrage
}else{
return true
}
}
do {
try checkValidAgeForGovernmentJob(age: 17)
print("You are valid for government job ")
}catch CheckValidAge.underage{
print("You are underage for government job ")
}catch CheckValidAge.overrage{
print("You are overrage for government job ")
}
try checkValidAgeForGovernmentJob(age: 17) OutPut : You are underage for government job
try checkValidAgeForGovernmentJob(age: 26) OutPut : You are overrage for government job
try checkValidAgeForGovernmentJob(age: 18) OutPut : You are valid for government job

Typhoon loading storyboard programmatically appears to perform asynchronous instantiation without blocking

I am developing an iOS application and am trying to integrate Typhoon into the testing. I am currently trying to mock out a dependency in a view controller that comes from the storyboard, so with in my assembly:
public dynamic var systemComponents: SystemComponents!
public dynamic func storyboard() -> AnyObject {
return TyphoonDefinition.withClass(TyphoonStoryboard.self) {
(definition) in
definition.useInitializer("storyboardWithName:factory:bundle:") {
(initializer) in
initializer.injectParameterWith("Main")
initializer.injectParameterWith(self)
initializer.injectParameterWith(NSBundle.mainBundle())
}
}
}
I want to create a CameraModeViewController (the class I am unit testing) with its dependency upon a system-camera-functions-providing protocol mocked out. The dependency is dynamic var cameraProvider: CameraAPIProvider?. I think I correctly created a replacement collaborating assembly to replace systemComponents; MockSystemComponents is a subclass of SystemComponents that overrides functions. This is where I inject the mock:
let assembly = ApplicationAssembly().activateWithCollaboratingAssemblies([
MockSystemComponents(camera: true)
])
let storyboard = assembly.storyboard()
subject = storyboard.instantiateViewControllerWithIdentifier("Camera-Mode") as! CameraModeViewController
The next line of code in the tests is let _ = subject.view, which I learned is a trick to call viewDidLoad and get all the storyboard-linked IBOutlets, one of which is required for this test.
However, I am getting very mysterious result: sometimes but not always, all the tests fail because in the viewDidLoad I make a call to the dependency (cameraProvider), and I get an "unrecognized message sent to class" error. The error seems to indicate that at the time the message is sent (which is a correct instance method in protocol CameraAPIProvider) the field is currently a CLASS and not an instance: it interprets the message as +[MockSystemCamera cameraStreamLayer] as reported in the error message.
~~~BUT~~~
Here's the kicker: if I add a breakpoint between the calls to assembly.storyboard() and subject.view, the tests always pass. Everything is set up correctly, and the message is correctly sent to an instance without this "class method" bogus interpretation. Therefore, I have to wonder if Typhoon does some kind of asynchronous procedure in the injection that I have to wait for? Possibly only when dealing with storyboard-delivered view controllers? And if so, is there any way to make sure it blocks?
After digging around in Typhoon's source for a while, I get the impression that in the TyphoonDefinition(Instance Builder) initializeInstanceWithArgs:factory: method there is an __block id instance that is temporarily a Class type, and then is replaced with an instance of that type; and possibly this can be called asynchronously without blocking, so the injected member is left as a Class type?
UPDATE: Adding the code for MockSystemComponents(camera:). Note that SystemComponents inherits from TyphoonAssembly.
#objc
public class MockSystemComponents: SystemComponents {
var cameraAvailable: NSNumber
init(camera: NSNumber) {
self.cameraAvailable = camera
super.init()
}
public override func systemCameraProvider() -> AnyObject {
return TyphoonDefinition.withClass(MockSystemCamera.self) {
(definition) in
definition.useInitializer("initWithAvailable:") {
(initializer) in
initializer.injectParameterWith(self.cameraAvailable)
}
}
}
}
UPDATE #2: I tried replacing the constructor injection in the MockSystemComponents.systemCameraProvider() with a property injection. Different issue, but I suspect it's equivalent in cause: now, the property that is injected (declared optional) is still nil some of the time when I go to unwrap it (but not always -- probably about 4/5 of test runs fail, about the same as before).
UPDATE #3: have tried using the following code block, using factory construction according to this answer (note that setting factory directly didn't work as that OP did, but I think I correctly used the feature added in response to Jasper's issue). The results are the same as when using property injection like Update #2 above), so no dice there.
This issue was in fact arising even before the call to the instantiation. In fact, the problem was assemblies aren't generally intended to be stateful. There are a few ways to get around this, but the one I used -- having a member variable and an initializer method -- is NOT recommended. The problem with doing this is that in the activateWithCollaboratingAssemblies method, all the instance methods of the assembly are enumerated for definitions, and initializers will actually get called on the collaborating assembly. Consequently, even if you create your assembly with an initializer, it may get called again with a bogus value.
Note that the reason there appeared to be async behavior is actually that there is nondeterministic order in which definitions are assembled (property of storing them in an NSDictionary). This means that if activateWithCollaboratingAssemblies happens to enumerate methods which depend on state first, they'll work fine; but if the initializer is enumerated first, and the state is destroyed, definitions that are created after will be borked.

How to add values to existing enum

I use the Alamofire swift library to do some networking in my App.
I'm currently building my own ResponseSerializer.
According to Alamofire's docs you throw errors in the serialization process like this
let failureReason = "Data could not be serialized. Input data was nil."
let error = Error.errorWithCode(.DataSerializationFailed, failureReason: failureReason)
return .Failure(data, error)
So my thought was to use this method to throw some more errors not related to Alamofire that can appear in my serialization. For example if there is a problem on the server-side, the response includes an error description as a string.
I want to add some custom error codes like .ServerErrorInJson that I can use in the errorWithCode(Alamofire.Error.Code, String) -> NSError method.
Is there a way I can add those cases to the enum Alamofire.Error.Code?