App Development with Swift - Lesson 13 - Exercise: "isBelow13" - swift3

I'm pretty new to Swift and I learn by using the "App Development with Swift"-Book.
Could you please check the code below - it doesn't work and I can't figure out why.
import UIKit
func isBelow13(number: Int) -> Bool {
let isBelow: Bool = false
if number < 13 {
let isBelow = true
} else {
let isBelow = false
}
return isBelow
}
isBelow13(number: 11) // returns false, should return true
isBelow13(number: 14) // returns false
Cheerio!

You're defining isBelow as false first, then inside your if statements you're defining a new constant called isBelow. This new constant has the same name as the above one, but is actually totally different. When you're outside of your if statements that one you created inside the if statements doesn't exist anymore and it just returns the one you first created. You're not actually changing the first isBelow that you set initially at all.
What you're actually trying to do can be accomplished like this:
func isBelow13(number: Int) -> Bool {
var isBelow: Bool = false
if number < 13 {
isBelow = true
} else {
isBelow = false
}
return isBelow
}
isBelow13(number: 11) // returns true
isBelow13(number: 14) // returns false
This way you are creating a variable that can be re-assigned "var" instead of "let" and then you are changing the value of that variable based on the if statement condition. Then you're returning that changed value. Make sense?
A more concise version of the same above code would look like this: (From #MartinR in the comments)
func isBelow13(number: Int) -> Bool {
return number < 13
}
The expression number < 13 returns a boolean value so you can just return the result of that expression from your function.

Related

Cannot read property 'split' of undefined while testing getting this error

https://www.youtube.com/watch?v=8xDM8U6h9Pw
This is my string I am trying to split that url from = sign. But I am getting error as
TypeError: Cannot read property 'split' of undefined
My code is as follows:
getVideoURL() {
if (this.mealDetails !== null && this.mealDetails !== undefined) {
// let splitUrl = this.mealDetails.strYoutube.split("=");
console.log("strYoutube", this.mealDetails.strYoutube);
this.splitUrl = this.mealDetails.strYoutube.split("=");
const id = this.splitUrl[1];
let url = `https://www.youtube.com/embed/${id}`;
return url;
}
},
please tell me how to resolve that error
Not sure what you intend to do .. but how about this?
const mealDetails = {
strYoutube: "https://www.youtube.com/watch?v=8xDM8U6h9Pw"
}
function getVideoURL(obj) {
if (obj) { // check for non-empty object
const splitUrl = obj.strYoutube.split("=")
return 'https://www.youtube.com/embed/'+splitUrl[1]
}
}
const newUrl = getVideoURL(mealDetails)
console.log(newUrl)
This works great -> https://jsfiddle.net/9r13t4v6/3/
Please use parameters when calling functions ... and to check empty object you can use if(value) ... if it's not empty (=> NaN, null, undefined, 0, false) it will eval as true ..
plus you don't have to use that many variables :) try to stick with as many as possible (-> better readability)

AUDIOKIT - Why is the AKOscillatorBank RELASE, DECAY, SUSTAIN, and ATTACK being updated long after I change their values?

I have a simple AudioKit app in the works.
I initialize an AKOsicillatorBANK at runtime and allow you to press a button to access a new viewCONTROLLER which has SLIDERS that when changed change the values of the oscillatorBANK properties [releaseDURATION, attackDURATION, decayDURATION, sustainLEVEL].
At the end of the slider's PANGESTURE [state == ended], I call a function that sets the oscillatorBANKS properties to their new values, which are stored in class variables.
I do not know why, but the changes only take effect some time (a few minutes) after the function is called. Does anyone know why this is?
ALSO PLEASE DO NOT COMMENT ON MY CAPITALIZATION STYLE. I PREFER THIS AS IT ALLOWS ME TO EMPHASIZE WHAT I BELIEVE TO BE IS IMPORTANT, WHICH CAN THEN HELP YOU SEE MY TRAIN OF THOUGHTS BETTER.
HERE IS THE CODE. Please note that I ONLY included the code for DECAY within the panGESTURE, because attack, release, and sustain code is all the same design:
// MAIN VIEW CONTROLLER CLASS, GLOBAL SCOPE, INITIALIZED WITH VIEW DID LOAD //
let bank = AKOscillatorBank()
var bankATTACK: Double = 0
var bankDECAY: Double = 0
var bankSUSTAIN: Double = 1
var bankRELEASE: Double = 0
func updateBANKWAVE() {
self.bank.rampDuration = 1
self.bank.attackDuration = self.bankATTACK
self.bank.decayDuration = self.bankDECAY
self.bank.sustainLevel = self.bankSUSTAIN
self.bank.releaseDuration = self.bankRELEASE
print("NEW BANK RAMP [\(self.bank.rampDuration)]")
print("NEW BANK ATTACK [\(self.bank.attackDuration)]")
print("NEW BANK DEC [\(self.bank.decayDuration)]")
print("NEW BANK SUS [\(self.bank.sustainLevel)]")
print("NEW BANK REL [\(self.bank.releaseDuration)]")
}
func prepareforSEGUE() {
if let sliderCONTROLLER = segue.destination as? SLIDERVIEWCONTROLLER {
sliderCONTROLLER.mainCONTROLLER = self
}
}
// SLIDER VIEW CONTROLLER CLASS //
if panGESTURE.state == changed {
// ... update a UILabel to reflect the value to be set ...
decayTEXT = String(format: "%.3f", (self.decayTRANSLATION.truncatingRemainder(dividingBy: 100.0)))
// ... update the MAIN controller variables ...
self.mainCONTROLLER.bankDECAY = Double(self.decayTRANSLATION.truncatingRemainder(dividingBy: 100.0))
// ... protect against values above 10 or below 0 ...
if decayTRANSLATION > 10 { decayVALUE.text = "10.000" ; decayTRANSLATION = 10.01 ; self.mainCONTROLLER.bankDECAY = 10 }
if decayTRANSLATION < 0 { decayVALUE.text = "0.000" ; decayTRANSLATION = -0.01 ; self.mainCONTROLLER.bankDECAY = 0 }
}
if panGESTURE.state == ended {
self.mainCONTROLLER.updateBANKWAVE()
}
By changing the oscillatorBANK.rampDURATION property to 0 instead of 1, I get instantaneous results. However, even though the release is being set to 1, the note can still be heard after 4 or 5 seconds... so...

parse and replace a list of object in kotlin

I am currently having a list of obeject defined as:
fun updateList(tools: List<Tool>, updateTools: List<Updated>){
... code below
}
the Tool data class is defined as:
data class Tool(
var id: String = ""
var description: String = ""
var assignedTo: String = ""
)
the Updated data class is defined as:
data class Updated(
var id: String = ""
var assignedTo: String = ""
)
Basically, I parse the list updateTools and if I found a id match in tools, I update the assignedTo field from the Tool type object from tools by the one from updateTools
fun updateList(tools: List<Tool>, updateTools: List<Updated>){
updateTools.forEach{
val idToSearch = it.id
val nameToReplace = it.name
tools.find(){
if(it.id == idToSearch){it.name=nameToReplace}
}
}
return tools
}
it's not working but I do not see how to make it easier to work. I just started kotlin and I feel that it's not the good way to do it
any idea ?
Thanks
First of all:
you're not assigning assignedTo, you're assigning name...
in the predicate passed to find, which
should only return a Boolean value to filter elements, and
should probably not have any side effects,
those should be done later with a call to i.e. forEach.
Additionally, your constructor parameters to the data class are normal parameters, and as such, need commas between them!
Your last code block, corrected, would be:
updateTools.forEach {
val idToSearch = it.id
val nameToReplace = it.name
tools.find { it.id == idToSearch }.forEach { it.assignedTo = nameToReplace }
}
return tools
I'd do it like this (shorter):
updateTools.forEach { u -> tools.filter { it.id == u.id }.forEach { it.assignedTo = u.name } }
This loops through each update, filters tools for tools with the right ID, and sets the name of each of these tools.
I use forEach as filter returns a List<Tool>.
If you can guarantee that id is unique, you can do it like this instead:
updateTools.forEach { u -> tools.find { it.id == u.id }?.assignedTo = u.name }
firstOrNull returns the first element matching the condition, or null if there is none. Edit: it seems find is firstOrNull - its implementation just calls firstOrNull.
The ?. safe call operator returns null if the left operand is null, otherwise, it calls the method.
For = and other operators which return Unit (i.e. void, nothing), using the safe call operator simply does nothing if the left operand is null.
If we combine these, it effectively sets the name of the first element which matches this condition.
First, you're missing comma after properties in your data classes, so it should be:
data class Tool(
var id: String = "",
var description: String = "",
var assignedTo: String = ""
)
data class Updated(
var id: String = "",
var assignedTo: String = ""
)
As for second problem, there're probably number of ways to do that, but I've only corrected your idea:
fun updateList(tools: List<Tool>, updateTools: List<Updated>): List<Tool> {
updateTools.forEach{ ut ->
tools.find { it.id == ut.id }?.assignedTo = ut.assignedTo
}
return tools
}
Instead of assigning values to variables, you can name parameter for forEach and use it in rest of the loop.

Swift: clear array in iteration function

I want to use this iteration function to check a array, and store the result with new array, and then check the new array again and again...however I don't know how to clear the the old useless arrays so that the memory can be released.
func checkEnvironment(environment: [MGLPolygon], seed: MGLPolygon) -> [MGLPolygon]?{
var newEnv: [MGLPolygon]?
var alien: MGLPolygon?
var check = false
for i in 0..<environment.count{
if detectIntersect(poly1: seed, poly2: environment[i]) && check == false{
check = true
alien = seed
newEnv?.append(alien!)
}else{
newEnv?.append(environment[i])
}
}
if check == false{
// error occurs on here
environment.removeAll()
return newEnv!
}else{
checkEnvironment(environment: newEnv!, seed: alien!)
}
return nil
}
in the code
environment.removeAll()
gets a error that says environment is a let static so I can't change it. My question is how do I release the memory form those array?
Any help would be appreciate.
In swift3, function parameter are immutable so in order to change the value you need to add a var and assign into it like below.
func checkEnvironment(environment: inout [MGLPolygon], seed: MGLPolygon) -> [MGLPolygon]?{
if check == false{
environment.removeAll()
return newEnv!
}
}

Convert NSUUID to UnsafePointer<UInt8>

Following the update to Swift 3, it appears both getUUIDBytes and getBytes are not available on the UUID object.
let uuid = UIDevice.current.identifierForVendor
let mutableUUIDData = NSMutableData(length:16)
uuid.getBytes(UnsafeMutablePointer(mutableUUIDData!.mutableBytes))
// ^^^ compiler error, value of type UUID? has no member getBytes
I get this error even when getBytes is listed as a method on UUID in the documentation: https://developer.apple.com/reference/foundation/nsuuid/1411420-getbytes
One right way:
let uuid = UIDevice.current.identifierForVendor!
var rawUuid = uuid.uuid
withUnsafePointer(to: &rawUuid) {rawUuidPtr in //<- `rawUuidPtr` is of type `UnsafePointer<uuid_t>`.
rawUuidPtr.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout<uuid_t>.size) {bytes in
//Use `bytes` only in this closure. (Do NEVER export `bytes` out of the closure.)
print(bytes[0],bytes[1])
//...
}
}
Another right way:
withUnsafePointer(to: &rawUuid) {rawUuidPtr in //<- `rawUuidPtr` is of type `UnsafePointer<uuid_t>`.
let bytes = UnsafeRawPointer(rawUuidPtr).assumingMemoryBound(to: UInt8.self)
//Use `bytes` only in this closure. (Do NEVER export `bytes` out of the closure.)
print(bytes[0],bytes[1])
//...
}
As already commented by Rob, exporting the pointer passed to the closure argument of withUnsafeBytes is completely NOT guaranteed. A slight change of the context (32-bit/64-bit, x86/ARM, Debug/Release, adding seemingly unrelated code...) would make your app a crasher.
And one more important thing is that UTF-8 Data of the uuidString and the byte sequence of NSUUID.getBytes are completely different:
let nsUuid = uuid as NSUUID //<-Using the same `UUID`
let mutableUUIDData = NSMutableData(length:16)!
nsUuid.getBytes(mutableUUIDData.mutableBytes.assumingMemoryBound(to: UInt8.self))
print(mutableUUIDData) //-><1682ed24 09224178 a279b44b 5a4944f4>
let uuidData = uuid.uuidString.data(using: .utf8)!
print(uuidData as NSData) //-><31363832 45443234 2d303932 322d3431 37382d41 3237392d 42343442 35413439 34344634>
You are thinking too complicated:
func getUUID ( ) -> Data {
let uuid = NSUUID()
var bytes = [UInt8](repeating: 0, count: 16)
uuid.getBytes(&bytes)
return Data(bytes: bytes)
}
Why does that work?
Consider you have:
func printInt(atAddress p: UnsafeMutablePointer<Int>) {
print(p.pointee)
}
then you can in fact do this:
var value: Int = 23
printInt(atAddress: &value)
// Prints "23"
but you can also do this:
var numbers = [5, 10, 15, 20]
printInt(atAddress: &numbers)
// Prints "5"
It's a form of "implicit bridging". To quote from Swiftdoc.org:
A mutable pointer to the elements of an array is implicitly created
when you pass the array using inout syntax.
This implicit bridging only guarantees valid pointers until the current function returns. Such pointers must never "escape" the current function context, but using them as an inout argument is always safe, as inout arguments were always only guarantee to be valid until the called function returns and the called function must return prior to the current one, so this cannot go wrong.
And for those that don't know, casting UUID to NSUUID (... as NSUUID) and the other way round (... as UUID) is guaranteed to always succeed. But if you insist on using UUID, the easiest way is:
private
func getUUID ( ) -> Data {
var uuid = UUID().uuid
return withUnsafePointer(to: &uuid) {
return Data(bytes: $0, count: MemoryLayout.size(ofValue: uuid))
}
}