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

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

Related

How to omit argument name in a struct

Swift beginner here... When passing a value into a struct instance during instantiation, is there a way to omit the argument name? I can't find anything in Swift documentation.
struct Dog {
var _ name: String
}
var buddy = Dog("Buddy")
Obviously this does not work. In functions you can use an underscore before the parameter name to omit it during calling, is this possible in some way with structs?
Properties always have names. What can differ are the keyword labels of the initializer. If you don't specify your own initializer, the compiler will synthesize a default memberwise initializer for you, which will have argument labels that match the member names.
If you wish to change that, you would forego the compiler-provided initializer, and specify your own:
struct Dog {
var name: String
init(_ name: String) {
self.name = name
}
}

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)
}

Assigning a Function with Generic Parameter to a Variable

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.

What exactly differentiate two functions in Swift?

I wonder what makes two "similar functions" different with each other and can be called unambiguously
This is what I've learned from self-study
The uniqueness comes from: Function name + Argument order and argument name + return type. The combination of them must be unique to be able to make the function unique (Please see the example below)
Nmu1 and Num2 would cause ambiguous, because the return type (doesn't has the so called return name, the function name already act as this role) is not referred when calling a function
Although Num3 function has a different parameter name, it won't uniquely separate it from the function in Num1 and Num2. Because the argument name won't referred when the function had been called therefore, only a different argument name won't make a function unique; And the function in Num4 is different with all functions above, because its signature Function name + Argument order and argument name + return type is unique among all previous 3 functions.
Num5 and Num6 functions are different with each other, for they have different argument orders when they were defined
//Num1
func foo(guy name: String) -> String {
return "Hi \(name)"
}
//Num2
func foo(guy name: String) {
print("Hi \(name)")
}
//Num3
func foo(guy called: String) -> String {
return "Hi \(called)"
}
//Num4
func foo(dude name: String) -> String {
return "What's up \(name)"
}
//Num5
func foo(man name: String, from place: String) {
print("Hi! I'm \(name) I come from \(place)")
}
//Num6
func foo(from place: String, man name: String) {
print("Hi! I'm \(name) I come from \(place)")
}
Question: I might miss or possibly even misunderstood some parts. It would be very nice of you that you can correct me and add the parts I missed
Thanks
[Update]
Let's discuss a bit more on this issue. Let's started with the question that discuss the difference between Argument and Parameter. This question makes a lot of sense here!
Back to the quote from the official swift document
Each function parameter has both an argument label and a parameter name. The argument label is used when calling the function; each argument is written in the function call with its argument label before it. The parameter name is used in the implementation of the function. By default, parameters use their parameter name as their argument label.
Argument, Parameter, Argument Labels and Parameter Names are different. The difference here can be used to differential functions.
Functions with same parameter name, same parameter order, same return type and even same function body can be differentiated from different argument Labels.
func foo(dude name: String) -> Int {
print("Hi \(name)!")
return 1
}
func foo(man name: String) -> Int {
print("Hi \(name)!")
return 1
}
//foo(dude: String) & foo(man: String) is identical when calling
For more information please address to Multiple functions with the same name
Defined as Obj-C- compatible methods, Num1 and Num2 can not exist together, and you will have error defining them in NSObject subclass; however, you can mark them as #nonobjc and error will gone, since Swift able to choose correct overload by return type expected:
//Num1
#nonobjc func foo(guy name: String) -> String {
return "Hi \(name)"
}
//Num2
#nonobjc func foo(guy name: String) {
print("Hi \(name)")
}
All your other points, except this mark, seems correct to me

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.