I extend my custom protocol with mutable function. And then apply it on the instance of the corresponding type. But instance is changed only on that row. On the next row, it has previous value. Why changes made on the instance doesn't persist?
If I assign the result of mutating to the var/let. Then the result saves. Or if I apply harderWorkout() in the print() statement it print changed value.
struct Workout {
var distance: Double
var time: Double
var averageHR: Int
}
extension Workout: CustomStringConvertible {
var description: String {
return "Workout(distance: \(distance), time: \(time), averageHR: \(averageHR)"
}
}
extension Workout {
mutating func harderWorkout() -> Workout {
return Workout(distance: (self.distance * 2), time: (self.time * 2), averageHR: (self.averageHR + 40))
}
}
var workout = Workout(distance: 500, time: 50, averageHR: 100)
print(workout) //Workout(distance: 500.0, time: 50.0, averageHR: 100, Speed: 10.0
workout.harderWorkout()
print(workout) //Workout(distance: 500.0, time: 50.0, averageHR: 100, Speed: 10.0
In the last print I expected to see Workout(distance: 1000.0, time: 100.0, averageHR: 140 but it's not clear to me why harderWorkout() method doesn't change the workout instance. Maybe it's because of the value type. But I put the mutable prefix...
Will be very thankful if someone explains to me the reason and its mechanism.
Instead of returning Workout instance from harderWorkout() method, assign the new Workout instance to self, i.e.
extension Workout {
mutating func harderWorkout() {
self = Workout(distance: (self.distance * 2), time: (self.time * 2), averageHR: (self.averageHR + 40))
}
}
Alternatively, you can simply change the distance, time and averageHR values of the same instance, i.e.
extension Workout {
mutating func harderWorkout() {
self.distance *= 2
self.time *= 2
self.averageHR += 40
}
}
It is pretty, simple - in your workoutHarder() you create a new Workout and return that, instead of mutating it.
If you expect it to mutate, you will need to to the following:
extension Workout {
mutating func harderWorkout() -> Workout {
self.distance *= 2
self.time *=2
self.averageHR += 40
return self
}
}
You see that it now returns from self, and maybe the method dont need to return at all if you just want it to mutate?
Related
I can't find a way to implement a wait function, I'm using swiftforwindows and no examples online have been able to solve it so far. It's Swift 4.2
The class is basically an array that when a function is called each index on the array gets a constant value deducted. the tick function is what is being called. I'm new to Swift.
class resProj {
var list = [1,1,1,1]
var projReq = [100,200,300,50]
var completed = false
func tick(){
for count in 0..<projReq.count{
if projReq[count] <= list[count]{
projReq[count] = 0
}
else if projReq[count] > list[count]{
projReq[count] -= list[count]
}
}
print(projReq)
}
init(
mathsP mathsIn: Int,
scienceP sciecnceIn: Int,
enginerP enginerIn: Int,
businessP businessIn: Int) {
self.list [0] = mathsIn
self.list [1] = sciecnceIn
self.list [2] = enginerIn
self.list [3] = businessIn
}
}
var spaceElev = resProj(
mathsP: 10,
scienceP: 20,
enginerP: 30,
businessP: 5)
var x = false
while x == false{
//wait function here pls//
print("tick", terminator:"?")
let y = readLine()
if y == "y"{
spaceElev.tick()
}
else{
print("gotta put y")
}
var templist = spaceElev.projReq
var templistcount = 0
templistcount = templist.count
for loop in 0..<templistcount{
if templist[loop] == 0{
templistcount -= 1
}
}
if templistcount == 0 {
x = true
print("project completed")
}
}
}
Where it says //wait function here pls// I would like to make the program wait for 1 second.
There are a lot of way to do this but most common way is create a completion function. For example:
func doSth(_ someParameter: String, _ completion: ()->()) {
print(someParameter)
// After your code is finish call completion
completion()
}
And when you call (there is two way to call):
doSth("Done") {
print("You can be sure that this block will work after your func finish")
}
or you can simply create another func and send it as a parameter.
You can also use DispatchQueue:
DispatchQueue.main.asyncAfter(deadline: .now()+1) {
// put your func here...
}
You can simple use the UNIX-Functin func sleep(_: UInt32) -> UInt32.
In your case use sleep(1) to wait one second.
You could use Grand Central Dispatch or perform.
GCD solution:
let delayInSeconds = 1
DispatchQueue.main.asyncAfter(deadline: .now() + delayInSeconds) {
print("tick", terminator:"?")
}
If you want to learn more about Grand Central Dispatch (GCD) I suggest you read through this:
Grand Central Dispatch - Wikipedia
Grand Central Dispatch Tutorial - Ray Wenderlich
Perform solution:
Create a function like this:
#objc func delayedFunc() {
//write the code here that you want to execute with a one second delay
}
Then call this where you want the delayed function to execute:
let delayInSeconds = 1
perform(#selector(delayedFunc), with: nil, afterDelay: delayInSeconds)
You can use the RunLoop class:
func wait(for interval: TimeInterval) {
RunLoop.current.run(until: Date() + interval)
}
This is a multiple audiofile playback project I am currently working on, where multiple AKPlayers are played in a random order, through AKSequencer.
First, I have an array of filenames:
Let filenames = [“1.mp3”, “2.mp3, “3.mp3”, … “10.mp3”]
This is loaded on AKPlayer individually, to later call it in a random order:
Let players: [AKPlayer] = {
Do {
Let filenames = [“1.mp3”, “2.mp3, “3.mp3”, … “10.mp3”]
Return try filenames.map { AKPlayer(audioFile: try AKAudioFile(readRileName: $0)) }
} catch {
fatalError()
}
}()
Then, I called AKPlayer through AKsequencer, by triggering it through ‘playRandom’ function:
Let sequencer = AKSequencer()
Let callbackInst = AKCallbackInstrument()
func playRandom() {
let playerIndex = Int(arc4random_uniform(UInt32(players.count)))
players[playerIndex].play()
}
func addTracks() {
let track = sequencer.newTrack()!
track.add(noteNumber: 48, velocity: 127, position: AKDuration(beats: 0), duration: AKDuration(beats: 16), channel: 0)
track.setMIDIOutput(callbackInst.midiIn)
callbackInst.callback = { status, note, vel in
guard status == .noteOn else { return }
self.playRandom()
}
}
Lastly, I set AKPlayer as AudioKit.output, and started the sequencer.
So far this was successful! The sequencer plays AKPlayer seamlessly, in a random order.
But I wanted to try different kind of randomness: repeating randomly selected player 2 or 3 times. (like 2, 2, 3, 3, 3, 1, 1, 5, 5, 5, 9, 9, …) Right now, ‘playRandom’ simply chooses different AKPlayer on each repeat.
As one solution, thanks to StackOverFlow masters, I tried something like:
class RandomWithRepeats {
var range: ClosedRange<Int>
var repeatRange: ClosedRange<Int>
var repeatCount = 0
var value = 0
init(range: ClosedRange<Int>, repeatRange: ClosedRange<Int>) {
self.range = range
self.repeatRange = repeatRange
}
// generate a random number in a range
// Just use Int.random(in:) with Swift 4.2 and later
func random(in range: ClosedRange<Int>) -> Int {
return Int(arc4random_uniform(UInt32(range.upperBound - range.lowerBound + 1))) + range.lowerBound
}
func nextValue() -> Int {
// if repeatCount is 0, its time to generate a new value and
// a new repeatCount
if repeatCount == 0 {
// For Swift 4.2, just use Int.random(in:) instead
value = self.random(in: range)
repeatCount = self.random(in: repeatRange)
}
repeatCount -= 1
return value
}
}
And then I modified playRandom function like:
func playRandom() {
Let rand = randomWithRepeats(range: 1…players.count, repeatRange: 2…3)
Do { players[rand.nextValue()].play() }
}
Turns out, this is the exact same result because the playRandom function itself triggered (by AKSequencer) each repeat, so it doesn’t actually ‘repeat’ random AKPlayer 2 or 3 times. Can I solve this issue in a different way? Much appreciated. <3
The job of the RandomWithRepeats class is to keep track of when to deliver you a different AKPlayer instance, but you are creating a new instance of RandomWithRepeats each time you call playRandom(), so it can't keep track of anything.
Create a single instance of RandomWithRepeats when you create the array of players and use this instance in your playRandom() method.
let rand = RandomWithRepeats(range: 0 ... players.count, repeatRange: 2 ... 3)
func playRandom() {
try? players[rand.nextValue()].play()
}
For a cleaner solution, you might encapsulate the logic of RandomWithRepeats together with the AKPlayer array into a single class, such as a RandomlyRepeatingPlayer class.
I'm trying to create MIDI content for sequencer playback by using an array of structs. Each struct creates a 'measure', and each measure has properties of location, numberOfBeats, and tempo. I'd like to loop through each measure for each beat within the measure, and play one of two MIDI notes based on whether the beat is the first in the measure or not.
The code below will print correctly (click1, click 2, click2, click1, click 2, click1, click2, click2), however the MIDI content (attached to a wav) will only play once through and not advance to the next measure in the array. I've been trying to get the func to work it's way sequentially through the array of measures using a variety of loop types, but can't get it to progress correctly. Thanks!
import UIKit
import AudioKit
class ViewController: UIViewController {
let sequencer = AKSequencer ()
var metronomeTrack1: AKMusicTrack!
var metronomeTrack2: AKMusicTrack!
let click1 = AKMIDISampler()
let click2 = AKMIDISampler ()
func setupTracks() {
metronomeTrack1 = sequencer.newTrack()
metronomeTrack1?.setMIDIOutput(click1.midiIn)
metronomeTrack2 = sequencer.newTrack()
metronomeTrack2?.setMIDIOutput(click2.midiIn)
}
struct Measure {
var location : Int
var numberOfBeats : Int
var tempo : Int
}
var measureOne = Measure(location: 1, numberOfBeats: 4, tempo: 100)
var measureTwo = Measure(location: 2, numberOfBeats: 2, tempo: 100)
var measureThree = Measure(location: 3, numberOfBeats: 4, tempo: 100)
var arrayIndex = [Measure]()
//Variables to track location through loops
var arrayLocator = 0
var beatWatcher = 0
func playMeasures () {
for _ in arrayIndex {
for beats in 0...(arrayIndex[arrayLocator].numberOfBeats - 1) {
if beatWatcher == 0 {
metronomeTrack1?.add(noteNumber: 60, velocity: 100, position: AKDuration(beats: Double(beats)), duration: AKDuration(beats: 1))
print("click1")
beatWatcher += 1
} else {
metronomeTrack2?.add(noteNumber: 60, velocity: 100, position: AKDuration(beats: Double(beats)), duration: AKDuration(beats: 1))
print("click2")
beatWatcher += 1
if beatWatcher == (arrayIndex[arrayLocator].numberOfBeats) {
beatWatcher = 0
arrayLocator += 1
}
}
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
arrayIndex = [measureOne, measureTwo, measureThree]
metronomeTrack1?.clear()
metronomeTrack2?.clear()
sequencer.setLength(AKDuration(beats: 100))
AudioKit.output = click1
AudioKit.output = click2
setupTracks()
try! click1.loadWav("blockhigh")
try! click2.loadWav("blocklow")
playMeasures()
sequencer.play()
try!AudioKit.start()
}
}
After further research; looks like using stride is the best way to work through the loops, and the "position" in each MIDI track needs to increment by 1 each loop to get added to the sequence in proper order.
I've write a simple code:
extension String {
func trailingSpaces (width: Int) -> String {
var s = "\(self)"
for i in count(s)..<width {
s = s + " "
}
return s
}
func leadingSpaces (width: Int) -> String {
var s = "\(self)"
for i in count(s)..<width {
s = " " + s
}
return s
}
}
class ViewController: UIViewController {
var users = ["Marco", "Gianni", "Antonio", "Giulio", "Franco"]
var ages = [29, 45, 17, 33, 37]
override func viewDidLoad() {
super.viewDidLoad()
var merged = [String: Int] ()
var totalAge = 0.0
for var i = 0; i < ages.count; i++ {
merged[users[i]] = ages[i]
}
for user in sorted(merged.keys) {
let age = merged[user]
totalAge += Double(age!)
let paddedUser = user.trailingSpaces(10)
let paddedAge = "\(age)".leadingSpaces(3)
println("\(paddedUser) \(age!)")
}
println("\n\(merged.count) users")
println("average age: \(totalAge / Double(merged.count))")
}
}
but I can't make it work the leadingSpaces function and I can't understand the reason, it's quite identical to the other extension func that works.
It give the error
fatal error: Can't form Range with end < start
on runtime
in case you run into this kind of problem, always do a println() of the variable you are using
println("\(age)") right before let paddedAge = "\(age!)".leadingSpaces(3)
reveals the problem
age is an optional, meaning that you are trying to do the padding on a String which has this value "Optional(17)"
Thus, your count(s) is higher than 3, and you have an invalid range
Your variable age is not an Int - it's an optional - Int?. You know this already as you are unwrapping it in the lines totalAge += Double(age!) and println("\(paddedUser) \(age!)") - but you are not unwrapping it in the failing line let paddedAge = "\(age)".leadingSpaces(3). The string being passed to leadingSpaces is not "17", it's "Optional(17)", which is why your padding function is failing, as the length is greater than the requested width.
Having said that, as the commentator #milo256 points out, Swift can only iterate upwards, and so unless you put a check on width >= .count in your padding functions they will crash at some point.
I'm working with Swift, Xcode 6 and SpriteKit,
I want make a game where some sprites fall down from the top of the screen, but each sprite have a defined speed, position and activation time. I have this working code, but I really don't think that it's the most optimised way to do it:
class obstacles: SKSpriteNode
{
var isOnScreen = false
var type: Int = 0
var initTime: Int = 0
}
var obstacle = [obstacles]() // obstacle is an array of SKSpriteNode
// type : initTime : speed : position : xScale : yScale
var level1: [String] = ["0:120:3:0:1:1", "0:130:4:80:2:2","1:140:8:120:6:1","0:150:6:240:2:2"]
override func didMoveToView(view: SKView)
{
initObstacles()
}
func initObstacles()
{
for var x = 0; x < level1.count; x++ // for each obstacle
{
var index = 0
var type = String("")
var initTime = String("")
var speed = String("")
var position = String("")
var xScale = String("")
var yScale = String("")
var lastIndex = obstacle.count
for Character in level1[x] // we read each character one by one
{
if Character == ":" { index++ } // if it's a ":" we change the variable
else
{
switch index
{
case 0:
type += String(Character)
case 1:
initTime += String(Character)
case 2:
speed += String(Character)
case 3:
position += String(Character)
case 4:
xScale += String(Character)
case 5:
yScale += String(Character)
default:
break;
}
}
}
obstacle.append(obstacles(imageNamed: "Rectangle")) // we add an element to the array
obstacle[lastIndex].type = type.toInt()! // we init all the values
obstacle[lastIndex].initTime = initTime.toInt()!
obstacle[lastIndex].speed = CGFloat(speed.toInt()!)
obstacle[lastIndex].size.width = DEFAULT_OBSTACLE_SIZE * CGFloat(xScale.toInt()!)
obstacle[lastIndex].size.height = DEFAULT_OBSTACLE_SIZE * CGFloat(yScale.toInt()!)
obstacle[lastIndex].position = CGPointMake(CGFloat(position.toInt()!) - obstacle[lastIndex].size.width/2, CGRectGetMaxY(frame) + obstacle[lastIndex].size.height/2)
}
}
Do you know how could I manage to do the same thing, but more "clean" ?
I would suggest to create a class or struct that holds all necessary data for an obstacle and additionally change your type from a standard number to an enum, e.g.:
enum ObstacleType {
case Block, Tree, WhatEverObstaclesYouHave...
}
struct Obstacle {
var type: ObstacleType
var initializationTime: NSTimeInterval
var speed: Double
// and similarly for position, your scales and what you may need in future
}
and create them using, e.g.
Obstacle(type: .Block, initializationTime: 0, speed: 12.0, ...)
Advantage (obviously) is that you have no problems anymore parsing your string (there is no string anymore) and can provide all necessary information using the appropriate type directly. And you can easily use an enum for your type, which should be an enum, because your Obstacle is not a 1, but a Block, Tree or whatever.
Your level1 variable could then be defined like this:
var level1 : [Obstacle] = [
Obstacle(type: .Block, initializationTime: 120, speed: 3.0, ...),
Obstacle(type: .Block, initializationTime: 130, speed: 4.0, ...),
Obstacle(type: .Tree, initializationTime: 140, speed: 8.0, ...),
Obstacle(type: .Block, initializationTime: 150, speed: 6.0, ...)
]
To get rid of the labels in the initializer, just define your own initializer. Just add this to your struct:
init(_ type: ObstacleType, _ time: NSTimeInterval, _ speed: Double, ...) {
self.type = type
self.initializationTime = time
self.speed = speed
// other variables...
}
Then you can create every Obstacle like this:
Obstacle(.Block, 120, 3.0, ...)
But now you can not easily tell anymore which number has what meaning from reading the instantiation. I do not recommend this just to type less as autocomplete will present you with most of it.