Swift 2.0 Guard Statement Fails Struct Initializer - swift

There was a similarly named topic but the example was an error due to user mistake. I believe this example is an actual XCode issue.
I was following a treehouse tutorial and in the spirit of swift 2.0 I used guard statements instead of if lets in the initializer. My code was identical to the instruction except for the use of guard statements. It had one error that said "return from initializer without initializing all stored properties". Once I changed it to if let statements, it worked. Perhaps I made a mistake somewhere but I stared at it for atleast an hour, no properties were left un-initialized.
I made the properties equal to nil in the else clauses just in case but that didnt affect anything.
struct DailyWeather {
let maxTemp: Int?
let minTemp: Int?
let humidity: Int?
let precipChance: Int?
var summary: String?
var icon: UIImage? = UIImage(named: "default.png")
var largeIcon: UIImage? = UIImage(named: "default_large.png")
var sunriseTime: String?
var sunsetTime: String?
var day: String?
let dateFormatter = NSDateFormatter()
init(dailyWeatherDictionary: [String:AnyObject]) {
minTemp = dailyWeatherDictionary["temperatureMin"] as? Int
maxTemp = dailyWeatherDictionary["temperatureMax"] as? Int
guard let humidityFloat = dailyWeatherDictionary["humidity"] as? Double else { humidity = nil ; return }
humidity = Int(humidityFloat * 100)
guard let precipFloat = dailyWeatherDictionary["precipProbability"] as? Double else { precipChance = nil ; return }
precipChance = Int(precipFloat * 100)
summary = dailyWeatherDictionary["summary"] as? String
guard let
iconString = dailyWeatherDictionary["icon"] as? String,
iconEnum = Icon(rawValue: iconString) else { icon = nil ; largeIcon = nil ; return }
(icon, largeIcon) = iconEnum.toImage()
guard let sunriseDate = dailyWeatherDictionary["sunriseTime"] as? Double else { sunriseTime = nil ; return }
sunriseTime = timeStringFromUnixTime(sunriseDate)
guard let sunsetDate = dailyWeatherDictionary["sunsetTime"] as? Double else { sunsetTime = nil ; return }
sunsetTime = timeStringFromUnixTime(sunsetDate)
guard let time = dailyWeatherDictionary["time"] as? Double else { day = nil ; return }
day = dayStringFromUnixTime(time)
}
func timeStringFromUnixTime(unixTime: Double) -> String {
let date = NSDate(timeIntervalSince1970: unixTime)
dateFormatter.dateFormat = "hh:mm a"
return dateFormatter.stringFromDate(date)
}
func dayStringFromUnixTime(unixTime: Double) -> String {
let date = NSDate(timeIntervalSince1970: unixTime)
dateFormatter.locale = NSLocale(localeIdentifier: NSLocale.currentLocale().localeIdentifier)
dateFormatter.dateFormat = "EEEE"
return dateFormatter.stringFromDate(date)
}
}

let's have
struct S {
var i: Int?
init(b: Bool){
guard b == false else { return }
//if b == true { return }
i = 0 // if b == true, this statement doesn't execute
}
}
let s1 = S(b: true)
let s2 = S(b: false)
print(s1, s2) // S(i: nil) S(i: Optional(0))
because var i: Int? has a default value nil, even though i = 0 is not reachable if parameter of init is true, the compiler doesn't complain.
struct S {
let i: Int?
init(b: Bool){
guard b == false else { return }
//if b == true { return }
i = 0 // if b == true, this statement doesn't execute
}
}
will NOT compile, with error: return from initializer without initializing all stored properties and note: 'self.i' not initialized, because constant let i: Int? doesn't have any default value
Your trouble is, that you return from init. Normally, avoid return from an initializer if your initializer is not fail-able / init? /. In case of fail-able init? the only accepted return value is nil.

Related

How to Format Number Converted From String Without Losing Decimal Precision

I have the following JSON payload that I need to convert to numbers and subsequently format for display.
{
"kilometers_per_second": "14.4578929636",
"kilometers_per_hour": "52048.4146691173",
"miles_per_hour": "32340.8607703746"
}
Using Codable, I created the following structure:
struct RelativeVelocity: Codable, Equatable {
let kilometersPerSecond: String?
let kilometersPerHour: String?
let milesPerHour: String?
enum CodingKeys: String, CodingKey {
case kilometersPerSecond = "kilometers_per_second"
case kilometersPerHour = "kilometers_per_hour"
case milesPerHour = "miles_per_hour"
}
}
The properties are String instances because that's what the API returns, and I am learning to use view models for the first time, so I would like to use a view model to convert the String instances into numbers prior to returning formatted String instances.
My view model has the following structure:
struct RelativeVelocityViewModel {
private let relativeVelocity: RelativeVelocity
init(relativeVelocity: RelativeVelocity) {
self.relativeVelocity = relativeVelocity
}
}
extension RelativeVelocityViewModel {
var formattedKilometersPerHour: String? {
guard
let stringValue = relativeVelocity.kilometersPerHour,
let decimalValue = Decimal(string: stringValue),
let formatted = NumberFormatter.relativeVelocityFormatter.string(from: decimalValue as NSNumber)
else { return nil }
return formatted
}
var formattedKilometersPerSecond: String? {
guard
let stringValue = relativeVelocity.kilometersPerSecond,
let decimalValue = Decimal(string: stringValue),
let formatted = NumberFormatter.relativeVelocityFormatter.string(from: decimalValue as NSNumber)
else { return nil }
return formatted
}
var formattedMilesPerHour: String? {
guard
let stringValue = relativeVelocity.kilometersPerSecond,
let decimalValue = Decimal(string: stringValue),
let formatted = NumberFormatter.relativeVelocityFormatter.string(from: decimalValue as NSNumber)
else { return nil }
return formatted
}
}
As you can see, it converts the String instances into Decimal instances, and the Decimal instances are then formatted by the following NumberFormatter:
extension NumberFormatter {
static let relativeVelocityFormatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.maximumFractionDigits = .max
formatter.numberStyle = .decimal
formatter.usesGroupingSeparator = true
return formatter
}()
}
My XCTestCase subclass for testing my view models is:
class Tests_RelativeVelocityViewModel: XCTestCase {
let kilometersPerSecond = "14.4578929636"
let kilometersPerHour = "52048.4146691173"
let milesPerHour = "32340.8607703746"
var populatedViewModel: RelativeVelocityViewModel!
var emptyViewModel: RelativeVelocityViewModel!
override func setUpWithError() throws {
try super.setUpWithError()
let populatedRelativeVelocity = RelativeVelocity(
kilometersPerSecond: kilometersPerSecond,
kilometersPerHour: kilometersPerHour,
milesPerHour: milesPerHour
)
populatedViewModel = RelativeVelocityViewModel(relativeVelocity: populatedRelativeVelocity)
let emptyRelativeVelocity = RelativeVelocity(
kilometersPerSecond: nil,
kilometersPerHour: nil,
milesPerHour: nil
)
emptyViewModel = RelativeVelocityViewModel(relativeVelocity: emptyRelativeVelocity)
}
override func tearDownWithError() throws {
emptyViewModel = nil
populatedViewModel = nil
try super.tearDownWithError()
}
func test_RelativeVelocityViewModel_ReturnsNilFormattedKilometersPerHour_WhenValueIsMissing() {
XCTAssertNil(emptyViewModel.formattedKilometersPerHour)
}
func test_RelativeVelocityViewModel_ReturnsFormattedKilometersPerHour_WhenValueIsPresent() {
let expected = "52,048.4146691173"
XCTAssertEqual(populatedViewModel.formattedKilometersPerHour, expected)
}
func test_RelativeVelocityViewModel_ReturnsNilFormattedKilometersPerSecond_WhenValueIsMissing() {
XCTAssertNil(emptyViewModel.formattedKilometersPerSecond)
}
func test_RelativeVelocityViewModel_ReturnsNilFormattedMilesPerHour_WhenValueIsMissing() {
XCTAssertNil(emptyViewModel.formattedMilesPerHour)
}
}
The following test...
func test_RelativeVelocityViewModel_ReturnsFormattedKilometersPerHour_WhenValueIsPresent() {
let expected = "52,048.4146691173"
XCTAssertEqual(populatedViewModel.formattedKilometersPerHour, expected)
}
...produces the following failure:
XCTAssertEqual failed: ("Optional("52,048.414669")") is not equal to ("Optional("52,048.4146691173")")
I know that I can use XCTAssertEqual(_:_:accuracy:_:file:line:), but I want to retain all of the decimal values.
What am I doing incorrectly that is causing the formatted result to be rounded by losing the value's precision?
Try this:
class MyProjectTests: XCTestCase {
func testExample() throws {
let stringValue = "52048.12345678911111"
let decimalValue = Decimal(string: stringValue)!
let formatted = NumberFormatter.relativeVelocityFormatter(maxFractionDigits: decimalValue.significantFractionalDecimalDigits).string(from: decimalValue as NSNumber)
XCTAssert(formatted == stringValue)
}
}
extension NumberFormatter {
static func relativeVelocityFormatter(maxFractionDigits: Int) -> NumberFormatter {
let formatter = NumberFormatter()
formatter.maximumFractionDigits = maxFractionDigits
formatter.numberStyle = .none
formatter.usesGroupingSeparator = true
return formatter
}
}
extension Decimal {
var significantFractionalDecimalDigits: Int {
return max(-exponent, 0)
}
}
Anyway, there is always a limit:
33 decimal digits.

How to Clear Shared Dictionary which is causing saved values not to clear even when I login with other user

How can I clear the shared dictionary on logout in which I am saving login response?
Here is the code I am doing on getting status 1.
if(status == 1)
{
DispatchQueue.main.async {
GAReusableClass.sharedInstance.hideActivityIndicator()
UserDefaults.standard.set(self.DataDict, forKey:MaindataKey)
let Dict = self.mainDict[KData] as! [String: AnyObject]
print("self.DataDict", self.DataDict)
let User_ID = Dict[KUuid]as! String
print(User_ID)
let HMACSECRETKEY = self.deviceToken + "+" + User_ID
kHMACKey = HMACSECRETKEY
let cipher:String = CryptoHelper.encrypt(input:HMACSECRETKEY)!;
print(HMACSECRETKEY)
UserDefaults.standard.setValue(cipher, forKey:HmacKey)
UserDefaults.standard.set(true, forKey: "isLogin")
GAloginUserInfo.shared.saveUserInfo(dict: Dict )
let tabar = self.storyboard?.instantiateViewController(withIdentifier: "GAtHomeTabbarViewController") as! GAtHomeTabbarViewController
self.navigationController?.pushViewController(tabar, animated: true)
}
Here is the shared dictionary which I am using to save the values of login response.
import UIKit
import Firebase
class GAloginUserInfo: NSObject {
var loginUserMobileNo : String?
var loginUserId : String?
var loginUserUuid : String?
var loginUserCountry : String?
var loginUserCountryCode : String?
var loginUserEmail : String?
var loginUserlatitude : String?
var loginUserLongitude : String?
var loginUserName : String?
var loginUserQrcode : String?
var loginUserProfilePic : String?
var isverify : String?
var loginPassword : String?
var dateOfBirth: String?
var earnedPoints:String?
var loginUserGender:String?
var loginUserFollowers:Int = 0
static let shared = GAloginUserInfo()
func saveUserInfo (dict : [String : AnyObject?] ) {
if let loginUserMobileNo = dict["mobile"] as? String {
self.loginUserMobileNo = loginUserMobileNo
}
if let loginUserId = dict["id"] as? String {
self.loginUserId = loginUserId
}
if let loginUserUuid = dict["uuid"] as? String {
self.loginUserUuid = loginUserUuid
print(loginUserUuid)
}
if let loginUserCountry = dict["country"] as? String {
self.loginUserCountry = loginUserCountry
}
if let loginUserCountryCode = dict["country_code"] as? String {
self.loginUserCountryCode = loginUserCountryCode
}
if let loginUserEmail = dict["email"] as? String {
self.loginUserEmail = loginUserEmail
}
if let loginUserProfilePic = dict["profile_pic"] as? String {
self.loginUserProfilePic = loginUserProfilePic
}
if let loginUserLongitude = dict["logitude"] as? String {
self.loginUserLongitude = loginUserLongitude
}
if let loginUserName = dict["name"] as? String {
self.loginUserName = loginUserName
}
if let loginUserQrcode = dict["qr_code"] as? String {
self.loginUserQrcode = loginUserQrcode
}
if let Password = dict["password"] as? String{
self.loginPassword = Password
}
if let dateOfBirth = dict["dob"] as? String{
self.dateOfBirth = dateOfBirth
}
if let earnedPoints = dict["points"] as? String{
let myDouble = Double(earnedPoints)
let doubleStr = String(format: "%.2f", myDouble!)
self.earnedPoints = doubleStr
}
if let loginUserGender = dict["gender"] as? String{
self.loginUserGender = loginUserGender
}
if let loginUserFollowers = dict["followersCount"] as? Int{
self.loginUserFollowers = loginUserFollowers
}
}
}
Actually, the problem is when I log out and log in again with some other user it still shows some values of the previous user. I am clearing the userdefaults on the logout function. but I don't know how to clear this type of shared dictionary.
Use removeObject(forKey:)
to remove the values stored from user defaults in Logout method
UserDefaults.standard.removeObject(forKey: MaindataKey)
UserDefaults.standard.removeObject(forKey: HmacKey)
UserDefaults.standard.set(false, forKey: "isLogin")
Create a method to remove the values from the singleton class like this
extension GAloginUserInfo {
func removeUserInfo() {
self.loginUserMobileNo = nil
self.loginUserId = nil
self.loginUserUuid = nil
self.loginUserCountry = nil
self.loginUserCountryCode = nil
self.loginUserEmail = nil
self.loginUserlatitude = nil
self.loginUserLongitude = nil
self.loginUserName = nil
self.loginUserQrcode = nil
self.loginUserProfilePic = nil
self.isverify = nil
self.loginPassword = nil
self.dateOfBirth = nil
self.earnedPoints = nil
self.loginUserGender = nil
self.loginUserFollowers = 0
}
}
and call this method in logout
GAloginUserInfo.shared.removeUserInfo()

Label intValue not show for greater than (>) to work

I am new at Swift the code builds just fine but the greater than (>) dose not work. I'm trying to producing at a number in the "totalCoal" label, but never goes over the "coalPileHolding" Second label. I know that this code can be way better But i am trying to get the basic first. I also know that the timeDiffernt ">" dose not work also so somehow I am missing something. Thank you for your help
#IBOutlet weak var coalPileHoldingLabel: UILabel!
func loadBigCoalPile () {
var coalPileHolding = Int ()
if UserDefaults.standard.object(forKey: "coalPileResearch") == nil {
coalPileHolding = 0 } else {
coalPileHolding = UserDefaults.standard.object(forKey: "coalPileResearch") as! Int}
if coalPileHolding == 1 {
let coalPileHolding = 200
coalPileHoldingLabel.text = String(coalPileHolding) }
if coalPileHolding == 2 {
let coalPileHolding = 300
coalPileHoldingLabel.text = String(coalPileHolding) }
if coalPileHolding == 3 {
let coalPileHolding = 400
coalPileHoldingLabel.text = String(coalPileHolding) }
#objc func buttonIsInAction(){
}
#IBOutlet weak var coalRunButton: UIButton!
#IBAction func coalRunButton(_ sender: Any) {
func getMillisecondsNow() -> Int64{
let currentDate = Date()
return getMillisecondsFromDate(date: currentDate)
}
func getMillisecondsFromDate(date: Date) -> Int64{
var d : Int64 = 0
let interval = date.timeIntervalSince1970
d = Int64(interval * 1000)
return d
}
func getTimeDifferenceFromNowInMilliseconds(time: Int64) -> Int64{
let now = getMillisecondsNow()
let diff: Int64 = now - time
return diff
}
var terminationTime = Int64()
if UserDefaults.standard.object(forKey: "latestTerminationDate") == nil {
terminationTime = getMillisecondsNow()
UserDefaults.standard.set(terminationTime, forKey:"latestTerminationDate")
}
else {
terminationTime = UserDefaults.standard.object(forKey: "latestTerminationDate") as! Int64 }
let timeDiff = getTimeDifferenceFromNowInMilliseconds(time: terminationTime)
let timeDiffernt = Int(timeDiff)
let now = getMillisecondsNow()
UserDefaults.standard.set (now, forKey: "latestTerminationDate")
if timeDiffernt > 86400000 { _ = 86400000}
var methodOfCut = Int ()
var machineryButton = Int ()
var qualityOfWorkers = Int ()
if UserDefaults.standard.object(forKey: "methodOfCut") == nil {
methodOfCut = 0 } else {
methodOfCut = UserDefaults.standard.object(forKey: "methodOfCut") as! Int}
if UserDefaults.standard.object(forKey: "machineryButton") == nil {
machineryButton = 0 } else {
machineryButton = UserDefaults.standard.object(forKey: "machineryButton") as! Int}
if UserDefaults.standard.object(forKey: "qualityOfWorkers") == nil {
qualityOfWorkers = 0 } else {
qualityOfWorkers = UserDefaults.standard.object(forKey: "qualityOfWorkers") as! Int}
let coalMayham = (machineryButton) + (qualityOfWorkers) + (methodOfCut)
let (dailyCoalAccumulate) = ((timeDiffernt) * (coalMayham) + 1) / 10000
var coalPileHolding2 = 0
if let coalPile = Int(coalPileLabel.text!) {
let totalCoal = (dailyCoalAccumulate) + coalPile
coalPileHolding2 = Int(coalPileHoldingLabel.text!) ?? 0
if totalCoal > coalPileHolding2 { coalPileHolding2 = totalCoal }
coalPileLabel.text = String(totalCoal)
UserDefaults.standard.set(totalCoal, forKey:"totalCoal")}
callOutLabel.text = String(dailyCoalAccumulate)}}
That mix of numeric types (Int32, Float, Int) is rather confusing. In general you want to use Int or Double. All other variants should only be used when absolutely necessary, for example if an API requires a different type. So lets assume that dailyCoalAccumulate is Int and switch everything else to Int too:
let coalPileHolding = 0
if let coalPile = Int(coalPileLabel.text!) {
let totalCoal = dailyCoalAccumulate + coalPile
let coalPileHolding = Int((coalPileHoldingLabel.text as! NSString).intValue)
if totalCoal > coalPileHolding {
let coalPileHolding = totalCoal
}
coalPileLabel.text = String(totalCoal)
UserDefaults.standard.set(totalCoal, forKey:"totalCoal")
}
callOutLabel.text = String(dailyCoalAccumulate)
Here the intValue API of NSString returns Int32 but I immediately convert it to a regular Int. But of course there is a better way to do this without having to bridge to the Objective-C NSString. If the string doesn't contain a number intValue simply returns zero. We can produce the same behavior when we use the Int initializer to convert the string and then replace the nil value with zero: Int(coalPileHoldingLabel.text!) ?? 0.
Then we have three different variables named coalPileHolding. Since they are defined in different scopes they can share the same name, but are still different variables. My guess is that you want to actually update the coalPileHolding variable. Otherwise the assignment in the inner if makes no sense - the compiler even warns about that.
So lets change coalPileHolding to var and update its value.
var coalPileHolding = 0
if let coalPile = Int(coalPileLabel.text!) {
let totalCoal = dailyCoalAccumulate + coalPile
coalPileHolding = Int(coalPileHoldingLabel.text!) ?? 0
if totalCoal > coalPileHolding {
coalPileHolding = totalCoal
}
coalPileLabel.text = String(totalCoal)
UserDefaults.standard.set(totalCoal, forKey:"totalCoal")
}
callOutLabel.text = String(dailyCoalAccumulate)

Swift 4 function returning 0 for multiple return values

I have been searching around but couldn't find a solution. can someone please let me know what I am doing wrong.
here is my function code: in my database I have hour=5 as Int and Minute=45 as Int
but when I print, the values of the function prints 0,0
var docRefF : DocumentReference!
func getTime()-> (Int, Int){
var FHour = Int()
var FMinute = Int()
docRefF = Firestore.firestore().document("sampleTime/worktime")
dataListener = docRefF.addSnapshotListener { (docSnapshot, error) in
guard let docSnapshot = docSnapshot, docSnapshot.exists else { return }
let data = docSnapshot.data()
let Hour:Int = data["Hour"]! as! Int
let Minute: Int = data["Minute"]! as! Int
FHour = Hour
FMinute = Minute
}
return (FHour, FMinute)
}
let time = getTime()
print("\(time.0),\(time.1)" )
//printed 0,0
Clearly its issue of asynchronous execution. Use * function with Closure* Instead of function with returning value.
Please refer following code
var docRefF : DocumentReference!
func getTime(_ then:(_ first:Int, _ second:Int)->()){
var FHour = Int()
var FMinute = Int()
docRefF = Firestore.firestore().document("sampleTime/worktime")
dataListener = docRefF.addSnapshotListener { (docSnapshot, error) in
guard let docSnapshot = docSnapshot, docSnapshot.exists else { return }
let data = docSnapshot.data()
let Hour:Int = data["Hour"]! as! Int
let Minute: Int = data["Minute"]! as! Int
FHour = Hour
FMinute = Minute
//TODO:- Use other firebase related task same as your code, so that uncomment other lines same as your origional
then(FHour, FMinute) // This is closure call back line
}
}
//Calling your function
getTime { (first, second) in
print(first,second)
}

Check the language of numbers if it is English or Arabic/Persian

In my project I am planning to change the language of any type number to Persian and here is what I have done:
public extension String {
func perstianString (string: String)->String {
let digitSet = CharacterSet.decimalDigits
var finalString = String()
for ch in string.unicodeScalars {
if digitSet.contains(ch) {
let sh = convertoPersianNum(num: "\(ch)")
finalString += sh
}
} else {
finalString += "\(ch)"
}
}
return finalString
}
func convertoPersianNum(num: String)->String{
var retVlue = String()
var num1 = num
let number = NSNumber(value: Int(num1)!)
let numb = (num as NSString).intValue
let format = NumberFormatter()
format.locale = Locale(identifier: "fa_IR")
let faNumber = format.string(from: number)
return faNumber!
}
}
But when the source value has Persian numbers, the app crashes. Simply said, I want to check if it is a Persian number, don't do anything, else do the the conversion above:
let string = "ییسس ۱۲۳۴"
with this type do not do anything else do something.
public extension String {
private var isPersian: Bool {
let predicate = NSPredicate(format: "SELF MATCHES %#", "(?s).*\\p{Arabic}.*")
return predicate.evaluate(with: self)
}
private var toPersianNum: String {
let number = NSDecimalNumber(string: self)
let formatter = NumberFormatter()
formatter.locale = Locale(identifier: "fa_IR")
return formatter.string(from: number) ?? ""
}
var persian:String {
var retVal = String()
self.enumerateSubstrings(in: startIndex..<endIndex, options: .byComposedCharacterSequences) { (string, _, _, _) in
guard let string = string else {return}
if string.isPersian {
retVal += string
}else {
retVal += string.toPersianNum
}
}
return retVal
}
}
let string = "9001ییسس2345777 ۱۲۳۴2345"
string.persian // "۹۰۰۱ییسس۲۳۴۵۷۷۷ناعدد۱۲۳۴۲۳۴۵" (result)