All posts
Blog4 min read

Building Native Mobile Apps in 2026: What Actually Matters

After shipping a dozen production mobile apps, here's what I focus on now: platform conventions, performance budgets, and the tooling that actually saves time.

Building Native Mobile Apps in 2026: What Actually Matters

Start with the platform, not the framework

Every mobile project I've worked on that went sideways started with the same mistake: choosing a cross-platform framework before understanding what the app actually needed to do. React Native and Flutter are impressive, but they're solutions to specific problems. If you're building something that needs deep platform integration—camera processing, background location, push notifications that actually work—you'll spend more time fighting abstractions than writing features.

I built a food delivery app last year that needed reliable background GPS tracking and local notifications. We started with React Native because "faster development." Three months in, we rewrote the critical path in Swift and Kotlin. The native versions were smaller, faster, and required half the workarounds.

That's not an argument against cross-platform tools. It's an argument for making informed decisions. If you're building CRUD interfaces, dashboards, or content-heavy apps, React Native or Flutter will get you to market faster. If you need anything that touches hardware, sensors, or complex system APIs, native development pays off.

Performance isn't about the framework

The biggest performance wins in mobile apps come from three places: image handling, list rendering, and network requests. Everything else is noise.

// Bad: Loading full-resolution images
let image = UIImage(named: "hero-image.jpg")
imageView.image = image

// Good: Downsample to display size
let imageURL = Bundle.main.url(forResource: "hero-image", withExtension: "jpg")!
let imageSource = CGImageSourceCreateWithURL(imageURL as CFURL, nil)!
let options: [CFString: Any] = [
    kCGImageSourceThumbnailMaxPixelSize: 1024,
    kCGImageSourceCreateThumbnailFromImageAlways: true
]
let downsampledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options as CFDictionary)
imageView.image = UIImage(cgImage: downsampledImage!)

I've seen apps load 4000×3000 images into 200×200 containers. The device decodes the full image, holds it in memory, then scales it down for display. That's 48MB of memory for an image that should use 160KB.

For lists, the pattern is universal: reuse cells, load data in chunks, prefetch intelligently. RecyclerView on Android, UICollectionView on iOS—they both implement view recycling because it's the only way to scroll through thousands of items without killing battery life.

class FeedAdapter : RecyclerView.Adapter<FeedViewHolder>() {
    private val items = mutableListOf<FeedItem>()
    
    override fun onBindViewHolder(holder: FeedViewHolder, position: Int) {
        holder.bind(items[position])
        
        // Prefetch next page when approaching end
        if (position >= items.size - 5) {
            loadMoreItems()
        }
    }
}

Network requests need three things: caching, request deduplication, and timeouts. URLSession and OkHttp handle the first two well if you configure them properly. The third one everyone forgets.

The tooling that actually saves time

Fastlane is the only deployment automation I use anymore. It handles screenshots, beta distribution, app store uploads, and certificate management. Setting it up takes half a day. It saves hours every week.

lane :beta do
  increment_build_number
  build_app(scheme: "MyApp")
  upload_to_testflight(
    skip_waiting_for_build_processing: true
  )
  slack(
    message: "New beta build available"
  )
end

For UI work, I use SwiftUI and Jetpack Compose now. The preview systems make iteration faster than Interface Builder or XML layouts ever did. Real-time previews with different data states, device sizes, and color schemes mean less time running the app to see changes.

struct ProfileView: View {
    let user: User
    
    var body: some View {
        VStack(alignment: .leading) {
            Text(user.name)
                .font(.title)
            Text(user.bio)
                .foregroundColor(.secondary)
        }
    }
}

#Preview {
    ProfileView(user: User.mock)
}

Charles Proxy or Proxyman for debugging network traffic. Every mobile developer needs to see what the app is actually sending and receiving. Relying on backend logs or server-side tools means you're missing half the story.

What I actually test

Unit tests for business logic, UI tests for critical flows, and that's it. I don't write tests for every view controller or screen. I write them for the code that processes data, handles state transitions, and makes decisions.

func testCartTotalCalculation() {
    var cart = ShoppingCart()
    cart.add(item: Item(price: 10.00), quantity: 2)
    cart.add(item: Item(price: 5.50), quantity: 1)
    
    XCTAssertEqual(cart.total, 25.50)
}

func testEmptyCartCheckout() {
    let cart = ShoppingCart()
    XCTAssertThrowsError(try cart.checkout())
}

For UI tests, I focus on the happy path and the most common error cases. Can a user sign up? Can they complete a purchase? Does the app handle network failures gracefully? That's 90% of what matters.

The test pyramid for mobile apps should be heavily weighted toward unit tests because UI tests are slow, brittle, and expensive to maintain. Every UI test needs updating when designs change. Unit tests for pure functions rarely break unless the requirements change.

Platform conventions matter more than you think

Users notice when an app doesn't follow platform conventions. Android users expect a back button to work consistently. iOS users expect swipe-to-go-back. Tab bars on iOS should be at the bottom. Navigation on Android typically uses a drawer or top tabs.

I've reviewed apps where the team tried to create a "unified experience" across platforms. It always feels wrong on at least one platform. Better to accept that iOS and Android have different patterns and embrace them.

The Human Interface Guidelines and Material Design specs aren't just suggestions. They're documentation of what users expect. Every time I've ignored them to "be creative," user testing showed people struggled with the interface.

What I'd tell someone starting today

Pick one platform and go deep. Learn how its layout system works, how memory management works, how the lifecycle works. Building good mobile apps requires understanding the platform, and you can't understand two platforms simultaneously when you're learning.

Write small apps to explore APIs and patterns. Build a weather app that uses location services. Build a note-taking app that uses local storage. Build something with a camera. These exercises teach you more than tutorials because you'll hit real problems.

Read other people's code. The best mobile developers I know spend time reading popular open-source apps. Seeing how someone else solved performance problems or organized their architecture is worth more than most courses.

The fundamentals haven't changed: understand the platform, measure performance, test what matters, and ship often. Everything else is details.