agentsclimarketplace

Arimunandar ios performance

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

200+ iOS/Swift/Xcode agent skills from 28 authors with phase-aware routing. Claude Code plugin + Codex CLI + Mac App. Full lifecycle: ideation → design → develop → test → deploy → iterate.

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.

What its author says it does

Copied from the file, not written here

Memory management, profiling, optimization techniques, and Instruments usage for high-performance iOS apps

SKILL.md

11.6 KB, 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

Keep looking

Skills are one crate of 328,083. 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.