Which "Leap year counting" code run faster? - swift

while practicing coding, I just caught up with question: which code will process faster in swift?
Is this one faster:
var aYear = Int(readLine()!)!
func isLeap(year: Int)
{
if aYear%400 == 0
{
print("YES")
}
else if aYear%4 == 0 && !(aYear%100 == 0)
{
print("YES")
}
else
{
print("NO")
}
}
isLeap(year: aYear)
Or this one faster?
var aYear = Int(readLine()!)!
func isLeap(year: Int) {
var leap = "NO"
if year % 4 == 0 {
leap = "YES"
}
if year % 100 == 0 {
leap = "NO"
}
if year % 400 == 0 {
leap = "YES"
}
print(leap)
}
isLeap(year: aYear)
Thank you. Have a great day :)

One way to check function performance is to this helper struct. Add the following to your project:
struct Benchmark {
static func testElapsedTimeFor(_ title: String, operation: #escaping ()->()) {
let startTime = CFAbsoluteTimeGetCurrent()
operation()
let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
print("Time elapsed for \(title): \(timeElapsed) s.")
}
// If you would like to get the double instead
static func testElapsedTimeFor(operation: #escaping ()->()) -> Double {
let startTime = CFAbsoluteTimeGetCurrent()
operation()
let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
return Double(timeElapsed)
}
}
And use it like so:
Benchmark.testElapsedTimeFor("First Function") {
self.isLeap(year: 2019)
}

Related

trigger 2 different actions depending on duration between 2 ‘onTap’

For a button, each time it is tapped, how to trigger a code1 (set a specific var to value1) when ‘onTap’ occured less than 60 seconds ago or code2 (set a specific variable to value2) when onTap occured more than 60 seconds ago ?
==> Working solution implemented for duration = 10 seconds
Button(action: {
self.date = Date()
let hour = self.calendar.component(.hour, from: self.date)
let minutes = self.calendar.component(.minute, from: self.date)
let seconds = self.calendar.component(.second, from: self.date)
self.newdate = hour*3600+minutes*60+seconds
if (self.lastdate == 0){
print(" First Tap...")
}else
if (self.newdate-self.lastdate) < 10 {
print("Tap before 10 sec => call Func1")
} else {
print("Tap > 10 seconds => call Func2")
}
self.lastdate = hour*3600+minutes*60+seconds
}) {
Text("One")
}
Here's a complete example that works for an interval of 5 seconds (to make testing quicker).
It uses #State vars to update the Date of the lastTap and message and uses .timeIntervalSince(last) to measure the time in seconds. The first tap is detected when lastTap is still nil.
Changing the message causes the view to redraw and update the message on the screen.
struct ContentView: View {
#State private var lastTap: Date? = nil
#State private var message = ""
var body: some View {
VStack(spacing: 40) {
Text(message)
Button("Tap me") {
let now = Date()
if let last = self.lastTap {
if now.timeIntervalSince(last) > 5 {
self.message = "Greater than 5 seconds"
} else {
self.message = "Less than 5 seconds"
}
} else {
self.message = "First tap"
}
self.lastTap = now
}
}
}
}
var lastTapTimestamp: Date?
#IBAction func buttonTapped(_ sender: UIButton) {
defer { lastTapTimestamp = Date() }
guard let date = lastTapTimestamp else {
callFunc1()
return
}
if Int(Date().timeIntervalSinceReferenceDate) - Int(date.timeIntervalSinceReferenceDate) >= 3600 {
callFunc2()
} else {
callFunc1()
}
}

How to initialize enum from raw value

I'm having an enum that I'm trying to initialize to case twentyFourHours like this:
enum Duration: TimeInterval {
case twentyFourHours
var durationInSeconds: TimeInterval {
switch self {
case .twentyFourHours:
return TimeInterval.init(86400)
}
}
var durationInHours: Int {
switch self {
case .twentyFourHours:
return 24
}
}
}
let interval = TimeInterval.init(86400)
guard let duration = Duration.init(rawValue: interval) else { throw ChallengeError.invalidDuration }
But I always get nil. Does anyone know why?
Seems like a basic thing I already should know!
Try this
enum Duration: TimeInterval {
case twentyFourHours = 86400
var durationInSeconds: TimeInterval {
return self.rawValue
}
var durationInHours: Int {
return Int(self.rawValue / 3600)
}
}
This equals to:
Duration(1234) // <- nil
Duration(86400) // <- .twentyFourHours
You need to specify the rawValue if it's something other than 0, TimeInterval is a typealias for type Double and it defaults the first case rawValue to 0 if not specified.
enum Duration: TimeInterval {
case twentyFourHours = 86400
//...
}
let interval: TimeInterval = 86400
let duration = Duration(rawValue: interval) // and then you can initialize like this

Switch from Countdown to Countup

stackoverflow is my last chance. I hope you can help me. I have a countdown in my app which is set to 2 hours and 30 minutes. When it hits 0 it starts to count up to 30 minutes. After the 30 minutes are over, the countdown is back at 2:30. Everything is working fine except the part when the timer is switching from the countdown part to the countup part. Here is the problem: The timer is going negative when hitting zero.
#objc func startTimer() {
print("Countdown : \(totalTime)")
countdownTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
}
#objc func updateTime() {
if (totalTime > 10800) {
totalTime = totalTime - 10800
}
totalTime -= 1
var time = totalTime
//When in session...
if (time > 9000) {
//Calculate CountUP
time = 10800 - time
//if session has started
if(isSessionActive == false) {
isSessionActive = true
lastFiveMinutes = false
self.setSessionStarted()
}
} else {
//If session has ended...
if(isSessionActive == true) {
isSessionActive = false
lastFiveMinutes = false
self.setSessionEnded()
}
//If last 5 minutes have started
if(time < 300 && lastFiveMinutes == false) {
lastFiveMinutes = true
self.setLastFiveMinutes()
}
}
countdownLabel.text = "\(timeFormatted(time))"
//update Participant Count every x second, ONLY if in last 5 Minutes or Session is active
if (lastFiveMinutes == true || isSessionActive == true) {
if (totalTime % 5 == 0) {
setParticipants(n: httpController.getPar())
}
} else {
setParticipants(n: "...")
}
}
The part "var time = totalTime" is not necessary, i know.
In Android the code is exactly the same except the -=1 part, but it is working.
* Update *
Inital Value of totalTime:
#objc var totalTime = 0
And here is the next part where it gets a real value.
#objc func initCountdown() {
var currentSec = 0.0
// Get Current Time from Server
serverTimeReturn { (getResDate) -> Void in
let dFormatter = DateFormatter()
dFormatter.dateStyle = DateFormatter.Style.long
dFormatter.timeStyle = DateFormatter.Style.long
dFormatter.timeZone = NSTimeZone(abbreviation: "GMT")! as TimeZone
let dateGet = dFormatter.string(from: getResDate! as Date)
currentSec = Double((getResDate?.timeIntervalSince1970)!)
print("Formatted Hour : \(dateGet)")
let todaySec = Int(currentSec) % 86400
print("Today Sec Hour : \(todaySec)")
let countDownSec = 10800 - todaySec % 10800
// Check status to init
if(countDownSec > 9000) {
self.isSessionActive = true
self.setSessionStarted()
} else {
self.setSessionEnded()
}
if(countDownSec < 300) {
self.setLastFiveMinutes()
self.lastFiveMinutes = true
}
self.totalTime = countDownSec
}
print("Formatted Hour : \(totalTime)")
startTimer()
}
I don't see any code where you count upwards so I am not surprised it isn't working. I would introduce a boolean property isCountingDown to keep track of how to handle the timer and then have two separate methods for up and down
var isCountingDown: boolean
#objc func initCountdown() {
//existing code
isCountingDown = true
startTimer()
}
#objc func updateTime() {
if isCountingDown {
doCountDown()
} else {
doCountUp()
}
}
And then in doCountDown() and doCountUp() I would check against time is 0 or 30 and update isCountingDown accordingly
func doCountDown {
if (totalTime > 10800) {
totalTime = totalTime - 10800
}
totalTime -= 1
if totalTime == 0 {
isCountingDown = false
//Maybe return here?
}
var time = totalTime
//rest of code
}
func doCountUp() {
totalTime += 1
if totalTime == 30 {
isCountingDown = true
//Maybe return here?
}
//rest of code
}

CANT RESOLVE: unsafeAddressOf is abandoned in Swift 3

I just realized that my old app is not working anymore because unsafeAddressOf is abandoned in Swift 3. I have been searching in Apple documentations and online tutorials but still cant figure out how to change my code to be compliant with Swift 3. Here is my code:
import UIKit
import ImageIO
extension UIImage {
public class func gifWithData(data: NSData) -> UIImage? {
guard let source = CGImageSourceCreateWithData(data, nil) else {
print("SwiftGif: Source for the image does not exist")
return nil
}
return UIImage.animatedImageWithSource(source: source)
}
public class func gifWithName(name: String) -> UIImage? {
guard let bundleURL = Bundle.main.url(forResource: name, withExtension: "gif") else {
print("SwiftGif: This image named \"\(name)\" does not exist")
return nil
}
guard let imageData = NSData(contentsOfURL: bundleURL) else {
print("SwiftGif: Cannot turn image named \"\(name)\" into NSData")
return nil
}
return gifWithData(imageData)
}
class func delayForImageAtIndex(index: Int, source: CGImageSource!) -> Double {
var delay = 0.1
// Get dictionaries
let cfProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil)
let gifProperties: CFDictionary = unsafeBitCast(CFDictionaryGetValue(cfProperties, unsafeAddressOf(kCGImagePropertyGIFDictionary)), to: CFDictionary.self)
// Get delay time
var delayObject: AnyObject = unsafeBitCast(
CFDictionaryGetValue(gifProperties,
unsafeAddressOf(kCGImagePropertyGIFUnclampedDelayTime)),
to: AnyObject.self)
if delayObject.doubleValue == 0 {
delayObject = unsafeBitCast(CFDictionaryGetValue(gifProperties,
unsafeAddressOf(kCGImagePropertyGIFDelayTime)), to: AnyObject.self)
}
delay = delayObject as! Double
if delay < 0.1 {
delay = 0.1 // Make sure they're not too fast
}
return delay
}
class func gcdForPair( a: Int?, var _ b: Int?) -> Int {
// Check if one of them is nil
var a = a
if b == nil || a == nil {
if b != nil {
return b!
} else if a != nil {
return a!
} else {
return 0
}
}
// Swap for modulo
if a < b {
let c = a
a = b
b = c
}
// Get greatest common divisor
var rest: Int
while true {
rest = a! % b!
if rest == 0 {
return b! // Found it
} else {
a = b
b = rest
}
}
}
class func gcdForArray(array: Array<Int>) -> Int {
if array.isEmpty {
return 1
}
var gcd = array[0]
for val in array {
gcd = UIImage.gcdForPair(val, gcd)
}
return gcd
}
class func animatedImageWithSource(source: CGImageSource) -> UIImage? {
let count = CGImageSourceGetCount(source)
var images = [CGImage]()
var delays = [Int]()
// Fill arrays
for i in 0..<count {
// Add image
if let image = CGImageSourceCreateImageAtIndex(source, i, nil) {
images.append(image)
}
// At it's delay in cs
let delaySeconds = UIImage.delayForImageAtIndex(index: Int(i),
source: source)
delays.append(Int(delaySeconds * 1000.0)) // Seconds to ms
}
// Calculate full duration
let duration: Int = {
var sum = 0
for val: Int in delays {
sum += val
}
return sum
}()
// Get frames
let gcd = gcdForArray(array: delays)
var frames = [UIImage]()
var frame: UIImage
var frameCount: Int
for i in 0..<count {
frame = UIImage(CGImage: images[Int(i)])
frameCount = Int(delays[Int(i)] / gcd)
for _ in 0..<frameCount {
frames.append(frame)
}
}
// Heyhey
let animation = UIImage.animatedImage(with: frames,
duration: Double(duration) / 1000.0)
return animation
}
}
Does anyone have an idea how I can fix this code?

How to filter based on Date so there is only one element for each week?

I'm having difficulty wording this, but here goes.
I have an array of Datapoint objects and each of these objects has a createdAt NSDate property. The array has about 10 or so Datapoint objects for each day of the past week.
I want to filter the array so that there is only one datapoint for each day of the week.
I was thinking something along the lines of:
let today = NSDate()
var endRange = today.dateByAddingTimeInterval(-7 * 24 * 60 * 60)
var allThisWeeksPoints = datapoints.filter({ $0.createdAt >= endRange && $0.createdAt < today })
let c = NSCalendar.currentCalendar()
var thisWeeksPoints : [Datapoint] = []
while !c.isDate(endRange, inSameDayAsDate: today) {
//take one datapoint, eliminate all the other datapoints in the same day and then increment the endRange date by a day
}
Here is the code for my Datapoint object:
class Datapoint: NSObject {
let object : PFObject
let objectId : String
let userId : String
let createdAt : NSDate
let totalPosts : Int
let followerCount : Int
let followingCount : Int
let totalLikes : Int
let averageLikes : Float
let totalComments : Int
let averageComments : Float
}
Step One:
Make NSDate Comparable Found here
public func ==(lhs: NSDate, rhs: NSDate) -> Bool {
return lhs === rhs || lhs.compare(rhs) == .OrderedSame
}
public func <(lhs: NSDate, rhs: NSDate) -> Bool {
return lhs.compare(rhs) == .OrderedAscending
}
extension NSDate: Comparable { }
A few general remarks:
Use an input and output.
Respect upper and lower camel naming conventions
Split the function in two parts, one for dividing the points in weeks, another to apply the filter. This makes it easier to maintain.
Use dateByAddingUnit as suggested in the comments. This will prevent localisation issues. Not every week is the same and not every calendar used in the world uses the same system of weeks.
func weeklyPoints(dataPoints: [DataPoint]) -> [[DataPoint]]? {
guard let firstPoint = dataPoints.first else { // alternative to an .isEmpty check since we need the first point anyway
return nil
}
guard let gregorian = NSCalendar(identifier:NSCalendarIdentifierGregorian) else {
return nil
}
var weeklies : [[DataPoint]] = [[]]
var weekStart : NSDate = firstPoint.createdAt // force unwrap allowed because of the guard at the beginning
var maybeWeekEnd : NSDate? = gregorian.dateByAddingUnit(.WeekOfYear, value: 1, toDate: weekStart, options: [])
for point in dataPoints {
guard let weekEnd = maybeWeekEnd else {
break
}
guard point.createdAt >= weekStart && point.createdAt < weekEnd else {
weekStart = weekEnd
maybeWeekEnd = gregorian.dateByAddingUnit(.WeekOfYear, value: 1, toDate: weekStart, options: [])
weeklies.append([])
continue
}
let currentWeekIndex = (weeklies.count - 1)
weeklies[currentWeekIndex].append(point)
}
return weeklies
}
The very simple filter function.
func onePointPerWeek(dataPoints: [DataPoint]) -> [DataPoint]? {
guard let weeklies = weeklyPoints(dataPoints) else {
return nil
}
var pointPerWeek : [DataPoint] = []
for week in weeklies {
guard let point = week.first else { // maybe sort them first if needed
continue
}
pointPerWeek.append(point)
}
return pointPerWeek
}
So I figured out a way to do it:
func thisWeeksPoints() -> [Datapoint] {
let today = NSDate()
var endRange = today.dateByAddingTimeInterval(-7 * 24 * 60 * 60)
var allThisWeeksPoints = datapoints.filter({ $0.createdAt >= endRange && $0.createdAt < today })
let c = NSCalendar.currentCalendar()
var thisWeeksPoints : [Datapoint] = []
while !c.isDate(endRange, inSameDayAsDate: today) {
if let point = allThisWeeksPoints.first {
allThisWeeksPoints = allThisWeeksPoints.filter({!c.isDate(point.createdAt, inSameDayAsDate: $0.createdAt)})
thisWeeksPoints.append(point)
}
endRange = endRange.dateByAddingTimeInterval(24 * 60 * 60)
}
return thisWeeksPoints
}