agentsclimarketplace

Arimunandar ios performance

Skill JordanCoin/ios-skills-collection/skills/arimunandar--ios-performance

Memory management, profiling, optimization techniques, and Instruments usage for high-performance iOS appsFrom its SKILL.md

Install
npx -y skills add JordanCoin/ios-skills-collection --skill arimunandar--ios-performance

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

11.6 KB, ~2.7k tokens by cl100k_base, as published. Nobody here has run it

iOS Performance Optimization

Comprehensive guide to building high-performance iOS applications with proper memory management, profiling, and optimization techniques.

Memory Management

ARC (Automatic Reference Counting)

Strong vs Weak vs Unowned

// STRONG (default) - Increases retain count
class Parent {
    var child: Child?  // Strong reference
}

// WEAK - Does NOT increase retain count, becomes nil when deallocated
class Child {
    weak var parent: Parent?  // Weak to prevent retain cycle
}

// UNOWNED - Does NOT increase retain count, crashes if accessed after deallocation
class Customer {
    let card: CreditCard  // Customer owns the card

    init() {
        card = CreditCard(holder: self)
    }
}

class CreditCard {
    unowned let holder: Customer  // Card always has a holder
}

When to Use Each

Reference TypeUse WhenRisk
strongObject should stay aliveRetain cycles
weakOptional reference, may become nilNone
unownedNon-optional, same or longer lifetimeCrash if wrong

Retain Cycle Detection

Common Retain Cycle Patterns

// ❌ RETAIN CYCLE - Closure captures self strongly
class ViewController: UIViewController {
    var onComplete: (() -> Void)?

    func setup() {
        onComplete = {
            self.doSomething()  // Strong capture!
        }
    }
}

// ✅ FIXED - Weak capture
class ViewController: UIViewController {
    var onComplete: (() -> Void)?

    func setup() {
        onComplete = { [weak self] in
            self?.doSomething()
        }
    }
}

Delegate Retain Cycles

// ❌ RETAIN CYCLE
protocol MyDelegate {
    func didComplete()
}

class Parent: MyDelegate {
    var child: Child?

    init() {
        child = Child()
        child?.delegate = self  // Strong reference back!
    }
}

class Child {
    var delegate: MyDelegate?  // Strong!
}

// ✅ FIXED - Weak delegate
protocol MyDelegate: AnyObject {  // Must be AnyObject for weak
    func didComplete()
}

class Child {
    weak var delegate: MyDelegate?  // Weak!
}

Timer Retain Cycles

// ❌ RETAIN CYCLE - Timer retains target
class ViewController: UIViewController {
    var timer: Timer?

    override func viewDidLoad() {
        timer = Timer.scheduledTimer(
            timeInterval: 1.0,
            target: self,        // Timer retains self!
            selector: #selector(tick),
            userInfo: nil,
            repeats: true
        )
    }
}

// ✅ FIXED - Use closure-based timer with weak capture
class ViewController: UIViewController {
    var timer: Timer?

    override func viewDidLoad() {
        timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
            self?.tick()
        }
    }

    deinit {
        timer?.invalidate()
    }
}

Memory Leak Checklist

// Add to your ViewControllers to verify deallocation
deinit {
    print("✅ \(Self.self) deallocated")
}

Common Leak Sources:

  • Closures without [weak self]
  • Delegates not marked weak
  • NotificationCenter observers not removed
  • Timers not invalidated
  • DispatchWorkItem not cancelled
  • Strong references in singletons

Performance Profiling

Instruments Usage

Time Profiler

# Launch Time Profiler
xcrun xctrace record --template "Time Profiler" --launch -- /path/to/app.app

What to look for:

  • Functions taking > 16ms (60fps = 16.67ms per frame)
  • Main thread blocking
  • Excessive CPU usage

Allocations

# Track memory allocations
xcrun xctrace record --template "Allocations" --launch -- /path/to/app.app

What to look for:

  • Memory growth over time (leaks)
  • Transient allocations (excessive object creation)
  • Large allocations

Leaks

# Detect memory leaks
xcrun xctrace record --template "Leaks" --launch -- /path/to/app.app

In-Code Profiling

// Measure execution time
func measureTime(_ label: String, block: () -> Void) {
    let start = CFAbsoluteTimeGetCurrent()
    block()
    let end = CFAbsoluteTimeGetCurrent()
    print("⏱ \(label): \((end - start) * 1000)ms")
}

// Usage
measureTime("Load Data") {
    loadData()
}

// Using os_signpost for Instruments
import os.signpost

let log = OSLog(subsystem: "com.app", category: "Performance")

func loadData() {
    os_signpost(.begin, log: log, name: "Load Data")
    defer { os_signpost(.end, log: log, name: "Load Data") }

    // ... work
}

UIKit Optimization

UITableView/UICollectionView

// ✅ OPTIMIZED TableView
final class OptimizedTableViewController: UIViewController {

    private let tableView: UITableView = {
        let tv = UITableView()
        tv.rowHeight = UITableView.automaticDimension
        tv.estimatedRowHeight = 60  // Provide estimate!
        return tv
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        // Register cells for reuse
        tableView.register(MyCell.self, forCellReuseIdentifier: "MyCell")

        // Prefetching for smooth scrolling
        tableView.prefetchDataSource = self
    }
}

extension OptimizedTableViewController: UITableViewDataSourcePrefetching {
    func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
        // Preload images or data for upcoming cells
        for indexPath in indexPaths {
            imageLoader.prefetch(url: items[indexPath.row].imageURL)
        }
    }

    func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
        // Cancel preloading for cells no longer needed
        for indexPath in indexPaths {
            imageLoader.cancel(url: items[indexPath.row].imageURL)
        }
    }
}

Cell Optimization

final class OptimizedCell: UITableViewCell {

    // Use layer properties for shadows (GPU accelerated)
    override func awakeFromNib() {
        super.awakeFromNib()

        // Rasterize complex views
        layer.shouldRasterize = true
        layer.rasterizationScale = UIScreen.main.scale

        // Opaque views are faster
        backgroundColor = .white
        isOpaque = true
    }

    override func prepareForReuse() {
        super.prepareForReuse()
        // Cancel any pending image loads
        imageView?.image = nil
        imageLoadTask?.cancel()
    }
}

Image Optimization

// ✅ Downscale images to display size
extension UIImage {
    func downscaled(to size: CGSize) -> UIImage {
        let renderer = UIGraphicsImageRenderer(size: size)
        return renderer.image { _ in
            draw(in: CGRect(origin: .zero, size: size))
        }
    }
}

// ✅ Load images asynchronously
final class AsyncImageLoader {
    private let cache = NSCache<NSString, UIImage>()
    private var tasks: [URL: URLSessionTask] = [:]

    func load(url: URL, into imageView: UIImageView) {
        // Check cache first
        if let cached = cache.object(forKey: url.absoluteString as NSString) {
            imageView.image = cached
            return
        }

        // Load asynchronously
        let task = URLSession.shared.dataTask(with: url) { [weak self] data, _, _ in
            guard let data = data, let image = UIImage(data: data) else { return }

            // Cache the image
            self?.cache.setObject(image, forKey: url.absoluteString as NSString)

            DispatchQueue.main.async {
                imageView.image = image
            }
        }
        tasks[url] = task
        task.resume()
    }

    func cancel(url: URL) {
        tasks[url]?.cancel()
        tasks[url] = nil
    }
}

View Hierarchy Optimization

// ❌ SLOW - Deep view hierarchy
view.addSubview(container1)
container1.addSubview(container2)
container2.addSubview(container3)
container3.addSubview(label)

// ✅ FAST - Flat hierarchy
view.addSubview(label)

// ❌ SLOW - Transparent layers
view.backgroundColor = UIColor.black.withAlphaComponent(0.5)

// ✅ FAST - Opaque layers where possible
view.backgroundColor = .black
view.alpha = 0.5  // Or use solid color if acceptable

Concurrency Performance

Main Thread Protection

// ✅ Keep UI work on main thread
func updateUI(with data: Data) {
    DispatchQueue.main.async {
        self.label.text = String(data: data, encoding: .utf8)
    }
}

// ✅ Heavy work off main thread
func processData() {
    Task.detached(priority: .userInitiated) {
        let result = await self.heavyComputation()
        await MainActor.run {
            self.updateUI(with: result)
        }
    }
}

Batch Operations

// ❌ SLOW - Individual updates
for item in items {
    tableView.insertRows(at: [IndexPath(row: index, section: 0)], with: .automatic)
}

// ✅ FAST - Batch updates
tableView.performBatchUpdates {
    let indexPaths = items.indices.map { IndexPath(row: $0, section: 0) }
    tableView.insertRows(at: indexPaths, with: .automatic)
}

Network Performance

Request Optimization

final class OptimizedNetworkClient {
    private let session: URLSession

    init() {
        let config = URLSessionConfiguration.default

        // Connection pooling
        config.httpMaximumConnectionsPerHost = 6

        // Caching
        config.urlCache = URLCache(
            memoryCapacity: 50_000_000,  // 50MB memory
            diskCapacity: 100_000_000     // 100MB disk
        )
        config.requestCachePolicy = .returnCacheDataElseLoad

        // Timeouts
        config.timeoutIntervalForRequest = 30
        config.timeoutIntervalForResource = 300

        session = URLSession(configuration: config)
    }

    func fetch<T: Decodable>(_ url: URL) async throws -> T {
        var request = URLRequest(url: url)

        // Enable compression
        request.setValue("gzip, deflate", forHTTPHeaderField: "Accept-Encoding")

        let (data, _) = try await session.data(for: request)
        return try JSONDecoder().decode(T.self, from: data)
    }
}

Core Data Performance

// ✅ Batch fetching
let request = NSFetchRequest<Entity>(entityName: "Entity")
request.fetchBatchSize = 20  // Fetch in batches
request.returnsObjectsAsFaults = true  // Lazy loading

// ✅ Background context for heavy operations
let backgroundContext = persistentContainer.newBackgroundContext()
backgroundContext.perform {
    // Heavy operations here
    try? backgroundContext.save()
}

// ✅ Batch delete (much faster than individual deletes)
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
try context.execute(deleteRequest)

Performance Checklist

Launch Time

  • Defer non-essential work with DispatchQueue.main.async
  • Lazy-load view controllers
  • Use static/let for constants
  • Minimize work in application(_:didFinishLaunchingWithOptions:)

Scrolling

  • Reuse cells properly
  • Use estimated heights
  • Prefetch data
  • Avoid transparent views
  • Cache computed values

Memory

  • Use [weak self] in closures
  • Make delegates weak
  • Invalidate timers
  • Remove notification observers
  • Profile with Instruments

Network

  • Enable HTTP caching
  • Use compression
  • Batch requests where possible
  • Cancel unnecessary requests

Quick Wins

IssueSolution
Slow scrollingEnable cell reuse, prefetching
High memoryDownscale images, check leaks
Slow launchDefer work, lazy load
UI freezesMove work off main thread
Battery drainReduce background work, use efficient APIs

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Gives 0 of the 12 instructions most performance cost skills give in ~2.7k tokens

Counted across 797 of the 1,117 authors here whose files we hold, read 2026-09-06

  • Check for product marketing context firstin 46 of 797, across 20 files
  • Measure before optimizingin 31 of 797, across 25 files
  • Profile first to identify the actual bottleneckin 23 of 797, across 22 files
  • Verify your robots.txt allows AI crawlersin 21 of 797, across 12 files
  • Import directly and avoid barrel filesin 19 of 797, across 15 files
  • Spawn all runs in the same turnin 18 of 797, across 11 files
  • Write a draft of the skillin 17 of 797, across 10 files
  • Understand the user's intentin 17 of 797, across 10 files
  • Use React.cache for per-request deduplicationin 16 of 797, across 11 files
  • Profile before optimizingin 16 of 797, across 14 files
  • Include specific numbers with sourcesin 15 of 797, across 8 files
  • Add lazy loading to below-fold imagesin 15 of 797, across 10 files

Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.