Is the highlighted block of code a computed property? - swift

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.

Related

Cannot assign to property: <Enum:case> is not settable

for example we have simple enum
public enum CXActionSheetToolBarButtonItem {
case cancel
case done
case now
private static var titles: [CXActionSheetToolBarButtonItem: String] = [
.cancel: "Cancel",
.done: "Done",
.now: "Now",
]
public var title: String {
get { return CXActionSheetToolBarButtonItem.titles[self] ?? String(describing: self) }
// what am I want to do
set(value) { CXActionSheetToolBarButtonItem.titles[self] = value }
}
// what am I forced to do
public static func setTitle(_ title: String, for item: CXActionSheetToolBarButtonItem) {
CXActionSheetToolBarButtonItem.titles[item] = title
}
}
why I don't can set new title like this
CXActionSheetToolBarButtonItem.cancel.title = "asd"
compiler responded error - Cannot assign to property: 'cancel' is not settable, but I can set title with function
CXActionSheetToolBarButtonItem.setTitle("asd", for: .cancel)
what should I change for worked my var as settable? .cancel.title = "asd"
Using an enum for this seems inappropriate, but I'll address the porblem at face value. You need to mark your setter as nonmutating, so that it can be called on non-var instances of your enum:
public enum CXActionSheetToolBarButtonItem {
// ...
public var title: String {
get { return CXActionSheetToolBarButtonItem.titles[self] ?? String(describing: self) }
nonmutating set(value) { CXActionSheetToolBarButtonItem.titles[self] = value }
}
}
CXActionSheetToolBarButtonItem.cancel.title = "foo" // Works... but why would you want this?!
I think the main reason why you can't do that is because the Swift compiler sees enum cases as immutable by default (similar to an immutable struct declared with let), and here you are trying to mutate it.
A good way to see this is if you try to add a mutating function to this enum
mutating public func setTitle2(_ newValue: String) {
CXActionSheetToolBarButtonItem.titles[self] = newValue
}
You will then receive the error message
error: cannot use mutating member on immutable value: 'cancel' returns immutable value
How to work around this
One way to have a similar behavior is by changing this enum into a set of static variables (which is more consistent with what you are trying to achieve).
public struct CXActionSheetToolBarButtonItem {
var title: String
static var cancel = CXActionSheetToolBarButtonItem(title: "Cancel")
static var done = CXActionSheetToolBarButtonItem(title: "Done")
static var now = CXActionSheetToolBarButtonItem(title: "Now")
}
Now you can use the following
CXActionSheetToolBarButtonItem.cancel.title = "asd"
Also, you still have the ability to use the dot-syntax
let buttonItem: CXActionSheetToolBarButtonItem = .cancel // it works
Hope that helps!
TD;DR: Because enum is an immutable object.
First of all: Whatever you're trying to do here – it's certainly not a good approach and pretty dangerous. As Craig pointed out in his comment, you're messing around with instance properties and static properties. You can have multiple instances of an enum – but when you want to change the title of a particular instance, you also change the title for all other instances. That's unexpected behavior and you should really think of another solution.
That being said, your code actually does work – with a little modification: Instead of
CXActionSheetToolBarButtonItem.cancel.title = "asd"
you can write
var item = CXActionSheetToolBarButtonItem.cancel
item.title = "asd"
This will compile.
The reason behind it is that an enum is a value type. Everytime you create an instance of an enum type, e.g. var item = CXActionSheetToolBarButtonItem.cancel, the enum's value is copied into the new variable item. You can choose if you want that value to be mutable or not by using either var or let. That's how Swift enums are intended to be used.
A single enum case like CXActionSheetToolBarButtonItem.cancel is immutable by definition.
CXActionSheetToolBarButtonItem.cancel.title = "asd"
wouldn't have any meaning because there is no instance to which the title can be assigned. The enum case .cancel is not bound to a variable.

How to reference an attribute as a method default in a Swift class? [duplicate]

In a Swift class, I want to use a property as a default parameter value for a method of the same class.
Here is my code :
class animal {
var niceAnimal:Bool
var numberOfLegs:Int
init(numberOfLegs:Int,animalIsNice:Bool) {
self.numberOfLegs = numberOfLegs
self.niceAnimal = animalIsNice
}
func description(animalIsNice:Bool = niceAnimal,numberOfLegs:Int) {
// I'll write my code here
}
}
The problem is that I can't use my niceAnimal property as a default function value, because it triggers me a compile-time error :
'animal.Type' does not have a member named 'niceAnimal'
Am I doing something wrong ? Or is it impossible in Swift ? If that's impossible, do you know why ?
I don't think you're doing anything wrong.
The language specification only says that a default parameter should come before non-default parameters (p169), and that the default value is defined by an expression (p637).
It does not say what that expression is allowed to reference. It seems like it is not allowed to reference the instance on which you are calling the method, i.e., self, which seems like it would be necessary to reference self.niceAnimal.
As a workaround, you could define the default parameter as an optional with a default value of nil, and then set the actual value with an "if let" that references the member variable in the default case, like so:
class animal {
var niceAnimal: Bool
var numberOfLegs: Int
init(numberOfLegs: Int, animalIsNice: Bool) {
self.numberOfLegs = numberOfLegs
self.niceAnimal = animalIsNice
}
func description(numberOfLegs: Int, animalIsNice: Bool? = nil) {
if let animalIsNice = animalIsNice ?? self.niceAnimal {
// print
}
}
}
I think for now you can only use literals and type properties as default arguments.
The best option would be to overload the method, and you can implement the shorter version by calling the full one. I only used a struct here to omit the initializer.
struct Animal {
var niceAnimal: Bool
var numberOfLegs: Int
func description(#numberOfLegs: Int) {
description(niceAnimal, numberOfLegs: numberOfLegs)
}
func description(animalIsNice: Bool, numberOfLegs: Int) {
// do something
}
}

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.

What is the difference between property closure and a method in 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.

Using a property as a default parameter value for a method in the same class

In a Swift class, I want to use a property as a default parameter value for a method of the same class.
Here is my code :
class animal {
var niceAnimal:Bool
var numberOfLegs:Int
init(numberOfLegs:Int,animalIsNice:Bool) {
self.numberOfLegs = numberOfLegs
self.niceAnimal = animalIsNice
}
func description(animalIsNice:Bool = niceAnimal,numberOfLegs:Int) {
// I'll write my code here
}
}
The problem is that I can't use my niceAnimal property as a default function value, because it triggers me a compile-time error :
'animal.Type' does not have a member named 'niceAnimal'
Am I doing something wrong ? Or is it impossible in Swift ? If that's impossible, do you know why ?
I don't think you're doing anything wrong.
The language specification only says that a default parameter should come before non-default parameters (p169), and that the default value is defined by an expression (p637).
It does not say what that expression is allowed to reference. It seems like it is not allowed to reference the instance on which you are calling the method, i.e., self, which seems like it would be necessary to reference self.niceAnimal.
As a workaround, you could define the default parameter as an optional with a default value of nil, and then set the actual value with an "if let" that references the member variable in the default case, like so:
class animal {
var niceAnimal: Bool
var numberOfLegs: Int
init(numberOfLegs: Int, animalIsNice: Bool) {
self.numberOfLegs = numberOfLegs
self.niceAnimal = animalIsNice
}
func description(numberOfLegs: Int, animalIsNice: Bool? = nil) {
if let animalIsNice = animalIsNice ?? self.niceAnimal {
// print
}
}
}
I think for now you can only use literals and type properties as default arguments.
The best option would be to overload the method, and you can implement the shorter version by calling the full one. I only used a struct here to omit the initializer.
struct Animal {
var niceAnimal: Bool
var numberOfLegs: Int
func description(#numberOfLegs: Int) {
description(niceAnimal, numberOfLegs: numberOfLegs)
}
func description(animalIsNice: Bool, numberOfLegs: Int) {
// do something
}
}