Skip to content

Instantly share code, notes, and snippets.

@jscalo
jscalo / ls_app_group_entitlement.sh
Created March 9, 2025 04:48
List apps in Applications that have the app groups entitlement
#!/bin/bash
APPS_DIR="/Applications"
find "$APPS_DIR" -type d -maxdepth 1 -name "*.app" | while read -r app; do
if codesign -d --entitlements - "$app" 2>/dev/null | grep -q "com.apple.security.application-groups"; then
echo "$app"
fi
done
@jscalo
jscalo / GetUserAgent.swift
Last active November 2, 2024 16:29
Setting the user agent correctly on an iOS app's HTTP requests can be important, but it's a pain to get right. This lightweight solution creates an "offscreen" webView, loads a page, and grabs the user agent string. The result is cached so subsequent calls are instantaneous.
import WebKit
class GetUserAgent: NSObject, WKNavigationDelegate {
private static var cachedUserAgent: String?
private var continuation: CheckedContinuation<String, Error>?
private var webView: WKWebView?
func get() async throws -> String {
if let cached = Self.cachedUserAgent {
private var firstSampleToPlotIdx: Int {
get {
let idx = Int(-viewPort.xTrans * viewPort.screenScale)
return (0..<dataProvider.summarySampleCnt).clamp(idx)
}
}
private var samplesToPlotInVisibleCnt: Int {
get {
return viewPort.visibleXAxisUnits
func update() {
guard viewPort != nil else { return }
guard dataProvider != nil else { return }
prepare()
updateMidline()
updatePlot()
}
@objc func pinchGesture(_ gc: UIPinchGestureRecognizer) {
switch gc.state {
case .changed:
pauseUpdates = true
scrollView.recenterForScale(gc.scale)
pauseUpdates = false
let newScale = viewPort.zoom * gc.scale
viewPort.zoom = max(newScale, 1.0)
gc.scale = 1.0
case .ended, .cancelled, .failed:
override func layoutSubviews() {
super.layoutSubviews()
let scale = bounds.width / lastBounds.width
scrollView.recenterForScale(scale)
lastBounds = bounds
updateScrollViewSize()
update()
}
private func updateScrollViewSize() {
scrollView.contentSize =
CGSize(width: bounds.width * viewPort.zoom,
height: bounds.height)
}
private func viewPortZoomed() {
dataProvider?.summarySamples = nil
updateScrollViewSize()
CATransaction.begin()
CATransaction.setDisableActions(true)
update()
CATransaction.commit()
}
private var firstSampleToPlotIdx: Int {
get {
let idx = Int(-viewPort.xTrans * UIScreen.main.scale)
return (0..<dataProvider.summarySampleCnt).clamp(idx)
}
}
private func updateLines(samples: [Float], yMidline: CGFloat) {
let maxSampleMagnitude = max(dataProvider.summarySampleMax, -(dataProvider.summarySampleMin), Float.leastNonzeroMagnitude)
let yScalingFactor = bounds.height / 2 / CGFloat(maxSampleMagnitude)
var xPos: CGFloat = 0
let cnt = samples.count
var idx = 0
let visibleDur = dataProvider.duration / Double(viewPort.zoom)
let startTime = (Double(viewPort.startingXUnit) / Double(viewPort.xAxisUnits)) * dataProvider.duration