I have a singleton that manages a set of data. If for some reason that data is unavailable I'd like the creation of the singleton to fail by throwing an exception. The compiler isn't a fan of marking the getter with throws NOTE: I realize I could handle this other ways but now I'm curious if it's even possible.
class Foo {
class var `default`: Foo {
let instance = Foo()
return instance
}
private init() throws {
// Throw if unable to load required data...
}
}
You can do it like this(code from my playground), downside every call of your singleton will have to be done with try.
enum FooError : Error {
case RuntimeError(String)
}
class Foo {
static func defaultFoo() throws -> Foo {
if let instance = Foo("Success") {
return instance
} else {
throw FooError.RuntimeError("Singleton is not created")
}
}
private init?(_ parameter: String?) {
if let _ = parameter {
print("Init Succeded")
} else {
print("Init failed")
return nil;
}
}
}
try Foo.defaultFoo()
Related
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)
}
I have the following generic type:
public enum APIResult<T> {
case success(T)
case failure(NetworkError)
public var value: T? {
if case let .success(value) = self {
return value
}
return nil
}
public var error: NetworkError? {
if case let .failure(error) = self {
return error
}
return nil
}
}
And I have the following call that I make defined inside a singleton:
public func getGenericData<T>(urlEndPoint:String,completionHandler:#escaping(APIResult<T>)->()) {}
I am calling it using the following code:
APIManager.shared.getGenericData(urlEndPoint: "getuserprofile") { (result:Any) in
}
but swift refusing it saying:
Generic parameter 'T' could not be inferred.
What would be my problem?
You need to tell the compiler what kind of APIResult to expect, like this
getGenericData(urlEndPoint: "getuserprofile") { (result:APIResult<String>) in
}
Is there a way to create a throwing function with a specified Error sub type that will be thrown? Below code is an example of how I'd like it to be.
protocol ValidatorError: Error {
var somePrintableThingy: String { get }
}
protocol Validator {
associatedType T
var model: T { get set }
init(model: T)
func validate() throws where Error: ValidatorError
}
Example Use Case
class SomeModelErrorNotValidatorError: Error {
case invalidUsername
}
class SomeModelError: ValidatorError {
case invalidUsername
var somePrintableThingy: String {
switch self { case .invalidUsername: return "haha" }
}
}
class SomeModel {
var username: String = ""
init(username: String) { self.username = username }
}
class SomeModelValidator: Validator {
var model: SomeModel
init(model: SomeModel) { self.model = model }
func validate() throws {
guard self.model.username.isEmpty else {
// this line should be fine
throw SomeModelError.invalidUsername
// if i replace it with this line it should not be fine
throw SomeModelErrorNotValidatorError.invalidUsername
}
}
}
class SomeModelViewController: UIViewController {
// .. some other codes here
func buttonClicked(_ sender: Any? = nil) {
let model = SomeModel(username: self.txtUsername.text ?? "")
let validator = SomeModelValidator(model: model)
do {
try validator.validate()
} catch let error {
// since I already know that this is a `SomeModelError`
// I could just do this
print(error.somePrintableThingy)
}
}
}
PS: The possible workaround for this is to create a non throwing function with a callback but I don't want to do that
PS PS: Another possible workaround for this would be to use a linting tool, but that's too much.
I am trying to create a wrapper for my API return wrapper class for my project.
these are my classes
class Wrapper<T> {
let message = "Hello World"
let wrapped = T.self
public func getData() -> T.Type {
return wrapped
}
}
class Object {
let number = 100
public func getNumber() -> Int {
return number
}
}
class SecondObject {
let name = "Second Object"
public func getName() -> String {
return name
}
}
What I want to achieve is, is there any way I can call the Object function like this
let example = Wrapper<Object>()
example.getData().getNumber() // <<-- This is not working
let secondExample = Wrapper<SecondObject>()
secondExample.getData().getName() // <<-- This is not working
The error in my playground is this
error: instance member 'getNumber' cannot be used on type 'Object'
If you notice the Wrapper class, there is message property which will be used for all my API return object model
So my goal is, I could simply call the Wrapper class together with my object model class and just call the function that is inside the object model class.
I am still learning about generic in swift. What am I missing here?
You don't set wrapped to anything useful. You ned to set it to an instance of T. So you can pass a Tinto the constructor
class Wrapper<T>
{
let wrapped: T
init(wrapped: T)
{
self.wrapped = wrapped
}
}
Or you can have the class construct an instance of T, but if you want to do that, you need to tell it how to construct the instance. For example:
class Wrapper<T>
{
let wrapped: T
init()
{
self.wrapped = T() // << error!
}
}
won't work because the compiler knows nothing about T, not even if it has an init. You can change that with a protocol
protocol Initable
{
init()
}
class Wrapper<T: Initable>
{
let wrapped: T
init()
{
self.wrapped = T()
}
}
And you can apply the protocol to any type you like with an extension. In most cases the extension can be empty because mot types already have an init() method. For example:
class MyClass
{
init() { /* do stuff */ }
}
extension MyClass: Initable {}
class MyOtherClass
{
init(number: Int) { /* do stuff */ }
}
extension MyOtherClass: Initable
{
init() { self.init(number: 0) }
}
Another option is to supply a closure to the wrapper's init.
class Wrapper<T>
{
let wrapped: T
init(factory: ()-> T)
{
self.wrapped = factory()
}
}
let w = Wrapper() { return Array<Int>() }
Normally you'd only do this if you wanted to create multiple instances i.e. you'd keep a reference to the closure and call it each time you needed a new instance.
class Wrapper<T> {
private var wrapped: T // Storing your object of T type
init(value: T) { // init with instance of T
wrapped = value
}
public func getData() -> T { //returning instance of T
return wrapped
}
}
class Object {
let number = 100
public func getNumber() -> Int {
return number
}
}
let o = Object()
let example = Wrapper(value: o) // Here we creating instance of Wrapper with instance of Object
example.getData().getNumber()
How about this , in your example changing the type of wrapped from non-optional to an optional variable type.
class Wrapper {
let message = "Hello World"
var wrapped : T?
public func getData() -> T? {
return wrapped
}
}
class Object {
let number = 100
public func getNumber() -> Int {
return number
}
}
class SecondObject {
let name = "Second Object"
public func getName() -> String {
return name
}
}
and then using it as below
let example = Wrapper()
example.wrapped = Object()
let result1 = example.getData()?.getNumber() // ()
secondExample.wrapped = SecondObject()
let result2 = secondExample.getData()?.getName()
if let val1 = result1 , let val2 = result2 {
print("result1 = \(val1) result2 = \(val2)" )
}
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.