Swift - adding a constant, accepting argument and returning value - swift

I'm working through the Swift book from Apple and I have a scenario where I have this code
class Shape {
var numberOfSides = 0
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
The above code works fine in the playground.
The question then asks - Add a constant property with let, and add another method that takes an argument. and so I do the following:
class Shape {
var numberOfSides = 0
let color = "red"
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
func colorDescription() -> String {
return "This shape is of color \(color)."
}
}
My string "This shape is...." is not returned at all.
There is no error thrown and there is no change from what is returned in the first piece of code.
I have obviously searched GitHub for solutions and tried to understand but in this case, the problem is I don't understand why my own specific solution doesn't work.
What do I need to change here and why?

Your code is correct and returns This shape is of color red. using this
let shape = Shape()
let colorDescription = shape.colorDescription()
The Swift Tour provides this sample code for simpleDescription()
var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()
which does the same: create the instance and call the method.

Related

What does unnamed parameters must be written with the empty name '_' mean?

I'm learning Swift from a book and we are using Playgrounds to build out a class. I received an error that reads: unnamed parameters must be written with the empty name '_'.
I understand an underscore in Swift means "to ignore" but if I add an underscore followed by a space then I receive the error: Parameter requires an explicit type which is fairly easy to understand, meaning that a parameter must be declared as a certain type. :)
I'd like to know exactly what the error "unnamed parameters must be written with the empty name '_'" is trying to say in layman terms because its not making much sense to a noob like me.
Here is the code from the playground up to this point:
//: Playground - noun: a place where people can play
import UIKit
var str = "Hello, playground"
func fahrenheitToCelsius(fahrenheitValue: Double)-> Double {
var result: Double
result = (((fahrenheitValue - 32) * 5) / 9)
return result
}
var x = fahrenheitToCelsius(fahrenheitValue: 15.3)
print(x)
class Door{
var opened: Bool = false
var locked: Bool = false
let width: Int = 32
let height: Int = 72
let weight: Int = 10
let color: String = "Red"
//behaviors
func open(_ Void)->String{
opened = true
return "C-r-r-e-e-a-k-k-k...the door is open!"
}
func close(_ Void)->String{
opened = false
return "C-r-r-e-e-a-k-k-k...the door is closed!"
}
func lock(_ Void)->String{
locked = true
return "C-l-i-c-c-c-k-k...the door is locked!"
}
func unlock(_ Void)->String{
locked = false
return "C-l-i-c-c-c-k-k...the door is unlocked!"
}
}
I guess, your code was something like this, when you get unnamed parameters must be written with the empty name '_'.
func open(Void)->String{
opened = true
return "C-r-r-e-e-a-k-k-k...the door is open!"
}
Seems you are an experienced C-programmer.
In Swift, single-parameter functions (including methods) should have this sort of header:
func functionName(paramLabel paramName: ParamType) -> ResultType
When the paramLabel and paramName are the same, it can be like this:
func functionName(paramName: ParamType) -> ResultType
You can use _ both for paramLabel and paramName, so this is a valid function header in Swift, when a single argument should be passed to the function and it is not used inside the function body:
func functionName(_: ParamType) -> ResultType
But in old Swift, you could write something like this in the same case:
func functionName(ParamType) -> ResultType
Which is not a valid function header in the current Swift. So, when Swift compiler find this sort of function header, it generates a diagnostic message like: unnamed parameters must be written with the empty name '_' which is suggesting you need _: before the ParamType.
The actual fix you need is included in the Lawliet's answer. You have no need to put Void inside the parameter when your function takes no parameters.
func open()->String{
opened = true
return "C-r-r-e-e-a-k-k-k...the door is open!"
}
From my understanding, this is a practice from objective C that is carried and respected in swift. In objective C style, you name your parameters, but when you don't need them for description or readability purposes, you can just use _. Here's an example
init(_ parameter: Type)
Objective C protocols also follow this naming convention -
tableView(_ tableView: UITableView.......)
// in swift
protocol MyCustomProtocol: AnyObject {
func controller(_ controller: MyCustomControllerClass, DidFinishLoadingSomething something: Type)
}
When you do want to name your parameters in your functions, you can -
class CustomClass {
init(withUserId id: String)
}
// to use the above:
CustomClass(withUserId: "123123")
func insert(newIndexPath indexPath: IndexPath)
...
insert(newIndexPath: myNewIndexPath) // This is how you would use the above function
To help with your problem specifically, you specified that your func open does not need a parameter name. But you never specified what your parameter is. If you do want to pass a parameter, call it func open(_ open: Bool) -> String { , or if you don't want a parameter for that function, just use func open() -> String {
Parameter requires an explicit type. Therefore, the func open(_ Void)->String function declaration causes a compile error. If you just want to write a function that has no argument, remove _ Void.
func open()->String{
opened = true
return "C-r-r-e-e-a-k-k-k...the door is open!"
}
According to Apple's Swift book, the underscore (_) can be used in various cases in Swift.
Function: If you don't want an argument label for a parameter, _ can be used rather than having an explicit argument.
func sumOf(_ arg1: Int, arg2: Int) -> Int{
return arg1 + arg2
}
sumOf(1, arg2: 5)
Numeric Literals: Both Int and Float can contain _ to get better readability.
let oneBillion = 1_000_000_000
let justOverOneThousand = 1_000.000_1
Control Flow: If you don't need each value from a sequence, you can ignore the values by using an _, aka the Wildcard Pattern, in place of a variable name.
let base = 2
let power = 10
var result = 1
for _ in 1...power {
result *= base
}
Tuples: You can use _ to ignore parts of a tuple.
let http404Error = (404, "Not Found")
// Decompose to get both values
let (statusCode, statusMessage) = http404Error
print("The status code is \(statusCode)")
print("The status message is \(statusMessage)")
// Decompose to get the status code only
let (justTheStatusCode, _) = http404Error
print("The status code is \(justTheStatusCode)")

The following code works when I manually pass the value but fails when I unwrap the value

I just started learning Swift as my first coding language. My current challenge is trying to automate the transitions from a current level setup as LevelOne.sks to another level also created in Xcode level editor as LevelTwo.sks. What I'm attempting to do is trigger a transition to the next level with the following set of code.
In my base scene I have this function to send the player to the next level
private func goToNextLevel(nextLevel: String) {
//When hard coding the arguments as...
//loadScene(withIdentifier: .levelTwo)
//The level two scene loads and is playable...however,
//When trying to change the level argument in code
// by passing a nextLevel variable
// the optional unwraps nil and crashes the app.
loadScene(withIdentifier: SceneIdentifier(rawValue: nextLevel)!)
}
This is then passed to a SceneLoadManager File
enum SceneIdentifier: String {
case levelOne = "LevelOne"
case levelTwo = "LevelTwo"
// case levelThree = "LevelThree"
}
private let sceneSize = CGSize(width: 768, height: 1024)
protocol SceneManager { }
extension SceneManager where Self: SKScene {
func loadScene(withIdentifier identifier: SceneIdentifier) {
let reveal = SKTransition.flipHorizontal(withDuration: 0.5)
let nextLevel = SKScene(fileNamed: identifier.rawValue)
nextLevel?.scaleMode = .aspectFill
self.view?.presentScene(nextLevel!, transition: reveal)
}
I think this has something to do with how I'm trying to set nextLevel. Currently I'm setting this up as follows
let nxtLvl = String?
nxtLvl = ".levelOne"
goToNextLevel(nextLevel: nxtLvl)
Hopefully you can make sense of what I'm trying to achieve and that I'm at least close to being on the right track here. Any help would be greatly appreciated. Thanks!
What could really help you to solve :
SceneIdentifier.levelOne.rawValue returns this -> "LevelOne"
BUT
SceneIdentifier(rawValue : "LevelOne") returns -> SceneIdentifier.levelOne
See the difference ?
In the first you get the string , in that case what you want
And in the second you get the "left member" (I don't know the therm)
An example to see clearer if you have an enum :
enum SceneIdentifier: String {
case case1 = "LevelOne"
case case2 = "LevelTwo"
}
SceneIdentifier(rawValue : "LevelOne") !
returns this : SceneIdentifier.case1
and SceneIdentifier.case1.rawValue
returns this : "levelOne"
Since you are initializing with a raw value, you need to actually use the raw value.
let nxtLvl = String?
nxtLvl = "LevelOne" //this is the rawValue of the .levelOne case
goToNextLevel(nextLevel: nxtLvl)
Alternatively, you could change your API so that goToNextLevel simply accepts a SceneIdentifier like this.
//tip: use the parameter name as part of the method name
private func goTo(nextLevel: SceneIdentifier) {
//Then calling the function
goTo(nextLevel: .levelOne)
Though, that might not be appropriate in the larger scope of your API.

Swift: Cocoa binding value to a computed property does not work

I am learning KVC and binding. Currently, I am trying to bind an NSTextField to a computed property colorWallStr. I have bound the sliders' value to the corresponding color variable and also bound the value of the label to the computed property.
However, the content of the label does not change when I move the slide.
// Inside MainWindowController
dynamic var colorRed: CGFloat = 1.0
dynamic var colorGreen: CGFloat = 1.0
dynamic var colorBlue: CGFloat = 0.0
dynamic var colorWallStr: String {
get {
return "R: \(colorRed) G: \(colorGreen) B: \(colorBlue)"
}
}
It is working fine when I bond the label to the color variable directly.
Thanks #vadian's answer. Now I can update the label using property's didSet to trigger update label method (see below).
dynamic var colorBlue: CGFloat = 0.0 {
didSet {
updateLabel()
}
}
func updateLabel() {
colorWall = "R: \(colorRed) G: \(colorGreen) B: \(colorBlue)"
}
If properties used in string interpolation don't update the enclosing computed property, then why does the following code snippet does not work?
dynamic var colorWall: String {
get {
let red = colorRed
let green = colorGreen
let blue = colorBlue
return "R: \(red) G: \(green) B: \(blue)"
}
}
The Key-Value Observering API lets you handle situations like this by allowing you to register dependent keys. Here's how the documentation introduces the subject:
There are many situations in which the value of one property depends on that of one or more other attributes in another object. If the value of one attribute changes, then the value of the derived property should also be flagged for change.
In this situation the value of colorWallString depends on the value of your three color variables, so all you need to do is implement a class method that makes this clear:
// It's crucial that you get the signature of this method correct,
// otherwise it'll just be ignored.
class func keyPathsForValuesAffectingColorWallStr() -> Set<NSObject> {
return Set<NSObject>(arrayLiteral: "colorRed", "colorBlue", "colorGreen")
}
As noted in the code snippet, the format of the method you use to flag up dependent keys is crucial; you can (and should) read the relevant documentation here.
Properties used in string interpolation don't update the enclosing computed property.
You could do it this way
dynamic var colorRed: CGFloat = 1.0 { didSet { updateLabel() } }
dynamic var colorGreen: CGFloat = 1.0 { didSet { updateLabel() } }
dynamic var colorBlue: CGFloat = 0.0 { didSet { updateLabel() } }
dynamic var colorWallStr = ""
func updateLabel()
{
colorWallStr = String(format:"R: %.2f G: %.2f B: %.2f ", colorRed, colorGreen, colorBlue)
}
For Xcode 9.0, Swift 4:
class SampleViewController: NSViewController {
#objc dynamic var colorRed: CGFloat = 1.0
#objc dynamic var colorGreen: CGFloat = 1.0
#objc dynamic var colorBlue: CGFloat = 0.0
#objc dynamic var colorWallStr: String {
get {
return "R: \(colorRed) G: \(colorGreen) B: \(colorBlue)"
}
}
override class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String> {
switch key {
case "colorWallStr" :
return Set(["colorRed", "colorGreen", "colorBlue"])
default :
return super.keyPathsForValuesAffectingValue(forKey: key)
}
}
}
Small tips:
Use NSViewController, instead of NSWindowController, in this case.
Further information:
keyPathsForValuesAffecting with NSManagedObject
“In some cases a value may be dependent on another. For example, if you have a Person class with a computed property fullName that is dependent on the properties firstName and lastName, wouldn’t it be nice if observers of fullName could be notified when either firstName or lastName changes? KVO’s designers thought so, too, which is why they implemented a convention for defining dependent keys.
To define a key’s dependencies you must implement a specially named class method which returns a Set of key paths. In the example above you would implement keyPathsForValuesAffectingFullName():”
Excerpt From: Hillegass, Aaron. “Cocoa Programming for OS X: The Big Nerd Ranch Guide, 5/e (Big Nerd Ranch Guides).” iBooks.

How can we transfer data between classes when using swift programming language

With Objective-C we could transfer data to an array at an UIView class from ViewController using a method.
In an UIView class we were using something like this
float values[10];
-(void) getValue:(float) value index:(int) index {
values[index] = value
}
But when we try doing a similar thing with Swift such as
var values : [CGFloat] = [10]
func getValue (value:CGFloat, index:Int) {
values [index] = value
}
We are getting " fatal error: Array index out of range error " or if we use
var values : [CGFloat] = [10]
var index = 0
func getValue (value:CGFloat) {
values.append = value
++index
}
We are not getting error message but whenever we use setNeedsDisplay() array values are being set to initial values which is 10 for this example.
Till now we are unable to convert Objective-C UIView classes like that to Swift one.
First:
var values : [CGFloat] = [10]
That line says that values is a variable array of CGFloat values that currently holds a single value of 10. Therefore, only index 0 actually exists.
Also:
func getValue(value:CGFloat, index:Int) {
values [index] = value
}
Never mind the fact that you have put this on the UIView class, never mind that you have a method named "getValue" that actually sets a value...
EDIT:
I found a better solution:
var values = Array<Float>(count:10, repeatedValue: 0)
func getValue(value: Float, index: Int) {
values[index] = value
}
The above is the direct Swift equivalent to the Objective-C code you posted in your question.
Frankly, I think you should take a step back and find a better way to solve the problem you think this code is solving. As it stands it doesn't make a whole lot of sense.
var values : [CGFloat] = [10]
In swift, this will create an array of CGFloat with one element: 10.0, not 10 elements.
so values.count is one
Swift Collections
Yes, after studying some more Swift I found the answer to my question.
Using static variable in the UIView class is the answer.
Define variable like that in UIView class
struct StructToChange {
static var varToChange = 1
}
Write a function to change variable value like this in UIView class.
func getIt (newValue:Int) {
StructToChange.varToChange = newValue
}
Call the function in a controller class this way
let valueChanger = AnyUIViewClass()
valueChanger.getIt ( 10 )
This is it you changed the value of variable and you can use it as parameter in your " override func drawRect(rect: CGRect) { } "

My first swift hello world application, build error when trying access a class

So this is my hello world application with swift. Also not used to XCode. So, might be a silly mistake here.
I just have one class Deck.swift
class Deck {
let decks : Integer = 0
init () {
decks = 1
}
init (amountOfDecks : Integer){
decks = amountOfDecks
}
func getAmountOfCards() -> Integer {
return 0
}
}
Then I'm trying to run a unit test that look like this
import XCTest
import helloWorldv2
class helloWorldv2Tests: XCTestCase {
override func setUp() {
super.setUp()
}
override func tearDown() {
super.tearDown()
}
func testDeckConstructor() {
var deck = Deck(amountOfDecks: 2)
XCTAssert(true, "Pass")
}
func testExample() {
// This is an example of a functional test case.
XCTAssert(true, "Pass")
}
}
If I comment out the line var deck = Deck(amountOfDecks: 2) then it builds fine. If that line is included I get build failed.
Anyone know what I'm doing wrong here?
If you want to modify deck param you have to declare it like this
var decks : Int = 0
If you put let your variable is read only after first assignment.
Whilst #jaumard is correct in saying that it is var rather than let that you should be using, you should use "Int" as the type, not "Integer" (which is a protocol, not a type). You get away with it because both are superfluous because of type inference...
var decks = 0
is quite sufficient, or
var decks: Int = 0
if you want to be verbose.
See this answer.
No need to turn your let constant into a var.
Simply don't provide a default value when declaring it.
let decks : Int
Your = 0 already initializes your constant, and than you're trying to change it in init, which is not allowed for constants.