Optional value in swift 3 Optimised way - swift

I have create class Testing model which has 4 dataMember it should not be null when accessing (means return default value)
extension Double {
/// Rounds the double to decimal places value
func roundTo(places:Int = 2) -> Double
{
let divisor = pow(10.00, Double(places))
return (self * divisor).rounded() / divisor
}
}
class TestingModel{
var id : String!
var name : String! = "abc" /*It is not working*/
var price : Double! = 0.00
var uniqueId : Int! = 1
/**
* Instantiate the instance using the passed dictionary values to set the properties values
*/
init(dictionary: [String:Any])
{
id = (dictionary["id"] as? String) ?? "" //I dont want to do like this way
name = dictionary["name"] as? String
price = (dictionary["price"] as? Double)?.roundTo() ?? 0.00
uniqueId = dictionary["unique_id"] as? Int
}
}
let t:TestingModel = TestingModel.init(dictionary: ["x id" : "x012y12345z45","x name":"test1","x price":100.0,"uniqueId":1236.0])
let testString = "Jd " + t.id
print(testString) //Perfect
print(t.name)
print(t.price) /* Only one decemal point is printed */
Getting Output
Jd
nil
0.0
Expected output
Jd
abc /Should return abc instead of nil/
0.00 /Two decimal point complulsury/
What i actually mean in
if i assign nil value to variable then it should remain with its default value without writing this Optional chaining ?? "abc" in constructor

price is a Double type and what you are asking to do is to print that double value to 2 decimal places. Then you should make use of the following.
let a = 0.0
print(String(format: "%.2f", a))
this prints:
0.00
If you are planning to round it to decimal places, then also the above code will return that. But if you need it to round and return a double type then you can check this answer
Based on your updated question, I suggest to use the model as follows:
class TestingModel{
var id : String = ""
var name : String = "abc"
var price : Double = 0.0
var uniqueId : Int = 1
/**
* Instantiate the instance using the passed dictionary values to set the properties values
*/
init(dictionary: [String:Any])
{
id = (dictionary["id"] as? String) ?? ""
name = dictionary["name"] as? String ?? "abc"
price = (dictionary["price"] as? Double) ?? 0.0
uniqueId = dictionary["unique_id"] as? Int ?? 1
}
}

You seem to have asked two different questions here. The first one regarding doubles have already been answered by adev. I will answer the second one, which is:
if i assign nil value to variable then it should remain with its default value without writing this Optional chaining ?? "abc" in constructor
If you want to do this then it means that the variable shouldn't be optional at all, as nil isn't one of its valid values. Make the variable a non-optional type and give it a default value.
class TestingModel{
var id : String = ""
var name : String = "abc"
var price : Double = 0.00
var uniqueId : Int = 1
}
You can't really avoid using ?? in the constructor because of the nature of dictionaries. They will always return a nil value if the key does not exist. You have to check it. It does not make sense even if this is possible anyway. Imagine something like this:
someVariable = nil // someVariable is now 0
This is extremely confusing. someVariable is 0, even though it appears that nil is assigned to it.
A workaround will be to add a dictionary extension. Something like this:
extension Dictionary {
func value(forKey key: Key, defaultValue: Value) -> Value {
return self[key] ?? defaultValue
}
}
But I still recommend that you use ?? instead.

Related

Unresolved identifier error in swift function with nested if loop

I'm currently teaching myself Swift coming from a background of Python recently and Visual Basic originally. I would describe myself as competent in those codes but a novice in Swift. I'm trying to write a function that will return a set number of digits from either the start or end of a long integer. My chosen method has been to convert the integer to a string and then use the prefix or suffix command.
Whilst I can get the function to work if it has no flow control and uses either prefix or suffix (first lot of code), when I try to write one function that does both I get an unresolved identifier error on the turnStringToInteger variable (second lot of code). I'm pretty sure this is because the variable lives within the if {} but if I declare it outside of the if loop (hashed out) this also errors. I appreciate this will have a really simple answer but how do I use the return correctly with a nested if loop?
This works...
//Function to Trim Integer (Prefix Only)
func integerTrim(integer:Int, trimLength:Int) -> Int {
var strFromInteger = String(integer)
var trimmedString = strFromInteger.prefix(trimLength)
var intFromString = Int(trimmedString) ?? 0
return intFromString
}
//Declare Input
var inputInt = 12345678910
//Call Function
var result = integerTrim(integer: inputInt, trimLength: 4)
//Print Results
print(inputInt)
print(result)
This doesn't...!
//Function to trim integer prefix or suffix
func integerTrim(integer:Int, type:String, trimLength:Int) -> Int {
var typeID = type
//var turnStringToInteger: Int
if typeID == "P" {
var turnIntegerToString = String(integer)
var trimmedString = turnIntegerToString.prefix(trimLength)
var turnStringToIngeger = Int(trimmedString) ?? 0
}
else if typeID == "S" {
var turnIntegerToString = String(integer)
var trimmedString = turnIntegerToString.suffix(trimLength)
var turnStringToIngeger = Int(trimmedString) ?? 0
}
return turnStringToInteger
}
//Declare Input
var inputInt = 53737363856453
//Call Function
var result = integerTrim(integer: inputInt, type: "P", trimLength: 4)
//Print Results
print(inputInt)
print(result)
As I am self taught I appreciate I may also not be using best practices. I really want to learn to do this properly so if I am going about all of this the wrong way to begin with I would be equally happy to hear other approaches. For example I did consider turning the integer to an array and then creating the trimmed integer from positions within this array. Would this be more elegant?
If you want to access the variable outside of the scope where it is assigned, you need to declare it in the outer scope.
If you do that without assigning it an initial value, you get an error: variable 'turnStringToInteger' used before being initialized. That happens because Swift sees a path in which turnStringToInteger never gets assigned a value (imagine what happens if "X" is passed in for type).
So your real issue is the use of String as the type for type. It would be better to use an enum that expresses exactly what you want:
enum TrimType {
case prefix, suffix
}
func integerTrim(integer: Int, type: TrimType, trimLength: Int) -> Int {
let typeID = type
var turnStringToInteger: Int
switch typeID {
case .prefix:
let turnIntegerToString = String(integer)
let trimmedString = turnIntegerToString.prefix(trimLength)
turnStringToInteger = Int(trimmedString) ?? 0
case .suffix:
let turnIntegerToString = String(integer)
let trimmedString = turnIntegerToString.suffix(trimLength)
turnStringToInteger = Int(trimmedString) ?? 0
}
return turnStringToInteger
}
Now there are only 2 possibilities for type and the switch handles both.
You call it like this:
let result = integerTrim(integer: inputInt, type: .prefix, trimLength: 4)
... after a little refactoring:
func integerTrim(integer: Int, type: TrimType, trimLength: Int) -> Int {
let turnIntegerToString = String(integer)
let trimmedString: Substring
switch type {
case .prefix:
trimmedString = turnIntegerToString.prefix(trimLength)
case .suffix:
trimmedString = turnIntegerToString.suffix(trimLength)
}
return Int(trimmedString) ?? 0
}
There's a few ways to do this. The root of the problem is your turnIntegerToString lifetime is within the braces - and the return is outside the braces.
func integerTrim(integer:Int, type:String, trimLength:Int) -> Int {
var typeID = type
var turnStringToInteger: Int = 0
// If you don't want to assign it to zero (your nil coalesce implies we can) - instead use...
// var turnStringToInteger: Int! // However - this can crash since your if statement does not cover all situations
if typeID == "P" {
var turnIntegerToString = String(integer)
var trimmedString = turnIntegerToString.prefix(trimLength)
turnStringToIngeger = Int(trimmedString) ?? 0
}
else if typeID == "S" {
var turnIntegerToString = String(integer)
var trimmedString = turnIntegerToString.suffix(trimLength)
turnStringToIngeger = Int(trimmedString) ?? 0
}
return turnStringToInteger
}

Swift Convert Optional String to Int or Int32 (Unwrapping optionals question)

I am trying to read a string and convert it to an int. I have a solution but it seems way too complicated. I guess I am still trying to wrap my head around unwrapping.
I have posted code below along with the compiler errors that I get with each solution.
In this example I try to read a string from UserDefaults and convert to an integer value.
static func GetSelectedSessionNum() -> Int32 {
var sessionNum : Int32 = 0
let defaults = UserDefaults.standard
let optionalString: String? = defaults.string(forKey: "selectedSessionNum")
// this works but it's too complicated
if let string = optionalString, let myInt = Int32(string) {
return myInt
}
return 0
// Error : optional String? must be unwrapped to a value of type 'String'
let t : String = defaults.string(forKey: "selectedSessionNum")
if let s : String = defaults.string(forKey: "selectedSessionNum") {
// error - Int32? must be unwrapped to a value of Int32
return Int32(s)
}
return 0
}
You need to cast to non optional Int32 in order to match your return type.
You can use any optional binding approach, or change your return type to Int32?
If you want an uncomplicated solution save selectedSessionNum as Int
static func getSelectedSessionNum() -> Int32 {
return Int32(UserDefaults.standard.integer(forKey: "selectedSessionNum"))
}
otherwise double optional binding
if let string = UserDefaults.standard.string(forKey: "selectedSessionNum"), let myInt = Int32(string) {
return myInt
}
or the nil coalescing operator
if let string = UserDefaults.standard.string(forKey: "selectedSessionNum") {
return Int32(string) ?? 0
}
is the proper way
If you want to avoid optional bindings, you can use flatMap, when called on Optional's it allows you to convert one optional to another:
return UserDefaults.standard.string(forKey: "selectedSessionNum").flatMap(Int32.init) ?? 0
You'd also need the ?? (nil coalescing operator) to cover the scenarios where either the initializer fails, or the value is not present in user defaults.

Trouble converting a string to an Int

The following works in Playground:
func stringToInt(numberStr: String!) -> Int {
print(numberStr)
return Int(numberStr)!
}
let strNum1: String?
strNum1 = "1"
let result = stringToInt(numberStr: strNum1)
It returns 1 as expected.
In Xcode, a similar approach fails:
func stringToInt(numberStr: String!) -> Int {
print("\(numberStr!)")
let str = "\(numberStr!)"
print(Int(str))
return Int(str)!
}
The first print produces: Optional(1)
The second print produces: nil
The return statement fails because it is attempting to create an Int from a nil.
It must be something simple but I haven't been able to determine why it's not working. This is in Swift 3 and Xcode 8 BTW.
#Hamish:
In Xcode, I have a string with a numeric value. This:
print("number: (selectedAlertNumber) - unit: (selectedAlertUnit)")
...produces this:
number: Optional(1) - unit: Day
Then, I'm checking to see if either selectedAlertNumber of selecterAlertUnit != "-"
if selectedAlertNumber != "-" && selectedAlertUnit != "-" {
// set alert text
var unitStr = selectedAlertUnit
let alertNumber = stringToInt(numberStr: selectedAlertNumber)
if alertNumber > 1 {
unitStr.append("s")
}
let alertText = "...\(selectedAlertNumber) \(unitStr) before event."
alertTimeCell.setAlertText(alertText: alertText)
// set alert date/time
}
The let alertNumber = stringToInt... line is how I'm calling the function. I could just attempt the conversion there but I wanted to isolate the problem by wrapping the conversion in it's own function.
Using string interpolation to convert values to a String is usually not advised since the output may differ depending on optional status of the value. For example, consider these two functions:
func stringToInt(numberStr: String!) -> Int
{
print("\(numberStr!)")
let str = "\(numberStr!)"
return Int(str)!
}
func otherStringToInt(numberStr: String!) -> Int
{
print(numberStr)
let str = "\(numberStr)"
return Int(str)!
}
The only difference between these two is the ! in the second function when using string interpolation to get a String type value from numberStr. To be more specific, at the same line in function 1 compared to function 2, the string values are very different depending on whether or not the interpolated value is optional:
let str1: String = "1"
let str2: String! = "1"
let str3: String? = "1"
let otherStr1 = "\(str1)" // value: "1"
let otherStr2 = "\(str2)" // value: "Optional(1)"
let otherStr3 = "\(str2!)" // value: "1"
let otherStr4 = "\(str3)" // value: "Optional(1)"
let otherStr5 = "\(str3!)" // value: "1"
Passing otherStr2 or otherStr4 into the Int initializer will produce nil, since the string "Optional(1)" is not convertible to Int. Additionally, this will cause an error during the force unwrap. Instead of using string interpolation in your function, it would be better to just use the value directly since it's already a String.
func stringToInt(numberStr: String!) -> Int
{
return Int(numberStr)!
}
Let me know if this makes sense.
Also, my own personal feedback: watch out force unwrapping so frequently. In many cases, you're running the risk of getting an error while unwrapping a nil optional.

Create Simple Class with multiple calculating function( )

Trying to run a simple calculation via a function in my class. I simply want to add bill1 + bill2 and print the total amount spent on bills. So (bill1 + bill2 = total). And then print the total amount.
Current error states - "Code after 'return' will never be executed." Now, is my location for my print in the wrong location or did I declare my variables incorrectly? Should I be using vars instead of lets?
What do you recommend for my function in order to calculate and print the result?
class BillsCalculator
{
let nameOfBill1: String = "Medical"
let nameOfBill1: String = "Hulu"
let monthlyBillAmount1: Double = 34.25
let monthlyBillAmount2: Double = 7.99
let calculateTotalsPerMonth: Double = 0.0
//calculateTotalPerMonth ( = monthlyBillAmount_1 + monthlyBillAmount_2 + 3)
func calculateTotalsPerMonth(monthlyBillAmount: Double, monthlyBillAmount2: Double) -> Double
{
//totalBillsPerMonth = add(monthlyBillAmount1 + monthlyBillAmount2)
return totalBillsPerMonth(monthlyBillAmount1 + monthlyBillAmount2)
*Error println("You spend \(totalBillsPerMonth)")
}
}
First: "Code after 'return' will never be executed."
Yes it will not, after you call return you exit the function and return to the function that call it, you probably have an warning in XCode warning you about telling you that
Second: "Should I be using vars instead of lets"
If the value changes you MUST use var, if it does not you SHOULD use let.
Some problems I can see in your code:
class BillsCalculator
{
//use _ in the beginning of the name for class variables
//eg. _nameOfBill instead nameOfBill1
//It is not wrong use nameOfBill1 is just not recommended
//if nameOfBill1 change use var
let nameOfBill1: String = "Medical"
//Why is this declare twice
let nameOfBill1: String = "Hulu"
//Those values look like change should be var
var monthlyBillAmount1: Double = 34.25
var monthlyBillAmount2: Double = 7.99
var calculateTotalsPerMonth: Double = 0.0
func calculateTotalsPerMonth(monthlyBillAmount: Double, monthlyBillAmount2: Double) -> Double
{
totalBillsPerMonth = add(monthlyBillAmount1 + monthlyBillAmount2)
//print before return
println("You spend \(totalBillsPerMonth)")
return totalBillsPerMonth(monthlyBillAmount1 + monthlyBillAmount2)
}
}
Here you should either print the value of your total bill or return that value. As you just want to print the total bill amount so I would recommend you to just print, not to return anything. You can refer the below code.
class BillsCalculator
{
let nameOfBill1: String = "Medical"
let nameOfBill1: String = "Hulu"
let monthlyBillAmount1: Double = 34.25
let monthlyBillAmount2: Double = 7.99
let calculateTotalsPerMonth: Double = 0.0
//calculateTotalPerMonth ( = monthlyBillAmount_1 + monthlyBillAmount_2 + 3)
func calculateTotalsPerMonth(monthlyBillAmount: Double, monthlyBillAmount2: Double) -> Double
{
calculateTotalsPerMonth= add(monthlyBillAmount1 + monthlyBillAmount2)
println("You spend : "+totalBillsPerMonth);
}
}
One tiny error in your code
let nameOfBill1: String = "Medical"
let nameOfBill1: String = "Hulu"
These two variables have the same name, perhaps one should be:
let nameOfBill2: String = "Hulu"
And yes return is always the last line in the function, so any codes after return will never be executed. If you only want to get the total of two bills, you can simply do this:
func calculateTotalsPerMonth(monthlyBillAmount: Double, monthlyBillAmount2: Double) -> Double {
//println("You spend \(totalBillsPerMonth)")
return monthlyBillAmount1 + monthlyBillAmount2
}
and call this function with your bill variables, like:
let bill1 = 34.25
let bill2 = 7.99
let totalBill = calculateTotalsPerMonth(bill1, bill2)
println("You spent \(totalBill)")
Swift is a very smart language, and it is type safe. You can remove the type if you want, more like a personal programming style thing.
let bill1: Double = 34.25
let bill1 = 34.25
They both will be type "Double"
As others have said, you need to put your println statement before return since returns ends the execution of the method; thus println will never be run.
However, I would suggest a few changes to your current approach:
// A bill is an object - why not encapsulate it in a struct.
struct Bill {
let name: String
let amount: Double
}
// Using structs is generally preferred, unless you need inheritance and/or
// references to your BillsCalculator objects.
struct BillsCalculator {
let bill1: Bill
let bill2: Bill
// Using a read-only computed property means you don't need to set
// the total to have an initial value of zero.
var totalBilled: Double {
return bill1.amount + bill2.amount
}
}
// Since you're probably going to want to reuse BillsCalculator,
// don't have each bill set already. Instead, use BillsCalculator's
// initialiser and pass in bills.
let bill1 = Bill(name: "Medical", amount: 34.25)
let bill2 = Bill(name: "Hulu", amount: 7.99)
let cal = BillsCalculator(bill1: bill1, bill2: bill2)
print("You've spend \(cal.totalBilled) this month")

Get a Swift Variable's Actual Name as String

So I am trying to get the Actual Variable Name as String in Swift, but have not found a way to do so... or maybe I am looking at this problem and solution in a bad angle.
So this is basically what I want to do:
var appId: String? = nil
//This is true, since appId is actually the name of the var appId
if( appId.getVarName = "appId"){
appId = "CommandoFurball"
}
Unfortunately I have not been able to find in apple docs anything that is close to this but this:
varobj.self or reflect(var).summary
however, this gives information of what is inside the variable itself or the type of the variable in this case being String and I want the Actual name of the Variable.
This is officially supported in Swift 3 using #keyPath()
https://github.com/apple/swift-evolution/blob/master/proposals/0062-objc-keypaths.md
Example usage would look like:
NSPredicate(format: "%K == %#", #keyPath(Person.firstName), "Wendy")
In Swift 4 we have something even better: \KeyPath notation
https://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md
NSPredicate(format: "%K == %#", \Person.mother.firstName, "Wendy")
// or
let keyPath = \Person.mother.firstName
NSPredicate(format: "%K == %#", keyPath, "Andrew")
The shorthand is a welcome addition, and being able to reference keypaths from a variable is extremely powerful
As per the updated from this answer, it is supported in Swift 3 via #keyPath
NSPredicate(format: "%K == %#", #keyPath(Person.firstName), "Andrew")
This is my solution
class Test {
var name: String = "Ido"
var lastName: String = "Cohen"
}
let t = Test()
let mirror = Mirror(reflecting: t)
for child in mirror.children {
print(child.label ?? "")
}
print will be
name
lastName
This works:
struct s {
var x:Int = 1
var y:Int = 2
var z:Int = 3
}
var xyz = s()
let m = Mirror(reflecting: xyz)
print(m.description)
print(m.children.count)
for p in m.children {
print(p.label as Any)
}
I've come up with a swift solution, however unfortunately it doesn't work with Ints, Floats, and Doubles I believe.
func propertyNameFor(inout item : AnyObject) -> String{
let listMemAdd = unsafeAddressOf(item)
let propertyName = Mirror(reflecting: self).children.filter { (child: (label: String?, value: Any)) -> Bool in
if let value = child.value as? AnyObject {
return listMemAdd == unsafeAddressOf(value)
}
return false
}.flatMap {
return $0.label!
}.first ?? ""
return propertyName
}
var mutableObject : AnyObject = object
let propertyName = MyClass().propertyNameFor(&mutableObject)
It compares memory addresses for an object's properties and sees if any match.
The reason it doesn't work for Ints, Floats, and Doubles because they're not of type anyobject, although you can pass them as anyobject, when you do so they get converted to NSNumbers. therefore the memory address changes. they talk about it here.
For my app, it didn't hinder me at all because I only needed it for custom classes. So maybe someone will find this useful. If anyone can make this work with the other datatypes then that would be pretty cool.
Completing the accepted answer for extensions:
The property needs to be #objc.
var appId: String? {
....
}
You need to use #keyPath syntax, \ notation is not supported yet for extensions.
#keyPath(YourClass.appId)
The best solution is Here
From given link
import Foundation
extension NSObject {
//
// Retrieves an array of property names found on the current object
// using Objective-C runtime functions for introspection:
// https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html
//
func propertyNames() -> Array<String> {
var results: Array<String> = [];
// retrieve the properties via the class_copyPropertyList function
var count: UInt32 = 0;
var myClass: AnyClass = self.classForCoder;
var properties = class_copyPropertyList(myClass, &count);
// iterate each objc_property_t struct
for var i: UInt32 = 0; i < count; i++ {
var property = properties[Int(i)];
// retrieve the property name by calling property_getName function
var cname = property_getName(property);
// covert the c string into a Swift string
var name = String.fromCString(cname);
results.append(name!);
}
// release objc_property_t structs
free(properties);
return results;
}
}