What is the difference between property closure and a method in Swift? - swift

class Bartek {
var name: String = "Bartek"
var description: () -> String = {
return "Person name is" + self.name
}
func description() -> String {
return "Person name is" + name
}
}
Now I can use it like that:
var bartek = Bartek()
bartek.description()
Actually which on do i use? What is the better? a property closure or a method? When to use them?

Property closure can be optional and assignable. Example: var description: (() -> String)?
Property closure can capture variables (like self in your example). You have created a memory leak there.
If you implement protocols that defines a method, you must conform with method, not property closure.
Use property closure for strategy patterns where user of your object can change the behaviour. Use method in pretty much any other case.

Use a property if it's some (more or less) static information. Use a function to indicate that something is computed.

Related

Is the highlighted block of code a computed property?

class Fruit {
var name: String
var colour: String
init(name: String, colour: String) {
self.name = name
self.colour = colour
}
**var info: () -> String {
{
return "\(self.name) is \(self.colour) in colour"
}
}
}**
let fruit = Fruit(name: "apple", colour: "red")
print(fruit.info())
Hi,
Can someone explain the type of property of the variable name "info" in the above block of code.
If it is a computed property, can a computed property be written in swift without a get block?
Yes, it is a computed property, of type () -> String (ie. a function with no argument returning a string).
For a computed property returning just a String, which seem to be preferable in your case, you could write :
var computedProperty: String
{
get
{
return "\(self.name) is \(self.colour) in colour"
}
}
When a property is get-only, you do not need to write the get{} block.
var computedProperty: String
{
return "\(self.name) is \(self.colour) in colour"
}
And if it is only one line, you don't even need to write the return.
var computedProperty: String
{ "\(self.name) is \(self.colour) in colour" }
info is a read-only computed property. It is declared to have a type that is a closure that takes no parameters and returns a String.
Read-only computed properties do not need an explicit get. The info property is using a shorthand notation. You can actually get rid of one of the two pairs of curly braces.
It's odd to declare the info property to be a closure in this case. It would be much simpler to declare it as:
var info: String {
"\(self.name) is \(self.colour) in colour"
}
Then you can use it as normal:
print(fruit.info) // <-- Note the lack of () after `info` here.
You should read the Properties section of the Swift book. In this case, the Computed Properties section covers most of your question.

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

Property getter as function

Is it possible to access the getter of a property, so that it can be passed along to functions like filter/map/reduce?
For example, assuming I have a User entity:
struct User {
let firstName: String
let lastName: String
let isUnderaged: Bool
func fullName() -> String {
return "\(firstName) \(lastName)"
}
}
let users: [User] = [] // or some computed result
If I need to filter all users that are underaged, then I need to create a temporary, anonymous, closure that only returns the isUnderaged value:
let underagedUsers = users.filter { $0.isUnderaged }
Similar, if I want to collect all the given names:
let givenNames = users.map { $0.firstName }
, I have to create another anonymous closure.
The fullName() method, on the other hand can be nicely passed along:
let allNames = users.map(User.fullName)
#selector accepts the #selector(getter: User.isUnderaged) form (User would need to be a class for this to work, just used it as example).
Does Swift supports something like this - passing getters to functions that allow closures?
Searching for something similar to this users.filter(User.isUnderaged) - this currently doesn't compile as isUnderaged cannot be accessed without an instance.
Starting with Swift 5.2, this is now possible, thanks to this Swift Evolution proposal.
Basically, key paths can now be used in places where functions are needed, like map/filter, etc.
For the example from the question, the usage would be along the lines of:
users.filter(\.isUnderaged)
users.map(\.firstName.count) // yes, chaining is also permitted

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.

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