patternswiftMinor
Interview coding challenge for iOS Part 2 - the application in Objective-C and Swift
Viewed 0 times
swifttheapplicationioschallengepartinterviewforobjectiveand
Problem
I recently interviewed with a company that needed a C/C++ programmer to work on the iOS side of the products. The job description indicated they needed someone with 4 years of Objective-C and iOS programming and I was surprised that they wanted to interview me.
Prior to this coding challenge I have never worked in Xcode, or programmed in iOS, Objective-C or swift. I am an absolute beginner in these areas. I still don't think I know these programming environments but I am learning.
Environment
The following section is the extraction of the email the hiring manager sent me:
Programming Challenge:
Create a static library or iOS Framework using Objective-C that
performs the following 3 functions:
Build a simple application with 3 buttons and a label where text can
be displayed. Each button should call into the three functions of the
library described above and output the response to the label. Your
application should consist of two tabs, one written in Objective-C and
one written in Swift. Both tabs should call into the same Objective-C
library and perform the same function.
Only use Apple frameworks to complete this task. Fully comment your
code explaining your logic and choices where multiple choices are
available. For example, Apple provides numerous ways to retrieve a
network resource, document why you choose the solution you did.
This question has been divided into 2 parts based on the size of the code to be reviewed. One part contains the Objective-C static l
Prior to this coding challenge I have never worked in Xcode, or programmed in iOS, Objective-C or swift. I am an absolute beginner in these areas. I still don't think I know these programming environments but I am learning.
Environment
- OSX - El Capitan
- Xcode - Version 8.2 (8C38) // Swift 3
- Running in the iPhone 7 simulator
- Late 2010 17 inch MacBook Pro
The following section is the extraction of the email the hiring manager sent me:
Programming Challenge:
Create a static library or iOS Framework using Objective-C that
performs the following 3 functions:
- Collects the GPS location (latitude and longitude) of the user at a point in time
- Collects the battery state and returns whether or not the device is plugged in and what percentage of life is left
- Accesses any publicly available, free API to collect the data of your choice and returns it (this should be a network call)
Build a simple application with 3 buttons and a label where text can
be displayed. Each button should call into the three functions of the
library described above and output the response to the label. Your
application should consist of two tabs, one written in Objective-C and
one written in Swift. Both tabs should call into the same Objective-C
library and perform the same function.
Only use Apple frameworks to complete this task. Fully comment your
code explaining your logic and choices where multiple choices are
available. For example, Apple provides numerous ways to retrieve a
network resource, document why you choose the solution you did.
This question has been divided into 2 parts based on the size of the code to be reviewed. One part contains the Objective-C static l
Solution
(The question is more than two years old, and both Xcode and Swift have been developed substantially in that time. The following review is written with the current Xcode 10.3 and Swift 5 in mind.)
Creating the user interface
Both the Objective-C and the Swift implementation create the user interface purely in code, which is laborious and error-prone. There are also some problems with your implementation.
First, the buttons and labels are created in
Next, the layout is not updated correctly on an orientation change:
If you really want to create the user interface programmatically then the
However, this is much easier done in the Xcode Interface Builder where you can design the views in a Storyboard, connect the button actions to code, etc.
And a better method to place the user interface elements correctly is Auto Layout. With Auto Layout you define a set of constraints between all UI elements. This can be done in a way that the views automatically adapt to various screen sizes and orientation changes.
The layout constraints can be defined in the Xcode interface builder or in code (although defining them in code is more work).
The action methods
This
looks like a design error to me: The model should not create an UI element and interact with with the user.
Here
several things can be improved:
With these suggestions, the action method could look like this:
However, this does not provide the best user experience: If the data model could not be created (for whatever reason) then the user is notified of that fact only after pressing the button.
The better approach is to enable or disable a button depending on whether its functionality is available or not.
The same remarks apply to the other action method
Initializing the data model
Initializing the
(For Swift, see for example Whither dispatch_once in Swift 3? on Stack Overflow.)
Now you can simply test
to check if the model is available or not. However, this
has the only effect that the user is presented with a black screen if the model is not available. (The return value from the
As mentioned above, the better approach is to enable or disable UI elements accordingly.
Further remarks
-
There is a Core Data model and corre
Creating the user interface
Both the Objective-C and the Swift implementation create the user interface purely in code, which is laborious and error-prone. There are also some problems with your implementation.
First, the buttons and labels are created in
viewWillLayoutSubviews, which can be called multiple times per view. Using the "Debug View Hierarchy" feature of Xcode you can see that all labels and buttons are created twice right after the application start:Next, the layout is not updated correctly on an orientation change:
If you really want to create the user interface programmatically then the
viewDidLoad methods is a better place. This method is called exactly once.However, this is much easier done in the Xcode Interface Builder where you can design the views in a Storyboard, connect the button actions to code, etc.
And a better method to place the user interface elements correctly is Auto Layout. With Auto Layout you define a set of constraints between all UI elements. This can be done in a way that the views automatically adapt to various screen sizes and orientation changes.
The layout constraints can be defined in the Xcode interface builder or in code (although defining them in code is more work).
The action methods
This
let gpsAlert : UIAlertController? = self.displayDataModel!.provideGPSAlerters();
if (gpsAlert != nil) {
self.present(gpsAlert!, animated:false, completion:nil);
}looks like a design error to me: The model should not create an UI element and interact with with the user.
Here
@objc func setLabelWithBatteryLevelAndState() {
var actionString : String = "Get Battery Level and State";
if (self.displayDataModel != nil) {
actionString = (self.displayDataModel?.provideBatteryLevelAndState())!
}
else {
actionString = "Battery Button Action Failure: Data Model not created"
}
DispatchQueue.main.async {
self.displayButtonAction?.text = nil
self.displayButtonAction?.text = actionString
}
}several things can be improved:
- Setting
self.displayButtonAction?.texttonilbefore setting it to the actual message is not necessary.
- Action methods are always called on the main thread, the
DispatchQueue.main.asyncis not needed.
- You can define
actionStringas a constant (withlet) if it is initialized exactly once before used, the dummy initialization to "Get Battery Level and State" is not needed.
- Optional binding is almost always preferred over explicit testing against
niland forced-unwrapping, see for example When should I compare an optional value to nil? on Stack Overflow.
With these suggestions, the action method could look like this:
@objc func setLabelWithBatteryLevelAndState() {
let actionString : String
if let displayModel = self.displayDataModel {
actionString = displayModel.provideBatteryLevelAndState()
} else {
actionString = "Battery Button Action Failure: Data Model not created"
}
self.displayButtonAction?.text = actionString
}However, this does not provide the best user experience: If the data model could not be created (for whatever reason) then the user is notified of that fact only after pressing the button.
The better approach is to enable or disable a button depending on whether its functionality is available or not.
The same remarks apply to the other action method
setLabelActionNetwork().Initializing the data model
Initializing the
dataModelLibrary exactly once is easier done with a custom getter method and dispatch_once(), which is thread-safe:- (PCI7DataModelLibrary *)dataModelLibrary {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_dataModelLibrary = [[PCI7DataModelLibrary alloc] init];
});
return _dataModelLibrary;
}(For Swift, see for example Whither dispatch_once in Swift 3? on Stack Overflow.)
Now you can simply test
if (self.dataModelLibrary) { ... }to check if the model is available or not. However, this
if (![self dataModelLibraryInitialize]) {
// If the library can't be allocated or initialized none of the buttons will work in any view controller.
NSLog(@"application didFinishLaunchingWithOptions: Unable to alloc or init the PCI7DataModelLibrary object");
return NO;
}has the only effect that the user is presented with a black screen if the model is not available. (The return value from the
application:didFinishLaunchingWithOptions: method is only relevant if the app is started to handle a URL resource or continue a user activity, but otherwise ignored.)As mentioned above, the better approach is to enable or disable UI elements accordingly.
Further remarks
-
There is a Core Data model and corre
Code Snippets
let gpsAlert : UIAlertController? = self.displayDataModel!.provideGPSAlerters();
if (gpsAlert != nil) {
self.present(gpsAlert!, animated:false, completion:nil);
}@objc func setLabelWithBatteryLevelAndState() {
var actionString : String = "Get Battery Level and State";
if (self.displayDataModel != nil) {
actionString = (self.displayDataModel?.provideBatteryLevelAndState())!
}
else {
actionString = "Battery Button Action Failure: Data Model not created"
}
DispatchQueue.main.async {
self.displayButtonAction?.text = nil
self.displayButtonAction?.text = actionString
}
}@objc func setLabelWithBatteryLevelAndState() {
let actionString : String
if let displayModel = self.displayDataModel {
actionString = displayModel.provideBatteryLevelAndState()
} else {
actionString = "Battery Button Action Failure: Data Model not created"
}
self.displayButtonAction?.text = actionString
}- (PCI7DataModelLibrary *)dataModelLibrary {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_dataModelLibrary = [[PCI7DataModelLibrary alloc] init];
});
return _dataModelLibrary;
}if (self.dataModelLibrary) { ... }Context
StackExchange Code Review Q#162695, answer score: 2
Revisions (0)
No revisions yet.