Swift: function overload resolution via closure parameter? - ios

I am trying to solve a separate problem related to parsing JSON. In the process, I ran afoul of the Swift compiler, as I expected it to use closure template arguments to select a function overload for optional types.
I haven't seen anything explicit in their documentation on this topic, but is it not the expectation that all else equal, swiftc will use arguments of a closure parameter in a generic function to select the correct overload?
Here is the simplest sample that I could come up with:
import Foundation
let os:NSString = "foo!"
let d:[String:AnyObject] = ["a": os]
struct Model {
var opt:String?
var basic:String = ""
}
func read<T>(source:AnyObject?, set:T -> ()) {
if let t:T = source as? T {
println("basic: read type: \(T.self) from value \(source)")
}
else {
println("failed to read basic type \(T.self) from value \(source)")
}
}
func read<T>(source:AnyObject?, set:T? -> ()) {
assert(false, "this is not called")
if let t:T? = source as? T? {
println("optional: read optional type: \(T.self) from value \(source)")
}
else {
println("failed to read optional type \(T.self) from value \(source)")
}
}
var m = Model()
println(m)
let raw: AnyObject? = d["a"]
struct Property<T> {
typealias V = T
var get:() -> T
var set:T -> ()
func doRead(d:[String:AnyObject]) {
read(d["a"], set)
}
}
let b = Property(get: { m.basic }, set: { v in m.basic = v })
b.doRead(d)
let o = Property(get: { m.opt }, set: { v in m.opt = v })
o.doRead(d)
Per the comment inline, I expected the second overload to be used for optional types, but it is not. Am I missing something?
Edit
Note that the compiler is inferring the optional / non optional type from the property construct - it knows that the closure takes a optional, it just doesn't select the overload. I have restored some of my original logging code above, and the output is:
basic: read type: Swift.String from value Optional(foo!)
failed to read basic type Swift.Optional from value Optional(foo!)

From my usage of Swift, I see that the compiler prefers non-optionals over optionals. The operation:
let o = Property(set: { v in m.opt = v })
Is legal even when v is a non-optional, and therefore the compiler assumes as such.
This is a good question, and I assume a lot of other questions like this will be answered soon when Swift is made open-source.

Related

Safely unwrapping optional values and add it to Alamofire parameters [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I have a computed property of type Parameters in my APIRouter
// MARK: - Parameters
private var parameters: Parameters? {
switch self {
case .searchForDoctors(let doctorsFilter):
var params: Parameters = ["main_category_id": doctorsFilter.0, "page": doctorsFilter.1, "specialty_id": doctorsFilter.2, "city_id": doctorsFilter.3, "region_id": doctorsFilter.4, "name": doctorsFilter.5, "company_id": doctorsFilter.6, "order_by": doctorsFilter.7]
return params
default:
return nil
}
}
some values in the Typealias called doctorsFilter are optional.
currently I have a warning asking me to provide default value for the optional values, and I don't want to provide default values , I want to check if the value exist to add it, otherwise i will not add the key and the value
how can I safely unwrap the optional values and add it to the parameters dictionary with out saying if let for all optional values?
example:
if let specialtyID = doctorsFilter.2 {
params["specialty_id"] = specialtyID
}
I don't want to unwrap it this way as I will check for all optional values and it will take more lines of code
EDIT:-
the DoctorsFilter type is documented, when I initialize an instance of type DoctorsFilter the autocomplete tells me which of them is what, I I've thought about making the DoctorsFilter class before but I'm looking for another way if any, maybe a built in reserved word can handle the whole situation! , I want to make it simple as much as possible.
making a function that handles the dictionary and returns it in DoctorsFilter class is an option. I'm thinking of adding this function to the APIRouter, is it fine to add it there? is it the rule of the APIRouter to handle the parameters ? or the APIRouter just interested in taking the parameters and will not handle it ?
There is no "one line" solution, but you can use KeyPaths to reduce the series of if let ... statements down to a loop.
Start by creating a struct for your filter rather than using a tuple.
To facilitate this, we define a protocol for Parameterable - This protocol requires a dictionary that maps parameter names (String) to the property (KeyPath) that holds that parameter name as well as a function to return the parameters dictionary.
protocol Parameterable {
var paramNames: [String:KeyPath<Self,String?>] {get}
func parameters() -> [String:Any]
}
Use an extension to create a default implementation of the parameters() function, as the code will be the same for all Parameterables. It iterates over the dictionary entries and uses the associated KeyPath to access the relevant property and put it in the output dictionary. If a given property is nil then it simply isn't added to the output dictionary, because that is how dictionaries work. No need to explicitly check.
(If you import Alamofire then you can use the typedef Parameters where I have used [String:Any])
extension Parameterable {
func parameters() -> [String:Any] {
var parameters = [String:Any]()
for (paramName,keypath) in self.paramNames {
parameters[paramName]=self[keyPath:keypath]
}
return parameters
}
}
Use this protocol to create a DoctorsFilter implementation:
struct DoctorsFilter: Parameterable {
var mainCategoryId: String?
var page: String?
var specialtyId: String?
var cityID: String?
var regionId: String?
var name: String?
var companyId: String?
var orderBy: String?
let paramNames:[String:KeyPath<Self,String?>] = [
"main_category_id":\.mainCategoryId,
"page":\.page,
"specialty_id":\.specialtyId,
"city_id":\.cityID,
"region_id":\.regionId,
"name":\.name,
"company_id":\.companyId,
"order_by":\.orderBy]
}
private var parameters: Parameters? {
switch self {
case .searchForDoctors(let doctorsFilter):
return doctorsFilter.parameters()
case .someOtherThing(let someOtherThing):
return someOtherThing.parameters()
default:
return nil
}
}
}
The other approach is to simply split your creation of the parameters dictionary into multiple lines; If you assign nil against a dictionary key then there is no key/value pair stored in the dictionary for that key. In this case I have left your tuple approach in place, but you could use the struct (and I strongly suggest you do so)
private var parameters: Parameters? {
switch self {
case .searchForDoctors(let doctorsFilter):
var params: Parameters()
params["main_category_id"] = doctorsFilter.0
params["page"] = doctorsFilter.1
params["specialty_id"] = doctorsFilter.2
params["city_id"] = doctorsFilter.3
params["region_id"] = doctorsFilter.4
params["name"] = doctorsFilter.5
params["company_id"] = doctorsFilter.6
params["order_by"] = doctorsFilter.7
return params
default:
return nil
}
}
If we want to handle mixed properties, rather than just optional strings, we need to modify the code slightly. We need to use PartialKeyPath. This makes the code a little more complex since the subscript operator for a PartialKeyPath returns a double optional. This needs to be handled.
protocol Parameterable {
var paramNames: [String:PartialKeyPath<Self>] {get}
func parameters() -> [String:Any]
}
extension Parameterable {
func parameters() -> [String:Any] {
var parameters = [String:Any]()
for (paramName,keypath) in self.paramNames {
let value = self[keyPath:keypath] as? Any?
if let value = value {
parameters[paramName] = value
}
}
return parameters
}
}
struct DoctorsFilter:Parameterable {
var mainCategoryId: String?
var page: String?
var specialtyId: String?
var cityID: Int
var regionId: String?
var name: String?
var companyId: String?
var orderBy: String?
let paramNames:[String:PartialKeyPath<Self>] =
["main_category_id":\Self.mainCategoryId,
"page":\Self.page,
"specialty_id":\Self.specialtyId,
"city_id":\Self.cityID,
"region_id":\Self.regionId,
"name":\Self.name,
"company_id":\Self.companyId,
"order_by":\Self.orderBy]
}
There are three primary ways to safely unwrap an optional. You can also provide default values if you wish to unwrap an optional.
Guard Statement Unwrapping
var firstString: String?
// In some cases you might performa a break, continue or a return.
guard let someString = firstString else { return }
print(someString)
If Let Unwrapping
var secondString: String?
var thirdString: String?
thirdString = "Hello, World!"
// Notice that we are able to use the same "IF LET" to unwrap
// multiple values. However, if one fails, they all fail.
// You can do the same thing with "Guard" statements.
if let someString = secondString,
let someOtherString = thirdString {
print(someString)
print(someOtherString)
} else {
// With this code snippet, we will ALWAYS hit this block.
// because secondString has no value.
print("We weren't able to unwrap.")
}
Default Value Unwrapping
var fourthString: String?
// The ?? is telling the compiler that if it cannot be unwrapped,
// use this value instead.
print(fourthString ?? "Hello, World")
In Swift it is recommended that anytime you see a ! that you use some form of unwrapping. Swift is very "Type Safe".
Here's a resource you can use for Optionals.
https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html
Your Solution
Your solution might look something like this.
private var parameters: Parameters? {
switch self {
case .searchForDoctors(let doctorsFilter):
if let mainCatID = doctorsFilter.0,
let page = doctorsFilter.1,
let specialtyID = doctorsFilter.2,
let cityID = doctorsFilter.3,
let regionID = doctorsFilter.4,
let name = doctorsFilter.5,
let companyID = doctorsFilter.6,
let orderBy = doctorsFilter.7 {
params: Parameters = ["main_category_id": mainCatID,
"page": page,
"specialty_id": specialtyID,
"city_id": cityID,
"region_id": regionID,
"name": name,
"company_id": companyID,
"order_by": orderBy]
return params
} else {
//Unable to safely unwrap, return nothing.
return nil
}
default:
return nil
}
}

How do I Bind to a value stored in an Array or Dictionary? Bizarre/Inaccurate Errors reported by Xcode

Can anyone explain why this is not allowed...
Given the following class:
class CandidateAttribute {
var attributeType : AttributeType
var name : String
var value = ""
init(attributeType: AttributeType) {
self.attributeType = attributeType
self.name = attributeType.wrappedName
self.value = ""
}
}
And a class that has a Dictionary of these objects:
class Wrapper {
var candidates : [String: CandidateAttribute]()
// other code
func getCandidateAttribute(attributeType: AttributeType) -> CandidateAttribute{
if let candidate = attributesDict[attributeType.wrappedName] {
return candidate
}
let newCandidate = CandidateAttribute(attributeType: attributeType)
attributesDict[attributeType.wrappedName] = newCandidate
return newCandidate
}
}
Why can't I bind to a CandidateAttribute.value in the following code:
private func addAttributeRow(attributeType: AttributeType) -> some View {
let candidateAttr = candidateItem.getCandidateAttribute(attributeType: attributeType)
return HLabelTextField(label: candidateAttr.name,
hint: "Set value",
value: $candidateAttr.value) //candidateAttr.value)
}
With the code
return HLabelTextField(label: candidateAttr.name,
hint: "Set value",
value: candidateAttr.value)
I get the error Cannot convert value of type 'String' to expected argument type 'Binding<String>'
With the code:
return HLabelTextField(label: candidateAttr.name,
hint: "Set value",
value: $candidateAttr.value)
I get the error Use of unresolved identifier '$candidateAttr'
Is this down to:
a bug in my code?
a language limitation (Binding to objects stored in arrays is not allowed)?
Something else (e.g. I'm holding it wrong)?
If the reason is 2 that seems like a pretty poor language feature to me. The more I use SwiftUI the more I find it requires a lot of work-arounds because things don't 'just work'
Note: I have also tried making CandidateAttribute a struct as well...
As a workaround I can modify CandidateAttribute and add the following function:
func getValueBinding() -> Binding<String> {
let binding = Binding<String>(get: { () -> String in
return self.value
}) { (newValue) in
// This func updates the database
self.value = newValue
}
return binding
}
and change the referencing code to:
private func addAttributeRow(attributeType: AttributeType) -> some View {
let candidateAttr = candidateItem.getCandidateAttribute(attributeType: attributeType)
return HLabelTextField(label: candidateAttr.name,
hint: "Set value",
value: candidateAttr.getValueBinding()) //candidateAttr.value)
}
But that feels like way too much code to simply reference a value.

Define tuple error: expected ',' seperator [duplicate]

I'm developing an iOS application in Swift.
When I updated the Xcode to 7.0, I'm getting error in swiftyJSON.
static func fromObject(object: AnyObject) -> JSONValue? {
switch object {
case let value as NSString:
return JSONValue.JSONString(value as String)
case let value as NSNumber:
return JSONValue.JSONNumber(value)
case let value as NSNull:
return JSONValue.JSONNull
case let value as NSDictionary:
var jsonObject: [String:JSONValue] = [:]
for (k:AnyObject, v:AnyObject) in value {// **THIS LINE- error: "Definition conflicts with previous value"**
if let k = k as? NSString {
if let v = JSONValue.fromObject(v) {
jsonObject[k] = v
} else {
return nil
}
}
}
What's the problem? Can you help, please?
for (k:AnyObject, v:AnyObject) in value { .. }
must be written in Swift 2 as
for (k, v) : (AnyObject, AnyObject) in value { .. }
From the Xcode 7 release notes:
Type annotations are no longer allowed in patterns and are considered
part of the outlying declaration. This means that code previously
written as:
var (a : Int, b : Float) = foo()
needs to be written as:
var (a,b) : (Int, Float) = foo()
if an explicit type annotation is needed. The former syntax was
ambiguous with tuple element labels.
But in your case the explicit annotation is actually not needed at all:
for (k, v) in value { .. }
because NSDictionary.Generator is already defined as a generator
returning (key: AnyObject, value: AnyObject) elements.

Swift 2.0 Tuple pattern element label must be '_'

I have been trying to fix all my code since swift 2.0 update. I have a problem that seems to be the way tuples work now:
public func generate() -> AnyGenerator <(String, JSON)> {
switch self.type {
case .Array:
let array_ = object as! [AnyObject]
var generate_ = array_.generate()
var index_: Int = 0
return anyGenerator{
if let element_: AnyObject = generate_.next() {
return ("\(index_++)", JSON(element_))
} else {
return nil
}
}
case .Dictionary:
let dictionary_ = object as! [String : AnyObject]
var generate_ = dictionary_.generate()
return anyGenerator{
if let (key_: String, value_: AnyObject) = generate_.next() {
return (key_, JSON(value_))
} else {
return nil
}
}
default:
return anyGenerator{
return nil
}
}
}
Specifically the line:
if let (key_: String, value_: AnyObject) = generate_.next()
Is throwing the error: Tuple pattern element label 'key' must be '_'
I tried to make that change already, but I didnt work...
Any ideas?
The problem is: We cannot use type annotation inside of tuple patterns anymore.
In the release notes:
Type annotations are no longer allowed in patterns and are considered part of the outlying declaration. This means that code previously written as:
var (a : Int, b : Float) = foo()
needs to be written as:
var (a,b) : (Int, Float) = foo()
if an explicit type annotation is needed. The former syntax was ambiguous with tuple element labels. (20167393)
So, you can:
if let (key_, value_): (String, AnyObject) = generate_.next() {
But in this case, you could omit : (String, AnyObject):
if let (key_, value_) = generate_.next() {

is it possible to create a generic closure in Swift? [duplicate]

This question already has answers here:
Closure with generic parameters
(2 answers)
Closed 7 months ago.
func myfunc<T>(i:T) -> T {
return i
}
is it possible to make this generic function a closure?
let myfunc = { <T>(i:T) -> T in
return i
}
this doesn't work...
No, because variables and expressions can't be generic. There are only generic functions and generic types.
To clarify: In some languages you can have types with a universal quantifier, like forall a. a -> a. But in Swift, types cannot have a universal quantifier. So expressions and values cannot be themselves generic. Function declarations and type declarations can be generic, but when you use such a generic function or an instance of such a generic type, some type (which could be a real type or a type variable) is chosen as the type argument, and thereafter the value you get is no longer itself generic.
Probably you need something like this.
Type declaration:
typealias ResultClosure<T> = (ResultCode, String?, T?) -> Void
Function declaration:
func loginUser(userName: String, password: String, resultHandler: ResultClosure<TokenModel>?)
Usage:
NetConnector.shared.loginUser(userName: userName ?? "", password: password ?? "") { (code, message, data) in
self.display?.unlockScreen()
if code == .success {
if let activeToken = data {
AppData.shared.userToken = activeToken
}
self.display?.showHome()
} else {
self.display?.showError(errorMessage: message)
}
}
As mentioned, variables in Swift cannot be generic, so creating a closure, whose generic types are specified by the caller is not possible. However, there are workarounds:
With SE-253, it is possible to make arbitrary (nominal) types callable. So instead of declaring a generic closure, we can declare a (non-generic) struct that has a generic callAsFunction method:
struct MyFunc {
func callAsFunction<T>(_ i: T) -> T {
return i
}
}
Now, we can declare a non-generic variable that we can call with a generic value:
let myFunc = MyFunc()
let x = myFunc(42) // -> Int
let y = myFunc("foo") // -> String
Note that this workaround doesn't apply to all situations, but it can be helpful in some.
I have found some alternative way , you can use Anyobject in your closure and pass any values to your method .
typealias genericCompletion<T:AnyObject> = ((Bool,T,String) -> Void)
struct Student {
var name:String = "Kishore"
var age : String = "125"
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.createAGenericReturn { (success, object, message) in
}
self.createStructGeneric { (success, student, message) in
}
}
func createAGenericReturn(callback:#escaping(genericCompletion<AnyObject>)){
callback(true,434.433 as AnyObject,"kishoreTest")
}
func createStructGeneric(callback:#escaping(genericCompletion<AnyObject>)){
callback(true,Student.init() as AnyObject,"kishoreTest")
}
}
Here you can see I mentioned Generic as Anyobject typealias genericCompletion = ((Bool,T,String) -> Void) , So you can pass any values to it .

Resources