Небольшая предыстория

Доброго времени суток. Я iOS-разработчик (хлопки-хлопки).

Так получилось, что работал я iOS-разработчиком на одном легаси-проекте в компании-вендоре. В октябре прошлого года появилось желание сменить текущий проект, чтобы перестать волноваться о легаси и начать развиваться.

Поговорив с начальством, мне назначили встречу с лидом iOS, чтобы подготовить меня к предстоящим собеседованиям. Однако, после нескольких вопросов от лида вердикт был, что не подхожу на текущие проекты. На вопрос, какие, собственно, критерии оценивания я получил ответ:

"тебе никогда никто не расскажет???? в нашем деле все сам ????"

Сам так сам. Я начал читать, смотреть курсы, видеоуроки и активно подаваться в разные компании как в России, так и за рубежом, чтобы выяснить, какие, собственно говоря, критерии прохождения. Постепенно раз за разом сформировался список наиболее часто встречающихся вопросов на собеседованиях iOS-разработчика, который я решил опубликовать здесь.

Технические вопросы

Т.к. собеседования были в основном на английском языке, то с вашего позволения оставлю тут и вопросы и ответы на английском. Поехали.

1. What frameworks you used in your iOS projects?

You can specify frameworks like: UIKit, SwiftUI, Combine, AVFramework, PushNotification, CallKit, GCD, Core Bluetooth, etc.

2. How the Optional is implemented in Swift?

enum Optional<Wrapped>
{
    case none
    case some(Wrapped)
}

3. Is there a difference between .none and nil?

No

4. Ways to unwrap optional variables

var a:Int?
//1
a!
//2
if let s = a {
    print(s)
}
//3
if let a {
    print(a)
}
//4
guard let s = a else {
    print(s)
    return
}
//5
a ?? 0 //5

5. What's difference between reference and value types? What reference and value types you know?

Value types: structs, enums, arrays, dictionaries, strings

Reference types: classes, closures, NS types (NSString, for example) (because they are classes)

Two differences between value and reference types:

  1. Value types are stored in Stack and reference types are stored in Heap.

  2. If you assign one object to another for a reference type, you just copy the reference, not value:

// Reference type example
class C { var data: Int = -1 }
var x = C()
var y = x						// x is copied to y
x.data = 42						// changes the instance referred to by x (and y)
println("\(x.data), \(y.data)")	// prints "42, 42"

For value types you will copy the value of the variable.

6. What's difference between Class and Structure?

  • Structures are value types.

  • Classes are reference types.

  • Structures don’t support inheritance.

  • classes support inheritance.

  • Structures don’t support de-initializers. ( deinit )

  • Classes support de-initializers.

  • Structures don’t follow Reference Counting (look at the question 19 about ARC).

  • Classes follow Reference Counting.

  • Mutating Keyword is needed to modify the property values in Structure’s instance methods.

  • No need of mutating keyword to modify the class variable’s value.

7. Do you know what copy-on-write means?

If you assign one array to another (not only array, there are other objects), then the second object will refer to the first array address until the second array is not changed.

func addressOf(_ o: UnsafeRawPointer) -> String {
    let addr = unsafeBitCast(o, to: Int.self)
    return String(format: "%p", addr)
}
//our array
var array = [1, 2, 3, 4, 5]
addressOf(array) // 0x600002e30ac0
//here we assign one array to another
var array2 = array
//look - the address of the second array is the same
addressOf(array2) // 0x600002e30ac0
//here we change the second array 
array2.append(6)
//look again - address has changed
addressOf(array2) // 0x6000026119f0

8. Do you know what are SOLID principles?

SOLID is abbreviation.

S – single responsibility principle.

It’s when a class has just one purpose. A class shouldn’t contain functions that could be moved to other classes

O – open/closed principle (OCP)

A class should be opened for extension, but closed for changes.

Open closed principle allows to avoid this kind of code:

protocol SomeProtocol {
}

class A:SomeProtocol {
    func printClassAName() {
        print("I'm A")
    }
}

class B:SomeProtocol {
    func printClassBName() {
        print("I'm B")
    }
}

class Caller {
    func printClassName(obj:SomeProtocol){
      ////TO AVOID THIS KIND OF CODE!!!!!
        if let unwrappeObj = obj as? A {
            obj.printClassAName()
        }
        else if let unwrappeObj = obj as? B {
            obj.printClassBName()
        }
    }
}

It should be changed like that to avoid changes in Caller class in the future:

protocol SomeProtocol {
    func printClassName()
}

class A:SomeProtocol {
    func printClassName() {
        print("I'm A")
    }
}

class B:SomeProtocol{
    func printClassName() {
        print("I'm B")
    }
}

class Caller {
    func doSomething(obj:SomeProtocol){
        print(obj.printClassName())
    }
}

This principle is similar with D (dependency inversion principle), but OCP is more general. The DIP is an extension of the OCP.

L – Liskov principle.

In simple words this principle says that you need to have a possibility to use a parent and a child classes without any difference.

class A: SomeProtocol {
}

class B: SomeProtocol {
}

let a = A()
let b = B()
var a:[SomeProtocol] = [a, b]

I – interface segregation.

Classes SHOULDN'T implement protocol methods they don’t use. If we noticed this situation, just move these methods to a separate protocol.

D – dependency inversion.

It means that a class shouldn’t depend on low-level modules – they both should depend on an Abstraction

For example:

class FileSystemManager {
  func save(string: String) {
    // Open a file
    // Save the string in this file
    // Close the file
  }
}

class Handler {
    let fileManager = FilesystemManager()
    func handle(string: String) {
        fileManager.save(string: string)
    }
}

If in the future we’ll need to add other methods for saving data (data base, for example), we should inherit both FilesystemManager and this new data base interactor class from some Storage protocol and use it instead of FilesystemManager and other possible data saving ways:

class FileSystemManager:Storage {
    func save(string: String) {
        // Open a file
        // Save the string in this file
        // Close the file
    }
}

class DataBaseManager:Storage {
    func save(string: String) {
        // Open DB
        // Save the data
        // Close DB
    }
}

class Handler {
    let storage:Storage
    func handle(string: String) {
        storage.save(string: string)
    }
}

This principle is similar with OCP is more general. The DIP is an extension of the OCP.

Difference is that OCP is for similar functions, but DIP deals with the same input data

9. What is Singleton?

The main point of Singleton is to ensure that we initialized something only once and this "something" should be available from everywhere. For example, UIApplication.shared

P.S.: ServiceLocator – is a singleton with an array of some services

10. How are you doing your code reviews?

The best practice said that the code review should depend on CI/CD tests, a style guide, SOLID, and some linter (a syntax checker)

11. Application lifecycle

Use this:

Taken from Apple documentation
Taken from Apple documentation

12. ViewController lifecycle

  • ViewDidLoad - Called when you create the class and load from xib. Great for initial setup and one-time-only work.

  • ViewWillAppear - Called right before your view appears, good for hiding/showing fields or any operations that you want to happen every time before the view is visible. Because you might be going back and forth between views, this will be called every time your view is about to appear on the screen.

  • ViewDidAppear - Called after the view appears - great place to start an animations or the loading of external data from an API.

  • ViewWillDisappear/DidDisappear - Same idea as ViewWillAppear/ViewDidAppear.

  • ViewDidUnload/ViewDidDispose - In Objective-C, this is where you do your clean-up and release of stuff, but this is handled automatically so not much you really need to do here

P.S.: Can you say what ViewController lifecycle methods are calling when you started to segue from a view (A) to another view (B), but haven't finish it?

13. What architecture patterns you used?

Better to mention MVC, MVVM, VIPER, Clean Architecture. I recommend to implement test samples for each of these patterns.

14. What is VIPER?

VIPER – is an architecture pattern with these parts:

  • R – router – an entry point

  • E – entity – model (like in MVC, for example)

  • P – presenter – holds the reference to interactor, to a router and to a view

  • Presenter uses data, received using fetching data functions from Interactor to update View.

  • V – view. But with additional protocol with updating functions to call. For example, if we want to show an alert in a view, then we should ask the presenter to do that

  • I – Interactor handles business logic and data retrieval

15. What is Clean Architecture?

I used this scheme

Taken from here: https://medium.com/@info.vikaasyadav/flutter-clean-architecture-with-riverpod-7807e54228c4
Taken from here: https://medium.com/@info.vikaasyadav/flutter-clean-architecture-with-riverpod-7807e54228c4

16. What is MVVM?

MVVM - Model View ViewModel

  • Model - is the data layer.

  • View - is a view.

  • ViewModel - contains presentation logic (process data from Model to View, reacts on actions from the View and transfers these reactions to Model).

In the code tree there should be three different directories: Models, Views, ViewModels. Each of your classes should be represented separately there.

Sometimes it's wise to create an additional directory called Services, in which you can put your business logic.

P.S.: What is MVVM-C?

17. Who is an owner of data in MVVM?

Model is the data itself.

18. MVC and MVVM differences:

The main difference between MVC and MVVM is the role of the controller and view model. In MVC, the Controller handles user input and updates the Model and View. In MVVM the ViewModel handles user input and updates the Model and View, and the view is responsible for displaying the data.

19. What NS prefix means in some Swift and Objective-C classes?

It means next step (the name of one company)

20. How memory management works in iOS? (Automatic reference counter (ARC))

In this question it's better to tell about ARC and retain cycles.

Short explanation: ARC automatically keeps track of the number of references to an object, and when that number reaches zero, it deallocates the object. Counter decreases after an object releases. Any object deallocates after the counter is 0.

If two objects have a strong reference to each other – retain cycle. Use weak or unowned to avoid that.

(weak variables are presented as optional if to take a look on its type) (unowned variables are presented as usual (they can’t be nil))

My advise is to watch this video from Apple: 

https://developer.apple.com/videos/play/wwdc2021/10216/

21. What is map, flatMap, compatMap, reduce. Difference between map, flatMap, compatMap

Mathematically:

map:

var arr:[Int] = [1, 2, 3]
    arr = arr.map {
    return $0+1
}

flatMap (is equivalent to Array(s.map(transformation).joined())):

var arr:[Int] = [[1,2,3],[4,5,6]]
    arr = arr.flatMap {
    return $0
}

compatMap - same as map, but filters nil values

In Combine

map is used to transform each value emitted by a publisher using a provided closure. The closure takes in a value emitted by the publisher and returns a new value.

compatMap is similar to map, but it also filters out any values that are nil before emitting the new values

flatMap returns a new publisher with an emitted value as a parameter:

var imagesListSubject = PassthroughSubject<String, Error>()
                          imagesListSubject
                          .removeDuplicates()
                          .flatMap { [unowned self] day in
                            self.networkService.fetchDayImagesList(day: day)
                          }

22. How to make a multilevel dismiss in SwiftUI? (to dismiss multiple level navigation)

  1. You can use @EnvironmentObject, because it's available in all nested views

  2. You can transfer @Binding variable from the root view to new navigation levels and if you need, just toggle this variable

  3. Use an architecture pattern in which you can just set a current view. Like VIPER, REDUX and Composable architecture

23. What is a View protocol in SwiftUI?

In SwiftUI, the View protocol is the fundamental building block of layout and user interface. But without some content it can't exist

24. Why Views are structures in SwiftUI?

  • structs are simpler to work with and faster than classes

  • it takes less memory (it takes only what was set, without multilevel inheritance)

  • views that don’t mutate over time

25. Is there a way to use UIKit elements in SwiftUI?

Yes. You should create a class that conforms to UIViewRepresentable and UIViewControllerRepresentable protocols.

But this is a long story. If you are curious in implementation, just try to find a couple of examples.

26. Redux in iOS (example of button tapping)

A simple example in which a button increments a counter.

// The state of the application
struct AppState {
    var count: Int = 0
}

// The actions that can be dispatched to the store
enum CounterAction: Action {
    case increment
    case decrement
}

// The reducer, which handles the actions and updates the state
func counterReducer(action: Action, state: AppState?) -> AppState {
    var state = state ?? AppState()
    switch action {
    case let action as CounterAction:
        switch action {
        case .increment:
            state.count += 1
        case .decrement:
            state.count -= 1
        }
    default:
        break
    }
  
    return state
}

// The store, which holds the state and handles the actions
let store = Store<AppState>(
    reducer: counterReducer,
    state: nil
)

// The view, which displays the state and dispatches actions
struct ContentView: View {
    @ObservedObject var store: Store<AppState>
    var body: some View {
        VStack {
            Text("Count: \(store.state.count)")
            Button("Increment") {
                self.store.dispatch(action: CounterAction.increment)
            }
        }
    }
}

27. Composable architecture

  • View sends events to Action

  • Action sends an action to a Reducer (keeps state of the app alive)

  • Reduces mutate State, sends a call to Effect (outside world), interacts with Environment (dependencies, that are helpful for testing)

  • State influence View

28. What asynchronous functionality is available in Swift?

  • DispatchQueue

  • DispatchGroup

  • Operation queues

  • delegate events

  • Timer operations

  • Combine publisher that is sending data

  • async/await

A note: you should always update interface only on main thread (DispatchQueue.main), otherwise it can just stuck

29. What HTTP methods you know?

  • POST: Sends data to specific server to create or update information.

  • PUT: Sends data to specific server to create or update information without the risk of creating the resource more than once.

  • HEADER: Previews what the GET request response might be without the body of the text.

  • OPTIONS: Learns the communication channels used by the target source.

  • GET: Requests information from a specific source.

  • DELETE: Removes information.

30. How do you test network calls in Unit test?

There are several ways:

  1. You can mock network calls.

protocol NetworkServiceProtocol {
    func getDataFromServer(completion: @escaping (Result<Data, Error>) -> Void)
}

Then, create a mock implementation of the protocol that returns pre-defined data:

class MockNetworkService: NetworkServiceProtocol {
    func getDataFromServer(completion: @escaping (Result<Data, Error>) -> Void) {
        let data = Data("Mocked data".utf8)
        completion(.success(data))
    }
}

Now, in your test case, you can inject the mock network service into your code:

func testGetDataFromServer() {
    let mockService = MockNetworkService()
    let viewModel = MyViewModel(networkService: mockService)
    viewModel.getDataFromServer()
    // Assert that the view model processed the mocked data correctly
    XCTAssertEqual(viewModel.result, "Mocked data")
}
  1. You can mock not only network calls, you can mock entire classes using, for example, OCMock framework

  2. Apple recommends to do something like this, to mock URLSession configuration

https://developer.apple.com/videos/play/wwdc2018/417/

Here is the code example of mocked HTTP request using Combine:

import XCTest

class MockURLProtocol: URLProtocol {
    static var requestHandler: ((URLRequest) throws -> (HTTPURLResponse, Data))?
    
    override class func canInit(with request: URLRequest) -&gt; Bool {
        return true
    }

    override class func canonicalRequest(for request: URLRequest) -&gt; URLRequest {
        return request
    }

    override func startLoading() {
        guard let handler = MockURLProtocol.requestHandler else {
            XCTFail("Received unexpected request with no handler set")
            return
        }
        do {
            let (response, data) = try handler(request)
            client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
            client?.urlProtocol(self, didLoad: data)
            client?.urlProtocolDidFinishLoading(self)
        } catch {
            client?.urlProtocol(self, didFailWithError: error)
        }
    }

    override func stopLoading() {
    }
}

enum ServiceError: Error, Equatable {
    case invalidURL
    case noInternetConnection
    case requestTimeout
    case networkError
    case statusCodeError(code: Int?)
}

final class NetworkLayerTests: XCTestCase {
    var mockedUrlSession: URLSession!
    
    override func setUpWithError() throws {
        //exit test if something failes
        self.continueAfterFailure = false
        
        let configuration = URLSessionConfiguration.ephemeral
        
        //set up a mock for url session
        configuration.protocolClasses = [MockURLProtocol.self]
        mockedUrlSession = URLSession(configuration: configuration)
    }
  
    struct UserProfile:Codable {
        var name:String?
    }

    class ProfileAPI {
        let url = URL(string: "https://testURL.com/user")!
        private var cancellable: AnyCancellable?
        
        // session to be used to make the API call
        let session: URLSession
        
        // Make the session shared by default.
        // In unit tests, a mock session can be injected.
        init(urlSession: URLSession = .shared) {
            self.session = urlSession
        }
        
        // get user profile from backend
        func getProfile(completion: @escaping (UserProfile) -&gt; Void) {
            cancellable = session.dataTaskPublisher(for: url)
                .mapError { error -&gt; ServiceError in
                    switch error.code {
                    case .notConnectedToInternet:
                        return .noInternetConnection
                    case .timedOut:
                        return .requestTimeout
                    default:
                        return .networkError
                    }
                }
                .tryMap { data, response in
                    guard let httpResponse = response as? HTTPURLResponse,
                        200..&lt;300 ~= httpResponse.statusCode else {
                        throw ServiceError.statusCodeError(code: (response as! HTTPURLResponse).statusCode)
                    }
                    return data
                }
                .decode(type: UserProfile.self, decoder: JSONDecoder())
                .receive(on: RunLoop.main)
                .catch { _ in Just(UserProfile()) }
                .sink { user in
                    completion(user)
            }
        }
    }

    func testCase() throws {
        
        let example = UserProfile(name: "Some User")
        let mockData = try JSONEncoder().encode(example)
        
        //set return data in mock request handler
        MockURLProtocol.requestHandler = { request in
            let response = HTTPURLResponse(url: URL(string: "https://someURL.com/test")!,
                                           statusCode: 200,
                                           httpVersion: nil,
                                           headerFields: ["Content-Type": "application/json"])!
            return (response, mockData)
        }
    
        //this is simpler example, but without http-status mocking
    
    //        MockURLProtocol.requestHandler = { request in
    //            return (HTTPURLResponse(), mockData)
    //        }
        // Set expectation. Used to test async code.
        let expectation = XCTestExpectation(description: "response")
        
        // Make mock network request to get profile
        // here we use the previously set mocked UrlSession
        let profileAPI = ProfileAPI(urlSession: mockedUrlSession)
        
        profileAPI.getProfile { user in
            // Test
            XCTAssertEqual(user.name, "Some User")
            expectation.fulfill()
        }
        wait(for: [expectation], timeout: 1)
    }

}

31. What is the role of the "final" word in the class?

It prevents properties and functions from overriding.

32. What are lazy variables?

They initialize after the first time they are calling.

33. Pros and cons of using UIKit and SwiftUI

34. Is there a difference between "Codable" and "Encodable & Decodable" protocol inheritance?

No, Codable is a type alias for Encodable & Decodable

35. What are Encodable and Decodable protocols used for?

protocol Decodable : allows to decode bytes to the type that inherits Decodable

protocol Encodable : allows to represent a type as data bytes

Often they are used for interaction with JSON and plist.

P.S.: There could be a related question, can we rename keys during encoding/decoding?

Yes, we can using CodingKey syntax

struct Person: Decodable {
    var personName: String
    enum CodingKeys: String, CodingKey {
       case personName = "name"
    }
}

36. How would you explain App Transport Security (ATS) to a junior?

ATS blocks insecure URLSession connections. (Security criteria are shown here https://developer.apple.com/documentation/security/preventing_insecure_network_connections)

There are two ways to prevent connections blocking:

  1. Set "Allow Arbitrary Loads" - it is not a good approach, but it looks like that:

<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
  1. You can set the exceptions. That is the good approach. It looks like that:

<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<false/>
<key>NSExceptionDomains</key>
<dict>
<key>exception.com</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>

37. How would you explain Dependency Injection to a junior?

Dependency Injection is an approach, when functionality of one entity depends on the other entity and the FIRST gets the SECOND as a parameter.

I would provide an example of MVVM implementation, because link to ViewModel in View is a good example of Dependency Injection.

38. What's difference between @escaping and non-escaping closures?

@escaping launches after the function ends, non-escaping - just after its call.

39. What's difference between inout parameter of the function and a usual parameter?

func example(_ a: Int, _ b: inout Int) {}

inout keyword allows to change the value of the parameter variable.

40. Do classes support multiple inheritance in Swift?

If to try to inherit from multiple classes, then NO. Swift classes don't support multiple inheritance.

But you can inherit multiple protocols.

Некоторые выводы с собеседований

Главные вещи, которые я заметил на собеседованиях:

  1. Скорее всего то, что вы делаете на проекте с технической точки зрения не поможет пройти собеседование, потому как вопросы могут быть из совсем уж разных областей работы с iOS. То есть, готовиться придётся по-любому.

  2. Скорее всего, если вы на собеседовании начали смеяться над чем-то вместе с интервьюером, то вас точно не возьмут, ибо, "не время улыбаться".

  3. Если вы сделали ошибку во время life coding interview, то считайте, что вы завалили интервью:

    Почти все на тренингах по собеседованиям говорят, что если вы не знаете, как решить какую-то проблему или ответить на специфический технический вопрос, то это не значит, что вы не прошли интервью. По факту это именно это и означает. Всегда найдётся кто-то, кто на эти вопросы ответит правильно. Они это отрицают, но что есть, то есть. Поэтому готовьтесь лучше, больше, сильнее.

Комментарии (18)


  1. BugM
    02.04.2023 01:17

    Скорее всего, если вы на собеседовании начали смеяться над чем-то вместе с интервьюером, то вас точно не возьмут, ибо, "не время улыбаться".

    А вот это вы зря.

    Берем случайный старый и очень известный мем. (Выбирал быстро, если подготовиться то можно лучше. Даже под резюме подобрать можно. Чтобы максимально непонятно для всех, но человек с таким резюме должен понять)

    мем

    Разыгрываем первую его часть. Улыбаемся и смотрим на кандидата. Если у кандидата в резюме: "сеньор, много лет работы, софт скилы и все как обычно" и он при этом хлопает глазами и ничего не понимает то это очень тревожный звоночек. С ним скорее всего что-то не так.

    Всего 5 минут времени, а сразу куча информации о человеке получается.


    1. TERMIK Автор
      02.04.2023 01:17
      -1

      Возможно, это какая-то статистическая погрешность, но у меня практически в 100% случаев смех означал отказ. Самому интересно, почему)


      1. BugM
        02.04.2023 01:17

        Это значит что вы не поняли над чем смеялись.

        У вас есть небольшая БД на 100 гигабайт в прыжке. Приходит к вам тимлид фронта со словами надо туда положить поле с ямлом. В нем будут какие-то конфиги ваших юзеров. Бизнесу надо, тут все ок. Что делать будете?

        ответ

        Вежливо и культурно послать его описывать нормальные типы того что там лежать будет. И юзкейсы как это будет использоваться и расширяться. Помощь предложить тоже стоит. Любой другой ответ как раз вызовет смех у меня. Ладно не совсем любой, но многие варианты ответа. И no hire кандидату. Если кандидат вежливо посмеется вместе со мной то тем более no hire. Он мало того что неверно ответил, так еще и подхалим.


        1. TERMIK Автор
          02.04.2023 01:17

          По мне так проще не смеяться на собеседовании, чем выяснять, оскорбил ли я кого-нибудь смехом или шуткой.


          1. BugM
            02.04.2023 01:17
            +1

            Шутки на собеседовании только над кодом и над типичными проблемами разработки. Над собой шутить можно всем и всегда.

            Вы устраиваетесь работать к живым людям. С которыми вы будете проводить по 8 часов в день 5 дней в неделю годами. Мы все люди. Мы шутим. По разному шутим. Кандидат не понимающий о чем речь вызывает подозрения. Он вообще с коллегами говорил до этого? Лучше отказать.

            Звезды могли и не говорить с коллегами, но поймут о чем речь и так. На то они и звезды.


  1. opexa2
    02.04.2023 01:17
    +1

    С выводами не согласен, а шпаргалка хорошая.


    1. TERMIK Автор
      02.04.2023 01:17

      И рад бы согласиться, но увы. Зачастую даже правильно написанный код не означал прохождение этого собеседования. А когда кандидатов на должность от 50 человек и больше, то ошибка стоит дорого.


  1. varton86
    02.04.2023 01:17

    Спасибо за свежий фидбек с собеседований) Относительно простые вопросы, почти не затронута тема асинхронности/многопоточности, работа с памятью, zombi object, side table, responder chain, hit test, autolayout, size classes и т.д.


    1. TERMIK Автор
      02.04.2023 01:17

      Про autolayout спрашивали только в чём разница между frame и bounds. Но довольно редко, поэтому даже записать забыл. Да и в принципе, про UIKit редко кто спрашивал. Больше вопросов было про SwiftUI.

      Про асинхронность спрашивали в основном про то, какие механизмы есть, но не как они работают, поэтому уложил всё в один вопрос: "What asynchronous functionality is available in Swift?"

      По поводу работы с памятью никто дальше ARC не спрашивал. Хотя, три года назад памяти меня спрашивали про работу с памятью в Objective-C, про разницу между strong, retain, atomic, nonatomic, и т.д. Но за последнее время такого точно не было.

      А вот про остальное ни разу не спрашивали за последнее время. Разве что, года три назад.
      А можете рассказать, какие бы вы задали вопросы по темам: асинхронности/многопоточности, работа с памятью, zombi object, side table, responder chain, hit test, autolayout, size classes ?


      1. varton86
        02.04.2023 01:17

        Вопросов можно много задавать) стандартные вопросы,

        по асинхронности/многопоточности: какие бывают очереди, чем отличаются, что такое dead lock, race condition, инверсия приоритетов, как выполнить группу асинхронных запросов, как отменить задачу, чем отличаются GCD/Operation/async и т.д.

        по работе с памятью: как хранятся ссылки и выполняются методы в классах/потомках, как высвобождаются объекты, weak/self/unowned и т.д.

        responder chain, hit test: какие бывают жесты, как обрабатываются нажатия, какие есть нюансы и т.д.

        autolayout, size classes: что такое autolayout, как сделать адаптивную верстку на разных устройствах, как верстать кодом, когда лучше использовать сториборды, что такое констрейнты и для чего нужны и т.п.

        Интересно, что стало много вопросов по SwiftUI, при том что в проде его все-таки немного.


        1. varton86
          02.04.2023 01:17

          А, ну еще можно поспрашивать про хранение данных в CoreData, Keychain, UserDefaults, обработку пушей и диплинков.


          1. TERMIK Автор
            02.04.2023 01:17

            Вообще, эти комментарии заставили вспомнить из-за чего я и решил этот сборник сделать.
            Несколько разных компаний могут написать одни и те же требования в вакансии, к примеру, вот вполне стандартная вакансия:

            • 3+ years of experience in iOS development.

            • Experience with ObjC, Swift and SwiftUI.

            • Familiarity with RESTful APIs to connect applications to back-end services.

            • Knowledge of iOS design principles, patterns, and best practices.

            А как придёшь: одна компания спросит про многопоточность и App Transport Security, другая - про жизненный цикл приложения и ViewController, третья - про наследование классов и Codable protocol, четвёртая про то, какие бывают publisher-ы в Combine. И всё это вакансии с одним и тем же описанием.

            А ты при этом занимался какими-нибудь VPN-ками, Bluetooth Low Energy и REST-клиентскими приложениями.

            В iOS много непересекающихся друг с другом областей, а каждая компания при этом требует что-то своё и требует практически 100% совпадения с используемыми ими фреймворками.

            Потому и решил несколько обобщить.


            1. varton86
              02.04.2023 01:17
              +1

              Согласен, ведь еще есть, например, SpriteKit, SceneKit, AR и ML, которые вообще не так часто используются. Но, как правило, iOS разработчику приходилось с ними сталкиваться, если не по работе, то в пет проектах.


        1. TERMIK Автор
          02.04.2023 01:17

          Интересно, что стало много вопросов по SwiftUI, при том что в проде его все-таки немного.

          Наверное, от компании зависит. Последнее время часто слышал, что, мол, у нас всё было написано на UIKit, но мы всё перевели (или сейчас переводим) на SwiftUI.
          Со SwiftUI есть ещё проблемы, это да, но сейчас что-то новое нативное если пишут, то чаще выбирают SwiftUI.

          по асинхронности/многопоточности: какие бывают очереди, чем отличаются,
          что такое dead lock, race condition, инверсия приоритетов, как выполнить
          группу асинхронных запросов, как отменить задачу, чем отличаются
          GCD/Operation/async и т.д.

          Хорошие вопросы. Некоторые из них действительно встречались. Особенно dead lock, race condition.

          responder chain, hit test: какие бывают жесты, как обрабатываются нажатия, какие есть нюансы и т.д.

          autolayout, size classes: что такое autolayout, как сделать адаптивную верстку на разных устройствах, как верстать кодом, когда лучше использовать сториборды, что такое констрейнты и для чего нужны и т.п.

          Любопытно. Такое не встречалось, разве что, про constraints, но спасибо за дополнение.


  1. ws233
    02.04.2023 01:17

    А где же вопросы про мягкие навыки?

    Какой ответ вызвал смех у вас обоих?

    И устроились ли Вы в итоге, зазубрив ответы на самые популярные вопросы?


    1. TERMIK Автор
      02.04.2023 01:17

      Тут я привёл только технические вопросы. Вопросы про мягкие навыки общие для всех разработчиков, не только для iOS.

      Смех. Буду учитывать случаи, где технические вопросы были отвечены хорошо. Помню как минимум три случая:

      1) Как-то раз посмеялись над полнотой документации Apple, к примеру. Что описание частенько ограничивается прототипом функции.

      2) Стало как-то раз смешно после сравнения обработчиков ошибок в Си-образных языках и в Swift.

      3) Над удобством constrains

      Это просто к примеру. Почему это вызвало отказ на техническом собеседовании - не знаю, но смех был единственным общим моментом в этих собеседованиях при правильно отвеченных вопросах.

      И устроились ли Вы в итоге, зазубрив ответы на самые популярные вопросы?

      Ну, прям сидеть и зубрить - это не практично. Просто, если слышал вопрос, то пробовал это использовать в каком-нибудь тестовом проекте.

      Как я в одном из комментариев уже отвечал: в iOS множество непересекающихся областей, работая над которыми можно никогда за затрагивать другие области. А спросить могут что угодно при одном и том же описании вакансии.

      К примеру, вопрос, что такое ServiceLocator. Или надо ли держать в голове такие вопросы как, например, какие есть события у делегата UITableView, когда я уже 3 года использую SwiftUI и в описании вакансии стоит SwiftUI? Показывает ли реально твои навыки работы с iOS? На мой взгляд, не особо.

      Не думаю, что я поступаю нечестно, составляя такой список. Не всегда же держишь в голове вещи, которые раз в год от силы применяются, как например App Transport Security. А если спросят и не ответишь - откажут.

      P.S.: Устроился в одну московскую компанию.

      P.P.S.: А вы знаете более способ получше, чтобы подготовиться к iOS собеседованию, чем вот такой итеративный?


      1. ws233
        02.04.2023 01:17

        Нет. Способ подходящий. Сам им активно пользуюсь, только не заморачиваюсь со списком :)

        Просто мне кажется, что Вы ошибаетесь, когда говорите, что из-за одного неотвеченного хардварного вопроса Вам отказывают потому, что находится тот, кто отвечает и на этот вопрос.

        Хардварные вопросы, кажется, уже давно не являются критерием приема во многих компаниях (ну, если не ищут супер-пупер-архитектора). Они, скорее, просто повод, чтобы о чем-то поговорить. Чтобы заполнить пустоту на собеседовании. С таким же успехом можно было бы поговорить и о Вашем хобби, но это бы у Вас еще большее недоумение вызвало и гневную статью о бренде на Хабре. Поэтому и принято говорить о том, что меньший стресс у человека вызывает. :)

        А решение принимают все же по другим критериям. Иногда чисто по очень субъективным. Увы и ах, но да.

        Поэтому и спросил про мягкие навыки. Без их упоминания и анализа делать глубокие выводы, которые делаете Вы, немножко некорректно. Нужно анализировать всю картину целиком. Возможно, Вы как раз проваливали мягкую часть и шутейки ^.^


        1. TERMIK Автор
          02.04.2023 01:17

          Ну, поначалу были отказы с формулировкой: плавал в некоторых вопросах. Поэтому и поставил в вывод, что это фатально)

          Было и такое: с командой прямо как родные оказались - очень душевно побеседовали по многим вопросам, но я не знал, можно ли closure как weak сделать и это оказалось решающим фактором.

          Порой возникало ощущение, что половина компаний не прощупывает твои навыки, а ищет малейший повод тебя не взять)