Swift -Get Remainder by itself to Nearest Tenth from Double - ios

I have a video and I need to get the seconds and milliseconds separated and the milliseconds rounded down to the nearest tenth. If the video duration starts off at 4.333333333333333 secs I use floor(num * 10) / 10 to get it to 4.3. When I try to get the remainder by itself using .truncatingRemainder(dividingBy: 1) and/or modf(num).1 I keep getting 0.2999999999999998 instead of 0.3. Why is this happening?
let mixCompositionDuration = CMTimeGetSeconds(mixComposition.duration)
print("mixCompositionDuration: ", mixCompositionDuration) // 4.333333333333333
let dubl = Double(mixCompositionDuration)
print("dubl: ", dubl) // 4.333333333333333
let mixCompositionRoundDown = floor(dubl * 10) / 10
print("mixCompositionRoundDown: ", mixCompositionRoundDown) // 4.3
let remainder = mixCompositionRoundDown.truncatingRemainder(dividingBy: 1)
print("remainder: ", remainder) // 0.2999999999999998
let mod = modf(mixCompositionRoundDown)
print("mod: ", mod) // (4.0, 0.2999999999999998)
let modRemainder = mod.1
print("modRemainder: ", modRemainder) // 0.2999999999999998

Related

Swift: how to calculate the correct dispatch time in a metronome

I am developing a metronome in my App and I want to implement a feature where the metronome can change its tempo at a certain time. For example, plays 4 ticks at a tempo = 120 bpm and then plays 8 ticks at a tempo = 200 bpm and then go back to 120 bpm.
I try this by using the AKMetronome from AudioKit and update the tempo using DispatchQueue.main.asyncAfter. However, the tempo is kinda off and have noticeable lag. Is the calculation wrong or am I missing something?
metronome.tempo = 120
let first_interval = 60.0 / 120.0
let switchTime1 = DispatchTime.now() + (first_interval * 4.0)
metronome.play()
DispatchQueue.main.asyncAfter(deadline: switchTime1, execute: {
self.metronome.tempo = 200
})
let second_inter = 60.0 / 200.0
let switchTime2 = switchTime1 + (second_inter * 8.0)
DispatchQueue.main.asyncAfter(deadline: switchTime2, execute: {
self.metronome.tempo = 120
})

How to remove decimal point if the number more than "10" in swift

How to remove decimal point value if the number is more than 10.0. Below is the code what i have tried. At below code i am getting the value and i put the condition that if value is less than 1km then show number in meter, if value is more than 1 then show the number in km and if the value is greater than 10.0 then i am not able to remove the decimal point
let resultDelivery = String(format: "%.1f", Obj.distance)// Here getting value from api either i get 0.5 or 6.5 or 11.5
if (resultDelivery.starts(with: "0")){
let resultDelivery2 = String(format: "%.f", Obj.distance/1 * 1000)
cell.lblDeliverykm?.text = resultDelivery2.description + " " + "m".Localized() + " " + "" // result is 900 m
}
else if (resultDelivery.starts(with: "10.0")){
let resultDelivery2 = String(format: "%.0f", Obj.distance)
cell.lblDeliverykm?.text = resultDelivery2.description + " " + "km".Localized() + " " + "" // couldn’t able to remove decimal point
}
else {
cell.lblDeliverykm?.text = resultDelivery.description + " " + "km".Localized() + " " + "" // result is 8.6 km
}
Ah the joys of C-style formatting strings.
I present this as an alternative approach:
extension String.StringInterpolation
{
public mutating func appendInterpolation<F: BinaryFloatingPoint>(distance: F)
{
if distance < 1 {
appendLiteral("\(Int(distance * 1000))m")
}
else if distance >= 10 {
appendLiteral("\(Int(distance))km")
}
else
{
let d = (distance * 10).rounded(.toNearestOrEven) / 10
appendLiteral("\(d)km")
}
}
}
print("\(distance: 0.1)")
print("\(distance: 1)")
print("\(distance: 10)")
print("\(distance: 100)")
The output is
100m
1.0km
10km
100km
This will accept Double, Float, Float80, Float16 and any other type conforming to BinaryFloatingPoint.
If you want localizable formats, look into NumberFormatter.
[EDIT] as noted by #flanker in comments, LengthFormatter with its method, string(from: String, unit: LengthFormatter.Unit) -> String would be the way to go rather than NumberFormatter

Running sum in background

Modified Question.
My fitness app will calculate the number of calories burned based on a calculated value for each second. I have a timer that will allow the app to pick back up should it. I can't get the running sum to continue calculating when the app goes into the background. I tried to place the running sum inside of a DispatchQueue but not getting the sum as expected. Any guidance is greatly appreciated.
Here's the code I have placed inside the function that updates the timer.
//MARK: - Update Timer Label
func updateTimerLabel() {
interval = -Int(timerStartDate.timeIntervalSinceNow)
time = interval
let hours = interval / 3600
let minutes = interval / 60 % 60
let seconds = interval % 60
print("Current interval = \(interval)")
timerLabel.text = String(format:"%02i:%02i:%02i", hours, minutes, seconds)
DispatchQueue.global(qos: .background).async {
if self.activityArray[self.currentArrayRow].2 <= 4.5 {
self.cps = self.activityArray[self.currentArrayRow].2 * Double(self.user.userWeightInKilo) / 3600
self.runningCPS = self.runningCPS + self.cps
print("MET \(self.activityArray[self.currentArrayRow].2) <= 4.5 * KG (\(Double(self.user.userWeightInKilo))) * HR (\(Double(self.user.userHeartRate))) / MaxHR (\(Double(self.user.maxHeartRate)) * interval \(Double(self.interval)) / 3600. Gives a cps 0f \(self.cps) and a runningCPS of \(self.runningCPS) ")
} else {
self.cps = self.activityArray[self.currentArrayRow].2 * Double(self.user.userWeightInKilo) * Double(self.user.userHeartRate) / Double(self.user.maxHeartRate) / 3600
self.runningCPS = self.runningCPS + self.cps
print("MET \(self.activityArray[self.currentArrayRow].2) > 4.5 * KG (\(Double(self.user.userWeightInKilo))) * HR (\(Double(self.user.userHeartRate))) / MaxHR (\(Double(self.user.maxHeartRate)) * interval \(Double(self.interval)) / 3600. Gives a cps 0f \(self.cps) and a runningCPS of \(self.runningCPS) ")
}
}
activeLabel.text = String(format: "%0.1f", runningCPS) + " Calories Burned"
}

Confusion about NSTimeZone.secondsFromGMT

I am developing an app that has a feature to enter dark/night mode during night hours automatically. The app asks for the user location and determines the sunrise/sunset hour (in Universal Time) using this algorithm.
The only step that is not clear is to convert from UT to local time, since this is not explained in the algorithm. Say I get a sunrise time of 8.5 (8:30 in the morning UT). How could I convert it to user's local time to check if it's day or night? Or equivalently, how could I convert user's local time to UT in order to be able to compare them?
So far I've tried to use NSCalendar to get the NSDateComponents of the current date (NSDate()). One of these components is a NSTimeZone? from which I can get the secondsFromGMT. Something like this:
let dateComponents = NSCalendar.currentCalendar().components([.TimeZone], fromDate: NSDate())
let localOffset = Double(dateComponents.timeZone?.secondsFromGMT ?? 0)/3600
where localOffset should be the time difference (in hours) from UT (i.e. GMT if I am right) to local time, defaulting to 0 if dateComponents.timeZone == nil (I don't know under which situations this could happen). The problem is that I get the same localOffset for now than for 6 months in the future (when the daylight saving time will be different than it is now at my location, Spain). Does this mean that I need to use the properties daylightSavingTime and/or daylightSavingTimeOffset together with secondsFromGMT? Doesn't secondsFromGMT itself account for this?
Things get even more confusing to me when I read the results from the algorithm. The sun setting hour (in local time) is exactly the one given by Google, but the sun rising hour is one hour ahead of what Google says (for my location and date). I share with you the whole Swift implementation of the algorithm hoping that it can help someone spot what's that I'm doing wrong.
import Foundation
import CoreLocation
enum SunriseSunsetZenith: Double {
case Official = 90.83
case Civil = 96
case Nautical = 102
case Astronomical = 108
}
func sunriseSunsetHoursForLocation(coordinate: CLLocationCoordinate2D, atDate date: NSDate = NSDate(), zenith: SunriseSunsetZenith = .Civil) -> (sunrise: Double, sunset: Double) {
// Initial values (will be changed later)
var sunriseTime = 7.5
var sunsetTime = 19.5
// Get the longitude and latitude
let latitude = coordinate.latitude
let longitude = coordinate.longitude
// Get the day, month, year and local offset
let dateComponents = NSCalendar.currentCalendar().components([.Day, .Month, .Year, .TimeZone], fromDate: date)
let day = Double(dateComponents.day)
let month = Double(dateComponents.month)
let year = Double(dateComponents.year)
let localOffset = Double(dateComponents.timeZone?.daylightSavingTimeOffset ?? 0)/3600
// Calculate the day of the year
let N1 = floor(275*month/9)
let N2 = floor((month + 9)/12)
let N3 = 1 + floor((year - 4*floor(year/4) + 2)/3)
let dayOfYear = N1 - N2*N3 + day - 30
for i in 0...1 {
// Convert the longitude to hour value and calculate an approximate time
let longitudeHour = longitude/15
let t = dayOfYear + ((i == 0 ? 6.0 : 18.0) - longitudeHour)/24
// Calculate the Sun's mean anomaly
let M = 0.9856*t - 3.289
// Calculate the Sun's true longitude
var L = M + 1.916*sind(M) + 0.020*sind(2*M) + 282.634
L %= 360
// Calculate the Sun's right ascension
var RA = atand(0.91764 * tand(L))
RA %= 360
let Lquadrant = (floor(L/90))*90
let RAquadrant = (floor(RA/90))*90
RA += Lquadrant - RAquadrant
RA /= 15
// Calculate the Sun's declination
let sinDec = 0.39782*sind(L)
let cosDec = cosd(asind(sinDec))
// Calculate the Sun's local hour angle
let cosH = (cosd(zenith.rawValue) - sinDec*sind(latitude))/(cosDec*cosd(latitude))
if cosH > 1 { // The sun never rises on this location (on the specified date)
sunriseTime = Double.infinity
sunsetTime = -Double.infinity
} else if cosH < -1 { // The sun never sets on this location (on the specified date)
sunriseTime = -Double.infinity
sunsetTime = Double.infinity
} else {
// Finish calculating H and convert into hours
var H = ( i == 0 ? 360.0 : 0.0 ) + ( i == 0 ? -1.0 : 1.0 )*acosd(cosH)
H /= 15
// Calculate local mean time of rising/setting
let T = H + RA - 0.06571*t - 6.622
// Adjust back to UTC
let UT = T - longitudeHour
// Convert UT value to local time zone of latitude/longitude
let localT = UT + localOffset
if i == 0 { // Add 24 and modulo 24 to be sure that the results is between 0..<24
sunriseTime = (localT + 24)%24
} else {
sunsetTime = (localT + 24)%24
}
}
}
return (sunriseTime, sunsetTime)
}
func sind(valueInDegrees: Double) -> Double {
return sin(valueInDegrees*M_PI/180)
}
func cosd(valueInDegrees: Double) -> Double {
return cos(valueInDegrees*M_PI/180)
}
func tand(valueInDegrees: Double) -> Double {
return tan(valueInDegrees*M_PI/180)
}
func asind(valueInRadians: Double) -> Double {
return asin(valueInRadians)*180/M_PI
}
func acosd(valueInRadians: Double) -> Double {
return acos(valueInRadians)*180/M_PI
}
func atand(valueInRadians: Double) -> Double {
return atan(valueInRadians)*180/M_PI
}
Ans this is how I use the function to determine if it's night or not:
let latitude = ...
let longitude = ...
let coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
let (sunriseHour, sunsetHour) = sunriseSunsetHoursForLocation(coordinate)
let componetns = NSCalendar.currentCalendar().components([.Hour, .Minute], fromDate: NSDate())
let currentHour = Double(componetns.hour) + Double(componetns.minute)/60
let isNight = currentHour < sunriseHour || currentHour > sunsetHour
I'm not sure why your code to get the offset isn't working (I got the same result). But there's a simpler solution that does work. Just ask the local time zone, using secondsFromGMTForDate. With dates six months apart I get different results:
let now = NSDate()
let future = NSCalendar.currentCalendar().dateByAddingUnit(NSCalendarUnit.Month, value: 6, toDate: now, options: NSCalendarOptions(rawValue: 0))!
let nowOffset = NSTimeZone.localTimeZone().secondsFromGMTForDate(now)/3600
let futureOffset = NSTimeZone.localTimeZone().secondsFromGMTForDate(future)/3600

Could not find an overload for "+" that accepts the supplied arguments

Look at this code:
var timepenalty = UInt8(0)
var currentTime = NSDate.timeIntervalSinceReferenceDate()
// Find the difference between current time and start time
var elapsedTime: NSTimeInterval = currentTime - startTime
let adjustedTime = UInt8(timepenalty + elapsedTime)
error-
"Could not find an overload for "+" that accepts the requested arguments.
"
This is for a game that adds time to the stopwatch-style timer, every time the player makes a mistake. The code works when I just use an integer instead of the elapsedTime variable as so:
let adjustedTime = UInt8(elapsedTime + 5)
but replacing 5 with a variable gives an error.
Here's the full code for the updateTime function:
func updateTime() {
var currentTime = NSDate.timeIntervalSinceReferenceDate()
// Find the difference between current time and start time
var elapsedTime: NSTimeInterval = currentTime - startTime
let adjustedTime = UInt8(timepenalty + elapsedTime)
// calculate the minutes in elapsed time
let minutes = UInt8(elapsedTime / 60.0)
elapsedTime -= (NSTimeInterval(minutes) * 60)
// calculate the seconds in elapsed time
seconds = UInt8(elapsedTime)
elapsedTime -= NSTimeInterval(seconds)
// seconds += timepenalty
// find out the fraction of millisends to be displayed
let fraction = UInt8(elapsedTime * 100)
// if seconds > 20 {
// exceedMsgLabel.text = "超过20秒了"
// }
// add the leading zero for minutes, seconds and millseconds and store them as string constants
let startMinutes = minutes > 9 ? String(minutes):"0" + String(minutes)
let startSeconds = seconds > 9 ? String(seconds):"0" + String(seconds)
let startFraction = fraction > 9 ? String(fraction):"0" + String(fraction)
displayTimeLabel.text = "\(startMinutes):\(startSeconds):\(startFraction)"
var penalty = String(timepenalty)
penaltylabel.text = "+ " + penalty
}
#David's code is good, but I'd strongly recommend that you make adjustedTime be an NSTimeInterval. It is a time interval, and that's what the type is for. Then all your casting issues go away.
The UInt8 type is reserved for cases where you explicitly need an 8-bit bit-pattern (like for networking protocols or binary file formats). It isn't intended for "small numbers." Moving between signed and unsigned numbers and different sized-numbers are common sources of bugs, and is intentionally made cumbersome.
If you do need to force a Double to be a whole number, just use Int rather than UInt8 in most cases. In most of these cases it looks like you really mean floor() rather than Int() anyway. You're just normalizing to an whole number.
That said, a more typical way to do your formatting is:
import Foundation
let totalSeconds: NSTimeInterval = 100.51
let frac = Int((totalSeconds - floor(totalSeconds)) * 100)
let seconds = Int(totalSeconds % 60)
let minutes = Int((totalSeconds / 60) % 60)
let result = String(format: "%02d:%02d:%02d", minutes, seconds, frac)
This line:
let adjustedTime = UInt8(timepenalty + elapsedTime)
is attempting to add a UInt8 (time penalty) and an NSTimeInterval (double, elapsedTime) which fails as there is no implicit type conversion in Swift. Change it to:
let adjustedTime = timepenalty + UInt8(elapsedTime)
Which converts the NSTimeInterval to a UInt8 before the addition.
UInt8 and NSTimeInterval are two different types. You need to make each operand the same type. (Or you could use operator overloading.)

Resources