In this tutorial, we’ll walk through the steps to build a social media app using Ionic framework and Firebase backend. We’ll cover the essential features like user authentication, posting, liking, and commenting on posts, as well as displaying and updating user profiles. By the end of this tutorial, you’ll have a fully functional social media app that can be compiled for both iOS and Android devices.
Prerequisites
Before we start, make sure you have the following software installed on your system:
- Node.js: https://nodejs.org
- Ionic CLI:
npm install -g ionic
- Firebase CLI:
npm install -g firebase-tools
You’ll also need a Firebase project. If you don’t have one, sign in to the Firebase console (https://console.firebase.google.com), create a new project, and note down the project ID and API keys.
Setting up the Ionic Project
Let’s start by setting up our Ionic project. Open a terminal or command prompt and run the following commands:
ionic start social-media-app blank --type=angular
cd social-media-app
Next, add the iOS and Android platforms:
ionic capacitor add ios
ionic capacitor add android
User Authentication with Firebase
To allow users to sign up and sign in to our app, we’ll use Firebase Authentication. Run the following command to install the Firebase SDK:
npm install firebase @angular/fire
Next, open the src/environments/environment.ts
file and add the Firebase configuration. Replace YOUR_FIREBASE_CONFIG
with your actual Firebase project configuration:
export const environment = {
production: false,
firebase: {
apiKey: "YOUR_FIREBASE_API_KEY",
authDomain: "YOUR_FIREBASE_AUTH_DOMAIN",
projectId: "YOUR_FIREBASE_PROJECT_ID",
storageBucket: "YOUR_FIREBASE_STORAGE_BUCKET",
messagingSenderId: "YOUR_FIREBASE_MESSAGING_SENDER_ID",
appId: "YOUR_FIREBASE_APP_ID"
}
};
Now, open the src/app/app.module.ts
file and import the following modules:
import { AngularFireModule } from '@angular/fire';
import { AngularFireAuthModule } from '@angular/fire/auth';
import { environment } from '../environments/environment';
Then, add AngularFireModule.initializeApp(environment.firebase)
and AngularFireAuthModule
to the imports section of the @NgModule
decorator.
The final app.module.ts
file should look like this:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { IonicModule } from '@ionic/angular';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { AngularFireModule } from '@angular/fire';
import { AngularFireAuthModule } from '@angular/fire/auth';
import { environment } from '../environments/environment';
@NgModule({
declarations: [AppComponent],
entryComponents: [],
imports: [
BrowserModule,
IonicModule.forRoot(),
AppRoutingModule,
AngularFireModule.initializeApp(environment.firebase),
AngularFireAuthModule,
],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
Creating the Authentication Service
Let’s create a service to handle user authentication. Run the following command to generate a new service:
ionic generate service services/authentication
Open the newly created src/app/services/authentication.service.ts
file and replace the contents with the following code:
import { Injectable } from "@angular/core";
import { AngularFireAuth } from "@angular/fire/auth";
@Injectable({
providedIn: "root"
})
export class AuthenticationService {
constructor(private afAuth: AngularFireAuth) {}
signup(email: string, password: string) {
return this.afAuth.createUserWithEmailAndPassword(email, password);
}
login(email: string, password: string) {
return this.afAuth.signInWithEmailAndPassword(email, password);
}
logout() {
return this.afAuth.signOut();
}
getCurrentUser() {
return this.afAuth.user;
}
}
This service provides methods for signing up, logging in, logging out, and getting the current user.
Implementing the Authentication Flow
Now, let’s add the authentication flow to our app.
Open the src/app/app.component.html
file and replace its contents with the following code:
<ion-app>
<ion-router-outlet></ion-router-outlet>
</ion-app>
Next, create a new file src/app/pages/login/login.page.html
and add the following code:
<ion-header>
<ion-toolbar>
<ion-title>Login</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-grid>
<ion-row>
<ion-col size="12" offset="0" text-center>
<ion-label>Login to your account</ion-label>
</ion-col>
</ion-row>
<ion-row>
<ion-col size="12" offset="0" text-center>
<ion-item>
<ion-label position="floating">Email</ion-label>
<ion-input [(ngModel)]="email" type="email"></ion-input>
</ion-item>
</ion-col>
</ion-row>
<ion-row>
<ion-col size="12" offset="0" text-center>
<ion-item>
<ion-label position="floating">Password</ion-label>
<ion-input [(ngModel)]="password" type="password"></ion-input>
</ion-item>
</ion-col>
</ion-row>
<ion-row>
<ion-col size="12" offset="0" text-center>
<ion-button expand="full" (click)="login()">Login</ion-button>
</ion-col>
</ion-row>
<ion-row>
<ion-col size="12" offset="0" text-center>
<ion-button expand="full" color="medium" (click)="goToSignup()">Don't have an account? Sign up</ion-button>
</ion-col>
</ion-row>
</ion-grid>
</ion-content>
Create a new file src/app/pages/login/login.page.ts
and add the following code:
import { Component, OnInit } from "@angular/core";
import { Router } from "@angular/router";
import { AuthenticationService } from "../../services/authentication.service";
@Component({
selector: "app-login",
templateUrl: "./login.page.html",
styleUrls: ["./login.page.scss"],
})
export class LoginPage implements OnInit {
email: string;
password: string;
constructor(private authService: AuthenticationService, private router: Router) {}
ngOnInit() {}
login() {
this.authService.login(this.email, this.password).then(() => {
this.router.navigateByUrl("/home");
});
}
goToSignup() {
this.router.navigateByUrl("/signup");
}
}
Similarly, create a new file src/app/pages/signup/signup.page.html
with the following content:
<ion-header>
<ion-toolbar>
<ion-title>Sign Up</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-grid>
<ion-row>
<ion-col size="12" offset="0" text-center>
<ion-label>Create an account</ion-label>
</ion-col>
</ion-row>
<ion-row>
<ion-col size="12" offset="0" text-center>
<ion-item>
<ion-label position="floating">Email</ion-label>
<ion-input [(ngModel)]="email" type="email"></ion-input>
</ion-item>
</ion-col>
</ion-row>
<ion-row>
<ion-col size="12" offset="0" text-center>
<ion-item>
<ion-label position="floating">Password</ion-label>
<ion-input [(ngModel)]="password" type="password"></ion-input>
</ion-item>
</ion-col>
</ion-row>
<ion-row>
<ion-col size="12" offset="0" text-center>
<ion-button expand="full" (click)="signup()">Sign Up</ion-button>
</ion-col>
</ion-row>
<ion-row>
<ion-col size="12" offset="0" text-center>
<ion-button expand="full" color="medium" (click)="goToLogin()">Already have an account? Login</ion-button>
</ion-col>
</ion-row>
</ion-grid>
</ion-content>
In the src/app/pages/signup/signup.page.ts
file, add the following code:
import { Component, OnInit } from "@angular/core";
import { Router } from "@angular/router";
import { AuthenticationService } from "../../services/authentication.service";
@Component({
selector: "app-signup",
templateUrl: "./signup.page.html",
styleUrls: ["./signup.page.scss"],
})
export class SignupPage implements OnInit {
email: string;
password: string;
constructor(private authService: AuthenticationService, private router: Router) {}
ngOnInit() {}
signup() {
this.authService.signup(this.email, this.password).then(() => {
this.router.navigateByUrl("/home");
});
}
goToLogin() {
this.router.navigateByUrl("/login");
}
}
Now, let’s update the src/app/app-routing.module.ts
file to include the authentication routes. Replace its contents with:
import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import { LoginPage } from "./pages/login/login.page";
import { SignupPage } from "./pages/signup/signup.page";
const routes: Routes = [
{ path: "", redirectTo: "login", pathMatch: "full" },
{ path: "login", component: LoginPage },
{ path: "signup", component: SignupPage },
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
Let’s update the src/app/app.component.ts
file to check if the user is logged in and route to the correct page. Replace its code with:
import { Component } from "@angular/core";
import { Router } from "@angular/router";
import { AuthenticationService } from "./services/authentication.service";
import { User } from "firebase/app";
@Component({
selector: "app-root",
templateUrl: "app.component.html",
styleUrls: ["app.component.scss"],
})
export class AppComponent {
currentUser: User;
constructor(private authService: AuthenticationService, private router: Router) {
this.authService.getCurrentUser().subscribe((user) => {
this.currentUser = user;
if (this.currentUser) {
this.router.navigateByUrl("/home");
} else {
this.router.navigateByUrl("/login");
}
});
}
}
Now, if you run the app using ionic serve
, you should see the login page. You can sign up with a new account or log in with an existing account to proceed.
Building the Feed Page
Now that we have the authentication flow, let’s create the home page where users can see and interact with the feed.
First, update the src/app/app-routing.module.ts
file to include a new route:
import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import { LoginPage } from "./pages/login/login.page";
import { SignupPage } from "./pages/signup/signup.page";
import { HomePage } from "./pages/home/home.page";
const routes: Routes = [
{ path: "", redirectTo: "login", pathMatch: "full" },
{ path: "login", component: LoginPage },
{ path: "signup", component: SignupPage },
{ path: "home", component: HomePage },
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
Next, generate a new page for the home feed:
ionic generate page pages/home
Open the src/app/pages/home/home.page.html
file and replace the contents with the following code:
<ion-header>
<ion-toolbar>
<ion-title>Welcome to the Feed</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-button (click)="logout()">Logout</ion-button>
</ion-content>
In the src/app/pages/home/home.page.ts
file, add the following code:
import { Component, OnInit } from "@angular/core";
import { Router } from "@angular/router";
import { AuthenticationService } from "../../services/authentication.service";
@Component({
selector: "app-home",
templateUrl: "./home.page.html",
styleUrls: ["./home.page.scss"],
})
export class HomePage implements OnInit {
constructor(private authService: AuthenticationService, private router: Router) {}
ngOnInit() {}
logout() {
this.authService.logout().then(() => {
this.router.navigateByUrl("/login");
});
}
}
Now, if you navigate to the /home
route after logging in, you should see the feed page with a logout button.
Adding Firebase Realtime Database
To store and retrieve posts from our social media app, we’ll use Firebase Realtime Database.
First, let’s install the required Firebase modules:
npm install firebase @angular/fire
Now, open the src/app/app.module.ts
file and import the following modules:
import { AngularFireModule } from '@angular/fire';
import { AngularFireAuthModule } from '@angular/fire/auth';
import { AngularFireDatabaseModule } from '@angular/fire/database';
import { environment } from '../environments/environment';
Next, add AngularFireDatabaseModule
to the imports section of the @NgModule
decorator.
The final app.module.ts
file should look like this:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { IonicModule } from '@ionic/angular';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { AngularFireModule } from '@angular/fire';
import { AngularFireAuthModule } from '@angular/fire/auth';
import { AngularFireDatabaseModule } from '@angular/fire/database';
import { environment } from '../environments/environment';
@NgModule({
declarations: [AppComponent],
entryComponents: [],
imports: [
BrowserModule,
IonicModule.forRoot(),
AppRoutingModule,
AngularFireModule.initializeApp(environment.firebase),
AngularFireAuthModule,
AngularFireDatabaseModule,
],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
Building the Post Service
Let’s create a service to handle CRUD operations for posts.
Run the following command to generate a new service:
ionic generate service services/post
Open the src/app/services/post.service.ts
file and replace the contents with the following code:
import { Injectable } from "@angular/core";
import { AngularFireDatabase } from "@angular/fire/database";
import { Observable } from "rxjs";
@Injectable({
providedIn: "root",
})
export class PostService {
constructor(private db: AngularFireDatabase) {}
createPost(post: any) {
return this.db.list("posts").push(post);
}
getPosts(): Observable<any[]> {
return this.db.list("posts").valueChanges();
}
updatePost(postId: string, update: any) {
return this.db.object(`posts/${postId}`).update(update);
}
deletePost(postId: string) {
return this.db.object(`posts/${postId}`).remove();
}
}
Creating the Post Form
Now, let’s create a form to allow users to create posts.
Open the src/app/pages/home/home.page.html
file and replace its contents with the following code:
<ion-header>
<ion-toolbar>
<ion-title>Welcome to the Feed</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-button (click)="logout()">Logout</ion-button>
<form (submit)="createPost()">
<ion-item>
<ion-label position="floating">Post Content</ion-label>
<ion-textarea [(ngModel)]="postContent" required></ion-textarea>
</ion-item>
<ion-button expand="full" type="submit">Create Post</ion-button>
</form>
<ion-list>
<ion-item-sliding *ngFor="let post of posts">
<ion-item>
<ion-label>{{ post.content }}</ion-label>
</ion-item>
<ion-item-options>
<ion-item-option color="danger" (click)="deletePost(post.key)">
Delete
</ion-item-option>
</ion-item-options>
</ion-item-sliding>
</ion-list>
</ion-content>
In the src/app/pages/home/home.page.ts
file, add the following code:
import { Component, OnInit } from "@angular/core";
import { Router } from "@angular/router";
import { AuthenticationService } from "../../services/authentication.service";
import { PostService } from "../../services/post.service";
@Component({
selector: "app-home",
templateUrl: "./home.page.html",
styleUrls: ["./home.page.scss"],
})
export class HomePage implements OnInit {
postContent: string;
posts: any[];
constructor(
private authService: AuthenticationService,
private router: Router,
private postService: PostService
) {}
ngOnInit() {
this.fetchPosts();
}
fetchPosts() {
this.postService.getPosts().subscribe((posts) => {
this.posts = posts;
});
}
createPost() {
const post = {
content: this.postContent,
};
this.postService.createPost(post).then(() => {
this.fetchPosts();
this.postContent = "";
});
}
deletePost(postId: string) {
this.postService.deletePost(postId).then(() => {
this.fetchPosts();
});
}
logout() {
this.authService.logout().then(() => {
this.router.navigateByUrl("/login");
});
}
}
Now, if you navigate to the home page after logging in, you should see a form to create new posts and a section to display and delete posts.
Building the Profile Page
Finally, let’s create a profile page to display and update user information.
Run the following command to generate a new page:
ionic generate page pages/profile
Open the src/app/app-routing.module.ts
file and import the ProfilePage
and add a new route:
“`typescript
import { NgModule } from “@angular/core”;
import { RouterModule, Routes } from “@angular/router”;
import { LoginPage } from “./pages/login/login.page”;
import { SignupPage } from “./pages/signup/signup.page”;
import { HomePage } from “./pages/home/home.page”;
import { ProfilePage } from “./pages/profile/profile.page”;
const routes: Routes = [
{ path: “”, redirectTo: “login”, pathMatch: “full” },
{ path: “login”, component: LoginPage },
{ path: “signup”, component: SignupPage },
{ path: “home”, component: HomePage },
{ path: