Swift defer statement placement [duplicate] - swift

I have the following swift code executing in playground:
func A() {
print ("Hello")
guard 1 == 2 else {
return
}
defer {
print ("World")
}
}
A()
I expected to see
Hello
World
Instead only the Hello is printed. Why is this? What am I missing?
Here is a better example:
enum MyError: ErrorType {
case TriggerDefer
}
func throwsMyError() throws {
let myzero = Int(arc4random_uniform(1))
guard myzero > 1 else {
throw MyError.TriggerDefer
}
}
func A() throws {
try throwsMyError()
defer {
print ("Hello World")
}
}
As per the answers and comments, the correct way to do this (with an example) is
enum MyError: ErrorType {
case TriggerDefer
}
func throwsMyError() throws {
let myzero = Int(arc4random_uniform(1))
print("Hello")
guard myzero > 1 else {
throw MyError.TriggerDefer
}
}
func A() throws {
defer {
print ("World")
}
try throwsMyError()
}
The output will now be
Hello
World

What you're missing is that deferis not magic. It is executable code, just like any other code. If the path of execution never encounters it, there is nothing to be deferred. This is why it should always be dead first in the block on whose exit it is to be executed — so that we guarantee that it is encountered.

Put the defer block before the scope is exited:
func A() {
print ("Hello")
defer {
print ("World")
}
guard 1 == 2 else {
return
}
}
A()

Related

Cannot find in scope in swift in DispatchQueue.main.async

I have this code in SWift:
import UIKit
import Foundation
func whatever() async -> Int{
return 2;
}
func test(){
Task.init{
var testing:Int = 0
do {
testing = try await whatever()
}
catch {
print("some error happened")
}
}
DispatchQueue.main.async {
print("from dispa")
if(testing == 0){
print("testing was never set")
}
}
}
test()
It is a playground and upon running it, I get Cannot find 'testing' in scope in the if statement in the DispatchQueue
What am I doing wrong? Moving the code within the Task.init throws a different error: Reference to captured var 'testing' in concurrently-executing code
The variable testing is declared inside the Task.init scope right now, meaning the DispatchQueue closure has no access to it. To use it in both closures, you must move it to a scope in which both closures have access to it (ie a shared parent scope).
Once you've done that, I'll say it's a little unclear on what exactly you want happening here, but I'm guessing this is the result you are looking for. Note that you should be careful about mixing Tasks and DispatchQueues -- they are different paradigms and do not necessarily lead to the results that you think they would.
var testing = 0
func whatever() async throws -> Int {
return 2 //note that nothing async or throwing is actually happening here
}
func test(){
Task {
do {
testing = try await whatever()
}
catch {
print("some error happened")
}
}
DispatchQueue.main.async {
print("from dispatch")
if testing == 0 {
print("testing was never set")
}
}
}

How to get the string I put into a Error instance?

Basicly I got this,
// MyPackage.swift
enum Error: LocalizedError {
case general(String)
}
func foobar() throws {
throw Error.general("haha")
}
do {
try foobar()
} catch Error.general(let message) {
// will print "haha"
print(message)
}
And then in the unit test, I need to check if I got the exact same error,
import Quick
import Nimble
import MyPackage
class MySpec: QuickSpec {
override func spec() {
describe("") {
let input = "haha"
let count = 2
let expectation = Error.general("非纯数字字符串无法使用本方法")
context("输入_" + input) {
it("预期_" + expectation.localizedDescription) {
// got the error
// but expectation.localizedDescription was not what I looking for
expect(try numberStringByAddingZerosInto(input, tillReach: count))
.to(throwError(expectation))
}
}
}
}
}
It worked, but expectation.localizedDescription was not "haha", which made the name of the test case useless.
I also tried expectation.errorDescription with no luck.
Where could I get it?
And why is it like this?
override errorDescription var
enum Error: LocalizedError {
case general(String)
var errorDescription: String? {
switch self {
case .general(let errorMessage):
return errorMessage
}
}
}
Now, you can also write do-catch block by this
do {
try foobar()
} catch let error {
print(error.localizedDescription)
}

websiteUp set to false, but does not print Failure

I am trying to learn Swift/Xcode and have come across an error that I can't explain.
Here is some code that should print the error code, but still prints Success even though I have changed websiteUp to false!
enum WebpageError: Error {
case success
case failure(Int)
}
func getWebpage(uRL: String, siteUp: Bool) throws -> String {
if siteUp == false {
WebpageError.failure(404)
}
return "Success"
}
let webpageURL = "http://apple.com"
let websiteUp = false
do {
let status = try getWebpage(uRL: webpageURL, siteUp: websiteUp)
print(status)
} catch {
print(error)
}
I can't spot what is wrong, as it all looks OK to me!
You are doing two silly things:
How can a case of an Error enum be a .success? It's for failures!
It isn't enough to say WebpageError.failure. You have to throw the error.
So:
enum WebpageError: Error {
case failure(Int) // <== look
}
func getWebpage(uRL: String, siteUp: Bool) throws -> String {
if !siteUp {
throw WebpageError.failure(404) // <== look
}
return "Success"
}
let webpageURL = "http://apple.com"
let websiteUp = false
do {
let status = try getWebpage(uRL: webpageURL, siteUp: websiteUp)
print(status)
} catch {
print(error)
}
Having said all that, let's go back to the question of why you included a .success case. Maybe your idea was to return a value signalling success or failure. That would be a Result. Like this:
enum WebpageError: Error {
case failure(Int)
}
func getWebpage(uRL: String, siteUp: Bool) -> Result<String,Error> {
return Result {
if !siteUp {
throw WebpageError.failure(404)
}
return "Success"
}
}
let webpageURL = "http://apple.com"
let websiteUp = false
let status = getWebpage(uRL: webpageURL, siteUp: websiteUp)
do {
try print(status.get())
} catch {
print(error)
}
That might be more sophisticated than you had in mind at this stage, but it's worth thinking about.

Swift use of break in guard statement

I'm trying to use break in an guard statement, but the compiler tells me
'break' is only allowed inside a loop, if, do, or switch
Is possible to write something like in this snippet (this is just an MCV)?
func test(string: String?, x: Int) {
print("Function Scope BEGIN")
if x > 4 {
guard let pr = string else { break }
print(pr)
}
else {
print("Not")
}
print("Function Scope END")
}
Yes it is possible. You can use unlabeled break statements inside loops, but not inside an if block. You can use labeled break statements though. For example, this version of your code will work:
func test(string: String?, x: Int) {
print("Function Scope BEGIN")
someLabel: if x > 4 {
guard let pr = string else { break someLabel }
print(pr)
}
else {
print("Not")
}
print("Function Scope END")
}
A break statement can only be used inside a guard let if the guard-let is inside a loop.
In your use-case, I'd say you should use an if-let instead, since the alternative option of return is not what is desired.
func test(string: String?, x: Int) {
print("Function Scope BEGIN")
if x > 4 {
if let pr = string { print(pr) }
}
else {
print("Not")
}
print("Function Scope END")
}

"Missing return in a closure expected to return 'SomeType'" error in .map

I have the fallowing code:
struct AInt {
var aInt: Int
}
struct ADouble {
var aDouble: Double
static func convert(aInt: AInt) throws -> ADouble {
return ADouble(aDouble: Double(aInt.aInt))
}
}
struct B {
func doAction(aInts: [AInt]) throws -> [ADouble] {
return aInts.map { aInt in
do {
try ADouble.convert(aInt)
}
catch {
print(error)
}
}
// ^^^ error here: Missing return in a closure expected to return 'ADouble'
}
}
let aInts = [AInt(aInt: 2), AInt(aInt: 3)]
let b = B()
do {
print(try b.doAction(aInts))
}
catch {}
When i'm trying to convert [AInt] to [ADouble] in .map using function that can throw error, i get this error:
Missing return in a closure expected to return 'ADouble'.
Well, i decided to add return statement in the end of .map like this:
return aInts.map { aInt in
do {
try ADouble.convert(aInt)
}
catch {
print(error)
}
return ADouble(aDouble: 2.2)
}
Error disappear, but when i print try b.doAction(aInts) on same aInts array, i get this: [ADouble(aDouble: 2.2), ADouble(aDouble: 2.2)], i.e. it prints my ADouble(aDouble: 2.2) that i set manually. Obviously, it's not what i want, so then i try to add return before try ADouble.convert(aInt) like this:
return aInts.map { aInt in
do {
return try ADouble.convert(aInt)
}
catch {
print(error)
}
return ADouble(aDouble: 2.2)
}
And now i get right result: [ADouble(aDouble: 2.0), ADouble(aDouble: 3.0)]. But this code still doesn't work without return statement in the end of the .map. Any ideas how to get rid of it?
The map() method is declared as
#rethrows public func map<T>(#noescape transform: (Self.Generator.Element) throws -> T) rethrows -> [T]
which means (if I understand it correctly) that the transform can
either return a value or throw an error (and this will be
forwarded to the caller).
This compiles and works as expected:
struct B {
func doAction(aInts: [AInt]) throws -> [ADouble] {
return try aInts.map { aInt in
return try ADouble.convert(aInt)
}
}
}
An error thrown from ADouble.convert(aInt) will be forwarded to
the caller of map() and from there to the caller of doAction().