How to solve this question that involves function and optionals? - swift

I'm done with the first part, I need help with second question that involves updating the function to return an Int. I've tried to solve it but what I get is an error
If an app asks for a user's age, it may be because the app requires a user to be over a certain age to use some of the services it provides. Write a function called checkAge that takes one parameter of type String. The function should try to convert this parameter into an Int value and then check if the user is over 18 years old. If he/she is old enough, print "Welcome!", otherwise print "Sorry, but you aren't old enough to use our app." If the String parameter cannot be converted into an Int value, print "Sorry, something went wrong. Can you please re-enter your age?" Call the function and pass in userInputAge below as the single parameter. Then call the function and pass in a string that can be converted to an integer.
Go back and update your function to return the age as an integer. Will your function always return a value? Make sure your return type accurately reflects this. Call the function and print the return value.
func checkage(age: String)->Int?
{
if let age_type = Int(age)
{
if age_type > 18 {
return "Welcome!"
}
else if age_type < 18 {
return"Sorry, but you aren't old enough to use our app."
}
}
else {
return "Sorry, something went wrong. Can you please re-enter your age?"
}
return age_type
}
print(checkage(age:"23"))
error: 3. Exercise - Functions and Optionals.xcplaygroundpage:20:12: error: use of unresolved identifier 'age_type'
return age_type
^~~~~~~~

Several issues:
Inside your method, it looks like you're returning strings, but the method signature (checkage(age: String) -> Int?) indicates you're returning an optional integer value.
Your age_type is a non-optional value, but you're using if let, which is one way to unwrap optionals. No idea what's up there.
Your age_type declaration is less than or greater than, but if it's exactly equal, you'd hit your else block indicating there's something wrong, but that would be exactly age (assuming it would work, which it won't).
Anyway, here's a cleaned up version of what I think you're trying to do.
Based on your declarations in what you originally wrote, I think you may or may not have an age, so if you don't have one, you want to return an error. With that assumption, I made the age parameter optional (?). The guard statement checks if there's an age, if there is one, it unwraps it, otherwise it returns a string saying it ****ed up.
If it gets past the guard, then if it's over 18, it returns the "Welcome" string, otherwise it returns an error.
func check(age: Int?) -> String {
guard let age = age else {
return "Sorry, something went wrong. Can you please re-enter your age?"
}
if age > 18 {
return "Welcome!"
} else {
return "Sorry, but you aren't old enough to use our app."
}
}
Here are the outputs for the various ranges:
// Sorry, something went wrong. Can you please re-enter your age?
print(check(age: nil))
// Welcome!
print(check(age: 19))
// Sorry, but you aren't old enough to use our app.
print(check(age: 18))
Additionally, if you're writing a function to evaluate a number, it should take a number as a parameter. If you're getting the number from a textField, do the conversion from text to string on the view. If you're using a text field to get the number, look up ways to restrict input in the text field to a valid number string (your hint is look up UITextFieldDelegate methods.

Related

Swift return different Types from a function

Completing some old HackerRank challenges.
Some of these appear to be broken - for example "Fair Rations" gives us the following function signature (note: The capital for the parameter is not my fault, it is not changeable within this context.
func fairRations(B: [Int]) -> Int {
// Enter code answer code here
}
Now the problem test cases (the details of the problem are not important here) require that we return an
Int
(i.e. 4) for some test cases, and a
String
(i.e. "NO") for other tests.
So I need to return either a String, or an Int depending upon my answers. I've tried to return an enum, but I can't make any changes to the HackerRank tests - also returning any
like:
func fairRations(B: [Int]) -> Any {
// Enter code answer code here
}
will not work as Any is not implicitly convertible to either a String or an Int.
The HackerRank problem is here: https://www.hackerrank.com/challenges/fair-rations/problem
To clarify in response to Joakim Danielson, the problem description implies that you can output "NO" to the console, but that is not actually true (see screenshot below).
Is it possible to have a function that returns both a String and an Int in Swift?
Just change the function to return a String. Keep in mind that integers can be represented as a string as well. The string "4" represents the number 4.
I changed the function to this in hacker rank:
func fairRations(B: [Int]) -> String {
return "4"
}
And it passed this test:
Basically,
If you want to return an integer x, just return x.description
If you want to return NO, just return "NO".
Both of the above values are strings.
Returning a String here works because the test calls the String(...) initialiser. And if you pass a string to that, it will still create the same string you passed in.
EDIT:
I tried editing the client code and it works. You can just return a Int? and do this:
if let result = fairRations(B: B) {
fileHandle.write(String(result).data(using: .utf8)!)
} else {
fileHandle.write("NO".data(using: .utf8)!)
}

Casting an Int as a String from a Realm result Swift

I am asking this hesitantly as I know this is probably a dumb question.
I am returning a Realm result and then have gone ahead and tried to cast it to a String as normal (to put in a text label).
However I'm getting an error 'init' has been renamed to 'init(describing:)'.
When I try use the describing method instead, the label prints "Optional" inside it which obviously isn't what I want.
Is there a reason I can't use :
previousTimeLabel.text = String(lastRecord?.time)
I'm sure I've done this before and it's been fine, am I missing something? (lastRecord.time is an Int).
I've checked the answer here about Interpolation Swift String Interpolation displaying optional? and tried changing to something like this :
if let previousRounds = String(lastRecord?.rounds) {
previousRoundsLabel.text = previousRounds
}
but get the same error + Initializer for conditional binding must have Optional type, not 'String'
The issue isn't String(lastRecord?.time) being Optional. The issue is lastRecord being Optional, so you have to unwrap lastRecord, not the return value of String(lastRecord?.time).
if let lastRecord = lastRecord {
previousRoundsLabel.text = "\(lastRecord.time)"
}
To summarize Dávid Pásztor's answer, here's a way you can fix it:
previousTimeLabel.text = String(lastRecord?.time ?? 0)
This may not be the best way for your application. The point Dávid was making is that you need to deal with lastRecord possibly being nil before trying to pass its time Int into the String initializer. So the above is one simple way to do that, if you're ok with having "0" string as your previousTimeLabel's text if there was no lastRecord.

Argument labels do not match any availble overloads

I'm trying to create an anagram tester, and I'm pretty sure the code I have should work, but I'm getting an error 'Argument labels '(_:)' do not match any available overloads' I've looked at the other posts regarding the same error, but I'm still not sure what this means or how to fix it.
var anagram1 : String!
var anagram2 : String!
var failure : Bool = false
var counter : Int = 0
print("Please enter first word: ")
anagram1 = readLine()
print("Please enter Second word: ")
anagram2 = readLine()
if anagram1.count == anagram2.count {
for i in anagram1.characters{
if (!failure){
failure = true
for y in anagram2.characters {
counter += 1
if i == y {
failure = false
anagram2.remove(at: String.Index(counter)) // error here
}
}
}
else {
print("these words are not anagrams")
break;
}
}
if (!failure) {
print("these words ARE anagrams")
}
}
else{
print ("these words aren't even the same length you fucking nonce")
}
To answer your first question: the error message Argument labels '(_:)' do not match any available overloads means that you've given a function parameter names or types that don't match anything Swift knows about.
The compiler is also trying to tell you what parameters to look at. '(_:)' says that you're calling a function with an unlabeled parameter. (That means a value without any parameter name. A common example of a function that would look like this is print("something"). In Swift documentation, this would look like print(_:).
Finally, overloads are ways to call a function with different information. Again using the print function as an example, you can call it multiple ways. A couple of the most common overloads would be:
// print something, followed by a newline character
print("something")
// print something, but stay on the same line
// (end with an empty string instead of the default newline character)
print("something", terminator: "")
Documented, these might look like print(_:) and print(_:, terminator:).
Note: these are broken down for explanation. The actual Swift documentation shows func print(_: Any..., separator: String, terminator: String) which covers a number of different overloads!
Looking at the line where the error occurs, you see a function call and an initializer (which is essentially a function). Documented, the way you've entered the parameters, the functions would look like: remove(at:) and String.Index(_:).
String.Index(_:) matches the parameters of the error message, so that's where your error is. There is no overload of the String.Index initializer that takes an unnamed parameter.
To fix this error, you need to find the correct way to create a String.Index parameter for the remove(at:) function. One way might be to try something like this:
for y in anagram2.characters.enumerated() {
// `y` now represents a `tuple`: (offset: Int, element: Character)
// so, you don't need `counter` anymore; use `offset` instead
if i == y.element { //`i` is a Character, so it can compare to `element`
...
let yIndex: String.Index = anagram2.index(anagram2.startIndex, offsetBy: y.offset)
anagram2.remove(at: yIndex)
...
}
}
However, there are other issues with your code that will cause further errors.
For one, you're looping through a string (anagram2) and trying to change it at the same time - not a good thing to do.
Good luck to you in solving the anagram problem!
Thanks for the help Leo but I found a way of doing it :)
if anagram1.count == anagram2.count {
for i in anagram1.characters{
if (!failure){
counter = -1
failure = true
for y in anagram2.characters {
counter += 1
if i == y {
failure = false
if counter < anagram2.count {
anagram2.remove(at: (anagram2.index(anagram2.startIndex, offsetBy: counter)))
break;
}
}
}
}

Conditional instructions in swift

I am trying to write in swift something that should be very basic, but I can't seem to get a handle on it :
First, I create a global variable. For example:
var xx:Int
Then, I want to create a conditional instruction. Something like :
if (xx == 1){
//do something
}
else if (xx == 2) {
//do something else
}
I can do this very easily in Objective-C, but I can't seem to be able to do it in Swift. I have looked everywhere, and don't seem to find the answer.
With the code you provided you're probably getting the error: "Variable xx used before initialized". This is happening because the declaration of the variable is incomplete, you neither gave a value to the variable nor told the compiler it is an optional. You have three options:
Give a initial value to it; var xx: Int = //value here
Declare it as an optional (doing this you say that it may not have a value, if it does the code will be executed, if it doesn't it won't); var xx: Int?
Force unwrap the variable (it still an optional, but if you force-unwrap it you're assuring the compiler that the variable will have a value when needed, otherwise it'll crash); var xx: Int!
Or you can say var xx = Int() that way it's initialized and the default initialization is equal to 0. This is different than the other answers and allows you to have a value from the get go if you're not sure what value might be assigned during runtime.
In addition to the other poster's point that you must assign an initial value before you can use xx, you also need to lose the parentheses around the condition in your if statement:
var xx:Int
xx = 2
if xx == 1
{
//do something
}
else if xx == 2
{
//do something else
}

Proper way to handle a fail to init

I am looking for a proper way to handle a invalid argument during a initialization.
I am unsure how to do it using Swift as the init has't a return type. How can I tell whoever is trying to initialize this class that you are doing something wrong?
init (timeInterval: Int) {
if timeInterval > 0
self.timeInterval = timeInterval
else
//???? (return false?)
}
Thank you!
Use a failable initializer. Such an initializer looks very similar to a regular designated initializer, but has a '?' character right after init and is allowed to return nil. A failable initializer creates an optional value.
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}
See Apple's documentation on failable initializers for more detail.
In swift, you can't really abort a task half way through execution. There are no exceptions in swift and in general the philosophy is that aborting a task is dangerous and leads to bugs, so it just should't be done.
So, you verify a value like this:
assert(timeInterval > 0)
Which will terminate the program if an invalid value is provided.
You should also change timeInterval to be a UInt so that there will be a compiler error if anybody tries to give a < 0 value or an integer value that could be < 0.
It's probably not the answer you're looking for. But the goal is to check for bad parameters as early as possible, and that means doing it before you create any objects with those parameters. Ideally the check should be done at compile time but that doesn't always work.
I think this is the best solution, took it from:How should I handle parameter validation Swift
class NumberLessThanTen {
var mySmallNumber: Int?
class func instanceOrNil(number: Int) -> NumberLessThanTen? {
if number < 10 {
return NumberLessThanTen(number: number)
} else {
return nil
}
}
#required init() {
}
init(number: Int) {
self.mySmallNumber = number
}
}
let iv = NumberLessThanTen.instanceOrNil(17) // nil
let v = NumberLessThanTen.instanceOrNil(5) // valid instance
let n = v!.mySmallNumber // Some 5
In the Swift book by Apple, at the very bottom of this section:https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-XID_399
They say:
When to Use Assertions
Use an assertion whenever a condition has the potential to be false,
but must definitely be true in order for your code to continue
execution. Suitable scenarios for an assertion check include:
An integer subscript index is passed to a custom subscript
implementation, but the subscript index value could be too low or too
high. A value is passed to a function, but an invalid value means that
the function cannot fulfill its task. An optional value is currently
nil, but a non-nil value is essential for subsequent code to execute
successfully.
This sounds exactly like your situation!
Thus your code should look like:
init (timeInterval: Int) {
assert (timeInterval > 0, "Time Interval Must be a positive integer")
// Continue your execution normally
}