├── .github └── FUNDING.yml ├── .gitignore ├── .swiftpm └── xcode │ └── package.xcworkspace │ └── contents.xcworkspacedata ├── GDPerformanceView-Swift.podspec ├── GDPerformanceView-Swift.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── GDPerformanceView-iOS.xcscheme ├── GDPerformanceView-Swift ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── GDPerformanceMonitoring │ ├── LinkedFramesList.swift │ ├── MarginLabel.swift │ ├── PerformanceMonitor.swift │ ├── PerformanceView.swift │ ├── PerformanceСalculator.swift │ ├── Protocols.swift │ └── WindowViewController.swift └── Info.plist ├── GDPerformanceView-iOS ├── GDPerformanceView-iOS.h └── Info.plist ├── LICENSE ├── Package.swift ├── README.md ├── performance_view.PNG ├── performance_view_2.PNG ├── performance_view_3.PNG └── performance_view_4.PNG /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: ['dani-gavrilov'] 4 | # patreon: # Replace with a single Patreon username 5 | # open_collective: # Replace with a single Open Collective username 6 | # ko_fi: # Replace with a single Ko-fi username 7 | # tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | # community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | # liberapay: # Replace with a single Liberapay username 10 | # issuehunt: # Replace with a single IssueHunt username 11 | # otechie: # Replace with a single Otechie username 12 | # custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | build/ 4 | DerivedData 5 | .idea/ 6 | xcuserdata/ 7 | *.xcuserdatad 8 | *.xcuserstate 9 | *.xccheckout 10 | *.xcscmblueprint 11 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /GDPerformanceView-Swift.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "GDPerformanceView-Swift" 3 | s.version = "2.1.1" 4 | s.summary = "Shows FPS, CPU and memory usage, device model, app and iOS versions above the status bar and report FPS, CPU and memory usage via delegate." 5 | s.homepage = "https://github.com/dani-gavrilov/GDPerformanceView-Swift" 6 | s.license = { :type => "MIT", :file => "LICENSE" } 7 | s.author = { "Gavrilov Daniil" => "daniilmbox@gmail.com" } 8 | s.platform = :ios, "9.0" 9 | s.ios.deployment_target = "9.0" 10 | s.source = { :git => "https://github.com/dani-gavrilov/GDPerformanceView-Swift.git", :tag => s.version.to_s } 11 | s.source_files = "GDPerformanceView-Swift/GDPerformanceMonitoring/*.swift" 12 | s.frameworks = "UIKit", "Foundation", "QuartzCore" 13 | s.requires_arc = true 14 | s.swift_versions = ['4.2', '5.0'] 15 | end 16 | -------------------------------------------------------------------------------- /GDPerformanceView-Swift.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 0C340AB821A348C600734F1C /* MarginLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C340AB121A348C600734F1C /* MarginLabel.swift */; }; 11 | 0C340AB921A348C600734F1C /* MarginLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C340AB121A348C600734F1C /* MarginLabel.swift */; }; 12 | 0C340ABA21A348C600734F1C /* PerformanceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C340AB221A348C600734F1C /* PerformanceView.swift */; }; 13 | 0C340ABB21A348C600734F1C /* PerformanceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C340AB221A348C600734F1C /* PerformanceView.swift */; }; 14 | 0C340ABC21A348C600734F1C /* Protocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C340AB321A348C600734F1C /* Protocols.swift */; }; 15 | 0C340ABD21A348C600734F1C /* Protocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C340AB321A348C600734F1C /* Protocols.swift */; }; 16 | 0C340ABE21A348C600734F1C /* LinkedFramesList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C340AB421A348C600734F1C /* LinkedFramesList.swift */; }; 17 | 0C340ABF21A348C600734F1C /* LinkedFramesList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C340AB421A348C600734F1C /* LinkedFramesList.swift */; }; 18 | 0C340AC021A348C600734F1C /* WindowViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C340AB521A348C600734F1C /* WindowViewController.swift */; }; 19 | 0C340AC121A348C600734F1C /* WindowViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C340AB521A348C600734F1C /* WindowViewController.swift */; }; 20 | 0C340AC221A348C600734F1C /* PerformanceMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C340AB621A348C600734F1C /* PerformanceMonitor.swift */; }; 21 | 0C340AC321A348C600734F1C /* PerformanceMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C340AB621A348C600734F1C /* PerformanceMonitor.swift */; }; 22 | 0C340AC421A348C600734F1C /* PerformanceСalculator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C340AB721A348C600734F1C /* PerformanceСalculator.swift */; }; 23 | 0C340AC521A348C600734F1C /* PerformanceСalculator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C340AB721A348C600734F1C /* PerformanceСalculator.swift */; }; 24 | 42AD06F31E1D18C400A5C840 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42AD06E71E1D18C400A5C840 /* AppDelegate.swift */; }; 25 | 42AD06F41E1D18C400A5C840 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 42AD06E81E1D18C400A5C840 /* Assets.xcassets */; }; 26 | 42AD06F51E1D18C400A5C840 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 42AD06E91E1D18C400A5C840 /* LaunchScreen.storyboard */; }; 27 | 42AD06F61E1D18C400A5C840 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 42AD06EB1E1D18C400A5C840 /* Main.storyboard */; }; 28 | 42C2CBE81E244D6300A96A93 /* GDPerformanceView-iOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 42C2CBE61E244D6300A96A93 /* GDPerformanceView-iOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; 29 | 42C2CBEB1E244D6300A96A93 /* GDPerformanceView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 42C2CBE41E244D6300A96A93 /* GDPerformanceView.framework */; }; 30 | 42C2CBEC1E244D6300A96A93 /* GDPerformanceView.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 42C2CBE41E244D6300A96A93 /* GDPerformanceView.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 31 | /* End PBXBuildFile section */ 32 | 33 | /* Begin PBXContainerItemProxy section */ 34 | 42C2CBE91E244D6300A96A93 /* PBXContainerItemProxy */ = { 35 | isa = PBXContainerItemProxy; 36 | containerPortal = 4237B6D01E0FE2290059FF8B /* Project object */; 37 | proxyType = 1; 38 | remoteGlobalIDString = 42C2CBE31E244D6300A96A93; 39 | remoteInfo = "GDPerformanceView-iOS"; 40 | }; 41 | /* End PBXContainerItemProxy section */ 42 | 43 | /* Begin PBXCopyFilesBuildPhase section */ 44 | 42630D0B1E24439C005AAE56 /* Embed Frameworks */ = { 45 | isa = PBXCopyFilesBuildPhase; 46 | buildActionMask = 2147483647; 47 | dstPath = ""; 48 | dstSubfolderSpec = 10; 49 | files = ( 50 | 42C2CBEC1E244D6300A96A93 /* GDPerformanceView.framework in Embed Frameworks */, 51 | ); 52 | name = "Embed Frameworks"; 53 | runOnlyForDeploymentPostprocessing = 0; 54 | }; 55 | /* End PBXCopyFilesBuildPhase section */ 56 | 57 | /* Begin PBXFileReference section */ 58 | 0C340AB121A348C600734F1C /* MarginLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarginLabel.swift; sourceTree = ""; }; 59 | 0C340AB221A348C600734F1C /* PerformanceView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PerformanceView.swift; sourceTree = ""; }; 60 | 0C340AB321A348C600734F1C /* Protocols.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Protocols.swift; sourceTree = ""; }; 61 | 0C340AB421A348C600734F1C /* LinkedFramesList.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LinkedFramesList.swift; sourceTree = ""; }; 62 | 0C340AB521A348C600734F1C /* WindowViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WindowViewController.swift; sourceTree = ""; }; 63 | 0C340AB621A348C600734F1C /* PerformanceMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PerformanceMonitor.swift; sourceTree = ""; }; 64 | 0C340AB721A348C600734F1C /* PerformanceСalculator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PerformanceСalculator.swift"; sourceTree = ""; }; 65 | 4237B6D81E0FE22A0059FF8B /* GDPerformanceView-Swift.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "GDPerformanceView-Swift.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 66 | 42AD06E71E1D18C400A5C840 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 67 | 42AD06E81E1D18C400A5C840 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 68 | 42AD06EA1E1D18C400A5C840 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 69 | 42AD06EC1E1D18C400A5C840 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 70 | 42AD06F11E1D18C400A5C840 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 71 | 42C2CBE41E244D6300A96A93 /* GDPerformanceView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GDPerformanceView.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 72 | 42C2CBE61E244D6300A96A93 /* GDPerformanceView-iOS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "GDPerformanceView-iOS.h"; sourceTree = ""; }; 73 | 42C2CBE71E244D6300A96A93 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 74 | /* End PBXFileReference section */ 75 | 76 | /* Begin PBXFrameworksBuildPhase section */ 77 | 4237B6D51E0FE2290059FF8B /* Frameworks */ = { 78 | isa = PBXFrameworksBuildPhase; 79 | buildActionMask = 2147483647; 80 | files = ( 81 | 42C2CBEB1E244D6300A96A93 /* GDPerformanceView.framework in Frameworks */, 82 | ); 83 | runOnlyForDeploymentPostprocessing = 0; 84 | }; 85 | 42C2CBE01E244D6300A96A93 /* Frameworks */ = { 86 | isa = PBXFrameworksBuildPhase; 87 | buildActionMask = 2147483647; 88 | files = ( 89 | ); 90 | runOnlyForDeploymentPostprocessing = 0; 91 | }; 92 | /* End PBXFrameworksBuildPhase section */ 93 | 94 | /* Begin PBXGroup section */ 95 | 4237B6CF1E0FE2290059FF8B = { 96 | isa = PBXGroup; 97 | children = ( 98 | 42AD06E61E1D18C400A5C840 /* GDPerformanceView-Swift */, 99 | 42C2CBE51E244D6300A96A93 /* GDPerformanceView-iOS */, 100 | 4237B6D91E0FE22A0059FF8B /* Products */, 101 | ); 102 | sourceTree = ""; 103 | }; 104 | 4237B6D91E0FE22A0059FF8B /* Products */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 4237B6D81E0FE22A0059FF8B /* GDPerformanceView-Swift.app */, 108 | 42C2CBE41E244D6300A96A93 /* GDPerformanceView.framework */, 109 | ); 110 | name = Products; 111 | sourceTree = ""; 112 | }; 113 | 42AD06E61E1D18C400A5C840 /* GDPerformanceView-Swift */ = { 114 | isa = PBXGroup; 115 | children = ( 116 | 42AD06ED1E1D18C400A5C840 /* GDPerformanceMonitoring */, 117 | 42AD06E71E1D18C400A5C840 /* AppDelegate.swift */, 118 | 42AD06E81E1D18C400A5C840 /* Assets.xcassets */, 119 | 42AD06E91E1D18C400A5C840 /* LaunchScreen.storyboard */, 120 | 42AD06EB1E1D18C400A5C840 /* Main.storyboard */, 121 | 42AD06F11E1D18C400A5C840 /* Info.plist */, 122 | ); 123 | path = "GDPerformanceView-Swift"; 124 | sourceTree = ""; 125 | }; 126 | 42AD06ED1E1D18C400A5C840 /* GDPerformanceMonitoring */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | 0C340AB621A348C600734F1C /* PerformanceMonitor.swift */, 130 | 0C340AB221A348C600734F1C /* PerformanceView.swift */, 131 | 0C340AB721A348C600734F1C /* PerformanceСalculator.swift */, 132 | 0C340AB521A348C600734F1C /* WindowViewController.swift */, 133 | 0C340AB121A348C600734F1C /* MarginLabel.swift */, 134 | 0C340AB321A348C600734F1C /* Protocols.swift */, 135 | 0C340AB421A348C600734F1C /* LinkedFramesList.swift */, 136 | ); 137 | path = GDPerformanceMonitoring; 138 | sourceTree = ""; 139 | }; 140 | 42C2CBE51E244D6300A96A93 /* GDPerformanceView-iOS */ = { 141 | isa = PBXGroup; 142 | children = ( 143 | 42C2CBE61E244D6300A96A93 /* GDPerformanceView-iOS.h */, 144 | 42C2CBE71E244D6300A96A93 /* Info.plist */, 145 | ); 146 | path = "GDPerformanceView-iOS"; 147 | sourceTree = ""; 148 | }; 149 | /* End PBXGroup section */ 150 | 151 | /* Begin PBXHeadersBuildPhase section */ 152 | 42C2CBE11E244D6300A96A93 /* Headers */ = { 153 | isa = PBXHeadersBuildPhase; 154 | buildActionMask = 2147483647; 155 | files = ( 156 | 42C2CBE81E244D6300A96A93 /* GDPerformanceView-iOS.h in Headers */, 157 | ); 158 | runOnlyForDeploymentPostprocessing = 0; 159 | }; 160 | /* End PBXHeadersBuildPhase section */ 161 | 162 | /* Begin PBXNativeTarget section */ 163 | 4237B6D71E0FE2290059FF8B /* GDPerformanceView-Swift */ = { 164 | isa = PBXNativeTarget; 165 | buildConfigurationList = 4237B6EA1E0FE22A0059FF8B /* Build configuration list for PBXNativeTarget "GDPerformanceView-Swift" */; 166 | buildPhases = ( 167 | 4237B6D41E0FE2290059FF8B /* Sources */, 168 | 4237B6D51E0FE2290059FF8B /* Frameworks */, 169 | 4237B6D61E0FE2290059FF8B /* Resources */, 170 | 42630D0B1E24439C005AAE56 /* Embed Frameworks */, 171 | ); 172 | buildRules = ( 173 | ); 174 | dependencies = ( 175 | 42C2CBEA1E244D6300A96A93 /* PBXTargetDependency */, 176 | ); 177 | name = "GDPerformanceView-Swift"; 178 | productName = "GDPerfomanceView-Swift"; 179 | productReference = 4237B6D81E0FE22A0059FF8B /* GDPerformanceView-Swift.app */; 180 | productType = "com.apple.product-type.application"; 181 | }; 182 | 42C2CBE31E244D6300A96A93 /* GDPerformanceView-iOS */ = { 183 | isa = PBXNativeTarget; 184 | buildConfigurationList = 42C2CBED1E244D6300A96A93 /* Build configuration list for PBXNativeTarget "GDPerformanceView-iOS" */; 185 | buildPhases = ( 186 | 42C2CBDF1E244D6300A96A93 /* Sources */, 187 | 42C2CBE01E244D6300A96A93 /* Frameworks */, 188 | 42C2CBE11E244D6300A96A93 /* Headers */, 189 | 42C2CBE21E244D6300A96A93 /* Resources */, 190 | ); 191 | buildRules = ( 192 | ); 193 | dependencies = ( 194 | ); 195 | name = "GDPerformanceView-iOS"; 196 | productName = "GDPerformanceView-iOS"; 197 | productReference = 42C2CBE41E244D6300A96A93 /* GDPerformanceView.framework */; 198 | productType = "com.apple.product-type.framework"; 199 | }; 200 | /* End PBXNativeTarget section */ 201 | 202 | /* Begin PBXProject section */ 203 | 4237B6D01E0FE2290059FF8B /* Project object */ = { 204 | isa = PBXProject; 205 | attributes = { 206 | LastSwiftUpdateCheck = 0820; 207 | LastUpgradeCheck = 1200; 208 | ORGANIZATIONNAME = "Daniil Gavrilov"; 209 | TargetAttributes = { 210 | 4237B6D71E0FE2290059FF8B = { 211 | CreatedOnToolsVersion = 8.2; 212 | DevelopmentTeam = RU64C364MT; 213 | ProvisioningStyle = Automatic; 214 | }; 215 | 42C2CBE31E244D6300A96A93 = { 216 | CreatedOnToolsVersion = 8.2.1; 217 | DevelopmentTeam = RU64C364MT; 218 | LastSwiftMigration = 1020; 219 | ProvisioningStyle = Automatic; 220 | }; 221 | }; 222 | }; 223 | buildConfigurationList = 4237B6D31E0FE2290059FF8B /* Build configuration list for PBXProject "GDPerformanceView-Swift" */; 224 | compatibilityVersion = "Xcode 3.2"; 225 | developmentRegion = en; 226 | hasScannedForEncodings = 0; 227 | knownRegions = ( 228 | en, 229 | Base, 230 | ); 231 | mainGroup = 4237B6CF1E0FE2290059FF8B; 232 | productRefGroup = 4237B6D91E0FE22A0059FF8B /* Products */; 233 | projectDirPath = ""; 234 | projectRoot = ""; 235 | targets = ( 236 | 4237B6D71E0FE2290059FF8B /* GDPerformanceView-Swift */, 237 | 42C2CBE31E244D6300A96A93 /* GDPerformanceView-iOS */, 238 | ); 239 | }; 240 | /* End PBXProject section */ 241 | 242 | /* Begin PBXResourcesBuildPhase section */ 243 | 4237B6D61E0FE2290059FF8B /* Resources */ = { 244 | isa = PBXResourcesBuildPhase; 245 | buildActionMask = 2147483647; 246 | files = ( 247 | 42AD06F61E1D18C400A5C840 /* Main.storyboard in Resources */, 248 | 42AD06F41E1D18C400A5C840 /* Assets.xcassets in Resources */, 249 | 42AD06F51E1D18C400A5C840 /* LaunchScreen.storyboard in Resources */, 250 | ); 251 | runOnlyForDeploymentPostprocessing = 0; 252 | }; 253 | 42C2CBE21E244D6300A96A93 /* Resources */ = { 254 | isa = PBXResourcesBuildPhase; 255 | buildActionMask = 2147483647; 256 | files = ( 257 | ); 258 | runOnlyForDeploymentPostprocessing = 0; 259 | }; 260 | /* End PBXResourcesBuildPhase section */ 261 | 262 | /* Begin PBXSourcesBuildPhase section */ 263 | 4237B6D41E0FE2290059FF8B /* Sources */ = { 264 | isa = PBXSourcesBuildPhase; 265 | buildActionMask = 2147483647; 266 | files = ( 267 | 0C340ABE21A348C600734F1C /* LinkedFramesList.swift in Sources */, 268 | 42AD06F31E1D18C400A5C840 /* AppDelegate.swift in Sources */, 269 | 0C340ABC21A348C600734F1C /* Protocols.swift in Sources */, 270 | 0C340AC221A348C600734F1C /* PerformanceMonitor.swift in Sources */, 271 | 0C340AC421A348C600734F1C /* PerformanceСalculator.swift in Sources */, 272 | 0C340AC021A348C600734F1C /* WindowViewController.swift in Sources */, 273 | 0C340AB821A348C600734F1C /* MarginLabel.swift in Sources */, 274 | 0C340ABA21A348C600734F1C /* PerformanceView.swift in Sources */, 275 | ); 276 | runOnlyForDeploymentPostprocessing = 0; 277 | }; 278 | 42C2CBDF1E244D6300A96A93 /* Sources */ = { 279 | isa = PBXSourcesBuildPhase; 280 | buildActionMask = 2147483647; 281 | files = ( 282 | 0C340AC521A348C600734F1C /* PerformanceСalculator.swift in Sources */, 283 | 0C340AC121A348C600734F1C /* WindowViewController.swift in Sources */, 284 | 0C340AB921A348C600734F1C /* MarginLabel.swift in Sources */, 285 | 0C340ABB21A348C600734F1C /* PerformanceView.swift in Sources */, 286 | 0C340ABF21A348C600734F1C /* LinkedFramesList.swift in Sources */, 287 | 0C340AC321A348C600734F1C /* PerformanceMonitor.swift in Sources */, 288 | 0C340ABD21A348C600734F1C /* Protocols.swift in Sources */, 289 | ); 290 | runOnlyForDeploymentPostprocessing = 0; 291 | }; 292 | /* End PBXSourcesBuildPhase section */ 293 | 294 | /* Begin PBXTargetDependency section */ 295 | 42C2CBEA1E244D6300A96A93 /* PBXTargetDependency */ = { 296 | isa = PBXTargetDependency; 297 | target = 42C2CBE31E244D6300A96A93 /* GDPerformanceView-iOS */; 298 | targetProxy = 42C2CBE91E244D6300A96A93 /* PBXContainerItemProxy */; 299 | }; 300 | /* End PBXTargetDependency section */ 301 | 302 | /* Begin PBXVariantGroup section */ 303 | 42AD06E91E1D18C400A5C840 /* LaunchScreen.storyboard */ = { 304 | isa = PBXVariantGroup; 305 | children = ( 306 | 42AD06EA1E1D18C400A5C840 /* Base */, 307 | ); 308 | name = LaunchScreen.storyboard; 309 | sourceTree = ""; 310 | }; 311 | 42AD06EB1E1D18C400A5C840 /* Main.storyboard */ = { 312 | isa = PBXVariantGroup; 313 | children = ( 314 | 42AD06EC1E1D18C400A5C840 /* Base */, 315 | ); 316 | name = Main.storyboard; 317 | sourceTree = ""; 318 | }; 319 | /* End PBXVariantGroup section */ 320 | 321 | /* Begin XCBuildConfiguration section */ 322 | 4237B6E81E0FE22A0059FF8B /* Debug */ = { 323 | isa = XCBuildConfiguration; 324 | buildSettings = { 325 | ALWAYS_SEARCH_USER_PATHS = NO; 326 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 327 | CLANG_ANALYZER_NONNULL = YES; 328 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 329 | CLANG_CXX_LIBRARY = "libc++"; 330 | CLANG_ENABLE_MODULES = YES; 331 | CLANG_ENABLE_OBJC_ARC = YES; 332 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 333 | CLANG_WARN_BOOL_CONVERSION = YES; 334 | CLANG_WARN_COMMA = YES; 335 | CLANG_WARN_CONSTANT_CONVERSION = YES; 336 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 337 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 338 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 339 | CLANG_WARN_EMPTY_BODY = YES; 340 | CLANG_WARN_ENUM_CONVERSION = YES; 341 | CLANG_WARN_INFINITE_RECURSION = YES; 342 | CLANG_WARN_INT_CONVERSION = YES; 343 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 344 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 345 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 346 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 347 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 348 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 349 | CLANG_WARN_STRICT_PROTOTYPES = YES; 350 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 351 | CLANG_WARN_UNREACHABLE_CODE = YES; 352 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 353 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 354 | COPY_PHASE_STRIP = NO; 355 | DEBUG_INFORMATION_FORMAT = dwarf; 356 | ENABLE_STRICT_OBJC_MSGSEND = YES; 357 | ENABLE_TESTABILITY = YES; 358 | GCC_C_LANGUAGE_STANDARD = gnu99; 359 | GCC_DYNAMIC_NO_PIC = NO; 360 | GCC_NO_COMMON_BLOCKS = YES; 361 | GCC_OPTIMIZATION_LEVEL = 0; 362 | GCC_PREPROCESSOR_DEFINITIONS = ( 363 | "DEBUG=1", 364 | "$(inherited)", 365 | ); 366 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 367 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 368 | GCC_WARN_UNDECLARED_SELECTOR = YES; 369 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 370 | GCC_WARN_UNUSED_FUNCTION = YES; 371 | GCC_WARN_UNUSED_VARIABLE = YES; 372 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 373 | MTL_ENABLE_DEBUG_INFO = YES; 374 | ONLY_ACTIVE_ARCH = YES; 375 | SDKROOT = iphoneos; 376 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 377 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 378 | TARGETED_DEVICE_FAMILY = "1,2"; 379 | }; 380 | name = Debug; 381 | }; 382 | 4237B6E91E0FE22A0059FF8B /* Release */ = { 383 | isa = XCBuildConfiguration; 384 | buildSettings = { 385 | ALWAYS_SEARCH_USER_PATHS = NO; 386 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 387 | CLANG_ANALYZER_NONNULL = YES; 388 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 389 | CLANG_CXX_LIBRARY = "libc++"; 390 | CLANG_ENABLE_MODULES = YES; 391 | CLANG_ENABLE_OBJC_ARC = YES; 392 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 393 | CLANG_WARN_BOOL_CONVERSION = YES; 394 | CLANG_WARN_COMMA = YES; 395 | CLANG_WARN_CONSTANT_CONVERSION = YES; 396 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 397 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 398 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 399 | CLANG_WARN_EMPTY_BODY = YES; 400 | CLANG_WARN_ENUM_CONVERSION = YES; 401 | CLANG_WARN_INFINITE_RECURSION = YES; 402 | CLANG_WARN_INT_CONVERSION = YES; 403 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 404 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 405 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 406 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 407 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 408 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 409 | CLANG_WARN_STRICT_PROTOTYPES = YES; 410 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 411 | CLANG_WARN_UNREACHABLE_CODE = YES; 412 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 413 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 414 | COPY_PHASE_STRIP = NO; 415 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 416 | ENABLE_NS_ASSERTIONS = NO; 417 | ENABLE_STRICT_OBJC_MSGSEND = YES; 418 | GCC_C_LANGUAGE_STANDARD = gnu99; 419 | GCC_NO_COMMON_BLOCKS = YES; 420 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 421 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 422 | GCC_WARN_UNDECLARED_SELECTOR = YES; 423 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 424 | GCC_WARN_UNUSED_FUNCTION = YES; 425 | GCC_WARN_UNUSED_VARIABLE = YES; 426 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 427 | MTL_ENABLE_DEBUG_INFO = NO; 428 | SDKROOT = iphoneos; 429 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 430 | TARGETED_DEVICE_FAMILY = "1,2"; 431 | VALIDATE_PRODUCT = YES; 432 | }; 433 | name = Release; 434 | }; 435 | 4237B6EB1E0FE22A0059FF8B /* Debug */ = { 436 | isa = XCBuildConfiguration; 437 | buildSettings = { 438 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 439 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 440 | DEVELOPMENT_TEAM = RU64C364MT; 441 | INFOPLIST_FILE = "GDPerformanceView-Swift/Info.plist"; 442 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 443 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 444 | MARKETING_VERSION = 2.1.1; 445 | PRODUCT_BUNDLE_IDENTIFIER = "GD.GDPerformanceView-Swift"; 446 | PRODUCT_NAME = "$(TARGET_NAME)"; 447 | SWIFT_VERSION = 5.0; 448 | }; 449 | name = Debug; 450 | }; 451 | 4237B6EC1E0FE22A0059FF8B /* Release */ = { 452 | isa = XCBuildConfiguration; 453 | buildSettings = { 454 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 455 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 456 | DEVELOPMENT_TEAM = RU64C364MT; 457 | INFOPLIST_FILE = "GDPerformanceView-Swift/Info.plist"; 458 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 459 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 460 | MARKETING_VERSION = 2.1.1; 461 | PRODUCT_BUNDLE_IDENTIFIER = "GD.GDPerformanceView-Swift"; 462 | PRODUCT_NAME = "$(TARGET_NAME)"; 463 | SWIFT_VERSION = 5.0; 464 | }; 465 | name = Release; 466 | }; 467 | 42C2CBEE1E244D6300A96A93 /* Debug */ = { 468 | isa = XCBuildConfiguration; 469 | buildSettings = { 470 | CODE_SIGN_IDENTITY = ""; 471 | CURRENT_PROJECT_VERSION = 1; 472 | DEFINES_MODULE = YES; 473 | DEVELOPMENT_TEAM = RU64C364MT; 474 | DYLIB_COMPATIBILITY_VERSION = 1; 475 | DYLIB_CURRENT_VERSION = 1; 476 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 477 | INFOPLIST_FILE = "GDPerformanceView-iOS/Info.plist"; 478 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 479 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 480 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 481 | MARKETING_VERSION = 2.1.1; 482 | PRODUCT_BUNDLE_IDENTIFIER = "GD.GDPerformanceView-iOS"; 483 | PRODUCT_NAME = GDPerformanceView; 484 | SKIP_INSTALL = YES; 485 | SUPPORTS_MACCATALYST = NO; 486 | SWIFT_VERSION = 5.0; 487 | VERSIONING_SYSTEM = "apple-generic"; 488 | VERSION_INFO_PREFIX = ""; 489 | }; 490 | name = Debug; 491 | }; 492 | 42C2CBEF1E244D6300A96A93 /* Release */ = { 493 | isa = XCBuildConfiguration; 494 | buildSettings = { 495 | CODE_SIGN_IDENTITY = ""; 496 | CURRENT_PROJECT_VERSION = 1; 497 | DEFINES_MODULE = YES; 498 | DEVELOPMENT_TEAM = RU64C364MT; 499 | DYLIB_COMPATIBILITY_VERSION = 1; 500 | DYLIB_CURRENT_VERSION = 1; 501 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 502 | INFOPLIST_FILE = "GDPerformanceView-iOS/Info.plist"; 503 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 504 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 505 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 506 | MARKETING_VERSION = 2.1.1; 507 | PRODUCT_BUNDLE_IDENTIFIER = "GD.GDPerformanceView-iOS"; 508 | PRODUCT_NAME = GDPerformanceView; 509 | SKIP_INSTALL = YES; 510 | SUPPORTS_MACCATALYST = NO; 511 | SWIFT_VERSION = 5.0; 512 | VERSIONING_SYSTEM = "apple-generic"; 513 | VERSION_INFO_PREFIX = ""; 514 | }; 515 | name = Release; 516 | }; 517 | /* End XCBuildConfiguration section */ 518 | 519 | /* Begin XCConfigurationList section */ 520 | 4237B6D31E0FE2290059FF8B /* Build configuration list for PBXProject "GDPerformanceView-Swift" */ = { 521 | isa = XCConfigurationList; 522 | buildConfigurations = ( 523 | 4237B6E81E0FE22A0059FF8B /* Debug */, 524 | 4237B6E91E0FE22A0059FF8B /* Release */, 525 | ); 526 | defaultConfigurationIsVisible = 0; 527 | defaultConfigurationName = Release; 528 | }; 529 | 4237B6EA1E0FE22A0059FF8B /* Build configuration list for PBXNativeTarget "GDPerformanceView-Swift" */ = { 530 | isa = XCConfigurationList; 531 | buildConfigurations = ( 532 | 4237B6EB1E0FE22A0059FF8B /* Debug */, 533 | 4237B6EC1E0FE22A0059FF8B /* Release */, 534 | ); 535 | defaultConfigurationIsVisible = 0; 536 | defaultConfigurationName = Release; 537 | }; 538 | 42C2CBED1E244D6300A96A93 /* Build configuration list for PBXNativeTarget "GDPerformanceView-iOS" */ = { 539 | isa = XCConfigurationList; 540 | buildConfigurations = ( 541 | 42C2CBEE1E244D6300A96A93 /* Debug */, 542 | 42C2CBEF1E244D6300A96A93 /* Release */, 543 | ); 544 | defaultConfigurationIsVisible = 0; 545 | defaultConfigurationName = Release; 546 | }; 547 | /* End XCConfigurationList section */ 548 | }; 549 | rootObject = 4237B6D01E0FE2290059FF8B /* Project object */; 550 | } 551 | -------------------------------------------------------------------------------- /GDPerformanceView-Swift.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /GDPerformanceView-Swift.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /GDPerformanceView-Swift.xcodeproj/xcshareddata/xcschemes/GDPerformanceView-iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 52 | 53 | 59 | 60 | 66 | 67 | 68 | 69 | 71 | 72 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /GDPerformanceView-Swift/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright © 2017 Gavrilov Daniil 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | // 22 | 23 | import UIKit 24 | 25 | @UIApplicationMain 26 | class AppDelegate: UIResponder, UIApplicationDelegate { 27 | 28 | var window: UIWindow? 29 | 30 | var performanceView: PerformanceMonitor? 31 | 32 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 33 | 34 | #if DEBUG 35 | PerformanceMonitor.shared().start() 36 | #endif 37 | 38 | return true 39 | } 40 | 41 | func applicationWillResignActive(_ application: UIApplication) { 42 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 43 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 44 | } 45 | 46 | func applicationDidEnterBackground(_ application: UIApplication) { 47 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 48 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 49 | } 50 | 51 | func applicationWillEnterForeground(_ application: UIApplication) { 52 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 53 | } 54 | 55 | func applicationDidBecomeActive(_ application: UIApplication) { 56 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 57 | } 58 | 59 | func applicationWillTerminate(_ application: UIApplication) { 60 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /GDPerformanceView-Swift/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /GDPerformanceView-Swift/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /GDPerformanceView-Swift/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /GDPerformanceView-Swift/GDPerformanceMonitoring/LinkedFramesList.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright © 2017 Gavrilov Daniil 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | // 22 | 23 | import Foundation 24 | 25 | // MARK: Class Definition 26 | 27 | /// Linked list node. Represents frame timestamp. 28 | internal class FrameNode { 29 | 30 | // MARK: Public Properties 31 | 32 | var next: FrameNode? 33 | weak var previous: FrameNode? 34 | 35 | private(set) var timestamp: TimeInterval 36 | 37 | /// Initializes linked list node with parameters. 38 | /// 39 | /// - Parameter timeInterval: Frame timestamp. 40 | public init(timestamp: TimeInterval) { 41 | self.timestamp = timestamp 42 | } 43 | } 44 | 45 | // MARK: Class Definition 46 | 47 | /// Linked list. Each node represents frame timestamp. 48 | /// The only function is append, which will add a new frame and remove all frames older than a second from the last timestamp. 49 | /// As a result, the number of items in the list will represent the number of frames for the last second. 50 | internal class LinkedFramesList { 51 | 52 | // MARK: Private Properties 53 | 54 | private var head: FrameNode? 55 | private var tail: FrameNode? 56 | 57 | // MARK: Public Properties 58 | 59 | private(set) var count = 0 60 | } 61 | 62 | // MARK: Public Methods 63 | 64 | internal extension LinkedFramesList { 65 | /// Appends new frame with parameters. 66 | /// 67 | /// - Parameter timestamp: New frame timestamp. 68 | func append(frameWithTimestamp timestamp: TimeInterval) { 69 | let newNode = FrameNode(timestamp: timestamp) 70 | if let lastNode = self.tail { 71 | newNode.previous = lastNode 72 | lastNode.next = newNode 73 | self.tail = newNode 74 | } else { 75 | self.head = newNode 76 | self.tail = newNode 77 | } 78 | 79 | self.count += 1 80 | self.removeFrameNodes(olderThanTimestampMoreThanSecond: timestamp) 81 | } 82 | } 83 | 84 | // MARK: Support Methods 85 | 86 | private extension LinkedFramesList { 87 | func removeFrameNodes(olderThanTimestampMoreThanSecond timestamp: TimeInterval) { 88 | while let firstNode = self.head { 89 | guard timestamp - firstNode.timestamp > 1.0 else { 90 | break 91 | } 92 | 93 | let nextNode = firstNode.next 94 | nextNode?.previous = nil 95 | firstNode.next = nil 96 | self.head = nextNode 97 | 98 | self.count -= 1 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /GDPerformanceView-Swift/GDPerformanceMonitoring/MarginLabel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright © 2017 Gavrilov Daniil 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | // 22 | 23 | import UIKit 24 | 25 | // MARK: Class Definition 26 | 27 | /// Label indented from the edge on the left and right. 28 | internal class MarginLabel: UILabel { 29 | 30 | // MARK: Private Properties 31 | 32 | private var edgeInsets = UIEdgeInsets.init(top: 0.0, left: 5.0, bottom: 0.0, right: 5.0) 33 | 34 | // MARK: Properties Overriders 35 | 36 | override internal var intrinsicContentSize: CGSize { 37 | get { 38 | var size = super.intrinsicContentSize 39 | size.width += self.edgeInsets.left + self.edgeInsets.right 40 | size.height += self.edgeInsets.top + self.edgeInsets.bottom 41 | return size 42 | } 43 | } 44 | 45 | // MARK: Init Methods & Superclass Overriders 46 | 47 | override func drawText(in rect: CGRect) { 48 | super.drawText(in: rect.inset(by: self.edgeInsets)) 49 | } 50 | 51 | override func sizeThatFits(_ size: CGSize) -> CGSize { 52 | var sizeThatFits = super.sizeThatFits(size) 53 | sizeThatFits.width += self.edgeInsets.left + self.edgeInsets.right 54 | sizeThatFits.height += self.edgeInsets.top + self.edgeInsets.bottom 55 | return sizeThatFits 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /GDPerformanceView-Swift/GDPerformanceMonitoring/PerformanceMonitor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright © 2017 Gavrilov Daniil 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | // 22 | 23 | import UIKit 24 | 25 | // MARK: Class Definition 26 | 27 | public class PerformanceMonitor { 28 | 29 | // MARK: Enums 30 | 31 | public enum Style { 32 | case dark 33 | case light 34 | case custom(backgroundColor: UIColor, borderColor: UIColor, borderWidth: CGFloat, cornerRadius: CGFloat, textColor: UIColor, font: UIFont) 35 | } 36 | 37 | public enum UserInfo { 38 | case none 39 | case custom(string: String) 40 | } 41 | 42 | private enum States { 43 | case started 44 | case paused 45 | case pausedBySystem 46 | } 47 | 48 | // MARK: Structs 49 | 50 | public struct DisplayOptions: OptionSet { 51 | public let rawValue: Int 52 | 53 | /// CPU usage and FPS. 54 | public static let performance = DisplayOptions(rawValue: 1 << 0) 55 | 56 | /// Memory usage. 57 | public static let memory = DisplayOptions(rawValue: 1 << 1) 58 | 59 | /// Application version with build number. 60 | public static let application = DisplayOptions(rawValue: 1 << 2) 61 | 62 | /// Device model. 63 | public static let device = DisplayOptions(rawValue: 1 << 3) 64 | 65 | /// System name with version. 66 | public static let system = DisplayOptions(rawValue: 1 << 4) 67 | 68 | /// Default dispaly options - CPU usage and FPS, application version with build number and system name with version. 69 | public static let `default`: DisplayOptions = [.performance, .application, .system] 70 | 71 | /// All dispaly options. 72 | public static let all: DisplayOptions = [.performance, .memory, .application, .device, .system] 73 | 74 | public init(rawValue: Int) { 75 | self.rawValue = rawValue 76 | } 77 | } 78 | 79 | // MARK: Public Properties 80 | 81 | public weak var delegate: PerformanceMonitorDelegate? 82 | 83 | public var performanceViewConfigurator: PerformanceViewConfigurator { 84 | get { 85 | return self.performanceView 86 | } 87 | set { } 88 | } 89 | 90 | public var statusBarConfigurator: StatusBarConfigurator { 91 | get { 92 | guard let rootViewController = self.performanceView.rootViewController as? WindowViewController else { 93 | fatalError("Root view controller must be a kind of WindowViewController.") 94 | } 95 | return rootViewController 96 | } 97 | set { } 98 | } 99 | 100 | // MARK: Private Properties 101 | 102 | private static var sharedPerformanceMonitor: PerformanceMonitor! 103 | 104 | private let performanceView = PerformanceView() 105 | private let performanceCalculator = PerformanceCalculator() 106 | private var state = States.paused 107 | 108 | // MARK: Init Methods & Superclass Overriders 109 | 110 | /// Initializes performance monitor with parameters. 111 | /// 112 | /// - Parameters: 113 | /// - options: Display options. Allows to change the format of the displayed information. 114 | /// - style: Style. Allows to change the appearance of the displayed information. 115 | /// - delegate: Performance monitor output. 116 | required public init(options: DisplayOptions = .default, style: Style = .dark, delegate: PerformanceMonitorDelegate? = nil) { 117 | self.performanceView.options = options 118 | self.performanceView.style = style 119 | 120 | self.performanceCalculator.onReport = { [weak self] (performanceReport) in 121 | DispatchQueue.main.async { 122 | self?.apply(performanceReport: performanceReport) 123 | } 124 | } 125 | 126 | self.delegate = delegate 127 | self.subscribeToNotifications() 128 | } 129 | 130 | /// Initializes performance monitor singleton with default properties. 131 | /// 132 | /// - Returns: Performance monitor singleton. 133 | public class func shared() -> PerformanceMonitor { 134 | if self.sharedPerformanceMonitor == nil { 135 | self.sharedPerformanceMonitor = PerformanceMonitor() 136 | } 137 | return self.sharedPerformanceMonitor 138 | } 139 | 140 | deinit { 141 | NotificationCenter.default.removeObserver(self) 142 | } 143 | } 144 | 145 | // MARK: Public Methods 146 | 147 | public extension PerformanceMonitor { 148 | func hide() { 149 | self.performanceView.hide() 150 | } 151 | 152 | func show() { 153 | self.performanceView.show() 154 | } 155 | 156 | func start() { 157 | switch self.state { 158 | case .started: 159 | return 160 | case .paused, .pausedBySystem: 161 | self.state = .started 162 | self.performanceCalculator.start() 163 | } 164 | } 165 | 166 | func pause() { 167 | switch self.state { 168 | case .paused: 169 | return 170 | case .started, .pausedBySystem: 171 | self.state = .paused 172 | self.performanceCalculator.pause() 173 | } 174 | } 175 | } 176 | 177 | // MARK: Notifications & Observers 178 | 179 | private extension PerformanceMonitor { 180 | func applicationWillEnterForegroundNotification(notification: Notification) { 181 | switch self.state { 182 | case .started, .paused: 183 | return 184 | case .pausedBySystem: 185 | self.state = .started 186 | self.performanceCalculator.start() 187 | } 188 | } 189 | 190 | func applicationDidEnterBackgroundNotification(notification: Notification) { 191 | switch self.state { 192 | case .paused, .pausedBySystem: 193 | return 194 | case .started: 195 | self.state = .pausedBySystem 196 | self.performanceCalculator.pause() 197 | } 198 | } 199 | } 200 | 201 | // MARK: Configurations 202 | 203 | private extension PerformanceMonitor { 204 | func subscribeToNotifications() { 205 | NotificationCenter.default.addObserver(forName: UIApplication.willEnterForegroundNotification, object: nil, queue: .main) { [weak self] (notification) in 206 | self?.applicationWillEnterForegroundNotification(notification: notification) 207 | } 208 | 209 | NotificationCenter.default.addObserver(forName: UIApplication.didEnterBackgroundNotification, object: nil, queue: .main) { [weak self] (notification) in 210 | self?.applicationDidEnterBackgroundNotification(notification: notification) 211 | } 212 | } 213 | } 214 | 215 | // MARK: Support Methods 216 | 217 | private extension PerformanceMonitor { 218 | func apply(performanceReport: PerformanceReport) { 219 | self.performanceView.update(withPerformanceReport: performanceReport) 220 | self.delegate?.performanceMonitor(didReport: performanceReport) 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /GDPerformanceView-Swift/GDPerformanceMonitoring/PerformanceView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright © 2017 Gavrilov Daniil 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | // 22 | 23 | import UIKit 24 | 25 | // MARK: Class Definition 26 | 27 | /// Performance view. Displays performance information above status bar. Appearance and output can be changed via properties. 28 | internal class PerformanceView: UIWindow, PerformanceViewConfigurator { 29 | 30 | // MARK: Structs 31 | 32 | private struct Constants { 33 | static let prefferedHeight: CGFloat = 20.0 34 | static let borderWidth: CGFloat = 1.0 35 | static let cornerRadius: CGFloat = 5.0 36 | static let pointSize: CGFloat = 8.0 37 | static let defaultStatusBarHeight: CGFloat = 20.0 38 | static let safeAreaInsetDifference: CGFloat = 11.0 39 | } 40 | 41 | // MARK: Public Properties 42 | 43 | /// Allows to change the format of the displayed information. 44 | public var options = PerformanceMonitor.DisplayOptions.default { 45 | didSet { 46 | self.configureStaticInformation() 47 | } 48 | } 49 | 50 | public var userInfo = PerformanceMonitor.UserInfo.none { 51 | didSet { 52 | self.configureUserInformation() 53 | } 54 | } 55 | 56 | /// Allows to change the appearance of the displayed information. 57 | public var style = PerformanceMonitor.Style.dark { 58 | didSet { 59 | self.configureView(withStyle: self.style) 60 | } 61 | } 62 | 63 | /// Allows to add gesture recognizers to the view. 64 | public var interactors: [UIGestureRecognizer]? { 65 | didSet { 66 | self.configureView(withInteractors: self.interactors) 67 | } 68 | } 69 | 70 | // MARK: Private Properties 71 | 72 | private let monitoringTextLabel = MarginLabel() 73 | private var staticInformation: String? 74 | private var userInformation: String? 75 | 76 | // MARK: Init Methods & Superclass Overriders 77 | 78 | required internal init() { 79 | super.init(frame: PerformanceView.windowFrame(withPrefferedHeight: Constants.prefferedHeight)) 80 | if #available(iOS 13, *) { 81 | self.windowScene = PerformanceView.keyWindowScene() 82 | } 83 | 84 | self.configureWindow() 85 | self.configureMonitoringTextLabel() 86 | self.subscribeToNotifications() 87 | } 88 | 89 | required init?(coder aDecoder: NSCoder) { 90 | super.init(coder: aDecoder) 91 | } 92 | 93 | override func layoutSubviews() { 94 | super.layoutSubviews() 95 | 96 | self.layoutWindow() 97 | } 98 | 99 | override func becomeKey() { 100 | self.isHidden = true 101 | 102 | DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1)) { 103 | self.showViewAboveStatusBarIfNeeded() 104 | } 105 | } 106 | 107 | override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { 108 | guard let interactors = self.interactors, interactors.count > 0 else { 109 | return false 110 | } 111 | return super.point(inside: point, with: event) 112 | } 113 | 114 | deinit { 115 | NotificationCenter.default.removeObserver(self) 116 | } 117 | } 118 | 119 | // MARK: Public Methods 120 | 121 | internal extension PerformanceView { 122 | /// Hides monitoring view. 123 | func hide() { 124 | self.monitoringTextLabel.isHidden = true 125 | } 126 | 127 | /// Shows monitoring view. 128 | func show() { 129 | self.monitoringTextLabel.isHidden = false 130 | } 131 | 132 | /// Updates monitoring label with performance report. 133 | /// 134 | /// - Parameter report: Performance report. 135 | func update(withPerformanceReport report: PerformanceReport) { 136 | var monitoringTexts: [String] = [] 137 | if self.options.contains(.performance) { 138 | let performance = String(format: "CPU: %.1f%%, FPS: %d", report.cpuUsage, report.fps) 139 | monitoringTexts.append(performance) 140 | } 141 | 142 | if self.options.contains(.memory) { 143 | let bytesInMegabyte = 1024.0 * 1024.0 144 | let usedMemory = Double(report.memoryUsage.used) / bytesInMegabyte 145 | let totalMemory = Double(report.memoryUsage.total) / bytesInMegabyte 146 | let memory = String(format: "%.1f of %.0f MB used", usedMemory, totalMemory) 147 | monitoringTexts.append(memory) 148 | } 149 | 150 | if let staticInformation = self.staticInformation { 151 | monitoringTexts.append(staticInformation) 152 | } 153 | 154 | if let userInformation = self.userInformation { 155 | monitoringTexts.append(userInformation) 156 | } 157 | 158 | self.monitoringTextLabel.text = (monitoringTexts.count > 0 ? monitoringTexts.joined(separator: "\n") : nil) 159 | self.showViewAboveStatusBarIfNeeded() 160 | self.layoutMonitoringLabel() 161 | } 162 | } 163 | 164 | // MARK: Notifications & Observers 165 | 166 | private extension PerformanceView { 167 | func applicationWillChangeStatusBarFrame(notification: Notification) { 168 | self.layoutWindow() 169 | } 170 | } 171 | 172 | // MARK: Configurations 173 | 174 | private extension PerformanceView { 175 | func configureWindow() { 176 | self.rootViewController = WindowViewController() 177 | self.windowLevel = UIWindow.Level.statusBar + 1.0 178 | self.backgroundColor = .clear 179 | self.clipsToBounds = true 180 | self.isHidden = true 181 | } 182 | 183 | func configureMonitoringTextLabel() { 184 | self.monitoringTextLabel.textAlignment = NSTextAlignment.center 185 | self.monitoringTextLabel.numberOfLines = 0 186 | self.monitoringTextLabel.clipsToBounds = true 187 | self.addSubview(self.monitoringTextLabel) 188 | } 189 | 190 | func configureStaticInformation() { 191 | var staticInformations: [String] = [] 192 | if self.options.contains(.application) { 193 | let applicationVersion = self.applicationVersion() 194 | staticInformations.append(applicationVersion) 195 | } 196 | if self.options.contains(.device) { 197 | let deviceModel = self.deviceModel() 198 | staticInformations.append(deviceModel) 199 | } 200 | if self.options.contains(.system) { 201 | let systemVersion = self.systemVersion() 202 | staticInformations.append(systemVersion) 203 | } 204 | 205 | self.staticInformation = (staticInformations.count > 0 ? staticInformations.joined(separator: ", ") : nil) 206 | } 207 | 208 | func configureUserInformation() { 209 | var staticInformation: String? 210 | switch self.userInfo { 211 | case .none: 212 | break 213 | case .custom(let string): 214 | staticInformation = string 215 | } 216 | 217 | self.userInformation = staticInformation 218 | } 219 | 220 | func subscribeToNotifications() { 221 | NotificationCenter.default.addObserver(forName: UIApplication.willChangeStatusBarFrameNotification, object: nil, queue: .main) { [weak self] (notification) in 222 | self?.applicationWillChangeStatusBarFrame(notification: notification) 223 | } 224 | } 225 | 226 | func configureView(withStyle style: PerformanceMonitor.Style) { 227 | switch style { 228 | case .dark: 229 | self.monitoringTextLabel.backgroundColor = .black 230 | self.monitoringTextLabel.layer.borderColor = UIColor.white.cgColor 231 | self.monitoringTextLabel.layer.borderWidth = Constants.borderWidth 232 | self.monitoringTextLabel.layer.cornerRadius = Constants.cornerRadius 233 | self.monitoringTextLabel.textColor = .white 234 | self.monitoringTextLabel.font = UIFont.systemFont(ofSize: Constants.pointSize) 235 | case .light: 236 | self.monitoringTextLabel.backgroundColor = .white 237 | self.monitoringTextLabel.layer.borderColor = UIColor.black.cgColor 238 | self.monitoringTextLabel.layer.borderWidth = Constants.borderWidth 239 | self.monitoringTextLabel.layer.cornerRadius = Constants.cornerRadius 240 | self.monitoringTextLabel.textColor = .black 241 | self.monitoringTextLabel.font = UIFont.systemFont(ofSize: Constants.pointSize) 242 | case .custom(let backgroundColor, let borderColor, let borderWidth, let cornerRadius, let textColor, let font): 243 | self.monitoringTextLabel.backgroundColor = backgroundColor 244 | self.monitoringTextLabel.layer.borderColor = borderColor.cgColor 245 | self.monitoringTextLabel.layer.borderWidth = borderWidth 246 | self.monitoringTextLabel.layer.cornerRadius = cornerRadius 247 | self.monitoringTextLabel.textColor = textColor 248 | self.monitoringTextLabel.font = font 249 | } 250 | } 251 | 252 | func configureView(withInteractors interactors: [UIGestureRecognizer]?) { 253 | if let recognizers = self.gestureRecognizers { 254 | for recognizer in recognizers { 255 | self.removeGestureRecognizer(recognizer) 256 | } 257 | } 258 | 259 | if let recognizers = interactors { 260 | for recognizer in recognizers { 261 | self.addGestureRecognizer(recognizer) 262 | } 263 | } 264 | } 265 | } 266 | 267 | // MARK: Layout View 268 | 269 | private extension PerformanceView { 270 | func layoutWindow() { 271 | self.frame = PerformanceView.windowFrame(withPrefferedHeight: self.monitoringTextLabel.bounds.height) 272 | self.layoutMonitoringLabel() 273 | } 274 | 275 | func layoutMonitoringLabel() { 276 | let windowWidth = self.bounds.width 277 | let windowHeight = self.bounds.height 278 | let labelSize = self.monitoringTextLabel.sizeThatFits(CGSize(width: windowWidth, height: CGFloat.greatestFiniteMagnitude)) 279 | 280 | if windowHeight != labelSize.height { 281 | self.frame = PerformanceView.windowFrame(withPrefferedHeight: self.monitoringTextLabel.bounds.height) 282 | } 283 | 284 | self.monitoringTextLabel.frame = CGRect(x: (windowWidth - labelSize.width) / 2.0, y: (windowHeight - labelSize.height) / 2.0, width: labelSize.width, height: labelSize.height) 285 | } 286 | } 287 | 288 | // MARK: Support Methods 289 | 290 | private extension PerformanceView { 291 | func showViewAboveStatusBarIfNeeded() { 292 | guard UIApplication.shared.applicationState == UIApplication.State.active, self.canBeVisible(), self.isHidden else { 293 | return 294 | } 295 | self.isHidden = false 296 | } 297 | 298 | func applicationVersion() -> String { 299 | var applicationVersion = "" 300 | var applicationBuildNumber = "" 301 | if let infoDictionary = Bundle.main.infoDictionary { 302 | if let versionNumber = infoDictionary["CFBundleShortVersionString"] as? String { 303 | applicationVersion = versionNumber 304 | } 305 | if let buildNumber = infoDictionary["CFBundleVersion"] as? String { 306 | applicationBuildNumber = buildNumber 307 | } 308 | } 309 | return "app v\(applicationVersion) (\(applicationBuildNumber))" 310 | } 311 | 312 | func deviceModel() -> String { 313 | var systemInfo = utsname() 314 | uname(&systemInfo) 315 | let machineMirror = Mirror(reflecting: systemInfo.machine) 316 | let model = machineMirror.children.reduce("") { identifier, element in 317 | guard let value = element.value as? Int8, value != 0 else { 318 | return identifier 319 | } 320 | return identifier + String(UnicodeScalar(UInt8(value))) 321 | } 322 | return model 323 | } 324 | 325 | func systemVersion() -> String { 326 | let systemName = UIDevice.current.systemName 327 | let systemVersion = UIDevice.current.systemVersion 328 | return "\(systemName) v\(systemVersion)" 329 | } 330 | 331 | func canBeVisible() -> Bool { 332 | if let window = PerformanceView.keyWindow(), window.isKeyWindow, !window.isHidden { 333 | return true 334 | } 335 | return false 336 | } 337 | } 338 | 339 | // MARK: Class Methods 340 | 341 | private extension PerformanceView { 342 | class func windowFrame(withPrefferedHeight height: CGFloat) -> CGRect { 343 | guard let window = PerformanceView.keyWindow() else { 344 | return .zero 345 | } 346 | 347 | var topInset: CGFloat = 0.0 348 | if #available(iOS 11.0, *), let safeAreaTop = window.rootViewController?.view.safeAreaInsets.top { 349 | if safeAreaTop > 0.0 { 350 | if safeAreaTop > Constants.defaultStatusBarHeight { 351 | topInset = safeAreaTop - Constants.safeAreaInsetDifference 352 | } else { 353 | topInset = safeAreaTop - Constants.defaultStatusBarHeight 354 | } 355 | } else { 356 | topInset = safeAreaTop 357 | } 358 | } 359 | return CGRect(x: 0.0, y: topInset, width: window.bounds.width, height: height) 360 | } 361 | 362 | class func keyWindow() -> UIWindow? { 363 | if #available(iOS 13, *) { 364 | return UIApplication.shared.windows.first(where: { $0.isKeyWindow }) 365 | } else { 366 | return UIApplication.shared.keyWindow 367 | } 368 | } 369 | 370 | @available(iOS 13, *) 371 | class func keyWindowScene() -> UIWindowScene? { 372 | return UIApplication.shared.connectedScenes 373 | .filter { $0.activationState == .foregroundActive } 374 | .first as? UIWindowScene 375 | } 376 | } 377 | -------------------------------------------------------------------------------- /GDPerformanceView-Swift/GDPerformanceMonitoring/PerformanceСalculator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright © 2017 Gavrilov Daniil 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | // 22 | 23 | import QuartzCore 24 | 25 | // MARK: Class Definition 26 | 27 | /// Performance calculator. Uses CADisplayLink to count FPS. Also counts CPU and memory usage. 28 | internal class PerformanceCalculator { 29 | 30 | // MARK: Structs 31 | 32 | private struct Constants { 33 | static let accumulationTimeInSeconds = 1.0 34 | } 35 | 36 | // MARK: Internal Properties 37 | 38 | internal var onReport: ((_ performanceReport: PerformanceReport) -> ())? 39 | 40 | // MARK: Private Properties 41 | 42 | private var displayLink: CADisplayLink! 43 | private let linkedFramesList = LinkedFramesList() 44 | private var startTimestamp: TimeInterval? 45 | private var accumulatedInformationIsEnough = false 46 | 47 | // MARK: Init Methods & Superclass Overriders 48 | 49 | required internal init() { 50 | self.configureDisplayLink() 51 | } 52 | } 53 | 54 | // MARK: Public Methods 55 | 56 | internal extension PerformanceCalculator { 57 | /// Starts performance monitoring. 58 | func start() { 59 | self.startTimestamp = Date().timeIntervalSince1970 60 | self.displayLink?.isPaused = false 61 | } 62 | 63 | /// Pauses performance monitoring. 64 | func pause() { 65 | self.displayLink?.isPaused = true 66 | self.startTimestamp = nil 67 | self.accumulatedInformationIsEnough = false 68 | } 69 | } 70 | 71 | // MARK: Timer Actions 72 | 73 | private extension PerformanceCalculator { 74 | @objc func displayLinkAction(displayLink: CADisplayLink) { 75 | self.linkedFramesList.append(frameWithTimestamp: displayLink.timestamp) 76 | self.takePerformanceEvidence() 77 | } 78 | } 79 | 80 | // MARK: Monitoring 81 | 82 | private extension PerformanceCalculator { 83 | func takePerformanceEvidence() { 84 | if self.accumulatedInformationIsEnough { 85 | let cpuUsage = self.cpuUsage() 86 | let fps = self.linkedFramesList.count 87 | let memoryUsage = self.memoryUsage() 88 | self.report(cpuUsage: cpuUsage, fps: fps, memoryUsage: memoryUsage) 89 | } else if let start = self.startTimestamp, Date().timeIntervalSince1970 - start >= Constants.accumulationTimeInSeconds { 90 | self.accumulatedInformationIsEnough = true 91 | } 92 | } 93 | 94 | func cpuUsage() -> Double { 95 | var totalUsageOfCPU: Double = 0.0 96 | var threadsList: thread_act_array_t? 97 | var threadsCount = mach_msg_type_number_t(0) 98 | let threadsResult = withUnsafeMutablePointer(to: &threadsList) { 99 | return $0.withMemoryRebound(to: thread_act_array_t?.self, capacity: 1) { 100 | task_threads(mach_task_self_, $0, &threadsCount) 101 | } 102 | } 103 | 104 | if threadsResult == KERN_SUCCESS, let threadsList = threadsList { 105 | for index in 0...stride)) 126 | return totalUsageOfCPU 127 | } 128 | 129 | func memoryUsage() -> MemoryUsage { 130 | var taskInfo = task_vm_info_data_t() 131 | var count = mach_msg_type_number_t(MemoryLayout.size) / 4 132 | let result: kern_return_t = withUnsafeMutablePointer(to: &taskInfo) { 133 | $0.withMemoryRebound(to: integer_t.self, capacity: 1) { 134 | task_info(mach_task_self_, task_flavor_t(TASK_VM_INFO), $0, &count) 135 | } 136 | } 137 | 138 | var used: UInt64 = 0 139 | if result == KERN_SUCCESS { 140 | used = UInt64(taskInfo.phys_footprint) 141 | } 142 | 143 | let total = ProcessInfo.processInfo.physicalMemory 144 | return (used, total) 145 | } 146 | } 147 | 148 | // MARK: Configurations 149 | 150 | private extension PerformanceCalculator { 151 | func configureDisplayLink() { 152 | self.displayLink = CADisplayLink(target: self, selector: #selector(PerformanceCalculator.displayLinkAction(displayLink:))) 153 | self.displayLink.isPaused = true 154 | self.displayLink?.add(to: .current, forMode: .common) 155 | } 156 | } 157 | 158 | // MARK: Support Methods 159 | 160 | private extension PerformanceCalculator { 161 | func report(cpuUsage: Double, fps: Int, memoryUsage: MemoryUsage) { 162 | let performanceReport = (cpuUsage: cpuUsage, fps: fps, memoryUsage: memoryUsage) 163 | self.onReport?(performanceReport) 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /GDPerformanceView-Swift/GDPerformanceMonitoring/Protocols.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright © 2017 Gavrilov Daniil 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | // 22 | 23 | import UIKit 24 | 25 | /// Memory usage tuple. Contains used and total memory in bytes. 26 | public typealias MemoryUsage = (used: UInt64, total: UInt64) 27 | 28 | /// Performance report tuple. Contains CPU usage in percentages, FPS and memory usage. 29 | public typealias PerformanceReport = (cpuUsage: Double, fps: Int, memoryUsage: MemoryUsage) 30 | 31 | /// Performance monitor delegate. Gets called on the main thread. 32 | public protocol PerformanceMonitorDelegate: class { 33 | /// Reports monitoring information to the receiver. 34 | /// 35 | /// - Parameters: 36 | /// - performanceReport: Performance report tuple. Contains CPU usage in percentages, FPS and memory usage. 37 | func performanceMonitor(didReport performanceReport: PerformanceReport) 38 | } 39 | 40 | public protocol PerformanceViewConfigurator { 41 | var options: PerformanceMonitor.DisplayOptions { get set } 42 | var userInfo: PerformanceMonitor.UserInfo { get set } 43 | var style: PerformanceMonitor.Style { get set } 44 | var interactors: [UIGestureRecognizer]? { get set } 45 | } 46 | 47 | public protocol StatusBarConfigurator { 48 | var statusBarHidden: Bool { get set } 49 | var statusBarStyle: UIStatusBarStyle { get set } 50 | } 51 | -------------------------------------------------------------------------------- /GDPerformanceView-Swift/GDPerformanceMonitoring/WindowViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright © 2017 Gavrilov Daniil 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | // 22 | 23 | import UIKit 24 | 25 | // MARK: Class Definition 26 | 27 | /// A window controller to override the properties of the status bar so that developers can choose their own preferences. 28 | internal class WindowViewController: UIViewController, StatusBarConfigurator { 29 | 30 | // MARK: Public Properties 31 | 32 | /// Overrides prefersStatusBarHidden. 33 | public var statusBarHidden = false { 34 | didSet { 35 | self.setNeedsStatusBarAppearanceUpdate() 36 | } 37 | } 38 | 39 | /// Overrides preferredStatusBarStyle. 40 | public var statusBarStyle = UIStatusBarStyle.default { 41 | didSet { 42 | self.setNeedsStatusBarAppearanceUpdate() 43 | } 44 | } 45 | 46 | // MARK: Properties Overriders 47 | 48 | override var prefersStatusBarHidden: Bool { 49 | get { 50 | return self.statusBarHidden 51 | } 52 | } 53 | 54 | override var preferredStatusBarStyle: UIStatusBarStyle { 55 | get { 56 | return self.statusBarStyle 57 | } 58 | } 59 | 60 | // MARK: Init Methods & Superclass Overriders 61 | 62 | override func viewDidLoad() { 63 | super.viewDidLoad() 64 | 65 | self.view.backgroundColor = .clear 66 | } 67 | 68 | override func didReceiveMemoryWarning() { 69 | super.didReceiveMemoryWarning() 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /GDPerformanceView-Swift/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(MARKETING_VERSION) 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | UIInterfaceOrientationPortraitUpsideDown 37 | 38 | UISupportedInterfaceOrientations~ipad 39 | 40 | UIInterfaceOrientationPortrait 41 | UIInterfaceOrientationPortraitUpsideDown 42 | UIInterfaceOrientationLandscapeLeft 43 | UIInterfaceOrientationLandscapeRight 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /GDPerformanceView-iOS/GDPerformanceView-iOS.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright © 2017 Gavrilov Daniil 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | // 22 | 23 | #import 24 | 25 | //! Project version number for GDPerformanceView-iOS. 26 | FOUNDATION_EXPORT double GDPerformanceView_iOSVersionNumber; 27 | 28 | //! Project version string for GDPerformanceView-iOS. 29 | FOUNDATION_EXPORT const unsigned char GDPerformanceView_iOSVersionString[]; 30 | 31 | // In this header, you should import all the public headers of your framework using statements like #import 32 | -------------------------------------------------------------------------------- /GDPerformanceView-iOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | $(MARKETING_VERSION) 19 | CFBundleVersion 20 | 1 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Gavrilov Daniil 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.1 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "GDPerformanceView-Swift", 8 | platforms: [.iOS(.v8)], 9 | products: [ 10 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 11 | .library( 12 | name: "GDPerformanceView-Swift", 13 | targets: ["GDPerformanceView-Swift"]), 14 | ], 15 | dependencies: [ 16 | // Dependencies declare other packages that this package depends on. 17 | // .package(url: /* package url */, from: "1.0.0"), 18 | ], 19 | targets: [ 20 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 21 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 22 | .target( 23 | name: "GDPerformanceView-Swift", 24 | dependencies: [], 25 | path: "GDPerformanceView-Swift/GDPerformanceMonitoring"), 26 | ] 27 | ) 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GDPerformanceView-Swift 2 | Shows FPS, CPU and memory usage, device model, app and iOS versions above the status bar and report FPS, CPU and memory usage via delegate. 3 | 4 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-brightgreen.svg)](https://github.com/Carthage/Carthage) 5 | [![Pod Version](https://img.shields.io/badge/Pod-2.1.1-6193DF.svg)](https://cocoapods.org/) 6 | ![Swift Version](https://img.shields.io/badge/xCode-12.0+-blue.svg) 7 | ![Swift Version](https://img.shields.io/badge/iOS-9.0+-blue.svg) 8 | ![Swift Version](https://img.shields.io/badge/Swift-5.0+-orange.svg) 9 | ![Plaform](https://img.shields.io/badge/Platform-iOS-lightgrey.svg) 10 | ![License MIT](https://img.shields.io/badge/License-MIT-lightgrey.svg) 11 | 12 | ![Alt text](https://github.com/dani-gavrilov/GDPerformanceView-Swift/blob/master/performance_view.PNG?raw=true "Example PNG") 13 | ![Alt text](https://github.com/dani-gavrilov/GDPerformanceView-Swift/blob/master/performance_view_2.PNG?raw=true "Example PNG") 14 | ![Alt text](https://github.com/dani-gavrilov/GDPerformanceView-Swift/blob/master/performance_view_3.PNG?raw=true "Example PNG") 15 | ![Alt text](https://github.com/dani-gavrilov/GDPerformanceView-Swift/blob/master/performance_view_4.PNG?raw=true "Example PNG") 16 | 17 | ## Installation 18 | Simply add GDPerformanceMonitoring folder with files to your project, or use CocoaPods. 19 | 20 | #### Carthage 21 | Create a `Cartfile` that lists the framework and run `carthage update`. Follow the [instructions](https://github.com/Carthage/Carthage#if-youre-building-for-ios) to add `$(SRCROOT)/Carthage/Build/iOS/GDPerformanceView.framework` to an iOS project. 22 | 23 | ```ruby 24 | github "dani-gavrilov/GDPerformanceView-Swift" ~> 2.1.1 25 | ``` 26 | Don't forget to import GDPerformanceView by adding: 27 | 28 | ```swift 29 | import GDPerformanceView 30 | ``` 31 | 32 | #### CocoaPods 33 | You can use [CocoaPods](http://cocoapods.org/) to install `GDPerformanceView` by adding it to your `Podfile`: 34 | 35 | ```ruby 36 | platform :ios, '8.0' 37 | use_frameworks! 38 | 39 | target 'project_name' do 40 | pod 'GDPerformanceView-Swift', '~> 2.1.1' 41 | end 42 | ``` 43 | Don't forget to import GDPerformanceView by adding: 44 | 45 | ```swift 46 | import GDPerformanceView_Swift 47 | ``` 48 | 49 | ## Usage example 50 | 51 | Simply start monitoring. Performance view will be added above the status bar automatically. 52 | Also, you can configure appearance as you like or just hide the monitoring view and use its delegate. 53 | 54 | You can find example projects [here](https://github.com/dani-gavrilov/GDPerformanceViewExamples). 55 | 56 | #### Start monitoring 57 | 58 | By default, monitoring is paused. Call the following command to start or resume monitoring: 59 | 60 | ```swift 61 | PerformanceMonitor.shared().start() 62 | ``` 63 | or 64 | 65 | ```swift 66 | self.performanceView = PerformanceMonitor() 67 | self.performanceView?. start() 68 | ``` 69 | This won't show the monitoring view if it was hidden previously. To show it call the following command: 70 | 71 | ```swift 72 | self.performanceView?.show() 73 | ``` 74 | 75 | #### Pause monitoring 76 | 77 | Call the following command to pause monitoring: 78 | 79 | ```swift 80 | self.performanceView?.pause() 81 | ``` 82 | 83 | This won't hide the monitoring view. To hide it call the following command: 84 | 85 | ```swift 86 | self.performanceView?.hide() 87 | ``` 88 | 89 | #### Displayed information 90 | 91 | You can change displayed information by changing options of the performance monitor: 92 | 93 | ```swift 94 | self.performanceView?.performanceViewConfigurator.options = .all 95 | ``` 96 | You can choose from: 97 | 98 | * performance - CPU usage and FPS. 99 | * memory - Memory usage. 100 | * application - Application version with build number. 101 | * device - Device model. 102 | * system - System name with version. 103 | 104 | Also you can mix them, but order doesn't matter: 105 | 106 | ```swift 107 | self.performanceView?.performanceViewConfigurator.options = [.performance, .application, .system] 108 | ``` 109 | By default, set of [.performance, .application, .system] options is used. 110 | 111 | You can also add your custom information by using: 112 | 113 | ```swift 114 | self.performanceView?.performanceViewConfigurator.userInfo = .custom(string: "Launch date \(Date())") 115 | ``` 116 | Keep in mind that custom string will not automatically fit the screen, use `\n` if it is too long. 117 | 118 | #### Appearance 119 | 120 | You can change monitoring view appearance by changing style of the performance monitor: 121 | 122 | Call the following command to change output information: 123 | 124 | ```swift 125 | self.performanceView?.performanceViewConfigurator.style = .dark 126 | ``` 127 | 128 | You can choose from: 129 | 130 | * dark - Black background, white text. 131 | * light - White background, black text. 132 | * custom - You can set background color, border color, border width, corner radius, text color and font. 133 | 134 | By default, dark style is used. 135 | 136 | Also you can override prefersStatusBarHidden and preferredStatusBarStyle to match your expectations: 137 | 138 | ```swift 139 | self.performanceView?.statusBarConfigurator.statusBarHidden = false 140 | self.performanceView?.statusBarConfigurator.statusBarStyle = .lightContent 141 | ``` 142 | 143 | #### Interactions 144 | 145 | You can interact with performance view via gesture recognizers. Add them by using: 146 | 147 | ```swift 148 | self.performanceView?.performanceViewConfigurator.interactors = [tapGesture, swipeGesture] 149 | ``` 150 | If interactors is nil or empty `point(inside:with:)` of the view will return false to make all touches pass underneath. So to remove interactors just call the following command: 151 | 152 | ```swift 153 | self.performanceView?.performanceViewConfigurator.interactors = nil 154 | ``` 155 | By default, interactors are nil. 156 | 157 | #### Delegate 158 | 159 | Set the delegate and implement its method: 160 | 161 | ```swift 162 | self.performanceView?.delegate = self 163 | ``` 164 | 165 | ```swift 166 | func performanceMonitor(didReport performanceReport: PerformanceReport) { 167 | print(performanceReport.cpuUsage, performanceReport.fps, performanceReport.memoryUsage.used, performanceReport.memoryUsage.total) 168 | } 169 | ``` 170 | 171 | ## Requirements 172 | - iOS 9.0+ 173 | - xCode 12.0+ 174 | 175 | ## Donations 176 | 177 | Wanna say thanks? You can do it using [Patreon](https://www.patreon.com/dani_gavrilov). 178 | 179 | ## Meta 180 | 181 | Daniil Gavrilov - [VK](https://vk.com/dani_gavrilov) - [FB](https://facebook.com/danigavrilov) 182 | 183 | I will be pleased to know that your project uses this framework. You can send a link to your project in App Store to my email - [daniilmbox@gmail.com](mailto:daniilmbox@gmail.com). 184 | 185 | ## License 186 | 187 | GDPerformanceView is available under the MIT license. See the LICENSE file for more info. 188 | 189 | -------------------------------------------------------------------------------- /performance_view.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dani-gavrilov/GDPerformanceView-Swift/171a656040135d667f4228c3ec82f2384770d87d/performance_view.PNG -------------------------------------------------------------------------------- /performance_view_2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dani-gavrilov/GDPerformanceView-Swift/171a656040135d667f4228c3ec82f2384770d87d/performance_view_2.PNG -------------------------------------------------------------------------------- /performance_view_3.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dani-gavrilov/GDPerformanceView-Swift/171a656040135d667f4228c3ec82f2384770d87d/performance_view_3.PNG -------------------------------------------------------------------------------- /performance_view_4.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dani-gavrilov/GDPerformanceView-Swift/171a656040135d667f4228c3ec82f2384770d87d/performance_view_4.PNG --------------------------------------------------------------------------------