Below is some code I wrote:
func Begin(input: String) -> Double {
let inputs = (input as NSString).lowercaseString
var smth = AnalyzeInput(input: inputs)
return smth.findOutput()
}
Line 3 of this code (the 'smth' declaration) begins a long series of code that, at many instances, may throw some kind of an error (typically an index out of bounds error when creating a substring). Because this code is about 5,000 lines in 15 different files, and manually handling each exception would take a long time for each one, I would greatly prefer to not have to manually handle these exceptions.
I understand that there is no try / catch / finally structure in Swift, but is there anything where I can mimic that functionality? Or did I just dig myself into a giant hole?
If you're throwing exceptions in cases that are not catastrophic errors (i.e. you expect to crash soon), then you've dug yourself a hole. Cocoa does not use exceptions for general error handling. It uses them for truly exceptional situations. It's up to you to avoid index-out-of-bounds.
See the Error Handling Programming Guide for Cocoa for documentation on Cocoa error handling. Your function should likely look something like this:
func begin(input: String, error: NSErrorPtr) -> Double? {
let inputs = input.lowercaseString // "as NSString" is no longer needed
let smth = AnalyzeInput(input: inputs, error: NSErrorPtr) // pass along your error pointer
return smth?.findOutput() // Use ?. to return nil if smth is nil, or Double? otherwise
}
Some of us are exploring more functional approaches, such as a Result object. These are all still pretty experimental, but in principle it would look like this:
func begin(input: String) -> Result<Double, NSError> {
let inputs = input.lowercaseString
let smth = AnalyzeInput(input: inputs) // AnalyzeInput would return a Result
return smth.map { $0.findOutput() } // And then we map it to the final result
}
Related
Just learning about the Swift guard keyword, I'm wondering what advantage it shall accomplish.
For example, let's say I have the following code:
func getEmail(email: String?) -> String {
guard email != "" else {
return ""
}
return email! + "#somewhere.com"
}
print(getEmail(email: "test123"))
I could write ...
if email == "" {
return ""
}
return email! + "#somewhere.com"
... as well.
So what's the advantage of having the guard-keyword?
Is it just having (a little bit) of syntactic sugar?
I don't think that is a very good example of using guard, it is more common to use it with variables that might be nil (aka optional) or functions that might return nil. I suggest you read about the guard statement in the Swift Programming Language book (just scroll down a bit to "Early Exit")
We could make a better example from your code that is lacking some validation
func getEmail(email: String?) -> String? {
guard let input = email, !input.isEmpty else {
return nil
}
return input + "#somewhere.com"
}
Here we use guard to check that the parameter email is not nil by assigning it to a local variable input. If it is nil the function will return nil and otherwise it will check if it is empty and then it will also return.
If it is ok the function will continue and create and return an email address. Note that the function is declared to return an optional string since I think it is much clearer if a function like this returns nil rather than an empty string if it fails.
Guard statement is early exit statement the main use of guard statement is to transfer program control out of a scope on certain conditions. These statements are similar with if statements which executes statements based on certain condition but unlike if, the guard statements only run when certain conditions are not met.
i strongly recommended to read this article Swift guard statement
If your code needs to decide whether to exit early, then you use the guard construct. A guard statement consists of a condition and an else block. The else block must leave the current scope, by any of the means that Swift provides, such as return, break, continue, throw, or fatalError. In case of failure of the condition, execution absolutely will not proceed within the block that contains the guard construct. Most of the time the construct is used to check Optional values, similar to if let statement. Unlike if let and/or while let, guard let doesn’t declare the bound variable for a nested scope, it declares it for this scope. Guys gave good examples on their answers about that. I would like to pay more attention on other possible cases as well.
A guard construct will also come in handy in conjunction with try?. Let’s presume we can’t proceed unless String(contentsOfFile:) succeeds. Then we can do the following:
guard let text = try? String(contentsOfFile: path) else {return}
There is also a guard case construct, forming the logical inverse of if case, e.g.:
guard case let .number(n) = error else { return }
There is one more interesting problem that you can solve using guard case construct. Suppose we have a function whose returned value we want to check in a guard statement:
guard howMany() > 10 else { return }
All looks good, but suppose that in the next line you want to use the value returned from that function. You may not want to call that function over again, because that might be time-consuming or it may have any side effects. You can't capture the result of calling the function with with guard let, because that requires an Optional, and the function howMany() doesn’t return an Optional.
In this case, you can use guard case construct in following way:
guard case let amount = howMany(), amount > 10 else { return }
I don't think for email id it would be that beneficial,
But consider example where there is a function where among students,
for one student you have to perform some crucial operation but what if
the student does not exist then there is no point in doing anything so
there comes your here as early exist which you can do using guard
statement.
let student = studentArr.filter{$0.id == "1234"}
guard let stdnt = student[0], stdnt != nil else {
return nil
}
// calculate marks of this student for all years' maths subject to prepare analytics
if let and guard let serve similar, but distinct purposes.
The "else" case of guard must exit the current scope. Generally that
means it must call return or abort the program. guard is used to
provide early return without requiring nesting of the rest of the
function.
if let nests its scope, and does not require anything special of it.
It can return or not.
In general, if the if-let block was going to be the rest of the
function, or its else clause would have a return or abort in it, then
you should be using guard instead. This often means (at least in my
experience), when in doubt, guard is usually the better answer. But
there are plenty of situations where if let still is appropriate.
Long time IOS developer/tinkerer here. I mostly taught myself programming, OBJ-C back in the day and now Swift. So apologies in advance if things I ask are too basic, its partly because I may not be well versed on some fundamentals.
I am currently working on an app. Alongside it I have been reading a fair bit on writing testable code and testing in general. I am not talking about purely TDD but I would like the libraries that I am creating for the app to have a good testset available. Partly because its good practice and partly because its soemthing I want to learn to do better.
So here goes, in my app class(es) I have a number of functions that take in parameters and give an output (as you do!). However, a number of these functions also make changes to class properties as data in these properties will be used in other class functions. For example:
class SomeClass() {
var someArrayProperty: [String] = []
var someInputParameter: String
init(input: String) {
//some initialisation code
self.someInputParameter = input
//Call function to build object
let object = self.buildObject(inputParameter: self.someInputParameter)
}
func buildObject(inputParameter: String) -> SomeObject {
let objectToReturn = SomeObject(withInputParameter: inputParameter)
let stringToAddToArray = "Object 1 created"
self.someArrayProperty.append(stringToAddToArray)
return objectToReturn
}
}
From what I have read about the testing, the code should ideally be such that it should do one job and not change something outside of the code as it becomes untestable for complex code. Here, the issue I have is that I am directly changing the someArrayProperty from within the method, i.e. changing something outside of the method.
Although its not an issue for the code and everything works fine, I would like to understand what do you guys feel about things like this from a testing point of view in your own code? And what pattern/changes you generally follow in your own code to avoid it?
Again, apologies if its too basic a question but I think it will help me fill in gaps in my knowledge to be able to write more beautiful code rather than something that just works and breaks next time a minor update is done somwhere. :)
Thanks
So if your function is called buildObject, it should do job inside it and have no return value. But if you call it buildedObject, it should return constructed object. You can read more about in Apple Naming Methods documentation.
And so your code should look like this:
class SomeClass() {
var someArrayProperty: [String] = []
var someInputParameter: String
init(input: String) {
//some initialisation code
self.someInputParameter = input
//Call function to build object
let object = self.buildedObject(inputParameter: self.someInputParameter)
// Other code which modifies data
let stringToAddToArray = "Object 1 created"
self.someArrayProperty.append(stringToAddToArray)
}
func buildedObject(inputParameter: String) -> SomeObject {
let objectToReturn = SomeObject(withInputParameter: inputParameter)
return objectToReturn
}
}
What is the most elegant way to transform my ReactiveSwift's SignalProducer<A, NetworkError> into a Signal<A, NoError>?
Most of the time, my signal producer is the result of a network call, so I want to split the results into two cases:
if a value is available, send a Signal<A, NoError>
if an error happened, send a Signal<String, NoError> with the error's localized description
(why? because i'm trying to be as MVVM as possible)
So far, I end up writing a lot of boilerplate like the following:
let resultsProperty = MutableProperty<SearchResults?>(nil)
let alertMessageProperty = MutableProperty<String?>(nil)
let results = resultsProperty.signal // `Signal<SearchResults?, NoError>`
let alertMessage = alertMessageProperty.signal // `Signal<String?, NoError>`
// ...
searchStrings.flatMap(.latest) { string -> SignalProducer<SearchResults, NetworkError> in
return MyService.search(string)
}
.observe { event in
switch event {
case let .value(results):
resultsProperty.value = results
case let .failed(error):
alertMessageProperty.value = error
case .completed, .interrupted:
break
}
}
ie:
using MutableProperty instances, that I have to set as optional to be able to initialize them
creating signals from those, ie getting a signal sending optionals as well
it feels dirty and makes the code so intertwined it kind of ruins the point of being reactive
Any help on (A) keeping my signals non optional and (B) splitting them into 2 NoError signals elegantly would be greatly appreciated.
Edit - Second Attempt
I will try to answer all your questions / comments here.
The errors = part doesn't work as flatMapError expects a SignalProducer (ie your sample code works just because searchStrings is a Signal string, which coincidently is the same as the one we want for errors: it does not work for any other kind of input)
You are correct, this is because flatMapError does not change the value type. (Its signature is func flatMapError<F>(_ transform: #escaping (Error) -> SignalProducer<Value, F>) -> SignalProducer<Value, F>). You could add another call to map after this if you need to change it into another value type.
the results = part behaves weirdly as it terminates the signal as soon as an error is met (which is a behavior I don't want) in my real-life scenario
Yes, this is because the flatMap(.latest) forwards all errors to the outer signal, and any error on the outer signal will terminate it.
Okay so here's an updated version of the code, with the extra requirements that
errors should have different type than searchStrings, let's say Int
Any error from MyService.search($0) will not terminate the flow
I think the easiest way to tackle both these issues is with the use of materialize(). What it does is basically "wrap" all signal events (new value, error, termination) into a Event object, and then forward this object in the signal. So it will transform a signal of type Signal<A, Error> into a Signal<Event<A, Error>, NoError> (you can see that the returned signal does not have an error anymore, since it is wrapped in the Event).
What it means in our case is that you can use that to easily prevent signals from terminating after emitting errors. If the error is wrapped inside an Event, then it will not automatically terminate the signal who sends it. (Actually, only the signal calling materialize() completes, but we will wrap it inside the flatMap so the outer one should not complete.)
Here's how it looks like:
// Again, I assume this is what you get from the user
let searchStrings: Signal<String, NoError>
// Keep your flatMap
let searchResults = searchStrings.flatMap(.latest) {
// Except this time, we wrap the events with `materialize()`
return MyService.search($0).materialize()
}
// Now Since `searchResults` is already `NoError` you can simply
// use `filterMap` to filter out the events that are not `.value`
results = searchResults.filterMap { (event) in
// `event.value` will return `nil` for all `Event`
// except `.value(T)` where it returns the wrapped value
return event.value
}
// Same thing for errors
errors = searchResults.filterMap { (event) in
// `event.error` will return `nil` for all `Event`
// except `.failure(Error)` where it returns the wrapped error
// Here I use `underestimatedCount` to have a mapping to Int
return event.error?.map { (error) in
// Whatever your error mapping is, you can return any type here
error.localizedDescription.characters.count
}
}
Let me know if that helps! I actually think it looks better than the first attempt :)
First Attempt
Do you need to access the state of you viewModel or are you trying to go full state-less? If state-less, you don't need any properties, and you can just do
// I assume this is what you get from the user
let searchStrings: Signal<String, NoError>
// Keep your flatMap
let searchResults = searchStrings.flatMap(.latest) {
return MyService.search($0)
}
// Use flatMapError to remove the error for the values
results = searchResults.flatMapError { .empty }
// Use flatMap to remove the values and keep the errors
errors = searchResults.filter { true }.flatMapError { (error) in
// Whatever you mapping from error to string is, put it inside
// a SignalProducer(value:)
return SignalProducer(value: error.localizedDescription)
}
We have an app with a fairly broad Core Data model, with lots of custom subclasses implemented in Objective C, but which are also used by a growing fraction of the app that's written in Swift. (For what it's worth: we're building with Xcode 7.3.1 against iOS 9.3, and the Swift code is thus 2.2.)
There's a helper function written in Swift that looks like this:
extension NSManagedObject {
func inContext<T: NSManagedObject>(moc: NSManagedObjectContext) -> T? {
guard self.managedObjectContext != moc else {
return (self as! T)
}
do {
let obj = try moc.existingObjectWithID(self.objectID)
return (obj as! T) // <--- fails here
}
catch let error {
return nil
}
}
}
This is called in a fair number of places where objects jump contexts. Calling code generally looks like this:
let result: ECFoo? = foo.inContext(managedObjectContext)
This works flawlessly in debug builds of the app. But with optimizations turned on, I'm running into a case where this call fails at the line I've marked, where the fetched object is being cast from NSManagedObject to the correct subclass. The stack trace starts with swift_dynamicCastObjCClassUnconditional, and the message that gets logged to the console is:
Could not cast value of type 'ECFoo_Foo_' (0x7fb857d2c250) to 'ECFoo_Foo_' (0x7fb857d2c250).
If I put a breakpoint on that line, what I'm attempting to do seems fine in the debugger console:
(lldb) po moc.existingObjectWithID(self.objectID) is ECFoo
true
This is deeply confusing, because it's clearly the same type on both sides here, and they both appear to be the dynamically generated subclass, rather than the formal class that it should be trying to cast to (based on inference of the calling code). I can only assume that there's some piece of information being optimized away that is necessary to make this work, but I'm not entirely sure how to fix it.
I'm writing a test method where I want the SUT to throw an exception when under certain conditions. The code looks like this:
- (void) testCantStartTwice
{
XCTAssertThrows([self.sut start], #"");
}
Now, all is good and the test passes. However, I have Xcode set an Exception Breakpoint for all ObjC exceptions, which is pretty useful when testing out an app in the debugger. As you now, now when I execute my test suite with ⌘U, now it stops at that test and looks like if it's failing, even though it says "Test Succeeded".
Any way of making the breakpoint not stop at that test?
Thanks and all the best
Unfortunately, it does not seem possible to have exception breakpoints ignore anything wrapped in XCTAssertThrows (or now XCTAssertThrowsError).
Workaround 1: Deal with the breakpoints or script around them
When you execute these tests, you can either do the following:
Disable the exception breakpoint.
Hit run every time it hits one of your breakpoints.
Both are not ideal as you'll be doing some manual work.
There are suggestions for how to disable breakpoints for Swift XCTAssertThrowsError assertions here. I have not tested those.
Workaround 2: Return Result<T, Error> instead
My original method looked like this:
struct Truck {
static func makeTruck(numberOfWheels: Int) throws -> Self {
if numberOfWheels < 4 {
throw TruckError.tooFewWheels
}
// ...
return .init()
}
}
// Code example:
let truck: Truck? = try? Truck.makeTruck(numberOfWheels: 1)
// Test example:
XCTAssertThrowsError(try Truck.makeTruck(numberOfWheels: 1))
To work around this problem of breakpoints, I created a duplicate internal function that is more testable (since it won't cause the breakpoint problem) by returning a Result instead of throwing an Error.
struct Truck {
static func _makeTruck(numberOfWheels: Int) -> Result<Truck, Error> {
if numberOfWheels < 4 {
return .failure(TruckError.tooFewWheels)
}
// ...
return .success(.init())
}
static func makeTruck(numberOfWheels: Int) throws -> Self {
switch _makeTruck(numberOfWheels: numberOfWheels) {
case .failure(let error):
throw error
case .success(let value):
return value
}
}
}
// Code example:
let truck: Truck? = try? Truck.makeTruck(numberOfWheels: 1)
// Test example:
XCTAssertEquals(Truck._makeTruck(numberOfWheels: 1), .error(TruckError.tooFewWheels))
Workaround 3: Return T? instead
Finally, if you have a simpler use-case, and your function's failure is quite obvious (e.g. there is only one type of error it can return), consider just returning an optional instead of needing the complexity of the Result type:
struct Truck {
static func makeTruck(numberOfWheels: Int) -> Truck? {
if numberOfWheels < 4 {
return nil
}
// ...
return .init()
}
}
// Code example:
let truck: Truck? = Truck.makeTruck(numberOfWheels: 1)
// Test example:
XCTAssertNil(Truck.makeTruck(numberOfWheels: 1))
Perhaps you could use a Test Failure Breakpoint instead of an Exception breakpoint when you are testing. The wwdc 2013 video on unit testing outlined a pretty good workflow for inspecting test failures. It essentially said:
Set a Test Failure Breakpoint which will allow you to inspect the conditions that caused the failure.
If needed, set a manual breakpoint earlier in the test and rerun so you can step through the statements leading to the failure as usual.
This isn't really a direct answer, but as far as I know, I don't think there is a way to create exceptions to the exception breakpoint. Hope it helps
I had the same issue and I was looking for solution for 2 hours. Probably we can't do anything with that.