Categories
developer documentation v0.0.27
mimik Developer Documentation
  • Tutorials
  • Creating a Simple iOS Application that Uses an edge microservice

Creating a Simple iOS Application that Uses an edge microservice

Objective

The objective of this tutorial is to demonstrate how to modify a small iOS application to use a microservice at the edge. The application code will use the mimik Client Library to access an edge microservice that generates a random number on demand.

Intended Readers

The intended readers of this document are software developers who want to learn about the process of using edge microservices and the mimik Client Library in the iOS application environment.

What You'll Be Doing

In this tutorial, you are going to fix a bug in an existing iOS application. The application is a simple example that is supposed to generate a random number when a button is tapped. But, there is a problem. The application does not generate a random number as expected, as shown in Figure 1, below. Of course, you could fix the code by just using the Swift Standard Library random(in:) method to generate the random number directly in the code. However, we're going to take another approach. We're going to use the broken code as an opportunity to learn how to program iOS application code to bind to an edge microservice that provides a random number on demand.

In this tutorial, you'll be doing the following tasks to fix the iOS application:

  • Clone the example application code from GitHub.
  • Configure the example application to include the mimik Client Library using CocoaPods. We have setup the mimik cocoapod references in the Podfile file at the project level for you.
  • Configure the example application with the mimik Developer ID Token and mimOE license that will be used to generate the Access Token required to work with the edge microservice.
  • Modify the example application by adding code that will do the following:
    • Initialize the mimik Client Library.
    • Use mimik Client Library to start the mimOE Runtime.
    • Use mimik Client Library with an existing Developer ID Token to generate the Access Token.
    • Deploy an edge microservice using the Access Token.
    • Request a random number from the deployed edge microservice using the Access Token.

In terms of doing the actual programming, after we've identified the problem area, we'll alter the files storing the configuration information about the Developer ID Token and mimOE License. You'll copy the values from the mimik Developer Console.

Then, after the configuration is complete, we'll execute three phases of coding to do the work of actually getting the edge microservice up and running. The coding will take place in MainActivity.swift and ContentView.swift files.

In the first phase, we stub out the methods that relate to each programming step. Then, in the second phase, we'll add code to the methods in an isolated manner within the tutorial so that you can learn the reasoning and details about each function. Finally, we'll display the completed MainActivity.swift and ContentView.swift files that have all the code for all the methods. At that point, you'll be able to run the fixed code on an iOS Device.

Also, be advised that the example application source that you'll clone from GitHub has a branch named completed_code. This branch contains a version of the iOS application that has all the code you will be adding throughout the tutorial. You can checkout this branch on your local machine and run that code, should you experience difficulties running the code you've developed.

Prerequisites

Connecting a real iOS device to the Mac computer and selecting it as the target in Xcode later on, when the project opens. This example application will not work with an iOS Simulator.

NOTE:

Working with the iOS Simulator and the mimik Client Libraries entails some special consideration. For more more information about iOS Simulator support see this tutorial.

Working with the Example Application and the mimik Client Library

The sections below describe the details of the steps required to fix the broken application code using the mimik Client Library. The mimik Client Library simplifies usage and provides straightforward interfaces to streamline mimOE startup, authorization, and microservice deployment at the edge.

Getting the Source Code

As mentioned above, you'll be modifying an existing iOS application to fix a bug in the code. The application you'll modify is an Xcode project. The application code already has all the UI elements and initialization behaviour needed to get the code up and running. The code is operational, but as mentioned, it's buggy.

The place to start is cloning the code from GitHub and loading it into Xcode.

Execute the following command to clone the example code from GitHub:

git clone https://github.com/mimikgit/random-number-generator-iOS.git

Adding the mimik Client Library cocoapods

As mentioned above, the mimik Client Library comes in a form of EdgeCore and mimOE-SE-iOS-developer (or mimOE-SE-iOS) cocoapods, which need to be made available to the application source code.

We have setup these references in the Podfile file at the project level for you.

Step 1:** From the command line run the following command to get to the Xcode project directory.

cd random-number-generator-iOS/Random-Number-Generator-Example/

Step 2:** From the command line run the following command (from inside the Xcode project directory).

pod install --repo-update

Step 3: Start editing the Developer-ID-Token file with:

open Developer-ID-Token

Go to the mimik Developer Console and generate the Developer ID Token from an edge project that you create.

Once generated, copy the Developer ID Token and then paste it into Developer-ID-Token file, replacing any existing content already there. Save and Close the file.

Step 4: Continue by editing the Developer-mimOE-License file with:

open Developer-mimOE-License

Go to the mimik Developer Console and copy the Developer mimOE (Edge) License from there.

Learn more about the process by reading this the tutorial Getting the edgeEngine license and Identity server values from mimik Developer Console

Once copied, paste the mimOE License into the Developer-mimOE-License file, replacing any existing content already there. Save and Close the file.

Step 5: From the command line run the following command in your project directory.

open Random-Number-Generator-Example.xcworkspace

Figure 1 below shows the command line instructions described previously, from Steps 1-5.

code in Xcode
Figure 1: Command line output example for Steps 1-5

Now that references and configurations have been set, it's time to get into the business of programming to the microservice at the edge.

Identifying the Bug

As mentioned at the beginning of this tutorial, our objective is to fix a bug that is preventing the example application from displaying a random number when a button is tapped on the screen of the iOS device as seen in Figure 2 below.

iOS-buggy-behaviour
Figure 2: Buggy behaviour! A static value of 60 is being returned instead of a random number

The bad behaviour we need to fix is in the MainActivity.swift file as seen in the XCode IDE in Figure 3 below.

iOS-Xcode-project
Figure 3: Shows a version of the faulty code within the XCode IDE.

Notice that the code returns a hard-coded value of 60 in the generateRandomNumber() method at Line 16. This is the root cause. We're going to make the fix by using a microservice at the edge instead, as we discussed earlier.

Implementing the Fix

In order to fix the application code we'll be doing the following in the MainActivity.swift and ContentView.swift files.

  • Importing the mimik Client Library into the Project
  • Creating an instance of the mimik Client Library
  • Inserting the Method Placeholders
  • Modifying the inserted Method Placeholders in MainActivity
  • Modifying the ContentView code to call the fixed method

The mimik Client Library interface is represented in code as EdgeClient class, and we'll initialize its instance into a let edgeClient constant in one of the code examples below.

Creating an instance of the mimik Client Library components

First, we need to import the relevant mimik Client Library modules in our MainActivity class by inserting the import statements as shown below.

Then, in order to be able to start using the mimik Client Library interface we need to create its instance. We do this by establishing the let edgeClient constant in the MainActivity.swift file as shown below. edgeClient constant will be providing our code with the mimik Client Library interface access.

1: import Foundation
2: import EdgeCore
3: import EdgeEngine
4: import Alamofire
5:
6: final class MainActivity: NSObject {
7:
8: override init() {
9: super.init()
10: }
11:
12: // Buggy synchronous method that always returns the same number
13: func generateRandomNumber() -> Int {
14: return 60
15: }
16:
17: // mimik Client Library instance
18: let edgeClient: EdgeClient = {
19: return EdgeClient()
20: }()
21: }

Inserting the Method Placeholders

We want to transform the application to start using the new edge microservice design paradigm. To begin the work we add a few placeholder methods in the MainActivity.swift file as shown below.

The code is commented on to describe the intention of the particular placeholder methods.

1: import Foundation
2: import EdgeCore
3: import EdgeEngine
4: import Alamofire
5:
6: final class MainActivity: NSObject {
7:
8: override init() {
9: super.init()
10: }
11:
12: // Buggy synchronous method that always returns the same number
13: func generateRandomNumber() -> Int {
14: return 60
15: }
16:
17: // mimik Client Library instance
18: let edgeClient: EdgeClient = {
19: return EdgeClient()
20: }()
21:
22: // Asynchronous method returning the success or failure of mimOE Runtime startup
23: func start() async -> Result<Void, NSError> {
24: return .failure(NSError(domain: "", code: 500))
25: }
26:
27: // Asynchronous method returning the success or failure of generating an Access Token
28: func accessToken() async -> Result<String, NSError> {
29: return .failure(NSError(domain: "", code: 500))
30: }
31:
32: // Asynchronous method returning the success or failure of deploying an edge microservice
33: func deployMicroservice(accessToken: String) async -> Result<EdgeClient.Microservice, NSError> {
34: return .failure(NSError(domain: "", code: 500))
35: }
36:
37: // New asynchronous method returning a randomly generated number
38: func generateRandomNumberNew() async -> Result<Int, NSError> {
39: return .failure(NSError(domain: "", code: 500))
40: }
41: }

The sections that follow will show the code for each method we're programming. Also, we'll describe the reasoning behind each of the additions we're making to the code in the MainActivity.swift and ContentView.swift files.

Modifying the inserted Method Placeholders

In order to get the edge microservice installed and accessible we'll need to make the following changes in the MainActivity.swift and ContentView.swift files.

  • refactor the MainActivity.swift initialization method init()
  • modify the MainActivity.swift method start()
  • modify the MainActivity.swift method accessToken()
  • modify the MainActivity.swift method deployMicroservice()
  • modify the MainActivity.swift method generateRandomNumberNew()
  • modify the ContentView.swift body

Refactoring init()

We want to make the deployed microservice at the edge available for the ContentView.swift code to call. We're doing this so that the rendering view can get the randomly generated number whenever the user taps on its button. In order to implement the edge microservice that generates a random number we need an ordered sequence of actions to execute successfully during the MainActivity.swift initialization in init().

The four added methods are marked with async as asynchronous. We'll encapsulate asynchronous actions with a Task{} code wrapper as shown below in the code for the init() method.

First, the mimOE Runtime needs to be started as shown below on Line 6. Second, we need to generate the Access Token as shown below on Line 11. Third, the edge microservice needs to be deployed which is done at Line 16.

The code below is commented on. Take a moment to review statements using the code comments as your guide. Then, if you're following along by doing live programming against the tutorial code you downloaded from GitHub, modify the init() method code in the MainActivity.swift file as shown below:

1: override init() {
2: super.init()
3:
4: Task {
5: // Starting mimOE Runtime.
6: guard case .success = await self.start() else {
7: return
8: }
9:
10: // Generating Access Token.
11: guard case let .success(accessToken) = await self.accessToken() else {
12: return
13: }
14:
15: // Deploying Random Number edge microservice.
16: guard case .success = await self.deployMicroservice(accessToken: accessToken) else {
17: return
18: }
19: }
20: }

Modifying start()

We need to get the mimOE Runtime running, so that we'll be able to deploy the edge microservice to it. We'll get mimOE Runtime running by calling the relevant start method on the mimik Client Library.

In terms of the details of the start() function, first, we load the mimOE License from the Developer-mimOE-License file. Then we call the start method on the mimik Client Library and return the result. Either a success or a failure.

Take a moment to review the statements in the code below using the comments as your guide. If you're following along using the code downloaded from GitHub, modify the start() method code in the MainActivity.swift file as shown below.

1: // Asynchronous method returning the success or failure of edgeEngine Runtime startup
2: func start() async -> Result<Void, NSError> {
3:
4: // Loading the content of the Developer-mimOE-License file as a String
5: guard let file = Bundle.main.path(forResource: "Developer-mimOE-License", ofType: nil), let license = try? String(contentsOfFile: file).replacingOccurrences(of: "\n", with: "") else {
6: print(#function, #line, "Error")
7: return .failure(NSError(domain: "Developer-mimOE-License Error", code: 500))
8: }
9:
10: // License parameter is the only parameter required for mimOE Runtime startup. There are other optional parameters also available.
11: let params = EdgeClient.StartupParameters(license: license)
12:
13: // Using the mimik Client Library to start the mimOE Runtime
14: switch await self.edgeClient.startEdgeEngine(parameters: params) {
15: case .success:
16: print(#function, #line, "Success")
17: // Returning a success for mimOE startup
18: return .success(())
19: case .failure(let error):
20: print(#function, #line, "Error", error.localizedDescription)
21: // Returning a failure for mimOE startup
22: return .failure(error)
23: }
24: }

Modifying accessToken()

Once we have the mimOE Runtime running, we also need to have an Access Token for it generated. We do this so that we can deploy and call the edge microservice. In order to accomplish this task we need to call the relevant authorization method on the mimik Client Library.

The mimik Client Library authorize method requires a Developer ID Token in order to work. We previously saved the Developer ID Token to the Developer-ID-Token file.

In order to implement the authentication code, first, we need to find the application's bundle reference to the Developer-ID-Token file and load its contents into the let developerIdToken constant as shown below.

At this point, we are ready to make a call to the mimik Client Library's authorization method. We send the developerIdToken as the method's only parameter.

Next, we attempt to load the Access Token as let accessToken from the authorization result. If successful, we return a success with the Access Token. If unsuccessful, we return a failure.

Take a moment to review the statements in the code below using the comments as your guide. If you're following along using the code downloaded from GitHub, modify the accessToken() method code in the MainActivity.swift file as shown below.

1: // Asynchronous method returning the success or failure of generating an Access Token
2: func accessToken() async -> Result<String, NSError> {
3:
4: // Finding the application's bundle reference to the Developer-ID-Token file and loading its content
5: guard let developerIdTokenFile = Bundle.main.path(forResource: "Developer-ID-Token", ofType: nil), let developerIdToken = try? String(contentsOfFile: developerIdTokenFile).replacingOccurrences(of: "\n", with: "") else {
6: print(#function, #line, "Error")
7: return .failure(NSError(domain: "Developer-ID-Token Error", code: 500))
8: }
9:
10: // Calling mimik Client Library to generate the mimOE Access Token. Passing-in the Developer ID Token as the only parameter.
11: switch await self.edgeClient.authorizeDeveloper(developerIdToken: developerIdToken) {
12: case .success(let authorization):
13:
14: // Attempting to getting the access token from the authorization call response
15: guard let accessToken = authorization.token?.accessToken else {
16: print(#function, #line, "Error")
17: return .failure(NSError(domain: "Access Token Error", code: 500))
18: }
19:
20: print(#function, #line, "Success", accessToken)
21: // We have the Access Token, return a success
22: return .success(accessToken)
23:
24: case .failure(let error):
25: print(#function, #line, "Error", error.localizedDescription)
26: // We don't have the Access Token, returning a failure.
27: return .failure(error)
28: }
29: }

Modifying deployMicroservice()

At this point, we're going to deploy the edge microservice to the mimOE Runtime. For this, we need to call the relevant deployment method on the mimik Client Library, which requires the Access Token being passed through as the edgeEngineAccessToken parameter.

The mimik Client Library deploy method also requires a configuration object. We've taken the liberty of configuring it using hardcoded values as shown in Line 11. This is done to make the code easier to understand. In a production setting, you'd want to put all the hard-coded values in a configuration file and name the values accordingly.

Additionally, the mimik Client Library deploy method also requires a file path to where the edge microservice is bundled. In our case, the microservice is represented by the randomnumber_v1.tar file. This file resides in the application's bundle.

Now we have the deployment configuration setup and the path to the edge microservice file established. We also have the Access Token. Next, we call the mimik Client Library deploy method, passing the accessToken, config and imageTarPath objects as the parameters.

If successful, we return a success with the deployed edge microservice. If unsuccessful we return a failure.

Take a moment to review the statements in the code below using the comments as your guide. If you're following along using the code downloaded from GitHub, modify the deployMicroservice() method code in the MainActivity.swift file as shown below.

1: // Asynchronous method returning the success or failure of deploying an edge microservice
2: func deployMicroservice(accessToken: String) async -> Result<EdgeClient.Microservice, NSError> {
3:
4: // Finding the application's bundle reference to the randomnumber_v1.tar file
5: guard let imageTarPath = Bundle.main.path(forResource: "randomnumber_v1", ofType: "tar") else {
6: print(#function, #line, "Error")
7: return .failure(NSError(domain: "Tar file Error", code: 500))
8: }
9:
10: // Setting up the edge microservice deployment configuration
11: let config = EdgeClient.Microservice.Config(imageName: "randomnumber-v1", containerName: "randomnumber-v1", basePath: "/randomnumber/v1", envVariables: [:])
12:
13: // Using the mimik Client Library method to deploy the edge microservice, passing in the Access Token, deployment configuration and the reference to the randomnumber_v1.tar file
14: switch await self.edgeClient.deployMicroservice(edgeEngineAccessToken: accessToken, config: config, imageTarPath: imageTarPath) {
15: case .success(let microservice):
16: print(#function, #line, "Success", microservice)
17: // We have the deployed edge microservice reference, returning success.
18: return .success(microservice)
19: case .failure(let error):
20: print(#function, #line, "Error", error.localizedDescription)
21: // We failed to deploy the edge microservice, return a failure.
22: return .failure(error)
23: }
24: }

Modifying generateRandomNumberNew()

Previously the buggy function, generateRandomNumber() just returned a hard-coded value of 60. In order to implement a fix, we want the new generateRandomNumberNew() method to return a truly random number. For this, we will make an HTTP call to the randomNumber endpoint on the deployed microservice at the edge and return the random value from the HTTP response. We'll use the Alamofire networking library's request() method to make this call.

Before we call the microservice at the edge, we need to get the Access Token ready. We will accomplish this by calling the accessToken() method.

In order to establish the full path URL to the randomNumber endpoint on the deployed edge microservice, we first get a reference to the deployed edge microservice.

Next, we establish the full path URL to the edge microservice endpoint.

With all the prerequisites now established, we can use the request() method of the Alamofire library to call the endpoint on the deployed edge microservice.

Next, we look at the response result and if successful, return a success with the random number value. If there is an issue we return a failure.

Take a moment to review the statements in the code below using the comments as your guide. If you're following along using the code downloaded from GitHub, modify the generateRandomNumberNew() method code in the MainActivity.swift file as shown below.

1: // New asynchronous method returning a randomly generated number
2: func generateRandomNumberNew() async -> Result<Int, NSError> {
3:
4: // Getting the Access Token ready
5: guard case let .success(edgeEngineAccessToken) = await self.accessToken() else {
6: print(#function, #line, "Error")
7: // We don't have the Access Token, returning a failure.
8: return .failure(NSError(domain: "Access Token Error", code: 500))
9: }
10:
11: // Getting a reference to the deployed edge microservice
12: guard case let .success(microservice) = await
13: self.edgeClient.microservice(containerName: "randomnumber-v1", edgeEngineAccessToken: edgeEngineAccessToken) else {
14: print(#function, #line, "Error")
15: // We don't have the reference, returning a failure.
16: return .failure(NSError(domain: "Deployment Error", code: 500))
17: }
18:
19: // Establishing the full path url to edge microservice's /randomNumber endpoint
20: guard let endpointUrlComponents = microservice.fullPathUrl(withEndpoint: "/randomNumber"), let endpointUrl = endpointUrlComponents.url else {
21: print(#function, #line, "Error")
22: // We don't have the url, returning a failure.
23: return .failure(NSError(domain: "Microservice Error", code: 500))
24: }
25:
26: // Calling the edge microservice endpoint using the Alamofire networking library.
27: let dataTask = AF.request(endpointUrl).serializingDecodable(Int.self)
28: guard let value = try? await dataTask.value else {
29: print(#function, #line, "Error")
30: // We don't have the random number, returning a failure.
31: return .failure(NSError(domain: "Decoding Error", code: 500))
32: }
33:
34: print(#function, #line, "Success", value)
35: // We have the random number from the edge microservice, returning a success with the value
36: return .success(value)
37: }

We now have fully operational code in the MainActivity.swift file. Next, we'll change the code specific to this application's view in the ContentView.swift file so that we can see it in action on screen.

Refactoring ContentView.swift

What we need to do now is to make a change where the view rendering code calls the deployed microservice at the edge; more specifically where the user taps on the button to get the randomly generated number.

The code ContentView.swift runs synchronously. The new and improved generateRandomNumberNew() instance method of MainActivity runs asynchronously. This means a Task{} code wrapper needs to be used to encapsulate the call.

Then we switch to the new asynchronous generateRandomNumberNew() method. Now, when a user taps the button on the screen of an iOS device, a random number will appear.

Take a moment to review the statements in the code below using the comments as your guide. If you're following along using the code downloaded from GitHub, modify the code in the ContentView.swift file as shown below.

1: import SwiftUI
2:
3: struct ContentView: View {
4:
5: @State private var randomNumber: Int = 0
6: @State private var mainActivity = MainActivity()
7:
8: var body: some View {
9: VStack(alignment: .center, spacing: 30) {
10:
11: Text("mimik Random Number Generator")
12: .font(.title2)
13:
14: Button.init("GET RANDOM NUMBER") {
15:
16: // Wrapping around the asynchronous call with Task
17: Task {
18: // Calling the new asynchronous generateRandomNumberNew method
19: guard case let .success(generatedNumber) = await mainActivity.generateRandomNumberNew() else {
20: // We don't have the random number, just returning then
21: return
22: }
23:
24: // We have a new random number, so we'll set the view's UI with the new value
25: randomNumber = generatedNumber
26: }
27:
28: }.tint(Color.blue)
29:
30: // Showing the current random number value on the screen
31: Text("Got \(randomNumber)")
32: .font(.body)
33: }
34: }
35: }

We now have fully operational code in the ContentView.swift file as well. Your application is working correctly once again. And now it's [edgified] (https://devdocs.mimik.com/introduction/01-index)!

Viewing the Completed Code

The sections above showed you the details that go with getting the configuration settings, import statements, class variables and methods in place in order to implement an edge microservice. The microservice we added fixed the bug that was in the example application.

If you followed along by inserting and adding code as instructed throughout this tutorial, running the code is a matter of using the capabilities provided by Xcode to run the code on the attached iOS device.

If for some reason you can't get your code up and running, you can use the working version of this code that ships in the cloned repository. It is in a branch named completed_code. You can run that code by using the following steps:

  • git checkout completed_code
  • cd to the project directory where the Podfile file is.
  • pod install --repo-update
  • Saving your Developer ID Token to the Developer-ID-Token file.
  • Saving your mimOE (Edge) developer License to the Developer-mimOE-License file.
  • Opening the project workspace in Xcode.
  • Attaching and selecting a real iOS device as the target. This won't work with the iOS simulator.
  • Runing the code on the attached iOS device.
after-deployment
Figure 4: The example application with the working randomization code from the microservice at the edge

Congratulations! You have completed the example application tutorial that uses a microservice at the edge to provide behaviour to fix the operational bug in an application. Remember, the application was unable to display a random number each time the Get Random Number button was tapped. Now by binding the button tap handler to make a call to the edge microservice, it does.

Additional reading

In order to get more out of this article, the reader could further familiarize themselves with the following concepts and techniques:

  • Working with mimik Client Library components.
  • Understanding the mimik Client Library components integration and initialization process.
  • Starting the mimOE Runtime in an iOS application.
  • Generating a mimOE Access Token.

Review

In this document, you learned the following:

  • How to configure the example application to integrate the mimik Client Library cocoapods
  • How to configure the example application with Developer ID Token credentials
  • How to get a mimOE license and start the mimOE Runtime using the mimik Client Library
  • How to use a Developer ID Token to authorize an mimOE Runtime and generate an Access Token for it
  • How to use a generated Access Token to deploy a microservice at the edge
  • How to make a request to a microservice at the edge to retrieve a random value

Was this article helpful?

© mimik technology, Inc. all rights reserved