Tag Archives: ios

WKWebView and WKCookieStore in iOS 11

Today I am going to talk about the new WebkitView in iOS 11 and about our love/hate relationship.

The project I worked on was a simple iOS application which should provide a native component for login/signup, navigation, and use a web view to display his website and all the other features which were provided via the web page.

At first this task seems trivial and most probably there are many of you who already did it. However, not many of us implemented this on the iOS 11, using the latest tools provided by Apple and here I’m referring to the WKWebView with all its additional properties.

What I’m going to expose here are some least know features, or not clear from Apple’s documentation, or things we’ve struggled with while implementing this project.

The task is simple, set cookies on a webpage (so we know that the user is logged in), do some CSS manipulation on all pages (basically hide the navigation bar of the site and some other components) and add some custom JS so we know when the user navigates within an iframe.

So lets get started with it 💻

Cookies

The first task is to add some cookies to the WKWebView so when we fire any request to have those in place. Previous to iOS 11, the cookies used by the app would be shared and stored in the NSHTTPCookieStorage . UIWebView would use cookies from this cookie storage (parent app cookie storage).

All of those has changed with the introduction of WKHTTPCookieStore. Starting with iOS 8 we have a new component for displaying web content in our iOS apps, namely WKWebView.

iOS 11 came with some changes and added some extra features to the WKWebView, basically each WKWebView now has its own cookie storage and its not share by the whole app and all other web views like in the case of (UIWebView).

To add cookies to a WKWebview we can use the cookie store WKHTTPCookieStore which has a set of methods for cookie manipulation, setCookie, getAllCookies and delete cookie.

All of those methods work with a completion handler which gets called after the operation was made. All those operations are async operations and SHOULD BE CALLED FROM THE MAIN THREAD. The documentation for this is kind of thin and we haven’t found it to be written anywhere.

All those methods work with objects of HTTPCookie objects. The initialisation of those objects is pretty straight forward and we won’t cover it.

Another interesting thing we found is that those calls don’t always succeed, and what I mean by that is that the completion handler is not always called, event if those methods are called from the Main thread and this is not blocked by any operation.

We found this behaviour to be pretty odd and we couldn’t find any documentation for it as well. This approach worked for when we first displayed the WKWebView, but when we would display it the second or third time, the setting of the cookies would fail.

Initially this broke our code as we relied on the completion handler of the setCookie to be called at some point, but it didn’t so after a few hours of trying different approaches and scrapping the web for answers we have found a StackOverflow hint.

The solution was to use a shared WKProcessPool object between all the WKWebViews used in the app. WKProcessPool states where the cookies should be saved, so by using the same pool, we have found out that the get, save and delete methods on the shared cookie storage work properly.

Hope this will save you some time. We found out that Apple’s documentation in are of WebKit is pretty scarce and thats is the main reason why we have a love/hate relationship.

CSS manipulation

In the app we build the target was from start iOS 11, which meant we could use all the new 💎 things (WKWebView and WKHTTPCookieStore).

First of we have to initialise the WKWebView before we start using it, in order to do that we would need an instance of WKWebViewConfiguration, which is a collection of properties used for configuring the WKWebView. This configuration is responsible for determining how soon a page is rendered and many other options.

This configuration object will be used for CSS manipulation. We want some CSS to be applied to the web pages before they are displayed on the screen, we want the header of our website stripped hidden in the web view presented in the app.

There are many properties on this configuration object, but the one we are most interested in for fulfilling the above stated task is the .userContentController property. WKUserContentController is an object which allows the user to inject JavaScript code in the web view.

The whole idea is the following, write some JS script that will append a CSS to the current HTML document structure and execute that when the web view loads.

The JS script for achieving this is the following:

JS

We create a new style tag, we populate it with our custom CSS, in this example “header { display: none;}” for hiding the header element. You can place here any CSS code, in order to fulfil your desired behaviour. The last line adds the tag as a child tag to the current HTML document.

Now that we have all the elements, lets stick them together, we need to initialise a WKWebView, add the script and load a page.

The following code, does exactly that:

CODE

After calling the “configureWebView” method, the we can start loading pages using the load method of the WKWebView.

JS callbacks in Swift

The next step it is to add the a script which would notify us when a page on an iframe changes. Unfortunately the .navigationDelegate of the WKWebView doesn’t notify us regarding the changes in the iframes of a web page.

To achieve this behaviour we will use once again JavaScript, the process is similar to the one used before, but also has a little twist. We want a Swift callback every time the web page navigates to a new link, previously we have seen how we inject JavaScript into a web page (Swift -> JavaScript), but we haven’t talked about the backwards communication, JavaScript -> Swift.

In order to receive callbacks from JavaScript we need to implement the WKScriptMessageHandler protocol which has only one method

func userContentController(WKUserContentController, didReceive: WKScriptMessage)

This will be called every time our JS code posts a message back to the Swift code.

So how do we use it?

The process is very similar to the one used before

  1. Create a .js file for the JS code and populate it with the custom code which will notify the Swift code base when a certain event occurs

JS

The previous code does the following, every time the window has change function is called from the HTML we post a message to the webkit with the identifier “notification_name” and some message, in this example “pageHasChanged”,

2. Add the JS and the delegate

CODE

3. Implement the delegate method

CODE

As you probably saw, its important to use the same identifier in both JS and Swift code for a certain notification, in this current example that identifier is “notification_name”.

That’s it, if you are interested in other cool features of the WKWebView I strongly encourage you to watch the following WWDC video.

Happy coding!📱

Objective-C – Categories

Every developer, good or bad, at least those who have followed the programming courses at any university remembers some design patterns, MVC, Singleton and some of the also say Decorator.

In the iOS framework, Apple has used, and has guided the developers to use certain patterns, if we talk about the general architecture we most of the times find Model View Controller, if we want to use the notification system, we find out they used Observer pattern, and the list could go on.

Objective-C  is a hybrid language, which took, various parts for other languages, and evolved a lot in its over 32 years of existence. It has many great features, which helps you get your work done (eq. blocks), but it also has its lacks, you can NOT set the visibility of a a method as being private or protected because of the dynamism of the language.

Categories

In the beginning of this post, I’ve mentioned the Decorator pattern, and that is because this post should be about categories, Objective-C categories, and those two are somehow related.

With the Decorator pattern, you would add some new functionality to an existing class, that is the main purpose of Objective-C categories but with some constraints.

If you have started working in iOS you probably did already use categories, without knowing they are called like that, but enough talk lets jump to examples.

@interface HelloWorldObjC : NSObject

@property (strong, nonatomic) NSString *greeting;

- (void)saySomething;

@end

We declared an interface for a class which has one method, and one property, a greeting which is a string which will be printed by the say something method.

@interface HelloWorldObjC()

@property NSString *beforeGreetingString;

@end

@implementation HelloWorldObjC

#pragma mark - Lifecycle

- (id)init {

self = [super init];

if (self) {

self.beforeGreetingString = [NSStringstringWithFormat:@"%p", self];

self.greeting = @"Hello there!";

}

returnself;

}

#pragma mark - Public methods

- (void)saySomething {

NSLog(@"%@ says: %@", self.beforeGreetingString, self.greeting);

}

@end

As for the .m file, the implementation, as you can see there is another place where you could declare variables, making them “private”, that section @interface ClassName() is a special type of category called extensions. That is why I said in the beginning of this post that you might have used categories without knowing how they are called if you are new to ObjC. You can define an extension in the .m file of a certain class, where the @implementation of that class resides and create new properties which your class will be able to use. One thing to mention about this properties is the fact that they are synthesised, which means you get a getter, a setter and a backing ivar for your property. Moreover, you can also declare methods in the extension. Long story short, everything you declare here, can only be used in the @implementation section, if another class inherits or someone makes a category over this class, you will not be able to access anything defined inside the extension, and you will only have access to the things defined in the .h (@interface) file (without using some tricks at least.).

The initializer sets the greeting to a default one, in case someone forgets to set it when using the object and also sets the value of the interface defined property to the memory address of the current object.

The saySomething method only displays a string composed of the value of the address of the object in memory, and the defined greeting.

A real category

This is supposed to be about categories, but where are them? Ok lets see what a proper category looks like.

@interface HelloWorldObjC(Smile)

- (void)addSmileToGreeting;

@end

In another file usually named “HelloWorldObjC+Smile.h” we can add a new category, as you can see, the declaration is pretty straight forward, you declare it like any other class, without the inheritance and with a unique name in between parenthesis. What can we declare here? Well there are a few rules, you can define new methods and even properties, the compiler won’t complain if you provide the getters and setters in the implementation section your program won’t even crash. Basically what you can do here is add some new methods, some new functionalities to your original class, but you can not alloc new memory for it, that is why you can not add new properties backed by an ivar, because the categories are not new objects, they are the same object as the original class. To make it easier for you to understand, you can do an  category for every class you want (YES event for classes from Foundation, UIKit and so on), and you can use the methods you add to those classes using a category wherever you implement the header with the definition of the category. Now picture the following scenario, you are working with NSStrings all over the app, I come along and make a category over NSString and add new properties to it, what is suppose the system to do? Add those variables to all the objects, of NSStrings, wherever I import my header? The answer is NO, categories are not allowed to modify the size of the object in memory by adding some new values to it. That being said, you can not add properties, and ivars to a category. Another restriction is the fact that you can not overwrite methods on the base class, for obvious reasons.

@implementation HelloWorldObjC(WithSmile)

- (void)addSmileToGreeting {

[self.greeting stringByAppendingString:[NSStringstringWithFormat:@"%C", 0xe04f]];

}

@end

As for the implementation of the category in the “HelloWorldObjC+Smile.m” file, we only add an emoji to the current greeting message.

Well this doesn’t look like much, what can I use them for? Well there are many usages, for example you can use them to explicitly mark for the people in your team which methods are “private” or any other sort of example where you use a category in order to make your code more readable. But by far the most common usage is by adding some sort of functionality to a certain class, in my projects i usually make a category over UIColor so that i can initialize a colour from a hex string, you can put there all sorts of helpers on any class for the app you’re building.

The heart of the app

Today I’m going to talk a bit about the AppDelegate. In every demo project, and most of the times (I currently don’t know any examples where it does not) in every app, there is an class named AppDelegate.

Purpose

The app delegate is the entry point in an iOS application, here the app we are developing will receive different notifications regarding its state, push notifications and here is also the place where the app initialisation takes place.

Every iOS application has a lifecycle composed of 5 different states.

  1. Non-running – the application does not run, has NOT been launched
  2. Inactive – the application is in foreground but does not receive events, this is a transition state, and we usually find the app in an inactive state when launching the application or when the application runs and we receive an phone call
  3. Active – the application is in foreground and receives events (the application is open, we can interact with it)
  4. Background – the application runs in background and executes code (for eq. in an navigation app, fetching the current location of the user, and alert the user that its gonna need to change direction)
  5. Inactive – the application is in background and does not execute code (the application can become active if we tap the icon again, or press on the home button for all the apps to be displayed and select it from there, the application is loaded in memory, to not be confused with the non-running state)

 

Singleton

The app delegate is and singleton and is baiscally an object that comforms to the UIApplicationDelegate protocol. Besides the AppDelegate, every project has an singleton object of UIApplication type, which actually does all the hard work, and it just passes its events to the AppDelegate class of our application.

This means that the AppDelegate is an object that can be accessed during the whole time an iOS application is active, and keeps its state (is a singleton we don’t have multiple instances).

One common pattern when beginning to develop apps for iOS is to have a fat AppDelegate and multiple links with other classes from your app, as it keeps its state and is accessible from all over the app for beginner is the perfect place to keep different informations. The app delegate should be lightweight, should only handle the state changes of the app, and the initialisation of it, everything else, different properties or variables most of the times they should NOT be there.

 

Design

What I do for my apps in order to have a light app delegate is create classes for various classes of notifications. For eq. I have a class that handles all the push notification flow, basically the app delegate only tells my NotificationClass, that there is a new notification, the handling (the parsing of the notification, decide how to act on it) is done by my NotificationClass object. Respecting this rule, you’ll end up with a very light weight app delegate, and classes that are focused on only one job (as they should be, by respecting the “Single responsibility” from the OOP principles), which in the long term is easier to maintain and modify.

I’ve seen app delegates which had over 2000 lines of code, and I tell you now, thats some ugly stuff. We have to look at the app delegate like a phone company, its job is to link various people (the app delegate job would be to link an event to a handler), it should not care about how it can handle an event, it should only care about sending it to the right object for handling it.

 

Links

Apple

So you wanna learn iOS …

Hi there, this is my first post from many (hopefully) to follow in which I try to explain things I struggled with or things I found interesting while learning how to make apps for iPhone.

One of the first things someone who is willing to learn iOS development has to do is get his tools. Easier done then said if you are on a Mac, all you need to do is download Xcode and you are good to go. However, if you are on a Windows or Linux machine, you need to install a virtual machine which has macOS, which is not legal, but it can be achieved, or install macOS directly in you computer, if your hardware supports it. Otherwise, if you want to get you hands dirty, you can always build yourself a hackintosh, but in my humble opinion is if your can afford it, you should by a Mac, it will save you of a tons of “troubles” and you will love it.

Assuming you’ve installed Xcode, on any of the setups described above, we can move to the next step and explain a bit what is Xcode and why we are using it. Xcode is one of the most powerful and widely used IDE on Mac for developing C, C++, ObjectiveC and Swift apps. It’s highly optimised for building iOS, tvOS, watchOS and macOS apps, but people also use it to develop other types of apps.

Why use Xcode?

Apple has tried (successful in my opinion) to make the developers life as easy as possible. What do I mean by that? Basically all you need to develop, test and lunch your client side app is build in this IDE. It has a lot of powerful tools for debug (see Instruments), development, and deploying your app, and you don’t need to struggle with other things, such as different command line tools, or multiple programs or IDE’s just to achieve your purpose (creating an iOS app). Most of the developers I know, experts or beginners use it for development, but the development can be done in other environments as well for eq. Facebook developed Nuclide.

Xcode is definitely a perfect tool, but most of the people I know, in fact all the developers I know personally, get along with it pretty well and are contempt with its performance. Of course depending on the project, Xcode might not be the right tool for you but, if you are just starting developing apps for Apple platforms, I strongly suggest you to use it.

Other tools

What other tools? Like I said Xcode is all you need! Next time we’ll start to talk about actual development as its pretty straight forward to get a simple project up and running.

Start Xcode -> “Create new Xcode project” -> iOS Application -> Single View Application -> Enter a product name such as “My demo app” -> Next -> Choose a location on the disk and press create!

Now just press the “play” icon on the right of the macOS close, minimise and maximise buttons and your first iOS application will start on the selected simulator.

Thats it, you new shiny demo app is running.

See you next time!