2022-02-01 17:05:03 -08:00
//
// W i d g e t D e b u g T i m e r . s w i f t
// W i d g e t E x t e n s i o n
//
// C r e a t e d b y D a v i d S i n c l a i r o n 2 0 2 2 - 0 1 - 3 1 .
// B a s e d o n D e j a l c o d e .
//
import Foundation
// / T i m e r f o r d e b u g g i n g p e r f o r m a n c e .
class WidgetDebugTimer {
// / P r i v a t e s i n g l e t o n s h a r e d i n s t a n c e . A c c e s s v i a t h e c l a s s f u n c t i o n s .
private static let shared = WidgetDebugTimer ( )
// / P r i v a t e i n i t i a l i z e r t o p r e v e n t o t h e r s c o n s t r u c t i n g a n e w i n s t a n c e .
private init ( ) {
formatter = NumberFormatter ( )
formatter . minimumIntegerDigits = 1
formatter . minimumFractionDigits = 6
formatter . maximumFractionDigits = 6
}
// / I n f o r m a t i o n a b o u t e a c h t i m e r o p e r a t i o n .
private struct Info {
// / T h e d a t e t h e o p e r a t i o n w a s s t a r t e d .
var start : Date
// / T h e d a t e t h i s s t e p w a s s t a r t e d .
var step : Date
// / T h e i n d e n t a t i o n l e v e l .
var level : Int
// I f I e v e r a d d t h e c l o s u r e - b a s e d l o n g - r u n n i n g t i m e r s , a d d t h o s e p r o p e r t i e s .
}
// / A d i c t i o n a r y o f o p e r a t i o n i n f o , k e y e d o n t h e o p e r a t i o n s t r i n g .
private typealias InfoDictionary = [ String : Info ]
// / A d i c t i o n a r y o f o p e r a t i o n i n f o , k e y e d o n t h e o p e r a t i o n s t r i n g .
private var info = InfoDictionary ( )
// / A n u m b e r f o r m a t t e r f o r t h e n u m b e r o f s e c o n d s .
private var formatter : NumberFormatter
// / G i v e n a n o p e r a t i o n n a m e , s t a r t s a d e b u g t i m e r . U s e ` p r i n t ( _ : s t e p : ) ` a f t e r t h e c o d e t o t i m e .
// /
// / - P a r a m e t e r o p e r a t i o n : T h e n a m e o f t h e o p e r a t i o n t o t i m e ( u s e d a s b o t h a k e y t o g r o u p t i m e r s , a n d a d e b u g l a b e l ) .
// / - P a r a m e t e r l e v e l : H o w m u c h t o i n d e n t t h e o p e r a t i o n , f o r n e s t e d t i m e r s . D e f a u l t s t o z e r o ( n o i n d e n t a t i o n ) .
// / - R e t u r n s : T h e o p e r a t i o n n a m e , s o i t c a n b e a s s i g n e d t o a v a r i a b l e i n s t e a d o f t y p i n g i t a g a i n . D i s c a r d a b l e .
@ discardableResult
class func start ( _ operation : String , level : Int = 0 ) -> String {
let date = Date ( )
shared . info [ operation ] = Info ( start : date , step : date , level : level )
return operation
}
// / G i v e n a n o p e r a t i o n n a m e , t h a t m u s t h a v e b e e n p r e v i o u s l y s t a r t e d v i a ` s t a r t ( _ : ) ` , p r i n t s t h e t o t a l t i m e s o f a r a n d ( i f a s t e p i s p r o v i d e d ) t h e t i m e s i n c e t h a t t h i s s t e p t o o k , i . e . s i n c e t h e s t a r t o r t h e p r e v i o u s s t e p .
// /
// / - P a r a m e t e r o p e r a t i o n : T h e n a m e o f t h e o p e r a t i o n t o t i m e .
// / - P a r a m e t e r s t e p : T h e n a m e o f t h e s t e p o f t h e o p e r a t i o n . M a y b e o m i t t e d i f t h e r e ' s o n l y o n e i n t e r e s t i n g s t e p .
class func print ( _ operation : String , step : String ? = nil ) {
let date = Date ( )
guard let currentInfo = shared . info [ operation ] else {
NSLog ( " \( operation ) : forgot to call start(_:) first! " )
return
}
let totalDuration = date . timeIntervalSince ( currentInfo . start )
2022-09-25 20:35:46 -06:00
guard let step else {
2022-02-01 17:05:03 -08:00
NSLog ( " \( String ( repeating : " " , count : currentInfo . level * 2 ) ) \( operation ) took \( shared . formatter . string ( from : NSNumber ( value : totalDuration ) ) ? ? " ? " ) seconds " )
return
}
let stepDuration = date . timeIntervalSince ( currentInfo . step )
var newInfo = currentInfo
let alert = stepDuration < 0.001 ? " " : stepDuration < 0.01 ? " 🚨 " : stepDuration < 0.1 ? " 🚨🚨 " : stepDuration < 1.0 ? " 🚨🚨🚨 " : " 🚨🚨🚨🚨 "
NSLog ( " \( String ( repeating : " " , count : currentInfo . level * 2 ) ) \( operation ) : \( step ) took \( shared . formatter . string ( from : NSNumber ( value : stepDuration ) ) ? ? " ? " ) seconds (total \( shared . formatter . string ( from : NSNumber ( value : totalDuration ) ) ? ? " ? " ) seconds) \( alert ) " )
newInfo . step = date
shared . info [ operation ] = newInfo
}
}
// / C o n v e n i e n c e t i m e r f o r d e b u g g i n g p e r f o r m a n c e o f a c o d e s c o p e ; a u t o m a t i c a l l y p r i n t s t h e i n f o w h e n e x i t i n g t h e s c o p e .
class DebugScopeTimer {
// / T h e c u r r e n t o p e r a t i o n .
let operation : String
// / I n i t i a l i z e r . A s s i g n t h i s t o a v a r i a b l e t o e s t a b l i s h t h e s c o p e , e . g . ` l e t d e b u g = D e b u g S c o p e T i m e r ( " T h i n g " ) ` ( t h i s w i l l r e s u l t i n a w a r n i n g , b u t t h a t c a n b e u s e f u l t o r e m i n d m e t o r e m o v e t h e t i m e r ; c a n ' t a s s i g n t o u n d e r s c o r e , a s t h a t i s i m m e d i a t e l y r e l e a s e d ) .
// /
// / - P a r a m e t e r o p e r a t i o n : T h e n a m e o f t h e o p e r a t i o n t o t i m e .
init ( _ operation : String ) {
self . operation = operation
WidgetDebugTimer . start ( operation )
}
// / D e i n i t i a l i z e r . P r i n t s t h e i n f o w h e n e x i t i n g t h e s c o p e .
deinit {
WidgetDebugTimer . print ( operation )
}
}