-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: UIKit Navigation instrimentation #22
Changes from 26 commits
0819b0b
04ce8d9
e95e6b4
96d5078
43d477f
4a17ec3
b7b5d12
13b93ef
809b582
1cdb040
aa274e0
b61fc95
65a8d2a
35fd328
e11980b
bbd647d
13ef5f3
807feb2
ca94603
aae1c71
bdc06a5
22b2e9a
6967340
a11a82e
81736a7
48a8ef8
9c18514
8586f74
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
<?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"> | ||
<device id="retina6_12" orientation="portrait" appearance="light"/> | ||
<dependencies> | ||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22685"/> | ||
<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"/> | ||
</dependencies> | ||
<scenes> | ||
<!--View Controller--> | ||
<scene sceneID="s0d-6b-0kx"> | ||
<objects> | ||
<viewController storyboardIdentifier="UIKitView" id="Y6W-OH-hqX" customClass="ViewController" sceneMemberID="viewController"> | ||
<view key="view" contentMode="scaleToFill" id="5EZ-qb-Rvc"> | ||
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/> | ||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> | ||
<subviews> | ||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" fixedFrame="YES" image="globe" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="O94-9o-FQ4"> | ||
<rect key="frame" x="169" y="216" width="54" height="46"/> | ||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> | ||
</imageView> | ||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Sample UIKit App" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="008-k5-M1T"> | ||
<rect key="frame" x="130" y="266" width="133" height="21"/> | ||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> | ||
<fontDescription key="fontDescription" type="system" pointSize="17"/> | ||
<nil key="textColor"/> | ||
<nil key="highlightedColor"/> | ||
</label> | ||
</subviews> | ||
<viewLayoutGuide key="safeArea" id="vDu-zF-Fre"/> | ||
<color key="backgroundColor" systemColor="systemBackgroundColor"/> | ||
</view> | ||
</viewController> | ||
<placeholder placeholderIdentifier="IBFirstResponder" id="Ief-a0-LHa" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/> | ||
</objects> | ||
<point key="canvasLocation" x="61.832061068702288" y="3.5211267605633805"/> | ||
</scene> | ||
</scenes> | ||
<resources> | ||
<image name="globe" catalog="system" width="128" height="123"/> | ||
<systemColor name="systemBackgroundColor"> | ||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> | ||
</systemColor> | ||
</resources> | ||
</document> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
// | ||
// UIKView.swift | ||
// SmokeTest | ||
// | ||
// Created by Arri Blais on 11/13/24. | ||
// | ||
|
||
import Foundation | ||
import SwiftUI | ||
import UIKit | ||
|
||
struct UIKitView: View { | ||
var body: some View { | ||
StoryboardViewControllerRepresentation() | ||
} | ||
} | ||
|
||
struct UIKView_preview: PreviewProvider { | ||
static var previews: some View { | ||
UIKitView() | ||
} | ||
} | ||
|
||
struct StoryboardViewControllerRepresentation: UIViewControllerRepresentable { | ||
func makeUIViewController(context: Context) -> some UIViewController { | ||
let storyboard = UIStoryboard(name: "UIKitView", bundle: Bundle.main) | ||
let controller = storyboard.instantiateViewController(identifier: "UIKitView") | ||
return controller | ||
} | ||
|
||
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) { | ||
} | ||
} | ||
|
||
class ViewController: UIViewController { | ||
|
||
override func viewDidLoad() { | ||
super.viewDidLoad() | ||
// Do any additional setup after loading the view. | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -153,6 +153,17 @@ final class SmokeTestUITests: XCTestCase { | |
app.buttons["Flush"].tap() | ||
} | ||
|
||
func testUIKitInstrimentation() throws { | ||
let app = XCUIApplication() | ||
app.launch() | ||
app.buttons["UIKit"].tap() | ||
XCTAssert(app.staticTexts["Sample UIKit App"].waitForExistence(timeout: uiUpdateTimeout)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since we're swizzling |
||
|
||
app.buttons["Core"].tap() | ||
XCTAssert(app.buttons["Flush"].waitForExistence(timeout: uiUpdateTimeout)) | ||
app.buttons["Flush"].tap() | ||
} | ||
|
||
func testRenderPerformace() throws { | ||
let app = XCUIApplication() | ||
app.launch() | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -76,6 +76,16 @@ To manually send a span: | |
The following auto-instrumentation libraries are automatically included: | ||
* [MetricKit](https://developer.apple.com/documentation/metrickit) data is automatically collected. | ||
|
||
### UIKit instrumentation | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: It looks like we capitalized "Instrumentation" everywhere else, so let's be consistent. |
||
|
||
UIKit views will automatically be instrumented, emitting `viewDidAppear` and `viewDidDisappear` events. Both have the following attributes: | ||
|
||
- `title` - Title of the view, if provided. | ||
- `nibName` - The name of the view controller's nib file, if one was specified. | ||
- `animated` - true if the transition to/from this view is animated, false if it isn't. | ||
- `className` - name of the swift/objective-c class this view | ||
controller has. | ||
|
||
## Manual Instrumentation | ||
### SwiftUI View Instrumentation | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
#if canImport(UIKit) | ||
import Foundation | ||
import OpenTelemetryApi | ||
import UIKit | ||
|
||
private let honeycombUIKitInstrumentationName = "@honeycombio/instrumentation-uikit" | ||
|
||
internal func getUIKitViewTracer() -> Tracer { | ||
return OpenTelemetry.instance.tracerProvider.get( | ||
instrumentationName: honeycombUIKitInstrumentationName, | ||
instrumentationVersion: honeycombLibraryVersion | ||
) | ||
} | ||
|
||
public func installUINavigationInstrumentation() { | ||
UIViewController.swizzle() | ||
} | ||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
#if canImport(UIKit) | ||
import Foundation | ||
import OpenTelemetryApi | ||
import UIKit | ||
|
||
extension UIViewController { | ||
@objc func traceViewDidAppear(_ animated: Bool) { | ||
let className = NSStringFromClass(type(of: self)) | ||
|
||
// Internal classes from SwiftUI will likely begin with an underscore | ||
if !className.hasPrefix("_") { | ||
let span = getUIKitViewTracer().spanBuilder(spanName: "viewDidAppear").startSpan() | ||
if self.title != nil { | ||
span.setAttribute(key: "title", value: self.title!) | ||
} | ||
span.setAttribute(key: "animated", value: animated) | ||
span.setAttribute(key: "className", value: className) | ||
span.end() | ||
} | ||
|
||
traceViewDidAppear(animated) | ||
} | ||
|
||
@objc func traceViewDidDisappear(_ animated: Bool) { | ||
|
||
let className = NSStringFromClass(type(of: self)) | ||
|
||
// Internal classes from SwiftUI will likely begin with an underscore | ||
if !className.hasPrefix("_") { | ||
let span = getUIKitViewTracer().spanBuilder(spanName: "viewDidDisappear") | ||
.startSpan() | ||
if self.title != nil { | ||
span.setAttribute(key: "title", value: self.title!) | ||
} | ||
span.setAttribute(key: "animated", value: animated) | ||
span.setAttribute(key: "className", value: className) | ||
span.end() | ||
} | ||
|
||
traceViewDidDisappear(animated) | ||
} | ||
|
||
public static func swizzle() { | ||
let originalAppearSelector = #selector(UIViewController.viewDidAppear(_:)) | ||
let swizzledAppearSelector = #selector(UIViewController.traceViewDidAppear(_:)) | ||
let originalDisappearSelector = #selector(UIViewController.viewDidDisappear(_:)) | ||
let swizzledDisappearSelector = #selector(UIViewController.traceViewDidDisappear(_:)) | ||
|
||
guard | ||
let originalAppearMethod = class_getInstanceMethod(self, originalAppearSelector), | ||
let swizzledAppearMethod = class_getInstanceMethod(self, swizzledAppearSelector) | ||
else { | ||
print("unable to swizzle \(originalAppearSelector): original method not found") | ||
return | ||
} | ||
|
||
method_exchangeImplementations(originalAppearMethod, swizzledAppearMethod) | ||
|
||
guard | ||
let originalDisappearMethod = class_getInstanceMethod( | ||
self, | ||
originalDisappearSelector | ||
), | ||
let swizzledDisappearMethod = class_getInstanceMethod( | ||
self, | ||
swizzledDisappearSelector | ||
) | ||
else { | ||
print("unable to swizzle \(originalDisappearSelector): original method not found") | ||
return | ||
} | ||
|
||
method_exchangeImplementations(originalDisappearMethod, swizzledDisappearMethod) | ||
} | ||
} | ||
|
||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should delete these headers. You can change the template in Xcode settings somewhere.