Replace switch cases to reduce complexity - ios

I wanted to replace switch cases with some other logic since it increases code complexity (CCN) on sonar.
enum ItemType {
case one, two, three, four, five, six, seven, eight, nine, ten
}
func handleNavigation(itemType: ItemType){
switch itemType {
case .one:
self.performSegue(withIdentifier: StoryboardSegue.One, sender: nil)
case .two:
self.performSegue(withIdentifier: StoryboardSegue.Two, sender: nil)
case .three:
self.performSegue(withIdentifier: StoryboardSegue.Three, sender: nil)
case .four:
self.performSegue(withIdentifier: StoryboardSegue.Four, sender: nil)
case .five:
self.performSegue(withIdentifier: StoryboardSegue.Five, sender: nil)
case .six:
self.performSegue(withIdentifier: StoryboardSegue.Six, sender: nil)
case .seven:
self.performSegue(withIdentifier: StoryboardSegue.Seven, sender: nil)
case .eight:
self.performSegue(withIdentifier: StoryboardSegue.Eight, sender: nil)
case .nine:
self.performSegue(withIdentifier: StoryboardSegue.Nine, sender: nil)
case .ten:
self.performSegue(withIdentifier: StoryboardSegue.Ten, sender: nil)
}
}
We have to avoid this switch case since it increases CCN when number of cases increase.
How can we replace switch cases with other logic?

Remove the named constants in StoryboardSegue. Use the enum raw value instead.
First, give ItemType raw values by adding : String
enum ItemType: String {
case one, two, three, four, five, six, seven, eight, nine, ten
}
Now you can do:
self.performSegue(withIdentifier: itemType.rawValue, sender: nil)
This will perform segues with identifiers "one", "two", "three" etc.
If your segue identifiers have slightly different identifiers from the names of the enum cases, you can write a transformation function. For example, if they all end with "Segue".
func segueIdentifier(for itemType: ItemType) {
itemType.rawValue + "Segue"
}
// ...
self.performSegue(withIdentifier: segueIdentifier(for: itemType), sender: nil)
If the identifiers are completely different from the enum case names, then you can use a dictionary:
enum ItemType: Hashable {
case one, two, three, four, five, six, seven, eight, nine, ten
}
let segueIdMapping: [ItemType: _] = [
.one: StoryboardSegue.One,
.two: StoryboardSegue.Two,
.three: StoryboardSegue.Three,
// and so on
]
// ...
self.performSegue(withIdentifier: segueIdMapping[itemType]!, sender: nil)

Related

Using multiple toggles I'would like to regroup all toggles in one action

I'm trying to create an array composed of multiple tools.
The array changes depending on which tools are carried.
Expecting a long list of tools I would like to avoid having to write an action for each single element to add or remove from the array. ( I managed this part as you'll see below)
Instead I'd like to use the name of the toggle switch and apply the same action to all the toggles on the screen.
I came up with this inelegant method:
import UIKit
class mainVC: UIViewController {
var myEquippement: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func shovelSwitch(_ sender: UISwitch) {
if sender.isOn{
myEquippement.append("Shovel")
}
else {
let subjectIndex = myEquippement.firstIndex(of: "Shovel")
myEquippement.remove(at: subjectIndex!)
}
}
#IBAction func hammerSwitch(_ sender: UISwitch) {
if sender.isOn{
myEquippement.append("Hammer")
}
else {
let subjectIndex = myEquippement.firstIndex(of: "Hammer")
myEquippement.remove(at: subjectIndex!)
}
}
#IBAction func screwdriverSwitch(_ sender: UISwitch) {
if sender.isOn{
myEquippement.append("Screwdriver")
}
else {
let subjectIndex = myEquippement.firstIndex(of: "Screwdriver")
myEquippement.remove(at: subjectIndex!)
}
}
#IBAction func sawSwitch(_ sender: UISwitch) {
if sender.isOn{
myEquippement.append("Saw")
}
else {
let subjectIndex = myEquippement.firstIndex(of: "Saw")
myEquippement.remove(at: subjectIndex!)
}
}
}
could you point me please to a better way of doing it.
I thought of using something like this:
#IBAction func toggleSwitched(_ sender: UISwitch) {
if sender.isOn{
myEquippement.append(sender.title!)
}
else {
let subjectIndex = myEquippement.firstIndex(of: sender.title!)
myEquippement.remove(at: subjectIndex!)
}
}
but sender.title always returns a nil value, the force unwrapping crashes the app.
Thanks in advance for your help.
One solution would be to have a master array of tool names. When creating a switch, assign its tag property to the corresponding index within that array.
Then in your single switch handler you can use the sender's tag to get the name of the tool from the master list of tool names. That will give you the title for the switch. Then your logic for adding/removing the title from myEquipment can be used.

Switch Save user preference

I am trying to send a tag with OneSignal when the switch is turned on, and send request to delete the tag when it is turned off again.
#IBAction func tagGeneral(_ sender: UISwitch) {
if (sender.isOn == true) {
OneSignal.sendTag("General", value: "value")
print("sendtag")
}
else {
OneSignal.deleteTag("General")
print("deletetag")
}
}
This is the code i use for it. Seems to be working but when the user goes to another page the switch is automatically turned off...
How can i fix this?
Regarding #Ryan's comment, Here's an answer:
First. there are many ways to save the user preference, i'll be doing it with UserDefaults() | Edit your button action code:
#IBAction func tagGeneral(_ sender: UISwitch) {
let userdef = UserDefaults.standard
if (sender.isOn == true) {
OneSignal.sendTag("General", value: "value")
print("sendtag")
// user made the choice
userdef.set(true, forKey: "sw_set")
} else {
OneSignal.deleteTag("General")
print("deletetag")
// reset
userdef.set(false, forKey: "sw_set")
}
}
Normally this wouldn't work without this little function, make sure you call this function in your viewDidAppear():
private func init_switch() {
// Thanks #Vadian for the tip
let userdef = UserDefaults.standard
self.yourSwitch.isOn = userdef.bool(forKey: "sw_set")
}
Call it in viewDidAppear():
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
self.init_switch()
}
And let me know if it helped.

Segmented control not changing index

I'm coding a game, and I have a segmented control for the user to select a difficulty level. I've added some simple code inside the cases to check if the index changes, but it doesn't appear to. Here's the code:
#IBAction func chooseOnePlayerDifficulty(_ sender: UISegmentedControl) {
switch onePlayerGameType {
case .sluggish:
onePlayerGameType = typeOfGame.sluggish
print("Sluggish difficulty selected.")
case .average:
onePlayerGameType = typeOfGame.average
print("Average difficulty selected.")
case .ninja:
onePlayerGameType = typeOfGame.ninja
print("Ninja difficulty selected.")
}
}
Now, when I test this in the simulator, the only thing that prints to the console is "Average difficulty selected." No matter which one I select. Why is this happening and how do I fix it?
EDIT: I've found an answer that works by asking this question on another forum. Thanks for the help though. Here's the answer from there that worked for me: Here is the link to the answer
This part of your code sets onePlayerGameType to .average, when you select the control again the value is still .average.
case .average:
onePlayerGameType = typeOfGame.average
I have a segmented control for the user to select a difficulty level
If that's all you need why do you need switch and not just assign it directly?
enum typeOfGame : Int {
case sluggish
case average
case ninja
}
#IBAction func chooseOnePlayerDifficulty(_ sender: UISegmentedControl) {
// assuming typeOfGame is enum
let difficultyLevel = typeOfGame(rawValue: sender.selectedSegmentIndex)
onePlayerGameType = difficultyLEvel
}
I hope you registered action method with segment control in StoryBoard/XIB or in code
segmentedControl.addTarget(self, action: "chooseOnePlayerDifficulty:", forControlEvents: .ValueChanged)
Try with following
#IBAction func chooseOnePlayerDifficulty(_ sender: UISegmentedControl) {
let selectedSegment = sender.selectedSegmentIndex
switch selectedSegment {
case 0:
onePlayerGameType = typeOfGame.sluggish
print("Sluggish difficulty selected.")
case 1:
onePlayerGameType = typeOfGame.average
print("Average difficulty selected.")
case 2:
onePlayerGameType = typeOfGame.ninja
print("Ninja difficulty selected.")
default:
print("Assing default")
}
}
Edit Right #JakeG, updated answer

Send many things through a segue - Swift 4

Edit - not really a question anymore - please feel free to delete this post or use it as you wish. A little red box says this post is mostly code and Grammarly is kind of helpful too so just rambling on now until the little red box goes away.
Working solution:
ViewController1 ->
enum to tag many buttons - (change each button tag in interface builder)
enum Answers : Int, CustomStringConvertible {
case bag = 1
case bird = 2
enum for text
var description : String {
switch self {
case .bag: return "Beg"
case .bird: return "Birds"
}
}
enum for images - (stored in Assets)
var pics : String {
switch self {
case .bag: return "beg"
case .bird: return "birds"
}
}
}
Prepare segue - (storyboard should match identifier name)
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ShowAnswers" {
guard
let controller = segue.destination as? proverbsViewController,
let category = sender as? Answers
else { return }
controller.answer = category.description
controller.image = UIImage(named: category.pics)!
}
}
Button to segue - all buttons are connected here (tagged in IB)
#IBAction func onButtonTap(_ sender: UIButton) {
performSegue(withIdentifier: "ShowAnswers", sender: Answers(rawValue: sender.tag))
}
}
Recieve data:
ViewController2 ->
var answer = ""
var image = UIImage()

I keep getting the errors... 'use of unresolved identifier' and 'Type gameType has no member pause game'

I created a button to pause my game and I'm trying to make some actions for it.
So I have no idea what I'm doing wrong, all other 'solutions' on this website haven't been working for me, so could you please tell me what I'm doing wrong, or rather what I'm not doing.
import Foundation
import UIKit
enum gameType {
case easy
case medium
case hard
case player2
case impossible
}
func pauseGame() { // HERE IS WHERE I GET THE ERRORS... IN THIS FUNCTION
self.isPaused = true
currentGameType = gameType.pauseGame
self.physicsWorld.speed = 0
self.speed = 0.0
}
class MenuVC : UIViewController {
#IBAction func Player2(_ sender: Any) {
moveToGame(game: .player2)
}
#IBAction func Easy(_ sender: Any) {
moveToGame(game: .easy)
}
#IBAction func Medium(_ sender: Any) {
moveToGame(game: .medium)
}
#IBAction func Hard(_ sender: Any) {
moveToGame(game: .hard)
}
#IBAction func Impossible(_ sender: AnyObject) {
moveToGame(game: .impossible)
}
func moveToGame(game : gameType) {
let gameVC = self.storyboard?.instantiateViewController(withIdentifier: "gameVC") as! GameViewController
currentGameType = game
self.navigationController?.pushViewController(gameVC, animated: true)
}
}
At the top of your file, gameType is an enumeration. That means that there are a finite set of cases that it can have (the game type can either be easy, medium, hard, etc.) In your code, you're attempting to use a case called pauseGame, but if you look at the definition of gameType there's no case with that name. So you either need to a) add a pauseGame case to gameType (does it make sense for pauseGame to be a "kind" of game?) or not set the gameType inside your pauseGame function.
My instinct is that you don't actually want to change the type of the game when the user pauses it, so you can probably just get rid of that line. Also, a note on style -- it's good to start the names of types with capital letters to distinguish them from functions and local variables (so GameType instead of gameType.)

Resources