Xiaomi iOS Developer Interview 4-Round Complete Record: Swift and iOS Principles Deep Dive
Complete review of Xiaomi iOS developer interview for 3-year experienced developer, covering 3 technical rounds and HR round with real questions on Swift advanced features, Runtime, RunLoop, memory management, and UI optimization
Background
Let me start with my situation: 3 years of iOS development experience, currently working at a mid-size app company building a social product with roughly 2 million DAU. My day-to-day work is primarily in Swift for business logic, with some legacy Objective-C modules I maintain. Honestly, after two-plus years at my current company, I hit a technical plateau — the business iterates fast, but I'm getting less and less exposure to anything low-level, feeling more and more like an "API-calling engineer."
I started looking for new opportunities in March this year with a clear goal: join a large tech company's system-level team where I could work on deeper iOS development. Xiaomi's MIUI/iOS team had been on my target list for a while — partly because of the breadth of Xiaomi's ecosystem, and partly because I'd heard their team really digs deep into iOS fundamentals during interviews, which seemed like exactly the push I needed.
The application process went smoothly. After chatting with HR on Boss Zhipin, interviews were scheduled quickly. The entire process from the first round to receiving the offer took about two and a half weeks. Below is my complete recap of all 4 interview rounds — I hope it helps anyone preparing for a Xiaomi iOS interview or any iOS developer interview in general.
Round 1 — Technical Interview 1 (Video, ~60 minutes)
The first round was scheduled for Monday at 2 PM. The interviewer was a young engineer who introduced himself as being from Xiaomi's iOS infrastructure team. The pace was fairly tight, covering a wide range of topics from Swift basics to iOS internals.
1. What's the difference between struct and class in Swift? Why does Swift recommend using struct by default?
I answered this fairly smoothly: structs are value types, classes are reference types; structs don't support inheritance, classes do; structs have memberwise initializers. The reasons to prefer structs include safer value semantics, no shared-state side effects, better performance from stack allocation, and inherent thread safety. The interviewer followed up on the copy-on-write mechanism, and I explained how Swift standard library types like Array and Dictionary optimize performance through COW.
2. What does the where keyword do in Swift generics? Can you give a practical example?
I explained that where is used to add constraints on generic types, such as requiring a generic parameter to conform to a protocol or inherit from a class. I gave a practical example: writing a function that requires elements to conform to both Hashable and Comparable. The interviewer nodded and didn't dig deeper.
3. What's the difference between Swift's protocol and Objective-C's protocol?
I mentioned that Swift protocols support default implementations (via extensions), can be used as generic constraints, and support associated types. OC protocols don't support default implementations or associated types. The interviewer asked whether you can declare stored properties in a protocol — I said no, you can only declare computed property requirements; stored properties need to be implemented through associated objects or concrete types.
4. What's the difference between Swift's async/await and traditional GCD/Completion Handlers?
I compared them across three dimensions: code readability, error handling, and task cancellation. async/await makes asynchronous code look synchronous, avoiding callback hell; combined with try-catch, error handling is more natural; Task and TaskGroup enable structured concurrency. The interviewer followed up on what actors are — I explained that actors are an isolation mechanism in Swift's concurrency model, ensuring thread-safe access to internal state and preventing data races through actor isolation. To be honest, I didn't go deep enough on actors — I only covered the basic concepts. The interviewer didn't press further, but I felt I should have prepared this topic more thoroughly.
5. What's the difference between weak and unowned in iOS? When should you use each?
weak is an optional type — it automatically becomes nil when the object is deallocated; unowned is a non-optional type — it assumes the object always exists, and accessing it after deallocation causes a crash. Use cases: weak is most common when capturing self in closures; unowned is for scenarios where you're certain the object outlives the closure, such as in lazy properties. The interviewer asked about choosing between [weak self] and [unowned self] in closures — I said weak is generally safer and should be preferred.
6. What's the difference between SwiftUI and UIKit? What's your take on SwiftUI's future?
I said SwiftUI is a declarative UI framework while UIKit is imperative; SwiftUI drives view updates through state, while UIKit requires manual view hierarchy management. SwiftUI's advantages include concise code, live previews, and cross-platform support; its disadvantages include an immature ecosystem, difficulty with complex customization, and limited performance debugging tools. Regarding the future, I believe SwiftUI will gradually replace UIKit, but in the short term UIKit remains the primary choice, especially for complex business scenarios. The interviewer seemed satisfied with this answer.
7. How does the iOS Responder Chain work?
I explained that hit-testing determines the first responder, then the event travels along the responder chain from child views to parent views until it's handled. If no one handles it, the event reaches UIWindow and UIApplication. The interviewer asked how to expand a view's tap area — I said you can override point(inside:with:) or use a pointInside extension.
8. Are you familiar with Method Swizzling in iOS? What are the risks?
I explained that Method Swizzling swaps two method implementations via the Runtime, commonly used for AOP programming like analytics tracking. Risks include: order-dependent swapping issues, infinite loops from multiple swaps, thread safety concerns, and unpredictable effects on system behavior. I recommended ensuring swizzling happens in +load, using dispatch_once to guarantee a single swap, and maintaining good logging. The interviewer nodded at this answer.
9. Briefly explain how ARC works in iOS memory management.
ARC works by having the compiler automatically insert retain/release/autorelease calls at compile time, managing memory through reference counting. Strong references increase the count, weak references don't. When the count drops to zero, the object is deallocated. I mentioned the role of autoreleasepool — it sends release messages when the pool is drained, preventing memory spikes. The interviewer followed up on the relationship between autoreleasepool and RunLoop — I briefly explained that each RunLoop iteration creates an autoreleasepool that gets drained at the end of the iteration. This connected to the RunLoop questions in Round 2.
Round 2 — Technical Interview 2 (Video, ~65 minutes)
Round 2 was scheduled for Thursday at 10 AM. The interviewer was the team's tech lead, and his style was noticeably deeper than Round 1 — lots of follow-up questions, pushing until you couldn't answer anymore. I was under more pressure in this round, and there were two questions I didn't handle well.
1. Explain Objective-C Runtime's message dispatch mechanism in detail.
I started from objc_msgSend: method calls are transformed at compile time into objc_msgSend(receiver, selector, ...), then go through: searching the cache → searching the class's method list → searching up the inheritance chain. If not found, the dynamic method resolution process kicks in: resolveInstanceMethod → forwardingTargetForSelector → forwardInvocation. The interviewer asked about the relationship between methodSignatureForSelector and forwardInvocation — I said methodSignatureForSelector returns the method signature, and forwardInvocation performs the forwarding based on that signature. Then the interviewer asked what happens if all forwarding fails — I said it throws a doesNotRecognizeSelector exception and crashes. Overall I did okay on this one, but the details of dynamic method resolution weren't clear enough — especially why the cache needs to be searched again after resolveInstanceMethod returns YES, I was a bit fuzzy on that.
2. What's the underlying mechanism of RunLoop? What are its practical applications in iOS?
I said RunLoop is essentially an event loop that receives events through mach_port, keeping the thread alive. Internally it maintains multiple Modes, each containing Source0, Source1, Timer, and Observer. Practical applications include: NSTimer implementation, AutoreleasePool management, event response, and GCD callback execution. The interviewer asked about the relationship between RunLoop and threads — I said every thread has a corresponding RunLoop, but only the main thread's RunLoop starts automatically; child threads need to manually obtain and run theirs. Then came a question that stumped me: "When RunLoop is sleeping, what state is the thread in? How does it get woken up?" I knew the thread gets suspended, and I managed to say "the thread sleeps via mach_msg and gets woken up when there's a port message," but I honestly didn't know the details about kernel-space and user-space transitions. I admitted, "I don't understand this deeply enough." The interviewer said it was fine and moved on.
3. How do you detect and resolve retain cycles in iOS?
I listed common scenarios: closures capturing self, delegates declared as strong, timers not invalidated. Detection methods: Instruments' Leaks and Allocations tools, Xcode's Memory Graph Debugger, and FBRetainCycleDetector. Solutions: use [weak self] in closures, declare delegates as weak, invalidate timers in deinit. The interviewer then asked something I hadn't anticipated: "How exactly does an NSTimer retain cycle form? Why can't marking the target as weak solve it?" I thought for a moment and said it's because the NSTimer's target is strongly held by the RunLoop — even if the target is weak, the RunLoop still holds the timer, and the timer holds the target, creating a RunLoop→Timer→Target cycle. weak only prevents the reference from incrementing the count, but the RunLoop's hold is strong. The correct approach is to use GCD Timer or the block-based Timer API. The interviewer said "basically correct," but I felt my explanation wasn't clear enough.
4. What's the difference between GCD and OperationQueue? When should you use each?
GCD is a C API based on queues and closures — lightweight and efficient; OperationQueue is object-oriented, supporting cancellation, dependencies, priorities, and max concurrent operation control. Use cases: simple async tasks with GCD, complex task orchestration with OperationQueue. The interviewer followed up on GCD queue types and QoS levels — I listed main queue, global queue, custom serial queue, custom concurrent queue, and the five QoS levels: userInteractive, userInitiated, default, utility, and background.
5. What's the difference between Category and Extension in iOS? Can Category add stored properties?
Category takes effect at runtime and can add methods to existing classes but can't directly add stored properties; Extension takes effect at compile time and can add properties and methods, but must be in the main class's implementation file. To add stored properties via Category, you use associated objects (objc_setAssociatedObject/objc_getAssociatedObject). The interviewer asked about the underlying implementation of associated objects — I said they're stored in a global AssociationsManager hash table, indexed by object address and key. When the object is deallocated, associated objects are cleaned up via objc_destructInstance.
6. What's the underlying implementation principle of KVO in iOS?
I explained that KVO dynamically creates a subclass of the observed object (NSKVONotifying_XXX) via the Runtime, overrides the setter method, and calls willChangeValueForKey and didChangeValueForKey in the setter to notify observers. The interviewer asked how to verify this mechanism — I said you can print the object's actual class using object_getClass. I also mentioned a KVO gotcha: if you modify an instance variable directly outside the setter, KVO won't trigger — you need to manually call willChange/didChange.
Round 3 — Technical Interview 3 (Video, ~55 minutes)
Round 3 was scheduled for the following Tuesday at 3 PM. The interviewer was a senior architect, and the questions leaned more toward architecture design and scenario analysis. The pace of this round felt more comfortable — the interviewer seemed more like he was discussing problems with you rather than just testing you.
1. What performance issues have you encountered in your projects? How did you optimize them?
I gave two real examples. First, TableView scrolling jank: the cause was image decoding and corner-radius clipping in cellForRow. The optimization was moving image decoding to a background thread, using pre-rendered corners instead of real-time clipping, and pre-calculating row heights. Second, slow app launch: I used the DYLD_PRINT_STATISTICS environment variable to identify pre-main phase bottlenecks, found too many dynamic libraries, merged several internal libraries, and reduced launch time from 2.3 seconds to 1.5 seconds. The interviewer asked about other launch optimization techniques — I added binary reordering, lazy loading of non-first-screen modules, and reducing logic in +load.
2. How would you design an image caching framework?
I designed a three-tier cache architecture: memory cache (NSCache with automatic eviction) → disk cache (file system with time- and size-based cleanup) → network requests (URLSession with concurrency and priority support). Key design points: clear memory cache on memory warnings, use LRU strategy for disk cache, decode images on background threads, support progressive JPEG loading. The interviewer asked about the difference between NSCache and NSDictionary — I said NSCache is thread-safe, automatically evicts objects under memory pressure, and doesn't copy keys. The interviewer then asked how to efficiently look up disk cache — I said use the MD5 of the file path as the filename and read directly from the file system.
3. If your app hits an OOM, how would you investigate?
I said first distinguish between memory leaks and memory spikes. Memory leaks can be found with Instruments Leaks and Memory Graph; memory spikes require analyzing the lifecycle of large objects. Common causes: large images loaded without compression, list caches without size limits, retain cycles. Investigation steps: use Memory Graph to see current object distribution in memory, then Allocations to track memory growth trends, and finally Leaks to detect leaks. The interviewer asked about the Jetsam mechanism — I said iOS uses Jetsam to prioritize killing processes with high memory usage and low priority, and you can check Jetsam events in logs.
4. What's your understanding of modularization? How does your project do it?
I said the core of modularization is decoupling — enabling independent development and testing of modules. Our project uses a protocol-based registration approach (similar to BeeHive), implementing service discovery through Protocol-Class registration, with modules communicating via protocols rather than direct dependencies. The interviewer asked about challenges during modularization — I said the biggest challenge was decoupling legacy code, since cross-module calls were scattered everywhere and needed gradual extraction. Another issue was determining the right granularity — too fine means high maintenance costs, too coarse means incomplete decoupling.
5. Scenario question: Design a configurable home feed that supports A/B testing. How would you architect it?
I designed a layered architecture: configuration layer (fetches AB experiment configs from the server, determining which cards to show and in what order) → data layer (requests corresponding data sources based on configuration) → presentation layer (dynamically creates corresponding Cells based on card type, using the factory pattern). Key points: configuration is delivered as JSON, parsed by the client into a card model array; Cell registration uses ReuseIdentifier mapping; data sources are abstracted via protocols, with different cards implementing their own data protocols. The interviewer asked what happens if a new card type requires an app update — I said you can reserve generic card types that render dynamically through server configuration, similar to a WebView or template rendering approach.
Round 4 — HR Interview (~35 minutes)
The HR round was scheduled three days after Round 3. The interviewer was a very friendly HR specialist. The overall atmosphere was relaxed, but some questions still required careful thought.
1. Self-introduction
I briefly covered my 3 years of iOS experience, the product I'm currently working on and its tech stack, and why I wanted to join Xiaomi.
2. Why Xiaomi?
I gave three reasons: Xiaomi's ecosystem is vast — from phones to IoT to cars — iOS development here isn't just about apps, there's system-level work too; Xiaomi has a strong technical culture with a rich open-source community; the MIUI team's pursuit of technical excellence aligns with my career goals.
3. What's your biggest achievement at your current company?
I talked about leading the app's migration from Objective-C to Swift — a six-month effort that migrated 80% of the codebase with zero production incidents. Through this process, I gained experience with mixed-language projects and a deep understanding of OC-Swift interoperability.
4. What's your salary expectation?
I stated my current salary and expected increase. HR didn't respond on the spot, saying they'd need to evaluate comprehensively before making an offer.
5. Do you have any questions for me?
I asked about the team's tech stack and future direction. HR said the team is currently researching cross-platform solutions and is also involved in adapting iOS for Xiaomi's car infotainment system. That answer made me even more excited.
Complete Interview Questions Summary
Below are all questions from the 4 rounds, organized by round:
Technical Round 1 (9 questions):
1. struct vs class, why prefer struct → Swift basics → ⭐⭐
2. where keyword in generics with example → Swift generics → ⭐⭐
3. Swift protocol vs OC protocol → Language comparison → ⭐⭐⭐
4. async/await vs GCD/Completion Handler → Swift concurrency → ⭐⭐⭐
5. weak vs unowned and use cases → Memory management → ⭐⭐
6. SwiftUI vs UIKit → Technical vision → ⭐⭐
7. Responder Chain mechanism → UI internals → ⭐⭐⭐
8. Method Swizzling principles and risks → Runtime → ⭐⭐⭐
9. How ARC works → Memory management → ⭐⭐
Technical Round 2 (6 questions):
1. Runtime message dispatch mechanism → Runtime internals → ⭐⭐⭐⭐
2. RunLoop underlying mechanism and applications → RunLoop → ⭐⭐⭐⭐
3. Retain cycle detection and resolution → Memory management → ⭐⭐⭐
4. GCD vs OperationQueue → Multithreading → ⭐⭐⭐
5. Category vs Extension → OC features → ⭐⭐⭐
6. KVO underlying implementation → Runtime → ⭐⭐⭐⭐
Technical Round 3 (5 questions):
1. Performance optimization experience → Practical skills → ⭐⭐⭐
2. Design an image caching framework → Architecture design → ⭐⭐⭐⭐
3. OOM investigation approach → Performance tuning → ⭐⭐⭐⭐
4. Understanding and practice of modularization → Architecture ability → ⭐⭐⭐
5. Configurable home feed architecture design → System design → ⭐⭐⭐⭐⭐
HR Round (5 questions):
1. Self-introduction → Communication skills → ⭐
2. Why Xiaomi → Motivation → ⭐⭐
3. Biggest achievement → Self-awareness → ⭐⭐
4. Salary expectations → Market awareness → ⭐⭐
5. Questions for us → Proactiveness → ⭐
Takeaways and Advice
In the end, I received Xiaomi's offer 5 days after the final interview, with roughly a 30% salary increase — overall, I'm quite satisfied. Looking back at the entire process, here are a few pieces of advice:
1. iOS fundamentals must be deep — surface-level knowledge won't cut it. Xiaomi's examination of Runtime, RunLoop, and memory management is very thorough. You can't get by just memorizing a few concepts. Things like RunLoop's sleep/wake mechanism and the complete Runtime message forwarding flow require genuine understanding of the underlying implementation, not just memorized conclusions. I recommend reading "Pro Objective-C" and Apple's official documentation, supplemented with source code reading.
2. Swift advanced features are a bonus — async/await and actors are must-prep topics. The async/await and actor questions in Round 1 made me realize that Swift's concurrency model is now a high-frequency interview topic. If you're still only using GCD, I'd recommend learning Swift Concurrency as soon as possible — it's not just for interviews, it's the future direction of iOS development.
3. Architecture design and scenario questions require accumulated experience — last-minute cramming has limited effect. The image caching framework design and home feed architecture design in Round 3 both require real project experience and design thinking. I recommend thinking more about "why it's designed this way" in your daily work, accumulating application scenarios for design patterns, rather than just focusing on "how to implement."
4. Be honest about what you don't know in interviews — don't make things up. In Round 2, I genuinely didn't know the kernel-space transition details for RunLoop, and I honestly said "I don't understand this deeply enough." The interviewer didn't deduct points for that. On the contrary, if you fabricate an answer and get caught in follow-up questions, it seriously hurts your evaluation. Admitting what you don't know and expressing a willingness to learn is a better strategy.
FAQ
Q1: Does the Xiaomi iOS interview require strong Objective-C fundamentals?
A: Yes, very much so. Even though daily development uses Swift, Runtime, RunLoop, and KVO are all OC-level underlying mechanisms that are guaranteed to come up. I recommend systematically studying OC's Runtime and memory management even if you primarily use Swift day-to-day.
Q2: Does the Xiaomi iOS interview test algorithms?
A: In my 4 rounds, there were no standalone algorithm questions, but the scenario design questions in Round 3 require a foundation in data structures and algorithms. I'd recommend at least completing LeetCode Hot 100, focusing on linked lists, trees, and dynamic programming.
Q3: How heavily is SwiftUI weighted in the interview?
A: Round 1 had one SwiftUI vs UIKit comparison question, and it wasn't deep. But if you list SwiftUI projects on your resume, the interviewer will definitely dig in. If you don't have real project experience, I'd recommend not over-emphasizing SwiftUI on your resume.
Q4: How does Xiaomi's interview difficulty compare to other big tech companies?
A: Personally, I'd rate Xiaomi's iOS interview difficulty as above-average — slightly easier than ByteDance's iOS interview, roughly on par with Baidu. The characteristic is deep fundamentals testing, but no intentional trick questions, and the interviewers are all quite friendly.
Q5: What project presentations should I prepare before the interview?
A: I'd recommend preparing 1-2 projects you can discuss in depth, highlighting the technical challenges you solved and the optimization results. Use the STAR method: Situation → Task → Action → Result. Especially for Results, quantify them — "reduced launch time from 2.3s to 1.5s" is far more convincing than "optimized launch speed."

