The average iOS Developer salary in Australia is $120,000 per year or $61.54 per hour. Entry level positions start at $66,250 per year while most experienced workers make up to $162,900 per year. Follow along and check 37 most common iOS/Swift/Objective-C interview questions for senior and experienced Apple developers.
x in the Swift code below. Explain your answer.Consider the code:
var a1 = [1, 2, 3, 4, 5]
var a2 = a1
a2.append(6)
var x = a1.countDetermine the value of x.
In Swift, arrays are implemented as structs, making them value types rather than reference types (i.e., classes). When a value type is assigned to a variable as an argument to a function or method, a copy is created and assigned or passed. As a result, the value of x or the count of array a1 remains equal to 5 (answer) while the count of array a2 is equal to 6, appending the integer 6 onto a copy of the array a1. The arrays appear in the box below.
a1 = [1, 2, 3, 4, 5]
a2 = [1, 2, 3, 4, 5, 6]Categories provide the ability to add functionality to an object without subclassing or changing the actual object. A handy tool, they are often used to add methods to existing classes, such as NSString or your own custom objects.
Consider a category to the Cocoa class NSString:
#import <Foundation/Foundation.h>
@interface NSString(MyAdditions)
+(NSString *)getCopyRightString;
@end
@implementation NSString(MyAdditions)
+(NSString *)getCopyRightString {
return @"Copyright TutorialsPoint.com 2013";
}
@end
int main(int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString *copyrightString = [NSString getCopyRightString];
NSLog(@"Accessing Category: %@",copyrightString);
[pool drain];
return 0;
}Core Data is an object graph management framework. It manages a potentially very large graph of object instances, allowing an app to work with a graph that would not entirely fit into memory by faulting objects in and out of memory as necessary. Core Data also manages constraints on properties and relationships and maintains reference integrity (e.g. keeping forward and backward links consistent when objects are added/removed to/from a relationship). Core Data is thus an ideal framework for building the "model" component of an MVC architecture.
To implement its graph management, Core Data happens to use SQLite as a disk store. It could have been implemented using a different relational database or even a non-relational database such as CouchDB. Core Data isn't so much a database engine as it is an API that abstracts over the actual data store. You can tell Core Data to save as an sqlite database, a plist, a binary file, or even a custom data store type.
A Serial Queue allows us to perform only one task at a time, no matter the way of execution, i.e. Synchronous or Asynchronous. In short, the code executed by Serial DispatchQueue is not parallel and has to wait for the first task to complete. This way of execution is also known as First In, First Out (FIFO).
All the queues need to wait for the completion of the previous queue. By default, DispatchQueue is a serial queue.
Consider:
let queue = DispatchQueue(label: "com.swiftpal.dispatch.serial")
queue.async() {
Thread.sleep(forTimeInterval: 3) // Wait for 3 seconds
print("Task 1 Done")
}
queue.async() {
Thread.sleep(forTimeInterval: 1) // Wait for 1 second.
print("Task 2 Done")
}
/* Output:
Task 1 Done
Task 2 Done
*/copy and retain?In a general setting, retaining an object will increase its retain count by one. This will help keep the object in memory and prevent it from being blown away. What this means is that if you only hold a retained version of it, you share that copy with whomever passed it to you.
Copying an object, however you do it, should create another object with duplicate values. Think of this as a clone. You do NOT share the clone with whomever passed it to you.
== and ===?== operator checks if the values are the same, comparing value types. "equal to"=== operator checks if the references point the same instance (both point to the same memory address), comparing reference types. "identical to"Take three objects: a grandparent, parent and child. The grandparent retains the parent, the parent retains the child and the child retains the parent. The grandparent releases the parent. Explain what happens.
There are a few different lifecycle events
- loadView
Creates the view that the controller manages. It’s only called when the view controller is created and only when done programatically. It is responsible for making the
viewproperty exist in the first place.
- viewDidLoad
Called after the controller’s view is loaded into memory. It’s only called when the view is created.
- viewWillAppear
It’s called whenever the view is presented on the screen. In this step the view has bounds defined but the orientation is not applied.
- viewWillLayoutSubviews
Called to notify the view controller that its view is about to layout its subviews. This method is called every time the frame changes
- viewDidLayoutSubviews
Called to notify the view controller that its view has just laid out its subviews. Make additional changes here after the view lays out its subviews.
- viewDidAppear
Notifies the view controller that its view was added to a view hierarchy.
- viewWillDisappear
Before the transition to the next view controller happens and the origin view controller gets removed from screen, this method gets called.
- viewDidDisappear
After a view controller gets removed from the screen, this method gets called. You usually override this method to stop tasks that are should not run while a view controller is not on screen.
- viewWillTransition(to:with:)
When the interface orientation changes, UIKit calls this method on the window’s root view controller before the size changes are about to be made. The root view controller then notifies its child view controllers, propagating the message throughout the view controller hierarchy.
Signing an application allows the system to identify who signed the application and to verify that the application has not been modi ed since it was signed. Signing is a requirement for submitting to the App Store (both for iOS and Mac apps). OS X and iOS verify the signature of applications downloaded from the App Store to ensure that they they do not run applications with invalid signatures. This lets users trust that the application was signed by an Apple source and hasn’t been modifed since it was signed.
Xcode uses your digital identity to sign your application during the build process. This digital identity consists of a public-private key pair and a certificate. The private key is used by cryptographic functions to generate the signature. The certificate is issued by Apple; it contains the public key and identifies you as the owner of the key pair.
An application’s executable code is protected by its signature because the signature becomes invalid if any of the executable code in the application bundle changes. Resources such as images and nib files are not signed; a change to these files does not invalidate the signature.
An application’s signature can be removed, and the application can be re-signed using another digital identity. For example, Apple re-signs all applications sold on the App Store. Also, a fully- tested development build of your application can be re-signed for submission to the App Store. Thus the signature is best understood not as indelible proof of the application’s origins but as a variable mark placed by the signer.
Every iOS app is always in one of the five app states. The operating system manages the app state, but the app itself is responsible for managing important tasks to ensure smooth transitions between the states. The five states of an iOS app - as listed in the iOS App Programming Guide - include the following:
NSUserDefaults: stores simple user preferences, nothing too complex or secure. If your app has a setting page with a few switches, you could save the data here.
Keychain (see SSKeychain for a great wrapper): used to store sensitive data, like credentials.
PLists: used to store larger structured data (but not huge): it is a really flexible format and can be used in a great number of scenarios. Some examples are:Object archiving could be useful to serialize more complex objects, maybe full of binary data, that can't (or that you don't want to) be mapped on simpler structures like plists.
Core Data is powerful, can be backed by different persistent stores (SQLite is just one of them, but you can also choose XML files or you can even write your own format!), and gives relationships between elements. It is complex and provides many features useful for the development, like KVO and contexts. You should use it for large data sets of many correlated records, that could be user generated or provided by a server.
Raw SQLite is useful when you need really, really fast access to a relational data source (Core Data introduces some overhead), or if you need to support the same SQLite format across multiple platforms (you should never mess with CoreData inner SQLite: it uses its own format, so you can't just "import" an existing SQLite in CoreData). For example, for a project I worked for, a webservice provided me some large SQLite instead of jsons or xmls: some of this SQLite were imported to CoreData (operation that could take a while, depending on the source size), because I needed all the features of it, while other SQLites were read directly for a really fast access.
Webserver storage well it should be obvious: if you need to store data to a server it is because the device shouldn't be the only owner of that data. But if you just need to synchronize the same App across different iOS devices (or even with a Mac-ported version of the App) you could also look at iCloud storage, obviously.Consider:
@interface HelloWorldController : UIViewController
@property (strong, nonatomic) UILabel *alert;
@end
@implementation HelloWorldController
- (void)viewDidLoad {
CGRect frame = CGRectMake(150, 150, 150, 50);
self.alert = [[UILabel alloc] initWithFrame:frame];
self.alert.text = @"Hello...";
[self.view addSubview:self.alert];
dispatch_async(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^{
sleep(10);
self.alert.text = @"World";
}
);
}
@endAll UI updates must be performed in the main thread. The global dispatch queue does not guarantee that the alert text will be displayed on the UI. As a best practice, it is necessary to specify any updates to the UI occur on the main thread, as in the fixed code below:
dispatch_async(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^{
sleep(10);
dispatch_async(dispatch_get_main_queue(), ^{
self.alert.text = @"World";
});
});Consider:
let list = [Int](1...5)
var arrayOfTuples = [(Int, Int)]()
for (index, element) in list.enumerated() {
arrayOfTuples += [(index, element)]
}
print(arrayOfTuples) // prints [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]Can you rewrite this code in a more "swiftier" way?
Yeap, the way to do it is:
let list = [Int](1...5)
let arrayOfTuples = Array(list.enumerated())
print(arrayOfTuples) // prints [(offset: 0, element: 1), (offset: 1, element: 2), (offset: 2, element: 3), (offset: 3, element: 4), (offset: 4, element: 5)]or with map:
let list = [Int](1...5)
let arrayOfDictionaries = list.enumerated().map { (a, b) in return [a : b] }
print(arrayOfDictionaries) // prints [[0: 1], [1: 2], [2: 3], [3: 4], [4: 5]]self.self.func someFunc{
//some code
}
class someClass{
func someMethod{
//some code
}
}as?, as! and as in Swift?as can only be used for upcasting (or disambiguation) and pattern matching:// 'as' for pattern matching
switch item {
case let obj as MyObject:
// this code will be executed if item is of type MyObject
case let other as SomethingElse:
// this code will be executed if item is of type SomethingElse
...
}as? produces an optional value, which is either the value if it can be cast to the specified type, or nil if it can't.as! doesn't produce an optional, it just produces a value of the specified type, and if the cast fails, it aborts the program. Saying foo as! SomeType is basically the same thing as saying (foo as? SomeType)! (except you get a better error message).You should only ever use as! if you're 100% certain the cast will succeed (because if you're wrong, the whole program aborts).
When an event happens in a view, for example a touch event, the view will fire the event to a chain of UIResponder objects associated with the UIView. The first UIResponder is the UIView itself, if it does not handle the event then it continues up the chain to until UIResponder handles the event. The chain will include UIViewControllers, parent UIViews and their associated UIViewControllers, if none of those handle the event then the UIWindow is asked if it can handle it and finally if that doesn't handle the event then the UIApplicationDelegate is asked.
Consider:
let and var in Swift?Is there any changes in your answer for reference types?
For fundamental types the let keyword defines a constant:
let theAnswer = 42The value of a constant doesn’t need to be known at compile time, but you must assign the value exactly once.
The var defines an ordinary variable.
Both var and let are references/pointers, therefore let is a const reference/pointers. Using fundamental types doesn't really show how let is different than const. The difference comes when using it with class instances (reference types):
class CTest
{
var str : String = ""
}
let letTest = CTest()
letTest.str = "test" // OK
letTest.str = "another test" // Still OK
//letTest = CTest() // Error
var varTest1 = CTest()
var varTest2 = CTest()
var varTest3 = CTest()
varTest1.str = "var 1"
varTest2.str = "var 2"
varTest3 = varTest1
varTest1.str = "var 3"
varTest3.str // "var 3"Generally there are the following ways to store data in order from simple to complex:
deinit?It's not required that you implement that method, but you can use it if you need to do some action or cleanup before deallocating the object.
Consider:
struct Bank {
static var coinsInBank = 10_000
static func vendCoins(var numberOfCoinsToVend: Int) -> Int {
numberOfCoinsToVend = min(numberOfCoinsToVend, coinsInBank)
coinsInBank -= numberOfCoinsToVend
return numberOfCoinsToVend
}
static func receiveCoins(coins: Int) {
coinsInBank += coins
}
}
class Player {
var coinsInPurse: Int
init(coins: Int) {
coinsInPurse = Bank.vendCoins(coins)
}
func winCoins(coins: Int) {
coinsInPurse += Bank.vendCoins(coins)
}
deinit {
Bank.receiveCoins(coinsInPurse)
}
}So whenever the player is removed from the game, its coins are returned to the bank.
strong, weak and unowned references?NSUserDefault?NSUserDefaults as the name suggests (vaguely) should be used for storing preferences and app settings only. You should not be storing critical data and or user data into them. Use UserDefaults in small projects or to store some flags. Don't use UserDefaults to store big data, such as image caching.
CoreData is a full fledged persistent framework which supports large data transactions. CoreData allows you to build relational entity–attribute model for storing user data. Use CoreData in big projects. CoreData entities are generally a better design than arbitrary values stored by keys, even if later serialized to objects.
If you are adding functions, but not data, use a category. An example of this is adding functions to NSMutableData which allow you to remove X bytes from a section of the data, or remove all BUT X bytes from the data. It doesn't make sense to create an entire sub-class simply to add these two functions, nor does it makes sense to write functions that aren't attached to a class (a plain C function). This allows you to attach the functionality to the class without creating a new, uneeded relationship, and doesn't change any part of existing functionality.
If you need to add/change data and add functionality to manipulate that data, or change functionality to represent a sub-type of object in your controller model, then you sub-class. For example, NSMutableData is a sub-class because it still uses NSData accessors, however, it changes the data representation internally so it can also manipulate the data. In this case a sub-class makes sense because it is no longer NSData, it is a /mutable/ NSData object (new keyword added to describe the object).
mutating function?@property with @dynamic or @synthesize?[String: Any] or [String: AnyObject]?static variable and a class variable?async and sync blocks@escaping and @nonescaping Closures in Swift?init?() and init()?Rust has been Stack Overflow’s most loved language for four years in a row and emerged as a compelling language choice for both backend and system developers, offering a unique combination of memory safety, performance, concurrency without Data races...
Clean Architecture provides a clear and modular structure for building software systems, separating business rules from implementation details. It promotes maintainability by allowing for easier updates and changes to specific components without affe...
Azure Service Bus is a crucial component for Azure cloud developers as it provides reliable and scalable messaging capabilities. It enables decoupled communication between different components of a distributed system, promoting flexibility and resili...