Swift - Not enough bits to represent the passed value - ios

Below code works fine on iOS devices and watchOS simulator.
static func getEventDateTime(startDateTime: Date?) -> String {
if let startDateTime = startDateTime {
let startTimeInMillis = Int(startDateTime.timeIntervalSince1970 * 1000)
let fiveMinutesInMillis = 300000
let eventStartDateTime = Date(timeIntervalSince1970: TimeInterval((startTimeInMillis-fiveMinutesInMillis)/1000))
return convertDateToString(eventStartDateTime)
}
return ""
}
However when I run it on Apple Watch Series 3, I get the following error: double value cannot be converted to int because the result would be greater than int.max on line let startTimeInMillis = Int(startDateTime.timeIntervalSince1970 * 1000).
So I changed
let startTimeInMillis = Int(startDateTime.timeIntervalSince1970 * 1000) to let startTimeInMillis = Int64(startDateTime.timeIntervalSince1970 * 1000)
and
let eventStartDateTime = Date(timeIntervalSince1970: TimeInterval((startTimeInMillis-fiveMinutesInMillis)/1000)) to let eventStartDateTime = Date(timeIntervalSince1970: TimeInterval((Int(startTimeInMillis)-fiveMinutesInMillis)/1000)).
Now I am getting following error: Not enough bits to represent the passed value on line let eventStartDateTime = Date(timeIntervalSince1970: TimeInterval((Int(startTimeInMillis)-fiveMinutesInMillis)/1000))
How do I change the function to make it work on Apple Watch Series 3 or watchOS 7?
Updated function code:
static func getEventDateTime(startDateTime: Date?) -> String {
if let startDateTime = startDateTime {
let startTimeInMillis = Int64(startDateTime.timeIntervalSince1970 * 1000)
let fiveMinutesInMillis = 300000
let eventStartDateTime = Date(timeIntervalSince1970: TimeInterval((Int(startTimeInMillis)-fiveMinutesInMillis)/1000))
return convertDateToString(eventStartDateTime)
}
return ""
}

Use the Calendar API to add/subtract time units (doesn't support milliseconds, but does support nanoseconds, which can be converted from): https://developer.apple.com/documentation/foundation/calendar.
func getEventDateTimeCal(startDateTime: Date?) -> String {
if let startDateTime = startDateTime,
let date = Calendar.current.date(byAdding: .minute, value: -5, to: startDateTime) {
return convertDateToString(date, startDateTime)
}
return ""
}
But also, if you don't need millisecond precision, subtract seconds from TimeInterval. Note that TimeInterval is typealias TimeInterval = Double and always represents seconds.
func getEventDateTime(startDateTime: Date?) -> String {
if let startDateTime = startDateTime {
let fiveMinutesInSeconds = 5.0 * 60
let eventStartDateTime = Date(timeIntervalSince1970: startDateTime.timeIntervalSince1970 - fiveMinutesInSeconds)
return convertDateToString(startDateTime, eventStartDateTime)
}
return ""
}

Related

Convert String minutes seconds to Int

I've a string with minutes and seconds in format "minutes:seconds". For example, "5:36". I want to convert it to Int value. For example "5:36" string should be 336 Int value. How this can be done?
let timeString = "5:36"
let timeStringArray = timeString.split(separator: ":")
let minutesInt = Int(timeStringArray[0]) ?? 0
let secondsInt = Int(timeStringArray[1]) ?? 0
let resultInt = minutesInt * 60 + secondsInt
print(resultInt)
Here's a simple extension you can use which will validate the format of your input string too:
import Foundation
extension String {
func toSeconds() -> Int? {
let elements = components(separatedBy: ":")
guard elements.count == 2 else {
print("Provided string doesn't have two sides separated by a ':'")
return nil
}
guard let minutes = Int(elements[0]),
let seconds = Int(elements[1]) else {
print("Either the minute value or the seconds value cannot be converted to an Int")
return nil
}
return (minutes*60) + seconds
}
}
Usage:
let testString1 = "5:36"
let testString2 = "35:36"
print(testString1.toSeconds()) // prints: "Optional(336)"
print(testString2.toSeconds()) // prints: "Optional(2136)"
I tried out your example on the playground here's the code:
import Foundation
let time1String = "0:00"
let time2String = "5:36"
let timeformatter = DateFormatter()
timeformatter.dateFormat = "m:ss"
let time1 = timeformatter.date(from: time1String)
let time2 = timeformatter.date(from: time2String)
if let time1 = time1 {
print(time2?.timeIntervalSince(time1)) // prints: Optional(336.0)
}

Convert currency formatter to double swift

For some reason, I can't convert the Price string to double.
When I do it always returns nil.
func calculateAirfare(checkedBags: Int, distance: Int, travelers: Int) {
let bagsPrices = Double(checkedBags * 25)
let mileCosts = Double(distance) * 0.10
let price = (bagsPrices + mileCosts) * Double(travelers)
/// Format price
let currencyFormatter = NumberFormatter()
currencyFormatter.numberStyle = .currency
let priceString = currencyFormatter.string(from: NSNumber(value: price))
print(priceString) -> "Optional("$750.00")"
if let double = Double(priceString) {
print(double) -> nil
}
}
You can use your same formatter to go back to a number like so:
let number = currencyFormatter.number(from: priceString)
and get the doubleValue like:
let numberDouble = number.doubleValue
The price is already double from the line
let price = (bagsPrices + mileCosts) * Double(travelers)
thus no need to convert it to double.
The code below will return a string with a $ symbol
currencyFormatter.string(from: NSNumber(value: price))
To get a double from that string then you need to remove the $ symbol
which you can do using removeFirst()
priceString?.removeFirst()
After that, the string can be converted to Double.
The complete code is:
func calculateAirfare(checkedBags: Int, distance: Int, travelers: Int) {
let bagsPrices = Double(checkedBags * 25)
let mileCosts = Double(distance) * 0.10
let price = (bagsPrices + mileCosts) * Double(travelers)
/// Format price
let currencyFormatter = NumberFormatter()
currencyFormatter.numberStyle = .currency
var priceString = currencyFormatter.string(for: price)
priceString?.removeFirst()
print(priceString!)
if let double = Double(priceString!) {
print(double)
}
}

Can I make it simpler turning an integer into a string in a specific format using Swift?

In my audio app, I am playing audio using a progress slider bar -- in the UI, I want to show the amount of time the episode has played. Here's how I am doing that.
#objc func updateSlider(){
Slider.value = Float(audioPlayer.currentTime)
func secondsToHoursMinutesSeconds (seconds : Int) -> (Int, Int, Int) {
return (seconds / 3600, (seconds % 3600) / 60, (seconds % 3600) % 60)
}
let example = (Float(audioPlayer.currentTime))
let myIntValue = Int(example)
self.goneTime.text = String(Float(describing: myIntValue)
This code updates a label dynamically but it does it in the format of (Int, Int, Int) as specified. Example Output: (1, 5, 20) when I want 1:5:20.
I have tried to modify the format (Int / Int / Int) which is flagged as an error.
A workaround -- but an ugly one -- I found using this Swift 3 answer: using .replacingOccurrencesOf. From the documentation, it says you can replace one part of the string at a time.
So I change my code to:
func secondsToHoursMinutesSeconds (seconds : Int) -> (Int, Int, Int) {
return (seconds / 3600, (seconds % 3600) / 60, (seconds % 3600) % 60)
}
let example = (Float(audioPlayer.currentTime))
let myIntValue = Int(example)
let updated = secondsToHoursMinutesSeconds(seconds: myIntValue)
let updated2 = String(describing: updated)
let str2 = updated2.replacingOccurrences(of: ",", with: ":", options:
NSString.CompareOptions.literal, range: nil)
let str3 = str2.replacingOccurrences(of: "(", with: "", options:
NSString.CompareOptions.literal, range: nil)
self.goneTime.text = str3
This works ok but is there a best practice to simplify these types of modifications? New to Swift and learning.
AVAudioPlayer currentTime instance property returns a TimeInterval (Double). You should use DateComponentsFormatter and set unitsStyle to positional:
extension Formatter {
static let positional: DateComponentsFormatter = {
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .positional
return formatter
}()
}
Playground testing:
let seconds: TimeInterval = 3920
let display = Formatter.positional.string(from: seconds) // "1:05:20"
Usage in your case:
goneTime.text = Formatter.positional.string(from: audioPlayer.currentTime)
You can do so with Swift Interpolation:
let time = (1, 5, 20)
let myString = "\(time.0):\(time.1):\(time.2)"
In Swift you can simply use String InterPolation, to achieve whatever data result you want as follows:
For example :
let val1 = 10
let val2 = 20
let val3 = 30
let result = "\(val1) : \(val2) : \(val3)"
print(result) // it will give output: 10:20:30
Hope it helps!
Just for fun, you can do this in functional way:
let time = [1, 5, 20]
let result = time.reduce("", { $0 + ($0.isEmpty ? "" : ":") + "\($1)" })
print(result) // "1:5:20"

NumberFormatter wrong result for 0.9972

I have a double:
let value = 0.99720317490866084
And a Double extention function:
extension Double {
func stringWithFixedFractionDigits(min: Int, max: Int) -> String {
let formatter = NumberFormatter()
formatter.minimumFractionDigits = min
formatter.maximumFractionDigits = max
formatter.minimumIntegerDigits = 1
let numberObject = NSNumber(value: self)
return formatter.string(from: numberObject) ?? "\(self)"
}
}
If I use:
value.stringWithFixedFractionDigits(min: 2, max: 2)
I get 1.00 but I would like to get 0.99
What can I change?
You just need to set your NumberFormatter rounding mode property to .down:
formatter.roundingMode = .down
Note that you don't need to create a new NSNumber object, you can safely cast from Double to NSNumber or use string(for: Any) method instead. As an alternative you can extend the protocol FloatingPoint to make it available to all Float types :
extension Formatter {
static let number = NumberFormatter()
}
extension FloatingPoint {
func formattedWithFractionDigits(minimum: Int = 2, maximum: Int = 2, minimumIntegerDigits: Int = 1, roundingMode: NumberFormatter.RoundingMode = .halfEven) -> String {
Formatter.number.roundingMode = roundingMode
Formatter.number.minimumFractionDigits = minimum
Formatter.number.maximumFractionDigits = maximum
Formatter.number.minimumIntegerDigits = minimumIntegerDigits
return Formatter.number.string(for: self) ?? ""
}
}
0.9972031749.formattedWithFractionDigits() // 1.00
0.9972031749.formattedWithFractionDigits(roundingMode: .down) // "0.99"
0.9972031749.formattedWithFractionDigits(minimumIntegerDigits: 0, roundingMode: .down) // ".99"

add 0 to hours/minutes under 10 [duplicate]

This question already has answers here:
Leading zeros for Int in Swift
(12 answers)
Closed 6 years ago.
i've some issue to found a proper solution in swift3 to do this :
I have a date like 10h20m. When my hours are under 10 it will be "5h30". I want to out "05h30".
class func getTime(_ user: SUser) -> String {
let currentDate = Date()
let firstDate = user.firstDate
let difference = firstDate?.timeIntervalSince(now)
let hours = String(Int(difference!) / 3600)
let minutes = String(Int(difference!) / 60 % 60)
let time = "\(hours)h\(minutes)m"
return time
}
if someone have an idea how do that simply et properly thank you !
You can do something like this
class func getTime(_ user: SUser) -> String {
let currentDate = Date()
let firstDate = user.firstDate
let difference = firstDate?.timeIntervalSince(now)
let hours = String(Int(difference!) / 3600)
if Int(hours)! < 10 {
hours = "0\(hours)"
}
var minutes = String(Int(difference!) / 60 % 60)
if Int(minutes)! < 10 {
minutes = "0\(minutes)"
}
let time = "\(hours)h\(minutes)m"
return time
}
or a better way
class func getTime(_ user: SUser) -> String {
let currentDate = Date()
let firstDate = user.firstDate
let difference = firstDate?.timeIntervalSince(now)
let hours = Int(difference!) / 3600
let minutes = Int(difference!) / 60 % 60
let time = "\(String(format: "%02d", hours))h\(String(format: "%02d", minutes))m"
return time
}
Suggested here - Leading zeros for Int in Swift
class func getTime(_ user: SUser) -> String {
let currentDate = Date()
let firstDate = user.firstDate
let difference = firstDate?.timeIntervalSince(now)
var hours = String(Int(difference!) / 3600)
if ((Int(difference!) / 3600)<10) {
hours = "0\(hours)"
}
var minutes = String(Int(difference!) / 60 % 60)
if ((Int(difference!) / 60 % 60)<10) {
minutes = "0\(minutes)"
}
let time = "\(hours)h\(minutes)m"
return time
}
I think you would be better off with a simple formatting function that you can call inline.
Try something like
func formatHour(hour:Int) -> String {
return (String(hour).characters.count == 1) ? "0\(hour)" : "\(hour)"
}
let hour = 5
print("\(formatHour(hour: hour))") // output "05"
You can adjust this as needed, maybe pass the string in instead and save doing the conversion in the function. could also add a guard statement to ensure that the number is within the bounds of hours, or a string is less than three characters etc.

Resources