How to cast the generic result of a generic Swift method? - swift

Assuming I have overloaded a Swift method like this
func someValue<T> ( _ name: PropertyName ) -> T? {
// ...
}
func someValue ( _ name: PropertyName ) -> URL? {
// ...
}
I can call this method like this
let u = o.someValue("propA")
And u will be of type URL?. Or I can also call it like this
let i: Int? = o.someValue("propB")
let s: String? = o.someValue("propC")
And i will be Int? and s will be String?.
But how can I use the result of this method call without assigning it to a variable? E.g. I want to use it for loop or maybe simpler, in a switch statement:
switch o.someValue("propB") {
This call will assume the result is URL?:
switch o.someValue("propB") as? Int {
I am told that URL? cannot be casted to Int, which is correct. But URL? means that already the wrong method is being called in the first place.
Update
Here is a minimal test case as I must not post the original code here.
https://swiftfiddle.com/srkys22vofdqlpl2sglnfs4q5u

switch o.someValue("propB") as Int? {
and
switch o.someValue("propB") as URL? {
work fine. You are mistaken in saying
Doesn't work
You may also disambiguate like
(o.someValue as (_ name: PropertyName) -> Int?)("propB")
(o.someValue as (_ name: PropertyName) -> URL?)("propB")
…though you should not have to.

switch o.someValue("propB") as? Int {
will not work as the compiler interprets that as
(switch o.someValue("propB")) as? Int {
in which case the URL? returning method wins, which is most specific overload, and then the case will fails as URL? cannot be cased to Int.
And then pointed out, that the correct code needs to be
switch o.someValue("propB") as Int? {
Note how the question mark is the only thing that moved here. As now the cast can match the generic version, too. This is basically hinted by the first line of Jessy's answer.

Related

Clear way to update some nested struct from a bigger struct in place

Say we have some complex struct with multiple nested levels(for simplicity, in the example will be only one level, but there could be more).
Example. We have a data structure:
struct Company {
var employee: [Int: Employee]
}
struct Employee {
var name: String
var age: Int
}
var company = Company(employee: [
1: Employee(name: "Makr", age: 25),
2: Employee(name: "Lysa", age: 30),
3: Employee(name: "John", age: 28)
])
Now we want to create a function which updates some Employee of the company in place. We could write it using an inout param:
func setAge(_ age: Int, forEmployee employee: inout Employee) {
employee.age = age
}
setAge(26, forEmployee: &company.employees[1]!)
This works, but as you can see we need to unwrap expression 'company.employees[1]' before passing it by ref. This forced unwrap can produce runtime error if there is no such employee for the provided key.
So we need to check if the employee exists:
if company.employees[1] != nil {
setAge(26, forEmployee: &company.employees[1]!)
}
This also works, but this code is kind of ugly because we need to repeat the expression 'company.employees[1]' two times.
So the question is: Is there some way to get rid of this repetition?
I tried to use optional inout param in the modifying function but could not get it working.
Based on your comments, like
What I wanted in the first place is just to have a reference to a substructure of a bigger structure so the part of code that is dealing with the substructure could know nothing about where is this substructure located in the bigger structure.
and
It would be ideal if I just could create a local inout var. Like if var employ: inout Employee? = company.employee[1] { // make whatever I want with that employee }.
I think that what you want is a generic update function. In the community this is part of the family of utility functions referred as with (https://forums.swift.org/t/circling-back-to-with/2766)
The version that you need in this case is one that basically guards on nil, so I suggest something like
func performUpdateIfSome <T> (_ value: inout T?, update: (inout T) throws -> Void) rethrows {
guard var _value = value else { return }
try update(&_value)
value = _value
}
with this utility then what you wanted to do would be done with
performUpdateIfSome(&company.employees[1], update: { $0.age = 26 })
Note
If you want to abstract away how to access the employee but not the company, then keypaths are an option as well :)
You need to hide the implementation and let the struct handle the logic with specific error handling strategy, like throwing an error or simply return true/false depending on success or simply ignore any problems. I don't know what the Int key stands for but here I guess it's an ID of some sort, so add this to Company struct
mutating func setAge(_ age: Int, forId id: Int) -> Bool {
if employee.keys.contains(id) {
employee[id]?.age = age
return true
}
return false
}
I would simply add extension to Employee which set employee's age
extension Employee {
mutating func setAge(_ age: Int) {
self.age = age
}
}
Then you can use optional chaining for calling. So if value for key 1 doesn't exist, nothing happens and code goes on
company.employee[1]?.setAge(26)
Edit:
If your goal is just to change some property and then return object, simply create method which takes optional parameter and returns optional value
func setAge(_ age: Int, forEmployee employee: inout Employee?) -> Employee? {
employee?.age = age
return employee
}
if let employee = setAge(26, forEmployee: &company.employees[1]) { ... }

Casting Any? to Int, String or Bool

I have a function in Swift that returns Any? value and I would like to cast the result (based on the value type) to Bool, String or Int.
The function is this one:
static func value(forKey: SettingKeys) -> Any? {
return settingManager.settings.filter({$0.key == forKey.rawValue}).first?.value
}
and then when I do:
let printingEnabled = AppSettings().value(forKey:"printingEnabled") as? Int
i get NIL. Without the casting, the value is Optional("1"). Similar when I try casting results to String or Int. Any idea of how I could cast the result to a type of my choosing?
Thank you.
You don't want a function to return Any?. It is a nightmare of a type. Instead, you want to use generics to make sure the value is the type you want. For example:
static func value<T>(ofType: T.Type, forKey: SettingKeys) -> T? {
return settingManager.settings.filter({$0.key == forKey.rawValue}).first?.value as? T
}
Then this would be:
let printingEnabled = AppSettings().value(ofType: Int.self, forKey:"printingEnabled")
printingEnabled will in this case be Int?.
You can also, of course, wrap this up in helpers like:
static func int(forKey: SettingKeys) -> Int? {
return value(ofType: Int.self, forKey: forKey)
}
(BTW, while you definitely want to avoid Any? and use this kind of approach, I can't actually reproduce your issue. printingEnabled is Int? for me, and is Optional(1) as expected. I don't see any case where it's nil. But I can't make your code compile as written, either, since it's not clear what SettingKeys and settings are, so my code is probably significantly different than yours.)

How to make a type alias that references itself?

I made a few gateways / providers to integrate with an API in my API, using RX Swift and I'm trying to handle the pagination in what seems to me like a clean and simple way.
Basically, the function signature would look like that:
func getPlaces(with location: CLLocationCoordinate2D) -> Observable<(value: [Place], next: Observable<(value: [Places], Observable<(value: [Place], next: ... ... >>
This quickly appears impractical, so I tried creating a typealias for that:
typealias Result = Observable<(value: [Place], next: Result?)>
So my function signature would look like this:
func getPlaces(with location: CLLocationCoordinate2D) -> Result
But Xcode wouldn't get fooled so easily and calls me out for referencing my typealias inside itself
So... is it even doable ? How ?
I don't think this is possible using a typealias because you are creating an infinite type. The only way I can think of is to make Observable a recursive enumeration:
enum Observable {
case end([Place])
indirect case node([Place], Observable)
}
So, I mixed my approach with Nibr's, using a single case. This allows to handle pagination much more simply (in my opinion) from the ViewModel's side
enum Result<T> {
indirect case node([T], Observable<Result>?)
var value: [T] {
if case let GatewayResult.node(value, _) = self {
return value
}
return []
}
var next: Observable<Result>? {
if case let GatewayResult.node(_, next) = self {
return next
}
return nil
}
}

Can I use tuples with AnyObject?

I'm wondering if I can do something like this:
override func someCocoaFunc(someParameter:AnyObject?) {
if let parameter = someParameter as! Tuple {
let parameterType = parameter.1
if parameterType == "Heads up, this is an Int" {
print(parameter.0 + 1) //prints 2
}
else {
//fallback
}
}
}
let myTuple = (1,"Heads up, this is an Int")
someCocoaFunc(myTuple)
Obviously this doesn't work because Tuple is not a class, or at least not one I can cast to anyway. Is there a way to get this to work, if so how? If not, what is the best way to determine the type of an AnyObject? I tried:
if parameter is Bool {
//some code
}
else if parameter is Int {
//some code
}
But it doesn't seem to work, I think because Bool is just a typealias'd Int or something? So to summarize, can I use Tuples here, and if not, what should I do? To add some context, I'm writing one class that is used in serval different targets, so I can't be sure what the value will be, just that it is either an Int or Bool.
You cannot use tuples here because they are compound types and therefore no classes.
The problem with the if is that for compatibility Bool and Int get converted to NSNumber that has a boolValue and integerValue property which gets called if you cast them. So 0 and 1 is equivalent to true and false and therefore ambiguous.
As solution I would suggest to make a class which holds both values as Optionals:
class Holder {
let boolValue: Bool?
let intValue: Int?
}

Generic Type in Swift as Return Value

I would like to implement some code so that I can call something like:
NSUserDefaults("key1", "value1")
let s = NSUserDefaults("key1") // "value1" expected
NSUserDefaults("key2", 2.01)
let s = NSUserDefaults("key2") // 2.01 expected
I have some code in concept as below, but obviously it's not going to work. So my question is, instead of writing a series of functions like class func bool(key: String, _ v: Bool? = nil) -> Bool? is there any way to take the advantage of generic please?
extension NSUserDefaults {
class func object<T: AnyObject>(key: String, _ v: T? = nil) -> T? {
if let obj: T = v {
NSUserDefaults.standardUserDefaults().setObject(obj, forKey: key)
NSUserDefaults.standardUserDefaults().synchronize()
} else {
return NSUserDefaults.standardUserDefaults().objectForKey(key) as T?
}
return v
}
}
Your syntax is going to wind up being very poor. This line can't work as written:
let s = NSUserDefaults("key1") // "value1" expected
Swift has to pick a type for s at compile time, not run time. So the only type it can assign here is Any (not even AnyObject is expansive enough if you want to return Double since Double is not AnyObject).
That means you have to explicitly call out let s : Any = ... (because Swift wisely won't let you create Any implicitly), and then you're going to wind up with an Any that you have to type-check somehow. When you're done, you're going to come full circle to objectForKey().
Even if you could get this syntax working, you shouldn't try to overload a single function syntax to do opposite things. That's very confusing. If you were going to build an extension like this, you should probably make it a subscript. That way you'd say defaults["key1"] and defaults["key2"] = 2.01. That's something may be able to build (though there will still be type annotation headaches required to deal with AnyObject?).