Assigning a Function with Generic Parameter to a Variable - swift

I'm trying to assign a function, which takes a generic parameter, to a variable.
I have a class, Loudspeaker, with a function, saySomething. The saySomething function is meant to take as a parameter any object conforming to the protocol SpeakProtocol:
class Loudspeaker {
func saySomething<T:SpeakProtocol> (speaker:T) {
print("\(speaker.thinkOfSomethingToSay())")
}
}
protocol SpeakProtocol {
func thinkOfSomethingToSay() -> String
}
The ThingSayer class implements SpeakProtocol:
class ThingSayer:SpeakProtocol {
func thinkOfSomethingToSay() -> String {
return "Hello"
}
}
I create a new member of the Loudspeaker class:
var aLoudspeaker:Loudspeaker
And I would like to assign the saySomething function (not the result of that function) to a variable:
var speakFunction = aLoudspeaker.saySomething
I would then invoke this function elsewhere by creating a ThingSayer and passing that to speakFunction:
var aSpeaker:Thingsayer
speakFunction(speaker:thingSayer)
The problem is with this line:
var speakFunction = aLoudspeaker.saySomething
Which gives me an error: "Generic parameter 'T' could not be inferred."
I've been beating my head against a wall for quite a while with this. Is there any way to do what I'm trying to do?
This is obviously a contrived example, but the gist of the problem is that I have a function, which takes a generic parameter, and I want to assign that function to a variable to be invoked elsewhere in my code. Any help would be most appreciated.

var speakFuction: (speaker: ThingSayer) -> Void = aLoudspeaker.saySomething
You'll need to explicit the type of your function when declaring the variable to help the compiler.

Related

Swift: Creating array of objects with generic type parameter

I have a type called Setting that takes a generic type parameter as such:
Setting<T>
Every setting contains a value that can be an Int32, String, Bool, or a custom object type, etc. Here is some of the full implementation of Setting:
class Setting<T> {
var key:String?
var defaultValue:T?
//...
}
This all works with various type params as expected, however, now there is a requirement for a collection that contains multiple Setting objects that could have various type parameters. When I declare an array variable of type [Setting], obviously the compiler expects a type which is unknown at runtime.
I've tried using a protocol and an extension on the types that could be used for the generic type parameter such as this:
protocol SettingProtocol {
func getType() -> Self.Type
}
extension Int32:SettingProtocol {
func getType() -> Int32.Type {
return Int32.self
}
}
extension String:SettingProtocol {
func getType() -> String.Type {
return String.self
}
}
//...
and declaring my array as
var settings = [Setting<SettingProtocol>]()
but this does not work when I try to append a Setting instance to the array as follows:
var newSetting = Setting<String>()
newSetting.setDefaultValue(value: "SomeString")
settings?.append(newSetting) // compile error here
and results in the following compiler error:
Cannot convert value of type 'Setting<String>' to expected argument type 'Setting<SettingProtocol>'
Also, using the protocol/extension route might require an extension on every type that might be encountered when building these objects which seems really clunky.
I feel like there should be a way to accomplish this. Also hoping that when I pull these items out of the array that I can avoid a lot of type checking.
Can anyone offer any advice?
Change
class Setting<T>
to
class Setting<T:SettingProtocol>
and try compiling.
Actually, you can't define:
var settings = [Setting<SettingProtocol>]()
because the generic type of Setting must be one of the concrete types but not the protocol itself. For example, you could declare it as:
var settings = [Setting<String>]() // since you already implemented extension String:SettingProtocol { ...
Therefore you could append objects of type Setting<String>, however that's not what are you looking for, you need settings to be a heterogeneous container.
So what you could do is:
class Setting {
var key:String?
var defaultValue:SettingProtocol?
}
protocol SettingProtocol { }
extension Int32:SettingProtocol {}
extension String: SettingProtocol {}
At this point, you declared defaultValue to be of type SettingProtocol, without the need of dealing with a generic.
Therefore:
var newStringSetting = Setting()
newStringSetting.defaultValue = "My String"
settings.append(newStringSetting)
var newInt32Setting = Setting()
newInt32Setting.defaultValue = Int32(100)
settings.append(newInt32Setting)
for setting in settings {
print(setting.defaultValue)
// Optional("My String")
// Optional(100)
}

Calling a method from a struct within the same class

I'd like to logically organize class properties to signify that they are one logical unit and to distinguish them from other class properties that are less tightly related.
I thought of doing this using a struct within my class. However, it seems that I can't call class methods from the struct property setters. I get what seems to be an inappropriate compile error: "missing argument for parameter #1 in call"
This seems to be different from calling method from struct in swift
where the function is within the struct. In my case, the methods are generic and don't just apply to my struct but to all class properties. Therefore, I don't want to move them within the struct.
Do you have ideas on how to organize properties into tight(er) logical units within classes?
class MyClass {
struct MyStruct {
static var aVarInMyStruct: String? {
didSet {
anotherVarInMyStruct = foo() // This gives compile error "missing argument for parameter #1 in call"
}
}
static var anotherVarInMyStruct: String?
}
func foo() {
println("I am foo()")
}
}
The inner type MyStruct knows nothing about its outer type MyClass. So there is no foo function to call from MyStruct. To better organize your code I suggest you to use // MARK: - whatever this section is comments. Types are not here to organize codes. Types are here to create right abstractions for the program.
I fix your bug:
class MyClass {
struct MyStruct {
static var aVarInMyStruct: String? {
didSet {
anotherVarInMyStruct = MyClass.foo() // This gives compile error "missing argument for parameter #1 in call"
}
}
static var anotherVarInMyStruct: String?
}
static func foo()->String {
println("I am foo()")
return "it is ok"
}
}

Swift: How to add a class method in 'String" extension

I want to add a class function into extension:
extension String {
class func test () {
}
}
I get the error: Class methods are only allowed within classes; use 'static' to declare a static method
Or how should i call " String.test()"
But for NSString
extension NSString {
class func aaa () {
}
}
no errors.
If i add static keyword:
extension String {
static func aaa () {
self.stringByAppendingString("Hello")
}
}
Got: Expression resolves to an unused function,
So how should i add a class function also want to use self. method.
EDIT: This works!
extension String {
static func aaa (path:String) -> String {
return path.stringByAppendingString("Hello")
}
}
but about #lan's answer:
mutating func bbb(path: String) {
self += "world"
}
When i type it appears like this:
String.bbb(&<#String#>)
String.bbb(&"nihao")
Cannot invoke 'bbb' with an argument list of type '(String)'
Class and static functions are not called on an instance of a class/struct, but on the class/struct itself, so you can't just append a string to a class.
Apple Documentation:
Within the body of a type method, the implicit self property refers to
the type itself, rather than an instance of that type.
You can, however, append a string to a variable instance of a String using the mutating keyword:
extension String {
mutating func aaa() {
self += "hello"
}
}
let foo = "a"
foo.aaa() // ERROR: Immutable value of type 'String' only has mutating members named 'aaa'
var bar = "b"
bar.aaa() // "bhello"
If you are trying to use a pointer to a string as a parameter, you can use the inout keyword to alter the inputed string:
extension String {
static func aaa(inout path: String) {
path += "Hello"
}
}
var foo = "someText"
String.aaa(&foo)
foo //someTextHello
While correct, it's somewhat atypical to see a mutating member added to a String extension as shown in Ian's answer. Strings (and value types in general) are meant to be immutable so the only way to use a mutating method is to declare instances var at the call site. Most of the time in your code you should be using let constants.
As such, it is much more common to extend structs to return new instances. So this is typical:
extension String {
func appending(_ string: String) -> String {
return self + string
}
}
and then at the call site:
let hello = "Hello, "
let helloWorld = hello.appending("World!")
You'll note of course that I'm not using static at all. That's because appending(_:) needs to use the current instance value of the String we're appending to, and class/static do not refer to instances and therefore do not have values.
"Within the body of a type method, the implicit self property refers to the type itself, rather than an instance of that type."
Thus when you extend a type by adding a type method you can only call other type methods through self. If you want to call an instance method you need to create an instance and call a method on that.

Can I specialize a generic function on a type passed as a parameter?

I want to be able to specialize a generic function by passing a type as an argument, instead of having to declare a variable having the desired type and writing an assignment to that variable.
In my use case, I'm walking up the responder chain looking for an object that conforms to a certain protocol. If found, I'd like to call a method on that object.
I'm trying to do it in a "swift-y" (i.e. type safe) way.
The code I'm currently using looks like this:
if let r:UndoManager = self.findResponder() {
r.undo(...)
}
but that makes it hard to chain in a statement.
I want to do something more succinct, like the following, passing the desired protocol as an argument to my function.
self.findResponder( UndoManager )?.undo(...)
In this example, say I have protocol UndoManager, defined as
protocol UndoManager {
func undo(...)
}
Also, my declaration of findResponder() currently looks like
public extension NSReponder {
public func findResponder<T>() -> T? {
...
}
}
If you want to do self.findResponder( UndoManager )?.undo(...) instead of (findResponder() as Undo?)?.undo(...), the method signature would be:
public func findResponder<T>(_: T.Type) -> T? {
// ...
}

Why do I have to pass arguments to classes in key-value form?

class NamedShape {
var numberOfSides: Int = 0
var name: String
init(name: String) {
self.name = name
}
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides named \(name)"
}
}
I have the following example class. To create a new instance, I do
let shape = NamedShape(name: "Test")
The arguments are captured by the init() function, right? However, if I do this:
let shape = NamedShape("Test")
I get a missing argument label error!
However, if I define a silly function like this:
func printInt(numberIn: Int) {
println(numberIn)
}
I can invoke it just fine using:
printInt(5)
However, if I attempt to invoke it with the arguments formatted in the way I create a class:
printInt(numberIn: 5)
I get an extraneous argument label error!
Help a swift noob understand. Why do I need to label class arguments, but I can't label function arguments? Why are functions and classes different this way? I'm sure there's something I'm missing.
Because initializer functions aren’t called with a function name, the parameter types and names are used to disambiguate among multiple init() functions. Thus, init() function parameters have external names by default.
You can explicitly opt out of default parameter names with an underscore:
init(_ name: String) {
self.name = name
}
init() functions are special cases and all parameters are required to have an external name.
https://developer.apple.com/library/ios/documentation/swift/conceptual/Swift_Programming_Language/Initialization.html#//apple_ref/doc/uid/TP40014097-CH18-XID_306