Skip to content

Conversation

@emmanuelOpenForge
Copy link

@emmanuelOpenForge emmanuelOpenForge commented Jun 5, 2024

Steps to Replace Existing View for Micro Frontend Ionic Portal

image

Initialize an Angular Project

To begin, we will initialize an Angular project at the root of your project directory using Angular CLI.
Note: Ensure you have Angular CLI installed with the command:

npm install -g @angular/cli

Now, create an Angular application with the command:

ng new HabiticaLoadingPortal

Install Ionic Portals

Once the project is created, proceed to install Ionic Portals in the Angular project to facilitate communication between the web app and the native application. Use the command:

npm install @ionic/portals

Additionally, add Ionic to design your loading page:

ng add @ionic/angular

Modify the Main View

In the main view of the web app (app.component.html), add elements to show that user information is being processed:

<ion-content class="">
  <!-- Loading screen while fetching data... -->
  <div class="container ion-align-items-center ion-justify-content-center">
    <ion-spinner color="light"></ion-spinner>
    <h3>
      Processing your profile...
    </h3>
  </div>
</ion-content>

Add Timeout in Component

In app.component.ts, add a timeout to delay the loading and better appreciate the web app:

import {AfterViewInit, Component} from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { publish } from '@ionic/portals';
import {IonContent, IonHeader, IonSpinner, IonTitle, IonToolbar} from "@ionic/angular/standalone";

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [RouterOutlet, IonContent, IonHeader, IonToolbar, IonTitle, IonSpinner],
  templateUrl: './app.component.html',
  styleUrl: './app.component.scss'
})
export class AppComponent implements AfterViewInit {

  ngAfterViewInit() {
    setTimeout(() => {
      publish({ topic: "loading", data: "end" });
    }, 3000);
  }
}

We have imported the publish method from Ionic Portals, which will be used to communicate with the native application through PubSub communication. Here, our web app will be the publisher, notifying that the view is loaded by communicating via the channel/topic loading with data end, indicating that 3 seconds have passed.

Build the Application

Finally, build the application with the command:

npm run build

Then, copy the generated code from the dist folder to the main folder of your native app (in this case, HabiticaRPG), and rename the folder to LoadingScreenPortal as this name will be used to initialize our Ionic portal later.

Replace Native iOS View

To start replacing a view in the native iOS application, first replace the superclass of ViewController. For example, replace:

class LoadingViewController: UIHostingController<LoadingPage>

with:

class LoadingViewController: UIViewController

This change allows us to initialize our Ionic portal by overriding the loadView() method:

override func loadView() {
    super.loadView()
    self.view = PortalUIView(portal: "LoadingScreenPortal")
}

Where LoadingScreenPortal is the name of the folder containing your web app generated with npm run build. Thus, the init() method is no longer necessary as the view will be generated from the web app.

Create a Subscriber

As a final step, create a subscriber within the viewDidLoad method:

dismissCancellable = PortalsPubSub.shared.publisher(for: "loading")
    .data(as: String.self)
    .filter { $0 == "end" }
    .receive(on: DispatchQueue.main)
    .sink { [weak self] _ in
        guard let self = self else { return }
        self.dismiss(animated: true, completion: nil)
        self.segueForLoggedInUser()
    }

Where dismissCancellable is of type AnyCancellable?. This code subscribes to the loading topic (the channel), receives data as a String on the main thread, and dismisses the view with the Ionic portal, continuing the user's navigation.

This guide demonstrates how to use micro frontends in your native application and communicate between the web app and the native app. I hope this information was useful and enjoyable. Please share this publication, as we frequently share similar material to keep everyone updated. Thank you for your attention, and see you in the next guide.


Steps to Create a New Ionic Portal

About Open Forge

Initialize an Angular Project

To begin, we will initialize an Angular project at the root of your project directory using Angular CLI.
Note: Ensure you have Angular CLI installed with the command:

npm install -g @angular/cli

Now, create an Angular application with the command:

ng new HabiticaAboutOFPortal

Install Ionic Portals

Once the project is created, proceed to install Ionic Portals in the Angular project to facilitate communication between the web app and the native application. Use the command:

npm install @ionic/portals

Additionally, add Ionic to design your loading page:

ng add @ionic/angular

Modify the Main View

In the main view of the web app (app.component.html), add elements to show the information about Open Forge:

<ion-content>
  <div class="container">
    <ion-card>
      <h3>About Open Forge</h3>
      <ion-card-header>
        <ion-card-title>Our commitment</ion-card-title>
      </ion-card-header>
      <ion-card-content>
        <p>
          We believe in the power of mobile transformation. Partner with us to elevate your strategy and lead the digital frontier with unparalleled business growth and innovation.
        </p>
      </ion-card-content>
      <ion-card-header>
        <ion-card-title>Why I'm seeing this page</ion-card-title>
      </ion-card-header>
      <ion-card-content>
        <p>
          This project is a fork of Habitica Open Source project and this is an example of an Ionic Portal page, which has as purpose to show how to create a simple page with Ionic components.
        </p>
      </ion-card-content>
      <ion-list>
        <ion-item>
          <div class="centered">
            <a href="https://openforge.io">Site</a>
          </div>
        </ion-item>
        <ion-item>
          <div class="centered">
            <a href="https://openforge.io/about-us/">About Us</a>
          </div>
        </ion-item>
        <ion-item lines="none">
          <ion-button size="md" (click)="goBack()">OK</ion-button>
        </ion-item>
      </ion-list>
    </ion-card>
  </div>
</ion-content>

Add Event for the Button

In app.component.ts, add an event for the button to return to the previous scene from the About view:

import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import {
  IonButton,
  IonCard,
  IonCardContent,
  IonCardHeader,
  IonCardTitle,
  IonContent, IonItem,
  IonList,
  IonTitle
} from "@ionic/angular/standalone";
import { publish } from "@ionic/portals";

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [RouterOutlet, IonContent, IonCardHeader, IonTitle, IonCard, IonCardTitle, IonCardContent, IonList, IonItem, IonButton],
  templateUrl: './app.component.html',
  styleUrl: './app.component.scss'
})
export class AppComponent {
  title = 'HabiticaAboutOFPortal';

  goBack() {
    publish({ topic: "navigate", data: "back" });
  }
}

Style the Application

In your styles file (app.component.scss), add the following styles:

ion-content {
  display: flex;
  justify-content: center;
  background-color: #7639ED;
}
ion-item {
  text-align: center;
}
ion-card {
  padding: 1rem;
  text-align: center;
}
ion-button {
  --background: #7639ED;
  width: 100%;
  margin-top: 1rem;
}
ion-card-title {
  text-align: center;
}
a, h3 {
  text-decoration: none;
  color: #7639ED;
}
.container {
  background-color: #7639ED;
  display: flex;
  height: 100vh;
  flex-direction: column;
  justify-content: center;
  padding: 4rem 1rem 1rem;
}
.centered {
  width: 100%;
  display: flex;
  justify-content: center;
}

Build the Application

Finally, build the application with the command:

npm run build

Then, copy the generated code from the dist folder to the main folder of your native app (in this case, HabiticaRPG), and rename the folder to HabiticaAboutOFPortal as this name will be used to initialize our Ionic portal later.

Create a New Scene in iOS

The first step to create our Ionic portal from the native application side is to create a new UIView scene which will use the AboutOFViewController controller with the following code:

//
//  AboutViewController.swift
//  Habitica
//
//  Created by Phillip Thelen on 09.10.18.
//  Copyright © 2018 HabitRPG Inc. All rights reserved.
//

import UIKit
import Realm
import Habitica_Models
import MessageUI
import IonicPortals
import Combine

class AboutOFViewController: UIViewController {
    
    private var dismissCancellable: AnyCancellable?

    override func loadView() {
        super.loadView()
        self.view = PortalUIView(portal: "HabiticaAboutOFPortal")
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        dismissCancellable = PortalsPubSub.shared.publisher(for: "navigate")
                   .data(as: String.self)
                   .filter { $0 == "back" }
                   .receive(on: DispatchQueue.main)
                   .sink { [weak self] _ in
                       guard let self = self else { return }
                       self.dismiss(animated: true, completion: nil)
                       self.performSegue(withIdentifier: "AboutSegue", sender: nil)
                   }
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        navigationController?.navigationBar.backgroundColor = .clear
    }
}

In the code above, HabiticaAboutOFPortal is the name of the folder containing your web app generated with npm run build.

This allows us to initialize our Ionic portal by overriding the loadView() method:

override func loadView() {
    super.loadView()
    self.view = PortalUIView(portal: "HabiticaAboutOFPortal")
}

Subscribe to the Event

The code that listens for the event from our web app in the viewDidLoad lifecycle hook is:

override func viewDidLoad() {
    super.viewDidLoad()
    dismissCancellable = PortalsPubSub.shared.publisher(for: "navigate")
               .data(as: String.self)
               .filter { $0 == "back" }
               .receive(on: DispatchQueue.main)
               .sink { [weak self] _ in
                   guard let self = self else { return }
                   self.dismiss(animated: true, completion: nil)
                   self.performSegue(withIdentifier: "AboutSegue", sender: nil)
               }
}

Where dismissCancellable is of type AnyCancellable?. This code subscribes to the navigate topic (the channel), receives data as a String on the main thread, and navigates to the next scene in our storyboard (in this case, the Loading scene).

Define the Segue

Define the segue in your storyboard to connect AboutOFViewController with LoadingViewController and name it AboutSegue.
Pasted Graphic

Final Remarks

This guide demonstrates how to use micro frontends in your native application and communicate between the web app and the native app. I hope this information was useful and enjoyable. Please share this publication, as we frequently share similar material to keep everyone updated. Thank you for your attention, and see you in the next guide.

- Implementation of Loading Screen with webapp
- Added Ionic to use components from ionic
@emmanuelOpenForge emmanuelOpenForge force-pushed the feature/ReplaceExistingScreen branch from 019e1ef to 5e2a828 Compare June 6, 2024 03:08
@emmanuelOpenForge emmanuelOpenForge marked this pull request as ready for review June 6, 2024 04:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants