Dictionary
Originally a web app, but I deemed it too simple to be one, and utilized this opportunity to learn ObjectiveC and UIKit.
Published onUpdated on
3.8
Overview
This was a intermediate level Frontend Mentor projects, primarily targeted at web developers. But I thought it would be too simple and trivial for me to implement it on the frontend, I decided to make it a challenge to re-implement it, including themes, and animations within an iOS 18 App.
But that’s not it! I will also not be leveraging modern features or modern patterns like Swift and SwiftUI (similar to Stateful React), but I will go the ancient way: pure ObjectiveC and UIKit.
Features
- Light/Dark Theme Switcher
- Dynamic Programmatic UI: This may feel like a nothing burger if you’re used to SwiftUI. But I can show you examples how it was dreadful with UIKit, but still the preferably way to working with Storyboards.
- Changing display fonts on the fly like a web application.
- Multiple States: Loading state, Error state and Normal state.



The Experience
Using OBJC to do this project, which at first I thought it would be quite small since the application only consists of one screen, but it started spiraling out as more and more components require inheritance from one of many UIKit’s components.
All in all, I learned a lot of new things and it was relatively fun trying to do things in ObjC, which also surprises me that Apple still supports ObjC up into iOS 18. If any, my next project would be in Swift, or even using SwiftUI because this was not that fun to setup.
Layout Anchors
// Create the main view to put in the middle of this view.
UIView *mainView = [[UIView alloc] init];
mainView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:mainView];
[NSLayoutConstraint activateConstraints:@[
[mainView.centerXAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.centerXAnchor],
[mainView.centerYAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.centerYAnchor],
[mainView.trailingAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.trailingAnchor],
[mainView.leadingAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.leadingAnchor],
]];
Anchors are a way for the app to have flexible layout, while still knowing exactly where to put each component of the UI tree. You can imagine anchors like glues, they stick each edge of the component to another edge of another component, and once one resizes or moves, the other resizes or moves together.
Android also uses the same mechanisms, but with XML directives like width=container,
I think.
Changing entrypoints
All UIKit apps start with a single Entrypoint storyboard, which is somewhat hard-coded in XCode. You need to remove all references to the Entrypoint storyboard, from Project Settings to any code-related things.
- (void)scene:(UIScene *)scene
willConnectToSession:(UISceneSession *)session
options:(UISceneConnectionOptions *)connectionOptions {
if (![scene isKindOfClass:UIWindowScene.class]) {
return;
}
// Create a UIWindow using the scene if it is a WindowScene.
UIWindowScene *windowScene = (UIWindowScene *)scene;
UIWindow *window = [[UIWindow alloc] initWithWindowScene:windowScene];
self.window = window;
// Setup the root view to be the navigation controller, and open up the main view controller.
DTMainViewController *rootController = [[DTMainViewController alloc] init];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:rootController];
self.window.rootViewController = navController;
[self.window makeKeyAndVisible];
}
After that, you can create a new UIWindow where the starting scene is the scene that
gets passed into SceneDelegate. You can now invoke your entrypoint view controller
and assign that the “root view”.
I additionally used a UINavigationController here to support navigations like back,
forwards, easily implementable by NavigationStackView in SwiftUI.
Async IO
Some of you are probably very well-versed in Asynchronous IO with certain languages
like Golang with native goroutines, or JavaScript with async-await, or even Python
got there async-await now, or even JAVA got Promises-like structures with
CompletableFutures.
ObjectiveC has NO SUCH THING. Everything asynchronous has to be done through passing callbacks, which also surprised me that ObjectiveC supported callbacks with a stronger reflections support than generic function pointers.
- (void)searchDidPress:(NSString *)query {
// First, we put up the "Loading" state.
[self setupChildController:[[DTLoadingViewController alloc] init]];
// Then we send a request asynchronously, and handle later.
NSString *urlString = [@"https://api.dictionaryapi.dev/api/v2/entries/en/"
stringByAppendingString:
[query stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLPathAllowedCharacterSet]];
// Create a URL request to handle it.
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:urlString]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:5];
request.HTTPMethod = @"GET";
NSURLSessionDataTask *task =
[NSURLSession.sharedSession dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// Handle UI changes on the main queue.
dispatch_async(dispatch_get_main_queue(), ^{
[self handleData:data response:response error:error];
});
}];
[task resume];
}
All in All
To look back onto this, I was very proud of being able to write up ObjectiveC in a few days, as the web project would have taken me less than a day. It was pretty fun to use pure UIKit systems that you can’t directly preview like SwiftUI, and composing even a simple “flexbox”-like takes up 24 lines of just configurations and assignments.
UIStackView *mainStackView = [[UIStackView alloc] init];
mainStackView.axis = UILayoutConstraintAxisVertical;
mainStackView.spacing = 32;
mainStackView.alignment = UIStackViewAlignmentFill;
mainStackView.translatesAutoresizingMaskIntoConstraints = NO;
[stackView addArrangedSubview:mainStackView];
Can you GUESS what it does? It’s THIS.
<div class="flex flex-col items-stretch gap-8"></div>
After this, I could say that even given a legacy iOS codebase, I think I could pull through with something.