As I come from a JS background this is how I'd call a function by name stored in a variable:
var obj = {
foobar: function(param) {
// do something
}
};
var key = "foobar";
obj[key](123);
Now I would like to recreate this in Swift, for example:
struct obj = {
func foobar(param) {
// do something
}
}
let key:String = "foobar"
obj[key](123)
The above code unfortunately gives Type 'obj.Type' has no subscript members
Is there any way to call functions by names in a Struct, Class or a Dictionary (if it's even possible to store functions in Dicts?)
EDIT - MORE CONTEXT:
I have a user-supplied array of things say:
let arr = ["apples", "oranges", "pears"]
but this array can be as long as 20 items. Based on each item of the array I need to perform certain action. So I iterate over the array:
for (key, _) in arr {
if (key == "apples") {
handleApples()
}
if (key == "oranges") {
handleOranges()
}
// and so on...
}
Sure, I can have a function with a simple switch that would consist of 20 cases but that's far from ideal. What if my array grows to say 100 items?
I was hoping to achieve something similar to this:
for (key, _) in arr {
myClass[key]()
}
Is there any way to call functions by names in a Struct, Class or a Dictionary (if it's even possible to store functions in Dicts?)
Yes you can store a function into a dictionary
Let's define a function type
typealias FuncType = () -> ()
and 2 functions
func func0() {
print("Apples")
}
func func1() {
print("Oranges")
}
Now we can create a dictionary where the key is String and the value is FuncType
let dict : [String:FuncType] = [
"Apples" : func0,
"Oranges" : func1
]
And of course we can invoke a function stored into the dictionary
dict["Apples"]?() // prints "Apples"
I think this might be what you're looking for:
var obj: [String: Int] = {
// do something, like call foobar(param) that's defined outside of the variable
// or just manipulate data directly in here
return ["foobar": 123]
// or whatever dictionary you want that matches the type you defined for obj
}()
Kind of tough to give you a better answer without you posting the kind of output or behavior you're looking for.
Related
I have an ObservableObject with a published dictionary of strings to arrays with arrays with Ints:
class MyObservableObject: ObservableObject {
#Published var myDict: [String: [[Int]]]
}
And I want to pass one array of Ints as a Binding from inside the same class to a function of a different struct:
{
...
func someFunc(key: String, index: Int) {
someStruct.func(myDict[key]![index])
}
...
}
I understand that #Published vars can't be passed as Bindings. I'm still hoping that there's any way to achieve this. I also tried storing a reference to the array in the other struct using an inout variable, also without success.
#Published vars can't be passed as Bindings
It is not true - it is possible, via projected value using '$', but you want to pass not a property, but part of value of a property, and this is different thing.
The context is not clear and this someFunc smells not-well :) - I'd say it is needed some refactoring here, but, anyway, technically it is possible to do what you want using dynamically in-place generated binding, like
func someFunc(key: String, index: Int) {
guard myDict[key] != nil else { return }
someStruct.func(Binding<[Int]>(
get: { self.myDict[key]![index] },
set: { self.myDict[key]![index] = $0 }
))
}
I currently have the following function:
fun createMask(mask : String){
val ssnField : mywidgets.SSNField = findViewById (R.id.editTextText)
ssnField.hint = mask
}
To unit test this I want to wrap the untestable code within createMask into a closure. (The untestable code is the view layer logic that's difficult to instantiate and execute in a unit test.) Here is what I want to do in pseudo code:
createMask(closure, mask : String){
closure = mask // closure function returns pointer to property (depending on closure return type, might need to use setter: closure.set(mask))
}
With the above, the caller then does:
fun caller(){
createMask((){
val ssnField : mywidgets.SSNField = findViewById (R.id.editTextText)
return ssnField.hint
}, "xxx-xx-xxx")
}
How do do what is expressed in pseudo code work in kotlin?
You can return a reference of the property if you make createMask accept a parameter of type () -> KMutableProperty0<String>. Then you can call the set method:
fun createMask(mask : String, block: () -> KMutableProperty0<String>) {
block().set(mask)
}
// caller
createMask("xxx-xx-xxx") {
val ssnField = ...
ssnField::hint
}
Alternatively, use (String) -> Unit to represent "any function that takes a string", if you want to allow callers to pass any function that has the "form" of a setter.
fun createMask(mask : String, block: () -> (String) -> Unit) {
block()(mask)
}
// caller
createMask("xxx-xx-xxx") {
val ssnField = ...
ssnField::hint.setter
}
Note that this method involves reflection, which may not be desirable. Alternatively, you can accept a closure that takes the string to be set, and let the caller set it in the closure:
fun createMask(mask: String, block: (String) -> Unit) {
block(mask)
}
// caller
createMask("xxx-xx-xxx") {
val ssnField = ...
// note that rather than responsible for returning a property, the caller
// is responsible for setting "it" to the property
ssnField.hint = it
}
(I'm assuming createMask does more than just setting a property. Otherwise it is quite pointless...)
I am trying to deep copy a Swift class instance. As copy and mutableCopy function are available only for NSObject class descendent, I am need to implement the copy function of my own for a Swift class object.
Here is what I did,
protocol Copying {
init(original: Self)
}
extension Copying {
func copy() -> Self {
return Self.init(original: self)
}
}
class A : Copying {
var name : String? = nil
var age : Int? = nil
var address : String? = nil
init(name : String) {
self.name = name
}
required init(original: A) {
//one way
self.name = original.name
self.age = original.age
self.address = original.address
//second way
let originalReflect = Mirror(reflecting: original)
let selfReflect = Mirror(reflecting: self)
for (_, originalAttr) in originalReflect.children.enumerated() {
for (_, attr) in selfReflect.children.enumerated() {
if originalAttr.label == attr.label {
//now I know the value of property and its value as well in original
//but how will I set it to self?
//If A was NSObject I could have said
self.setValue(originalAttr.value, forKey: originalAttr.label!)
}
}
}
}
}
Finally this is how I access the copy
let aInstance = A(name: "Sandeep")
let aCopy = aInstance.copy()
The first method shown in required init(original: A) works absolutely fine, but not scalable. In my project I have base class with 50 odd property and atlas 50 more subclasses. Now I cant write the required init(original: A) for each subclass and add self.property = original.property that looks very much dumb way to solve it.
Second method shows the usage of Mirror to iterate through all the properties of object and but gets stuck as there is no way to set the value of self as self.setValue(for key) is not available for swift classes.
Now I cant change the class type to NSObject for this benefit nor can I convert it to struct.
Reference :
Copy Protocol idea from : deep copy for array of objects in swift
Mirror idea from :
https://www.pluralsight.com/guides/microsoft-net/property-copying-between-two-objects-using-reflection
iterate over object class attributes in Swift
Any way to solve this? Suggestions would be really helpful.
I want to override the default structure of KeyValuePair in C#, so that I can make a KeyValuePair to accept a 'var' types.
Something like this :
List<KeyValuePair<string, var>> kvpList = new List<KeyValuePair<string, var>>()
{
new KeyValuePair<string, var>("Key1", 000),
new KeyValuePair<string, var>("Key2", "value2"),
new KeyValuePair<string, var>("Key3", 25.45),
};
Even if its possible for dictionary, then also it will solve my problem.
You could use object as your type, and then cast to/from object to desired outcomes. However, it's important to note that this is very much the opposite of object oriented programming, and generally indicates an error in your design and architecture.
Hmm I am wondering if this might help you: To have a list as you want, it is really possible BUT the "var" type (as you named it) must be the same for all KeyValuePair instances. For having whatever type you must use object or dynamic (use Haney's answer).
So considering that you want a single type for all KeyValuePair instances, here is a solution:
Firstly, create this helper class:
public static class KeyValuePairExtentions
{
public static List<KeyValuePair<string, T>> GetNewListOfType<T>(Expression<Func<T>> type)
{
return new List<KeyValuePair<string, T>>();
}
public static void AddNewKeyValuePair<T>(this List<KeyValuePair<string, T>> #this, string key, T element)
{
#this.Add(new KeyValuePair<string, T>(key, element));
}
}
To consume these functions, here is an example:
var lst = KeyValuePairExtentions.GetNewListOfType(() => new {Id = default (int), Name = default (string)});
lst.AddNewKeyValuePair("test1", new {Id = 3, Name = "Keith"});
The ideea is to rely on the powerfull type inference feature that we have in C#.
Some notes:
1) if T is anonymous and you create a new instance of a list in an assembly and consume it in another assembly it is VERY possible that this will NOT work due to the fact that an anonymous type is compiled per assembly (in other words, if you have a variable var x = new { X = 3 } in an assembly and in another var y = new { X = 3 } then x.GetType () != y.GeTType () but in the same assembly types are the same.)
2) If you are wondering whether an instance it's created or not by calling GetNewListOfType, the answer is NO because it is an expression tree function and the function is not even compiled. Even with a Func will work because I am not calling the function in my code. I am using the function just for type inference.
I have a function:
func parseJSON3(inputData: NSData) -> NSArray {
var tempDict: (id:Int, ccomments:Int, post_date:String, post_title:String, url:String) = (id: 0, ccomments: 0, post_date: "null", post_title: "null", url: "null")
var resultArray: (id:Int, ccomments:Int, post_date:String, post_title:String, url:String)[] = []
var error: NSError?
var jsonDictionary: NSDictionary = NSJSONSerialization.JSONObjectWithData(inputData, options: NSJSONReadingOptions.MutableContainers, error: &error) as NSDictionary
var firstArray = jsonDictionary.objectForKey("locations") as NSArray
for dict in firstArray {
tempDict.id = dict.valueForKey("ID") as Int
tempDict.ccomments = dict.valueForKey("ccomments") as Int
tempDict.post_date = dict.valueForKey("post_date") as String
tempDict.post_title = dict.valueForKey("post_title") as String
tempDict.url = dict.valueForKey("url") as String
resultArray.append(tempDict)
}
return resultArray
}
In line
resultArray.append(tempDict)
I have an error:
Missing argument for parameter 'ccomments' in call
Why? Help please....
It looks to me like resultArray.append() is treating the tuple a little bit like a variadic parameter, and trying to expand the tuple to match its own arguments. It's complaining about your second parameter because it's only expecting one. I haven't seen this behavior for Array.append() documented anywhere, so I would say it's a bug in Swift.
Using the appending operator += doesn't seem to have that issue:
resultArray += tempDict
So this is pretty wild - not sure if I would qualify it as a bug or as undocumented behavior, but it's definitely something that should be on the radar for a fix / clarification!
The situation is that append is treating your argument tempDict (which we would expect to be the only argument to an Array method that takes a single member and adds it to the collection) as the first argument in a signature where it is looking for 5 arguments (!), one for each member of the Tuple type that the Array holds.
See the following for some interesting behavior (including assigning a label to the single member of a 1-member 'Tuple' ??) ->
var arrayOne: Array<String> = []
arrayOne.append("hi")
println(arrayOne[0]) // hi
var arrayTwo: Array<(String)> = [] // where (String) is a single-member Tuple
arrayTwo.append("hi")
println(arrayTwo[0]) // hi
println(arrayTwo[0].0) // hi -> using .0 subscript to access the first member of the Tuple
// wanna see something crazy? remember arrayOne, that holds members of type String?
println(arrayOne[0].0) // hi -> this Array does not hold Tuples, but it looks like we can still treat its members like "single-member Tuples"?
var arrayThree: Array<(str: String)> = [] // members of the Array are single-member Tuples with the label 'str' for their member
arrayThree.append(str: "hi") // now we can't use append without providing the label 'str', acting as what looks like an argument label?
var byeString = "bye"
var byeTuple = ("bye")
arrayThree += byeString // += doesn't care about the label, and will take either a String or a single-member Tuple holding a String
arrayThree += byeTuple
println(arrayThree[0]) // hi
println(arrayThree[0].0) // hi
println(arrayThree[0].str) // hi -> accessing the single member of the Tuple by its label
...so in your case, where you are seeing the error with append what it wants you to do is (using the labels you used to declare the Tuple as something that looks like argument labels):
var resultArray: (id:Int, ccomments:Int, post_date:String, post_title:String, url:String)[] = []
...
resultArray.append(id: someIntValue, ccomments: someOtherIntValue, post_date: someStringValue, post_title: someOtherStringValue, url: someAnotherStringValue)
...and of course, as discussed, you can avoid doing that by just using += instead
Crazy stuff! could be by design to serve some purpose, could be a consequence of protocol inheritance that wasn't meant to have this effect... would be interesting to know the answer!
resultArray.append() seems to be taking tempDict as the first tuple element (id).
Changing it to :
resultArray += tempDict
seems to compile and work.
I'm not sure why append() doesn't behave the same way, maybe you can file a bug!