How do I use a switch on Xcode viewcontroller? - swift

I have read a book on Swift logic but I don't know how to use it with an interface to make an app in xCode 8. I'm making a very basic app where you have a currency (coins) and when you click a button (goldPan) it checks to see what upgrade the goldPan is on and depending on that upgrade the user gets so many coins, basically like the game cookie clicker. Instead of writing a ton of "if" statements I decided I have built a "switch" to check what upgrade the goldPan is on. Also, the goldPan has its own class. Im more of asking the question of "is Switch the way to go to check the upgrade? and if so how do I implement it with my code. Like I said, Im not sure if it's the most efficient way but when I do use this, Im getting the error of
Use of instance member 'checkUpgradeNumber' on type 'ViewController.goldPan'; did you mean to use a value of type 'ViewController.goldPan' instead?
and
Instance member 'production' cannot be used on type 'ViewController.goldPan'
Please help me, Im not sure if the switch is the best way to go or something else. Sorry for the very noob question. My code is below:
import UIKit
class ViewController: UIViewController {
#IBOutlet var topLabel: UILabel!
var coins = 0;
class goldPan {
var upgradeNumber = 0
var production = 1
func checkUpgradeNumber() {
switch upgradeNumber {
case 0:
production = 1;
case 1:
production = 2;
case 2:
production = 3;
default:
production = 1;
}
}
}
#IBAction func goldPanButton(_ sender: Any) {
goldPan.checkUpgradeNumber();
coins = coins + goldPan.production
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

First of all please consider the naming convention that class names start with capital letter (GoldPan).
You are calling a method and accessing a property on the class which is not possible with that code.
Either make the method and the properties static
...
static var upgradeNumber = 0
static var production = 1
class func checkUpgradeNumber() { ...
or create an instance of the class
let goldPan = Goldpan()
goldPan.checkUpgradeNumber()
coins = coins + goldPan.production
But basically I suppose you don't need to use a separate class for that. Remove the inner class block and use the method and the properties in the ViewController class.

Change class goldPan to func goldPan
I don't think you can have a class within a class. This may not fix the whole problem, but it might fix part of it. I'm not completely sure on this, but I just noticed that you are using a class instead of a function.

You can replace:
switch upgradeNumber {
case 0:
production = 1;
case 1:
production = 2;
case 2:
production = 3;
default:
production = 1;
}
With:
production = upgradeNumber + 1

Related

Variable declaration inside of class in Swift

I'm new in swift automated testing with XCTest framework.
In my work I faced with correct arrangement some parts of code inside of test
Please explain the difference between variable declaration in the beginning of the class and in the certain function. Which way is preferred?
Var.1
class someClass: XCTestCase {
let app = XCUIApplication()
func someFunc {
print (app.debugdescription)
}
}
Var. 2
class someClass: XCTestCase {
func someFunc {
let app = XCUIApplication()
print (app.debugdescription)
}
}
Var. 1 gives you the ability to configure your XCUIApplication in setup() and use it in the tests. Like this:
class someClass: XCTestCase {
var app: XCUIApplication!
func setUp {
app = XCUIApplication()
app.launchArguments.append("launchArgument")
setupSnapshot(app)
app.launch()
}
func test1 {
XCTAssertNotNil(app)
}
func test2 {
XCTAssertNotNil(app)
}
}
With Var. 2 you can setup your app differently in each test. Like this:
class someClass: XCTestCase {
func test1 {
let app = XCUIApplication()
app.launchArguments.append("withTestUser")
app.launch()
XCTAssertNotNil(app)
}
func test2 {
let app = XCUIApplication()
app.launchArguments.append("withNoData")
app.launch()
XCTAssertNotNil(app)
}
}
Both ways are possible but var 2 is preferred. For var 1 solution consider the following scenario:
class State {
var val: Int = 0
}
class TestClass: XCTestCase {
let state = State()
func test1() {
state.val = 5
XCTAssertEqual(5, state.val)
}
func test2() {
XCTAssertEqual(0, state.val)
}
}
This way result depends on which test will run first. If test2 will be the first then both tests will succeed. Other way, second test will fail.
For var 2 solution
class TestClass: XCTestCase {
func test1() {
let state = State()
state.val = 5
XCTAssertEqual(5, state.val)
}
func test2() {
let state = State()
XCTAssertEqual(0, state.val)
}
}
both tests succeeds no matter what test will run firstly. This makes var 2 solution preferred in most scenarios.
There is a case when var 1 is more convenient than var 2. It is when SUT have many dependencies and you have to copy and paste creation code for them in each test. Then you can declare dependencies as a class variables, but you must use test setUp (and possibly a tearDown) to ensure that their states is refreshed before each test
class TestClass: XCTestCase {
var dep1: Dependency1!
var dep2: Dependency2!
var sut: SUT!
func setUp() {
dep1 = Dependency1()
dep2 = Dependency2()
sut = SUT(dep1, dep2)
}
}
Variable declaration as Class level or the Function level is same as in other programming languages, so it would be great if you understand their basic principle.
Class level variable means that this variable can be accessed throughout the class, that means it can be used in any function of the same class. It also means that if you make the class as public, and that variable as public, then once the class is initialized it could be accessed within your program in other classes. Usually they could create memory issues if you don't manage them properly as they stay in the memory when the class itself is within the memory.
Function level variable can only be accessed within that specific function and not outside that function whether it's the same class or a different class, they have no reference outside their function. Usually they don't create memory issues in your whole program as these variables leave the memory when that function is completely executed.

EXC_BAD_ACCESS (code=2) when using NSNumberFormatter

I'm having a problem, which I can't figure out for the life of me. I've searched the internet, trying to understand Swifts's EXC_BAD_ACCESS, but nothing seemed to help.
The following code is quite long, but most of the time the comments are all the information needed to understand the item of relevance.
I have a class CalculatorController, which contains the following relevant methods and properties:
import UIKit
class CalculatorController: UIViewController {
// the actual `#IBOutlet` which is never accessed directly
#IBOutlet private weak var _mainDisplay: UILabel!
// an instance of `MainDisplayMicroController`
// holds a reference to `_mainDisplay`
// is used to manipulate `_mainDisplay` in a controlled way
private var mainDisplay: MainDisplayMicroController!
override func viewDidLoad() {
super.viewDidLoad()
// connects `mainDisplay` with `_mainDisplay`
mainDisplay = MainDisplayMicroController(label: _mainDisplay)
// sets `_mainDisplay`'s `text` property to "0"
mainDisplay.content = .Number(0)
//...
}
//...
}
In order to manage _mainDisplay in a certain way, I have created a class MainDisplayMicroController, which on the one hand contains a reference to the the UILabel itself, and on the other hand contains methods and properties, which perform actions on the UILabel:
import UIKit
class MainDisplayMicroController {
// used to express what `label.text` is currently showing
private enum DisplayState {
case ShowingNumber
case ShowingConstant
case ShowingErrorMessage
case Unknown
}
// holds the current state of what `label.text` is showing
private var state = DisplayState.Unknown
// used to pass different types of values in and out of this class
enum ContentType {
case Number(Double)
case Constant(String)
case ErrorMessage(String)
case Unknown(Any?)
}
// holds the reference to the label which is being manipulated/managed
private var label: UILabel?
// makes `label`'s `text` property directly accessible, as `label` is `private`
var text: String? {
get {
return label?.text
}
set {
label?.text = newValue
removeLeadingZeros()
transformToInteger()
}
}
// a property to allow controlled retrieval and manipulation of `label.text`
// uses `ContentType` to make clear what the information in `label.text` is/ is supposed to be
var content: ContentType {
get {
switch state {
case .ShowingNumber:
if let string = text {
if let value = NSNumberFormatter().numberFromString(string)?.doubleValue {
return .Number(value)
}
}
case .ShowingConstant:
if let symbol = text {
return .Constant(symbol)
}
case .ShowingErrorMessage:
if let message = text {
return .ErrorMessage(message)
}
default:
break
}
state = .Unknown
return .Unknown(text)
}
set {
switch newValue {
case .Number(let value):
text = "\(value)"
state = .ShowingNumber
removeLeadingZeros()
transformToInteger()
case .Constant(let symbol):
text = symbol
state = .ShowingConstant
case .ErrorMessage(let message):
text = message
state = .ShowingErrorMessage
case .Unknown(let thing):
text = "Error: Passed unknown value: \(thing)"
state = .ShowingErrorMessage
}
}
}
// removes the ".0" from `label.text`, if it is a whole number
private func transformToInteger() {
if state == .ShowingNumber {
switch content {
case .Number(let value):
if round(value) == value {
var doubleString = "\(value)"
if doubleString.rangeOfString("e") == nil {
dropLast(doubleString)
dropLast(doubleString)
}
text = doubleString
}
default:
break
}
}
}
// removes leading "0"s from `label.text` if they are redundant
private func removeLeadingZeros() {
if state == .ShowingNumber {
switch content {
case .Number(let displayedValue):
content = .Number(displayedValue)
default:
break
}
}
}
//...
}
Now, when I run the code I get the following error:
From what I've read on EXC_BAD_ACCESS, the error often occurs when trying to call methods on released objects. I've tried using NSZombieto check the issue, but I didn't find anything (probably due to my incompetence when using NSZombie).
If I try to follow what is happening by logic, I come to following conclusion:
mainDisplay is set successfully in viewDidLoad()
mainDisplay.content is called
in the content's setter the switch-statement executes the .Number case
text and state are successfully set
removeLeadingZeros() is called
the switch-statement accesses content's getter
the switch-statement in content's getter executes the .ShowingNumber case
the if-statements resolve to true, finally trying to evaluate the NSNumberFormatter expression
the EXC_BAD_ACCESS occurs
Does anyone know why this is happening? Does it have to do with me manipulating an #IBOutlet in a different class?
Any help is greatly appreciated!
Here are links to the complete CalculatorController and MainDisplayMicroController.
Update #1:
As #abdullah suggested I have tried directing the NSNumberFormatter expression in to multiple expressions. I still get the error though:
Update #2:
I've removed all references and external classes, to make it as simple as possible, while maintaining the same functionality.
All of the methods and properties defined in MainDisplayMicroController have been moved to CalculatorModel.
These methods and properties now access the original #IBOutlet, not any reference to it.
But still when trying to run it I get EXC_BAD_ACCESS(code=2) at the same line of code.
I'm just super confused, as it can't have anything to do with weird references, or objects being released too soon anymore.
Here's the complete code for the new CalculatorController.
Update #3:
I've removed the NSNumberFormatter line, by changing it to:
Now I get the following error though:
I assume there's some fundamental problem with the code, so I'm scrapping it. But thanks for all the help, and attempts at figuring this out.
Update #4:
This is what I get when adding a breakpoint on throw for all exceptions:
I don't really see anything in that line that can cause a crash. I suggest you do the following:
Make a clean build (clean, nuke your derived data folder, then build) and see if the crash persists
If the crash persists, set a breakpoint on throw for all exceptions to see which operation in the callstack caused the crash, and take it from there
#WarrenBurton is on to something.
Take your line that crashes out of your big class and run it in the playground and it works fine:
let string = "1.213"
if let value = NSNumberFormatter().numberFromString(string)?.doubleValue
{
println("value = \(value)")
}
Displays the result
value = 1.213
Where is your variable "string" defined in your class?
Notice that string is blue, like a keyword, not black, like other local variables.
I'd try local variable string ==> myString
just to know for sure.
Just 'cuz I was seeing the same thing and noticed no one had commented past your last edit (and maybe a fellow Googler for this issue will see this someday):
For both of our situations the issue is infinite recursion - we're calling a method from itself infinitely. That's the bug. The implication in the crash of NSNumberFormatter is a red herring.

Stuck on a DeckOfCards class

I'm working on a poker app. I have 2 questions,,,
1) Just started working on a DeckOfCards class to deal with the deck. I keep getting an error ("Expected Declaration") on one of the for loops. It worked on playgrounds but not in the project (not as a class though). How do I fix this?
2) Is it ok to have the cards represented this way (2 character strings in an array)?
import Foundation
class DeckOfCards {
var newDeck = ["A♠️", "2♠️", "3♠️", "4♠️", "5♠️",...."K♠️",
"A♥️", "2♥️", "3♥️", "4♥️", "5♥️",...."K♥️",
"A♣️", "2♣️", "3♣️", "4♣️", "5♣️",...."K♣️",
"A♦️", "2♦️", "3♦️", "4♦️", "5♦️",...."K♦️"]
var deck = [String]()
var randomNumber = 0
init() {
deck = []
}
for _ in 1...52 { // ERROR ON THIS LINE ("Expected Declaration")
randomNumber = Int(arc4random_uniform(UInt32(newDeck.count)))
deck.append(newDeck.removeAtIndex(randomNumber))
}
}
It is because you simply cannot have code hang like this within a class definition. You need to put it in a func. e.g.
func shuffleDeck() -> [String] {
var deck = [String]()
for _ in 1...52 {
randomNumber = Int(arc4random_uniform(UInt32(newDeck.count)))
deck.append(newDeck.removeAtIndex(randomNumber))
}
return deck
}
To answer your second part of the question, you are better off to use enum to represent the cards. Here is a good example: Add a method to Card that creates a full deck of cards, with one card of each combination of rank and suit

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.

Access variable in different class - Swift

i got two swift files :
main.swift and view.swift
In main.swift i have a variable (Int) initially set to 0.
With an IBACtion I set that variable to be 10, and everything is ok.
However, if I try access that variable from view.swift, with a simple call like main().getValue(), i get always 0 and not 10 even if the variable has changed it's value in main.swift.
The method getValue() in main.swift looks like this:
func getValue() -> Int {
return variable
}
EDIT
Here is the code (Translated from Italian :D )
import Cocoa
class Main: NSObject {
var variable: Int = 0
func getValue() -> Int {
return variable
}
#IBAction func updateVar(sender: AnyObject!) {
variable = 10
}
}
class View: NSView {
override func drawRect(dirtyRect: NSRect) {
println(Main().getValue()) //Returns always 0
}
}
Thanks in advance
Alberto
I have solved this by creating a generic main class which is accessible to all views. Create an empty swift file, name it 'global.swift' and include it in your project:
global.swift:
class Main {
var name:String
init(name:String) {
self.name = name
}
}
var mainInstance = Main(name:"My Global Class")
You can now access this mainInstance from all your view controllers and the name will always be "My Global Class". Example from a viewController:
viewController:
override func viewDidLoad() {
super.viewDidLoad()
println("global class is " + mainInstance.name)
}
There is an important distinction to be made between "files" in Swift and "classes". Files do not have anything to do with classes. You can define 1000 classes in one file or 1 class in 1000 files (using extensions). Data is held in instances of classes, not in files themselves.
So now to the problem. By calling Main() you are creating a completely new instance of the Main class that has nothing to do with the instance that you have hooked up to your Xib file. That is why the value comes out as the default.
What you need to do, is find a way to get a reference to the same instance as the one in your Xib. Without knowing more of the architecture of your app, it is hard for me to make a suggestion as to do that.
One thought, is that you can add a reference to your Main instance in your Xib using an IBOutlet in your View. Then you can simply do self.main.getValue() and it will be called on the correct instance.