Skip to content

Commit

Permalink
DEMRUM-620: Add exercise of slow frame detection to test app (#236)
Browse files Browse the repository at this point in the history
* DEMRUM-620: Add exercise of slow frame detection to test app

* DEMRUM-620: swiftlint: fix trailing spaces

* DEMRUM-620: fix typos

* DEMRUM-620: swiftlint: remove weak from IBOutlet

* DEMRUM-620: swiftlint: remove needless self references and fix whitespace

* DEMRUM-620: swiftlint: sort imports alphabetically

* DEMRUM-620: swiftlint: remove weak from IBOutlet
  • Loading branch information
carlosmcevilly authored Jan 21, 2025
1 parent 7795dfe commit 367d276
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 24 deletions.
8 changes: 8 additions & 0 deletions AgentTestApp/AgentTestApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
objects = {

/* Begin PBXBuildFile section */
52705BE02D08FDF600445B8E /* SlowFrameBeatingHeartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52705BDF2D08FDF600445B8E /* SlowFrameBeatingHeartView.swift */; };
52705BE12D08FDF600445B8E /* SlowFrameDetectorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52705BDE2D08FDF600445B8E /* SlowFrameDetectorViewController.swift */; };
BA188B202BA0C91B00DACB15 /* CrashesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA188B1E2BA0C91B00DACB15 /* CrashesViewController.swift */; };
BA188B212BA0C91B00DACB15 /* Crashes.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA188B1F2BA0C91B00DACB15 /* Crashes.swift */; };
BA338B462B9A610600D2F3B6 /* TestApiCalls.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA338B452B9A610600D2F3B6 /* TestApiCalls.swift */; };
Expand Down Expand Up @@ -38,6 +40,8 @@
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
52705BDE2D08FDF600445B8E /* SlowFrameDetectorViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SlowFrameDetectorViewController.swift; sourceTree = "<group>"; };
52705BDF2D08FDF600445B8E /* SlowFrameBeatingHeartView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SlowFrameBeatingHeartView.swift; sourceTree = "<group>"; };
BA188B1E2BA0C91B00DACB15 /* CrashesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CrashesViewController.swift; sourceTree = "<group>"; };
BA188B1F2BA0C91B00DACB15 /* Crashes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Crashes.swift; sourceTree = "<group>"; };
BA338B452B9A610600D2F3B6 /* TestApiCalls.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestApiCalls.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -114,6 +118,8 @@
BA188B1E2BA0C91B00DACB15 /* CrashesViewController.swift */,
BA5E5A582B6864A60022CEB2 /* NetworkCalls.swift */,
BA5E5A5A2B6864A60022CEB2 /* NetworkCallsViewController.swift */,
52705BDE2D08FDF600445B8E /* SlowFrameDetectorViewController.swift */,
52705BDF2D08FDF600445B8E /* SlowFrameBeatingHeartView.swift */,
BA338B452B9A610600D2F3B6 /* TestApiCalls.swift */,
BA338B472B9A612400D2F3B6 /* TestApiCallsViewController.swift */,
);
Expand Down Expand Up @@ -200,6 +206,8 @@
BA188B212BA0C91B00DACB15 /* Crashes.swift in Sources */,
BA5E5A442B6859BC0022CEB2 /* AppDelegate.swift in Sources */,
BA5E5A5C2B6864A60022CEB2 /* NetworkCalls.swift in Sources */,
52705BE02D08FDF600445B8E /* SlowFrameBeatingHeartView.swift in Sources */,
52705BE12D08FDF600445B8E /* SlowFrameDetectorViewController.swift in Sources */,
BA5E5A462B6859BC0022CEB2 /* SceneDelegate.swift in Sources */,
BA188B202BA0C91B00DACB15 /* CrashesViewController.swift in Sources */,
BA5E5A5E2B6864A60022CEB2 /* NetworkCallsViewController.swift in Sources */,
Expand Down
61 changes: 59 additions & 2 deletions AgentTestApp/AgentTestApp/Base.lproj/Main.storyboard
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="7Hk-jl-eXN">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="7Hk-jl-eXN">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22684"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
Expand Down Expand Up @@ -58,6 +58,7 @@
<segue destination="3c3-jA-U3Z" kind="show" identifier="Crashes" id="Dsu-Kf-Ox9"/>
<segue destination="Xqp-MF-bVJ" kind="show" identifier="TestApiCalls" id="LdI-pp-FX3"/>
<segue destination="GJu-EY-m7h" kind="show" identifier="Crashes" id="xHt-0h-0Ur"/>
<segue destination="VBl-p7-cVN" kind="show" id="SYQ-QU-pfj"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="WeE-BH-P6e" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
Expand Down Expand Up @@ -447,6 +448,62 @@
</objects>
<point key="canvasLocation" x="-1278.2608695652175" y="677.67857142857144"/>
</scene>
<!--Slow Frame Detector View Controller-->
<scene sceneID="IIK-5X-7Hc">
<objects>
<viewController id="VBl-p7-cVN" customClass="SlowFrameDetectorViewController" customModule="AgentTestApp" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="v16-Vh-cNW">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="hXJ-VI-BDm">
<rect key="frame" x="20" y="118" width="374" height="35"/>
<color key="backgroundColor" systemColor="systemRedColor"/>
<constraints>
<constraint firstAttribute="height" constant="35" id="exa-oW-6ug"/>
</constraints>
<color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<state key="normal" title="Button"/>
<buttonConfiguration key="configuration" style="plain" title="Slow Frame Condition"/>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="gya-8W-NUT">
<rect key="frame" x="20" y="180" width="374" height="35"/>
<color key="backgroundColor" systemColor="systemRedColor"/>
<constraints>
<constraint firstAttribute="height" constant="35" id="Qjd-cc-wVg"/>
</constraints>
<color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<state key="normal" title="Button"/>
<buttonConfiguration key="configuration" style="plain" title="Frozen Frame Condition"/>
</button>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="M9G-zc-DZ7" customClass="SlowFrameBeatingHeartView" customModule="AgentTestApp" customModuleProvider="target">
<rect key="frame" x="107" y="377" width="200" height="200"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstAttribute="width" constant="200" id="0Z0-0m-DDt"/>
<constraint firstAttribute="height" constant="200" id="Xg2-fa-FrJ"/>
</constraints>
</view>
</subviews>
<viewLayoutGuide key="safeArea" id="waZ-Zl-ctA"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="hXJ-VI-BDm" firstAttribute="top" secondItem="waZ-Zl-ctA" secondAttribute="top" constant="26" id="5uY-EE-T6I"/>
<constraint firstItem="gya-8W-NUT" firstAttribute="leading" secondItem="waZ-Zl-ctA" secondAttribute="leading" constant="20" id="8HI-YW-vJZ"/>
<constraint firstItem="M9G-zc-DZ7" firstAttribute="centerX" secondItem="waZ-Zl-ctA" secondAttribute="centerX" id="DdM-3d-Ns8"/>
<constraint firstItem="hXJ-VI-BDm" firstAttribute="leading" secondItem="waZ-Zl-ctA" secondAttribute="leading" constant="20" id="HLy-VC-EQq"/>
<constraint firstItem="waZ-Zl-ctA" firstAttribute="trailing" secondItem="gya-8W-NUT" secondAttribute="trailing" constant="20" id="Jtj-45-zNY"/>
<constraint firstItem="M9G-zc-DZ7" firstAttribute="centerY" secondItem="waZ-Zl-ctA" secondAttribute="centerY" id="csp-4l-gxc"/>
<constraint firstItem="waZ-Zl-ctA" firstAttribute="trailing" secondItem="hXJ-VI-BDm" secondAttribute="trailing" constant="20" id="gdK-qF-LMp"/>
<constraint firstItem="gya-8W-NUT" firstAttribute="top" secondItem="hXJ-VI-BDm" secondAttribute="bottom" constant="27" id="uJv-zg-4RI"/>
</constraints>
</view>
<navigationItem key="navigationItem" id="CUe-pG-h5R"/>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="yo3-rd-9B1" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1253.6231884057972" y="677.67857142857144"/>
</scene>
</scenes>
<resources>
<systemColor name="systemBackgroundColor">
Expand Down
84 changes: 84 additions & 0 deletions AgentTestApp/AgentTestApp/Features/SlowFrameBeatingHeartView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//
// MRUM SDK, © 2024 CISCO
//

import UIKit

class SlowFrameBeatingHeartView: UIView {
private let heartImageView = UIImageView()

// manual animation -- see below for why
private var timer: Timer?
private var scaleDirection: CGFloat = -1.0

override init(frame: CGRect) {
super.init(frame: frame)
setupView()
startAnimation()
}

required init?(coder: NSCoder) {
super.init(coder: coder)
setupView()
startAnimation()
}

func dealloc() {
stopAnimation()
}

override func layoutSubviews() {
super.layoutSubviews()
heartImageView.frame = bounds
}

private func setupView() {
heartImageView.image = UIImage(systemName: "heart.fill")
heartImageView.tintColor = .red
heartImageView.contentMode = .scaleAspectFit
heartImageView.translatesAutoresizingMaskIntoConstraints = false
addSubview(heartImageView)
}

func startAnimation() {
// Even with 0.04 it's still not the smoothest but
// it serves the purpose.
timer = Timer.scheduledTimer(withTimeInterval: 0.04, repeats: true) { [weak self] _ in
self?.updateAnimation()
}
}

func stopAnimation() {
timer?.invalidate()
timer = nil
}

private func updateAnimation() {

// Use a manually conducted animation so that we can
// keep it on the main thread and witness the results of
// the main thread stall in the UI. If we instead used a
// built-in animation here, Apple would put it on a
// background worker thread and it would not (reliably)
// pause when the main thread pauses, such as during a
// sleep on the main thread, so the results would not
// be visible or clearly shown in this test app.

// get the scale component of the transform matrix
let currentScale = heartImageView.transform.a

let scaleStep: CGFloat = 0.01
var newScale = currentScale + (scaleStep * scaleDirection)

if newScale <= 0.5 {
newScale = 0.5
scaleDirection = 1.0
} else if newScale >= 1.0 {
newScale = 1.0
scaleDirection = -1.0
}

heartImageView.transform = CGAffineTransform(scaleX: newScale, y: newScale)
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// MRUM SDK, © 2024 CISCO
//

import SwiftUI
import UIKit

class SlowFrameDetectorViewController: UIViewController {

@IBOutlet var slowFramesButton: UIButton!
@IBOutlet var frozenFramesButton: UIButton!
@IBOutlet var beatingHeartView: SlowFrameBeatingHeartView!

override func viewDidLoad() {
super.viewDidLoad()
}

@IBAction func slowFramesClick(_ sender: UIButton) {
// Sleep for 0.5 seconds on the main thread
print("Sleeping for 0.5 seconds to force slow frames")
DispatchQueue.main.asyncAfter(deadline: .now() + 0.001) {
Thread.sleep(forTimeInterval: 0.5)
}
}

@IBAction func frozenFramesClick(_ sender: UIButton) {
// Sleep for 1 second on the main thread
print("Sleeping for 2 seconds to force frozen frames")
DispatchQueue.main.asyncAfter(deadline: .now() + 0.001) {
Thread.sleep(forTimeInterval: 2.0)
}
}
}


45 changes: 23 additions & 22 deletions AgentTestApp/AgentTestApp/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,51 +7,52 @@

import UIKit

class ViewController : UIViewController, UITableViewDelegate, UITableViewDataSource {
// Data model: These strings will be the data for the table view cells and drive the seque
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

// Data model: These strings will be the data for the table view cells and drive the segue
// Each array must have the same number of elements
let displayName: [String] = ["Network Calls", "Crashes", "Test API Sample Calls", "Sample Next Task"] // Can be any descriptive text
let segueName: [String] = ["NetworkCalls", "Crashes", "TestApiCalls", "PlaceHolder"] // Segues declared in the storyboard
let displayName: [String] = ["Network Calls", "Crashes", "Test API Sample Calls", "Sample Next Task"] // Can be any descriptive text
let segueName: [String] = ["NetworkCalls", "Crashes", "TestApiCalls", "SlowFrameRenders", "PlaceHolder"] // Segues declared in the storyboard

// cell reuse id (cells that scroll out of view can be reused)
let cellReuseIdentifier = "cell"

// don't forget to hook this up from the storyboard
@IBOutlet weak var tableView: UITableView!
@IBOutlet var tableView: UITableView!

override func viewDidLoad() {
super.viewDidLoad()

// Register the table view cell class and its reuse id
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellReuseIdentifier)
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellReuseIdentifier)

// This view controller itself will provide the delegate methods and row data for the table view.
tableView.delegate = self
tableView.dataSource = self
}

// number of rows in table view
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.displayName.count
return displayName.count
}

// create a cell for each table view row
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

// create a new cell if needed or reuse an old one
let cell:UITableViewCell = (self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as UITableViewCell?)!
let cell: UITableViewCell = (self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as UITableViewCell?)!

// set the text from the data model
cell.textLabel?.text = self.displayName[indexPath.row]
cell.textLabel?.text = displayName[indexPath.row]

return cell
}

// method to run when table view cell is tapped
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You tapped row \(indexPath.row), for \(self.displayName[indexPath.row])")
self.performSegue(withIdentifier: self.segueName[indexPath.row], sender: self)
print("You tapped row \(indexPath.row), for \(displayName[indexPath.row])")

performSegue(withIdentifier: segueName[indexPath.row], sender: self)
}
}

0 comments on commit 367d276

Please sign in to comment.