Hello I am trying to learn swift. I have a little experience with javascript so i tried modeling this loop in the same manner i usually do. The function actually outputs what its supposed to but I keep getting an error message and I am unsure of what I am doing wrong. Here is my code:
import UIKit
let dir: [String] = ["north", "east", "south", "west"]
var num = dir.count
func move(){
for i in 0 ... num{
var holder = dir[i]
switch holder{
case "north":
print("you've moved north")
case "east":
print("you've moved east")
case "south":
print("you've moved south")
case "west":
print("you've moved west")
default:
print("where you going?")
}
if i == 3{
print("round the world")
}
}
}
move()
i get this error on the last line "move()"
error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION
(code=EXC_I386_INVOP, subcode=0x0).
this is what outputs to the console:
you've moved north
you've moved east
you've moved south
you've moved west
round the world
Fatal error: Index out of range: file
/Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-1103.2.25.8/swift/stdlib/public/core/ContiguousArrayBuffer.swift,
line 444
In your code trying to access 4th index due to you have used ... in loop control syntax. And 4th index not in array.
Here is some details about for swift loop.
for index in 0...4 {
...
}
The above snippet says, iterate over the range starting at 0 and inclusive of 4 i.e from 0–4
If you do not want 4 included, you use this called the half-open range operator (..<).
for index in 0..<4 {
...
}
This would loop from 0 to 3 and stop execution.
In swift, there're more efficient ways to loop...but to better understand what you implemented...
I've updated your code...it will run properly.
let dir: [String] = ["north", "east", "south", "west"]
var num = dir.count
func move(){
for i in 0..<num{
var holder = dir[i]
switch holder{
case "north":
print("you've moved north")
case "east":
print("you've moved east")
case "south":
print("you've moved south")
case "west":
print("you've moved west")
default:
print("where you going?")
}
if i == 3{
print("round the world")
}
}
}
move()
Output :-
you've moved north
you've moved east
you've moved south
you've moved west
round the world
Happy Coding in Swift :-)
import UIKit
class ViewController: UIViewController {
let dir: [String] = ["north", "east", "south", "west"]
override func viewDidLoad() {
super.viewDidLoad()
move()
// Do any additional setup after loading the view.
}
func move(){
for (index, element) in dir.enumerated() {
// print("Item \(index): \(element)")
switch element{
case "north":
print("you've moved north")
case "east":
print("you've moved east")
case "south":
print("you've moved south")
case "west":
print("you've moved west")
default:
print("where you going?")
}
if index == 3{
print("round the world")
}
}
}
}
So, here you are first taking count of the num which is here 4.
Related
So, need you help, I have loops, where I'd like to find first positive element and it will be text of label and exit from all loops, but every time I get last element:
for j in getArrayOfAllTimes[i].timeForGetDifference()
{
switch j - timeNow() {
case let x where x > 0:
nextTimeLabel.text = String(j - timeNow())
break
default:
break
}
}
How get first element > 0?
The first(where:) method of Array
Instead of explicitly breaking out of a loop when a first element that fulfills som predicate is found, you could simply make use of the Array method first(where:).
Since you haven't provided us with a minimal, complete and verifiable example (which you should) we'll construct such an example:
/* Example setup */
struct Foo: CustomStringConvertible {
private let bar: Int
init(_ bar: Int) { self.bar = bar }
func timeForGetDifference() -> Int {
return bar
}
var description: String {
return String(bar)
}
}
func timeNow() -> Int { return 10 }
let getArrayOfAllTimes = [Foo(6), Foo(2), Foo(9), Foo(4), Foo(11), Foo(3), Foo(13)]
// nextTimeLabel: some UILabel
For the example as per above, we could set the text property of the nextTimeLabel as follows, using first(where:) to find the first element fulfilling our predicate, given that it exists (otherwise; will return nil in which case we will not enter the optional binding block below).
if let firstNonNegativeFoo = getArrayOfAllTimes
.first(where: { $0.timeForGetDifference() - timeNow() > 0 }) {
nextTimeLabel.text = String(describing: firstNonNegativeFoo) // 11
}
As to why your own approach does not work as intended: a break statement within a case of a switch statement will simply end the execution of the switch statement (not the loop which is one level above the switch statement.
From the Language Reference - Statements:
Break Statement
A break statement ends program execution of a loop, an if statement,
or a switch statement.
In you case, you've added the break statements as the last statements of each case: here, particularly, the break has truly no effect (since the switch statement would break out anyway, after exiting the case which it entered).
for i in 1...3 {
switch i {
case is Int: print(i); break // redundant 'break'
case _: ()
}
} // 1 2 3
// ... the same
for i in 1...3 {
switch i {
case is Int: print(i)
case _: ()
}
} // 1 2 3
for j in getArrayOfAllTimes[i].timeForGetDifference() {
bool a = NO;
switch j - timeNow() {
case let x where x > 0:
a = YES
nextTimeLabel.text = String(j - timeNow())
break
default:
break
}
if a == YES {
break;
}
}
I am using the Chart Library (danielgindi/Charts).
I have a segment which displays a Day, Week, Month and Year option. Depending on what is selected will show on the Chart.
I have the following code:
var highestValue : Double = 0.0
#IBAction func segmentChanged(sender: AnyObject) {
switch durationSegment.selectedSegmentIndex
{
case 0:
segmentSelected = "Day"
highestValue = 8.0
case 1:
segmentSelected = "Week"
highestValue = 10.0
case 2:
segmentSelected = "Month"
highestValue = 12.0
case 3:
segmentSelected = "Year"
highestValue = 14.0
default:
break;
}
print("segment selected = \(segmentSelected)")
displayDurationLabel(segmentSelected, providedStartDate : NSDate())
}
func setChart(xValues: [String], valuesCandleChart: [Double], durationToBeDisplayed: String) {
chartView.descriptionText = ""
print("within setChart, durationToBeDisplayed = \(durationToBeDisplayed)")
chartView.noDataText = "No spends logged for \(durationToBeDisplayed)"
chartView.leftAxis.customAxisMax = highestValue
}
When the ViewController is initially called, it uses the segmentSelected = "Month" and the chart displays the correct message however when I change the segment, e.g. "Day", the print shows the correct output however the '.noDataText' doesn't update as expected.
Any help would be appreciated :)
call candleChartView.setNeedsDisplay() will redraw the chart and update the text
Within the setChart function, at the end call:
chartView.notifyDataSetChanged()
Immediately after calling the setChart function call:
chartView.legend.resetCustom()
I'm trying to teach myself Swift via the Stanford iTunes U course (currently working on the calculator app), but I just ran into a super weird issue that I've never seen before and can't figure out how to solve: one of my return statements (and only the return statement itself) is being skipped during runtime.
func evaluateOps(ops: [Op]) -> (result: Double?, remainingOps: [Op]) {
if !ops.isEmpty {
var remainingOps = ops
let op = remainingOps.removeLast()
switch op {
case ...
case ...
case .BinaryOperation(_, let operation):
// check that there are 2 operands before it
if remainingOps.count == 2 {
let op1Evaluation = evaluateOps(remainingOps)
if let operand1 = op1Evaluation.result {
let op2Evaluation = evaluateOps(op1Evaluation.remainingOps)
if let operand2 = op2Evaluation.result {
// PROBLEM AREA...
let x = (operation(operand1, operand2), op2Evaluation.remainingOps)
println("results: \(x.0) remainder: \(x.1)")
return (x.0, x.1) // skipped during runtime...
}
}
}
else { ... }
}
}
println("returning nil")
return (nil, ops)
}
// troublesome method is called here...
let (result, remainder) = evaluateOps(opStack)
println("\(opStrings) = \(result) with \(remainderStrings) leftover")
Everything works so that, if I tried to calculate 5*3 for example, the console would read:
results: 15.0 remainder: []
returning nil
[5.0, ×, 3.0] = nil with [5.0, ×, 3.0] leftover
I think the problem might have something to do with the fact that, in the above code, if I tried to simply return x, I get a compile error that reads Cannot express tuple conversion '(Double, [CalculatorModel.Op])' to '(result: Double?, remainingOps: [CalculatorModel.Op])'. I also have no idea what to do with this.
In my researching the problem, I've discovered the downcasting keyword as (see altered code below), which removed the compile error when returning x rather than (x.0, x.1) but results in the same console result (except that now it says results: Optional(15.0) remainder: []):
let x = (operation(operand1, operand2) as Double?, op2Evaluation.remainingOps as [Op])
println("results: \(x.0) remainder: \(x.1)")
return x // no error but same result as return (x.0, x.1)
I've also tried sticking a println(getClassName(x.0!)) just before the return statement to make sure I even had a double, and it spit out __NSCFNumber... which I also researched, and found to be highly underdocumented (or at least not documented well enough to figure out how that's affecting me or how I can fix it!)
Although as you can see, it should be a double...
enum Op {
case Operand(Double)
case UnaryOperation(String, Double -> Double)
case BinaryOperation(String, (Double, Double) -> Double)
var description: String {
get {
switch self {
case .Operand(let operand):
return "\(operand)"
case .UnaryOperation(let symbol, _):
return "\(symbol)"
case .BinaryOperation(let symbol, _):
return "\(symbol)"
}
}
}
}
As you can see, everything's working perfectly fine as it's performing the calculation and everything...until it gets to that little troublesome area. I've searched StackOverflow and did a bit of Googling, but this seems to be a rather uncommon problem... Any help is appreciated! Let me know if you need any further information. Thanks!
EDIT:
getClassName(_:) is defined below (found it online somewhere...):
func getClassName(obj : AnyObject) -> String
{
let objectClass : AnyClass! = object_getClass(obj)
let className = objectClass.description()
return className
}
So I have this,
func doStuff() {
//blahblahblah
}
var randomNumber = arc4random() % 4
randomNumber += 1
switch(randomNumber) {
case 1:
doStuff()
break
case 2:
doStuff()
break
case 3:
doStuff()
break
case 4:
doStuff()
break
}
Now I need it to do something like this
func doStuff() {
//blahblahblah
}
var alreadyUsed = [""]
var randomNumber = arc4random() % 4
randomNumber += 1
if randomNumber is not inside alreadyUsed {
switch(randomNumber) {
case 1:
doStuff()
alreadyUsed.append("1")
break
case 2:
doStuff()
alreadyUsed.append("2")
break
case 3:
doStuff()
alreadyUsed.append("3")
break
case 4:
doStuff()
alreadyUsed.append("4")
break
}
}
Essentially, I am trying to have something that will randomly select a case, then won't select the same case again the next time the function is run. I am using that first chunk of code as a function. After a case has been used in that function, I do not want the function to use it again. I hope this makes sense.
Thanks guys!
EDIT: Though I'd find it more useful, It doesn't even have to be an array, as long as it gets the job done.
var someArray = [1,2,3,4]
let index = Int(arc4random_uniform(UInt32(someArray.count)))
let randomNumber = someArray[index]
someArray.removeAtIndex(index)
This should do what you want.
First you create an array with the available numbers
Let it randomly pick a number for the index
Remove the number in the array
I've changed the conversion of UInt32 to Int because according to this post:
Int(arc4random()) will crash 50% of the time it's executed on a 32-bit platform because a UInt32 won't fit in an Int32
Im trying to ask for some values from a variable.
The variable is going to have the description of the weather and i want to ask for specific words in order to show different images (like a sun, rain or so)
The thing is i have code like this:
if self.descriptionWeather.description.rangeOfString("Clear") != nil
{
self.imageWeather.image = self.soleadoImage
}
if self.descriptionWeather.description.rangeOfString("rain") != nil
{
self.imageWeather.image = self.soleadoImage
}
if self.descriptionWeather.description.rangeOfString("broken clouds") != nil
{
self.imageWeather.image = self.nubladoImage
}
Because when i tried to add an "OR" condition xcode gives me some weird errors.
Is it possible to do a swich sentence with that? Or anyone knows how to do add an OR condition to the if clause?
I had a similar problem today and realized this question hasn't been updated since Swift 1! Here's how I solved it in Swift 4:
switch self.descriptionWeather.description {
case let str where str.contains("Clear"):
print("clear")
case let str where str.contains("rain"):
print("rain")
case let str where str.contains("broken clouds"):
print("broken clouds")
default:
break
}
Swift 5 Solution
func weatherImage(for identifier: String) -> UIImage? {
switch identifier {
case _ where identifier.contains("Clear"),
_ where identifier.contains("rain"):
return self.soleadoImage
case _ where identifier.contains("broken clouds"):
return self.nubladoImage
default: return nil
}
}
You can do this with a switch statement using value binding and a where clause. But convert the string to lowercase first!
var desc = "Going to be clear and bright tomorrow"
switch desc.lowercaseString as NSString {
case let x where x.rangeOfString("clear").length != 0:
println("clear")
case let x where x.rangeOfString("cloudy").length != 0:
println("cloudy")
default:
println("no match")
}
// prints "clear"
Swift language has two kinds of OR operators - the bitwise ones | (single vertical line), and the logical ones || (double vertical line). In this situation you need a logical OR:
if self.descriptionWeather.description.rangeOfString("Clear") != nil || self.descriptionWeather.description.rangeOfString("clear") != nil {
self.imageWeather.image = self.soleadoImage
}
Unlike Objective-C where you could get away with a bitwise OR in exchange for getting a slightly different run-time semantic, Swift requires a logical OR in the expression above.
If you do this a lot, you can implement a custom ~= operator that defines sub-string matching. It lends itself to this nice syntax:
switch "abcdefghi".substrings {
case "def": // calls `"def" ~= "abcdefghi".substrings`
print("Found substring: def")
case "some other potential substring":
print("Found \"some other potential substring\"")
default: print("No substring matches found")
}
Implementation:
import Foundation
public struct SubstringMatchSource {
private let wrapped: String
public init(wrapping wrapped: String) {
self.wrapped = wrapped
}
public func contains(_ substring: String) -> Bool {
return self.wrapped.contains(substring)
}
public static func ~= (substring: String, source: SubstringMatchSource) -> Bool {
return source.contains(substring)
}
}
extension String {
var substrings: SubstringMatchSource {
return SubstringMatchSource(wrapping: self)
}
}
I'd recommend using a dictionary instead, as a mapping between the substring you're searching for and the corresponding image:
func image(for weatherString: String) -> UIImage? {
let imageMapping = [
"Clear": self.soleadoImage,
"rain": self.soleadoImage,
"broken clouds": self.nubladoImage]
return imageMapping.first { weatherString.contains($0.key) }?.value
}
A dictionary gives you flexibility, adding new mappings is easy to do.
This link also describes overloading operator ~= which is actually used by the switch statement for matching cases to allow you to match regular expressions.