Can't initialize constant in Swift Structure while handling initialization errors - swift

Title is pretty self explanatory. Here's the code:
struct APIConnection {
enum APIConnectionError: ErrorType {
case InvalidRegion(invalidRegion:String)
}
let validRegions:Set<String> = ["na","eune","euw","kr","lan","las","br","oce","ru"]
let apiKey:String, region:String
var url:NSURL!
init(region:String,apiKey:String) {
func setRegion() throws {
if self.validRegions.contains(region) { self.region = region } // Can't assing since region is a let constant
else { throw APIConnectionError.InvalidRegion(invalidRegion: region) }
}
do { try setRegion() } /* If I change region to a var, compiler says setRegion() utilizes a yet non-initialized
apiKey constant. */
catch APIConnectionError.InvalidRegion(let invalidRegion) { print("Invalid region: \(invalidRegion)") }
catch let unknownError { print("Unknown error: \(unknownError)") }
}
}
I can't finish the initializer properly, since it won't assign self.region to the initializer's region constant inside the setRegion function.
apiKey also produces me a strange bug: It apparently is copied when I execute the setRegion function, not allowing the latter to be executed since it hasn't been initialized yet; though I didn't use it anywhere in my code yet.
Edit: Rewrote this part of the code:
func isRegionValid(region:String) throws -> Bool{
if self.validRegions.contains(region) { return true } // Can't assing since region is a let constant
else { throw APIConnectionError.InvalidRegion(invalidRegion: region) }
}
do { if try isRegionValid(region) { self.region = region } }
However, I still get an error saying the closure 'setRegion()' is capturing api-key's value, and I don't know why.

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")
}
}
}

Call to my struct dynamic member causes an infinite loop within my closure?

This made-up example displays a behavior I don't quite understand with structs/mutating functions/dynamic members. In a Playground, the changeStatus func will get called an infinite number of times and end up freezing Xcode. I expected a unique call to the closure to be made, executing the private method once.
// closure-returning subscript
#dynamicMemberLookup
struct LightManager {
private var status = false
private mutating func changeStatus() -> String {
status = !status
if status {
return "on"
} else {
return "off"
}
}
subscript(dynamicMember member: String) -> () -> Void {
return {
// following line causes an infinite loop
let newStatus: String = self.changeStatus()
print("Turn lights \(newStatus)")
}
}
}
let bedroomLights = LightManager()
bedroomLights.doStuff()

Using Do/Catch in Swift

I am working on an app and want to get data back from a function. However sometimes data is missing or is different from the kind of that I want to retrieve. I am new to Swift and I can't find a way to write a function that does a little bit of processing and returns this data. When this data is missing, the function should give back a string "Not Found". Like this:
func processData(data:String) {
do {
//processing
var result = processedData
} catch {
var result = "Not Found"
}
return result
}
It would be very nice if somebody could help me.
You should check if the result is nil.
func processData(data: String?) -> String {
guard let result = data else {
return "Not Found"
  }
return result
}
The most concise way of doing it would be using the guard-let construct:
func processData(data: String?) -> String {
// Assuming getProcessedData(:) returns your processed data
guard let result = getProcessedData(data) else {
return "Not found"
}
return result
}
Also, your function is missing a return type. You must specify the return type like -> TYPE in all functions that return some value.
Those answer were written till mine are right. There is one way: with handler check get result and use by your point.
enum Errors: Error {
case noData
case unknownError
}
func progress(data: String?, completionHandler: #escaping (_ result: String? , _ error: Error?) -> Void ) {
guard let data = data else {
// Data is missing
throw nil, Errors.noData
}
// Do other things, and throw if necessary
result = data
return result, nil
}
// example of calling this function
process(data: "A data to process"){(result, error) -> Void in
//do any stuff
/*if error == nil {
}*/
}
A good practice in swift would be to use correctly the throws errors
This is an example inspired from yours :
enum Errors: Error {
case noData
case unknownError
}
func progress(data: String?) throws -> String {
guard let data = data else {
// Data is missing
throw Errors.noData
}
// Do other things, and throw if necessary
result = data
return result
}
do {
try process(data: "A data to process")
} catch {
print("An error occurred: \(error)")
}
You can try this code as is in a Swift Playgound
Your function needs to be explicit about returning something with e.g. -> String Also do-catch is for methods that can throw an error. It seems like you need to take a look at how to use optionals. Optionals can have a value or they can have no value.
fun processData(data: String) -> String {
var result: String?
// Do some processing and assign the result to result variable
guard let result = result else { return "Not Found" }
return result
}

Why does iterating over closures cause a bus error in swift?

I'm getting a strange Bus Error when running what appears to be perfectly safe swift code. I've tried to reduce it down to a minimal test case, as follows:
Apple Swift version 2.2-dev (LLVM 3ebdbb2c7e, Clang f66c5bb67b, Swift 0ddf238ad7)
Target: x86_64-apple-macosx10.9
This code:
public enum MyError: ErrorType {
case SomeError(code: Int)
}
public typealias MyType = () throws -> Bool
public class Foo {
var a:MyType = { () throws -> Bool in
print("A")
return true
}
var b:MyType = { () throws -> Bool in
print("B")
return true
}
var c:MyType = { () throws -> Bool in
print("C")
throw MyError.SomeError(0)
}
}
public func handle<T>(test:T) {
let mirror = Mirror(reflecting: test)
print(mirror.subjectType)
for child in mirror.children {
if let callable = child.value as? MyType {
do {
try callable()
}
catch MyError.SomeError(let id) {
print(id)
}
catch {
print("unknown error")
}
}
}
}
let foo = Foo()
handle(foo)
Generates this output:
Foo
A
B
C
Bus error: 10
Running it in the debugger works fine, so I assume it has something to do with a timing issue at runtime.
Am I doing something illegal or unsafe in this code?
Are exceptions somehow illegal in closures?
What's causing this error?
Edit:
I've created a bug on the swift issue tracker for this now here: https://bugs.swift.org/browse/SR-324
What's causing this error?
The error doesn't happen until you get to the last closure:
var c:MyType = { () throws -> Bool in
print("C")
throw MyError.SomeError(0)
}
Obviously, you're throwing an exception here, and I suspect that the problem has less to do with iterating over the children and more to do with throwing an exception while you're doing that iterating. I tried calling c without iterating:
public func trythis() {
let foo = Foo()
do {
try (foo.c)()
}
catch MyError.SomeError(let id) {
print(id)
}
catch { print("unknown") }
}
trythis()
and found that it worked fine. I also tried removing the throw from c:
var c:MyType = { () throws -> Bool in
print("C")
// throw MyError.SomeError(code: 0)
return true
}
and found that the code works fine in that case. So it's the combination of throwing while iterating over the list that's the problem, and that makes me suspect that it's just a compiler bug or maybe some problem with the Mirror class.
I think you should file a bug report with Apple for this one.
I agree with Caleb that this must be a bug.
But just to be clear, it is not the combination of throwing while iterating. It is the combination of reflecting and throwing.
This is a modified version of your handle function:
public func handle<T>(test:T) {
let mirror = Mirror(reflecting: test)
print(mirror.subjectType)
// Extract only the last function, no iteration...
if let callable = mirror.children[AnyForwardIndex(2)].value as? MyType {
do {
try callable()
}
catch MyError.SomeError(let id) {
print(id)
}
catch {
print("unknown error")
}
}
}
This function will cause the same error as your function.
You simply can not call a function that throws, if found using reflection.
Bug I would say.

How should I handle parameter validation Swift

I am learning Swift. I am designing a class that needs to do parameter validation in it's initializer. How should I handle this if the value passed falls out of range ? I am really finding it difficult to find an appropiate way to design this, considering that:
Swift does not have exceptions, in languages with exceptions and built-in try/catch mechanism, I would have thrown an exception.
Swift does not allow returning nil / null / nothing from the initializer to indicate an error condition, like we can do in Objective-C.
I feel passing an NSErrorPointer to an initializer is cumbersome and places an unneccessary burden on the consumer of the class.
How would you validate a parameter for an initializer in Swift ?
Now with Swift 2.0 you can throw exceptions. For example:
enum Parameter: ErrorType {
case Empty
case Short
}
And then in your functions you can use the super useful guard to check if the received stuff is valid or not and do something like this:
guard parameter.characters.count > 0 else { throw Parameter.Empty }
And then you have to catch those exceptions:
do {
// call your method with try for example something like 'let var = try MyClass("test")'
} catch .Empty {
} catch .Short {
} catch {
print("Something else went wrong!")
}
You can do it using class functions. See below. There are two points to note - the class function has to return Self? not Self, to allow the nil return, and the class must have an #required init().
class Validate {
class func instanceOrNil(valid: Bool) -> Self? {
if valid {
return self()
} else {
return nil
}
}
#required init() {
}
}
let iv = Validate.instanceOrNil(false) // nil
let v = Validate.instanceOrNil(true) // Validate instance
An actual "practical" example might look more like
class NumberLessThanTen {
var mySmallNumber: Int?
class func instanceOrNil(number: Int) -> NumberLessThanTen? {
if number < 10 {
return NumberLessThanTen(number: number)
} else {
return nil
}
}
#required init() {
}
init(number: Int) {
self.mySmallNumber = number
}
}
let iv = NumberLessThanTen.instanceOrNil(17) // nil
let v = NumberLessThanTen.instanceOrNil(5) // valid instance
let n = v!.mySmallNumber // Some 5
One technique: make a Thing.createThing( ... ) class method that wraps the initializer and returns an optional Thing?. Perform the validation inside that method: if the parameters pass validation, call the initializer with them and return the result. If the validation fails, return nil.