TECH

June 22, 2024

MVP Design Pattern in iOS programming

The MVP (Model-View-Presenter) pattern is a design pattern used to separate the user interface logic from the business logic in applications. MVP helps make code easier to maintain and test. In an iOS application using Objective-C, you can organize the MVP pattern as follows:
Key Components of MVP:

1. Model

Contains the data and business logic of the application.
 
Student.h
@interface Student : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age;
@end
 
Student.m
@implementation Student
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age {
self = [super init];
if (self) {
_name = name;
_age = age;
}
return self;
}
 

2. View Protocol

The View protocol will include methods to display data and may have methods to show errors or other statuses.
The presenter communicate to the related view through view protocol
 
StudentViewProtocol.h
 
@protocol StudentViewProtocol <NSObject>
- (void)showStudentName:(NSString *)name;
- (void)showStudentAge:(NSString *)age;
@end

 

3. Presenter

The Presenter communicates with the View via the StudentViewProtocol. Additionally, the Presenter needs a protocol for the View to notify user events.
 
StudentPresenter.h
 
#import "StudentViewProtocol.h"
@protocol StudentPresenterProtocol <NSObject>
- (void)onLoadStudentButtonClicked;
@end
 
@interface StudentPresenter : NSObject <StudentPresenterProtocol>
@property (nonatomic, weak) id<StudentViewProtocol> view;
- (instancetype)initWithView:(id<StudentViewProtocol>)view;
@end
 
StudentPresenter.m
 
#import "StudentPresenter.h"
#import "Student.h"
@implementation StudentPresenter
- (instancetype)initWithView:(id<StudentViewProtocol>)view {
self = [super init];
if (self) {
_view = view;
}
return self;
}
- (void)onLoadStudentButtonClicked {
// In a real application, you might fetch data from a service or database
Student *student = [[Student alloc] initWithName:@"Phuoc Nguyen" age:45];
// Format data before sending to the View
NSString *ageString = [NSString stringWithFormat:@"%ld years old", (long)student.age];
// Update the View
[self.view showStudentName:student.name];
[self.view showStudentAge:ageString];
}
@end

4. View

View contains only view-related logic
In iOS, ViewController acts as the View and is also the place where user events are notified to the Presenter.
StudentViewController.h
#import <UIKit/UIKit.h>
#import "StudentViewProtocol.h"
#import "StudentPresenter.h"
@interface StudentViewController : UIViewController <StudentViewProtocol>
@property (nonatomic, strong)id<StudentPresenterProtocol> presenter;
@property (weak, nonatomic) IBOutlet UILabel *nameLabel;
@property (weak, nonatomic) IBOutlet UILabel *ageLabel;
@property (weak, nonatomic) IBOutlet UIButton *loadStudentButton;
@end

 

StudentViewController.m
#import "StudentViewController.h"
@implementation StudentViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.presenter = [[StudentPresenter alloc] initWithView:self];
[self.loadStudentButton addTarget:self.presenter action:@selector(onLoadStudentButtonClicked) forControlEvents:UIControlEventTouchUpInside];
}
- (void)showStudentName:(NSString *)name {
self.nameLabel.text = name;
}
- {
self.ageLabel.text = age;
}
@end
 
In this example, the View notifies the Presenter when the "Load Student" button is clicked. The Presenter then processes the request, fetches data from the Model, formatsit, and updates the View. This ensures that all handling logic is separated from the View, making the code more maintainable and testable.
The class diagram:

(The post is used free image from Pexels source)
View More
TECH

June 20, 2024

Setting Up MySQL Master-Slave Replication with Docker.

In today's data management environment, ensuring database availability and scalability is critical. MySQL master-slave replication is a popular technique to achieve this, helping to distribute the workload and enhance system reliability. Combined with Docker, the process of deploying and managing this system becomes easier and more flexible. In this article, we will explore how to set up MySQL master-slave replication using Docker.

What is MySQL Master-Slave Replication?

MySQL master-slave replication is a mechanism in which data from a "master" server is automatically replicated to one or more "slave" servers. This helps distribute read load, enhance resiliency, and provide easier backups.

Setup steps

1. We need to set up the directory structure as follows.

2. Edit my.cnf file information for master and slave.

master/my.cnf

[mysqld]
socket = /var/run/mysqld/mysqld.sock
lower_case_table_names = 2
log_error=/var/log/mysql/error.log
character-set-server=utf8
collation-server=utf8_general_ci
server-id=101
binlog_format=ROW
log-bin=/var/lib/mysql/mysql-bin.log

[mysql]
default-character-set=utf8

[client]
default-character-set=utf8

slave/my.cnf

[mysqld]
socket = /var/run/mysqld/mysqld.sock
lower_case_table_names = 2
log_error=/var/log/mysql/error.log
character-set-server=utf8
collation-server=utf8_general_ci
server-id=201
binlog_format=ROW
log-bin=/var/lib/mysql/mysql-bin.log

[mysql]
default-character-set=utf8

[client]
default-character-set=utf8

3. Edit docker-compose.yml file information.

version: '3'
services:
  mysql-master:
    image: percona:ps-8.0
    volumes:
      - ./master/data:/var/lib/mysql
      - ./master/my.cnf:/etc/my.cnf
    environment:
      TZ: Asia/Tokyo
      MYSQL_USER: master
      MYSQL_PASSWORD: Master123@
      MYSQL_ROOT_PASSWORD: Mastermaster123

  mysql-slave:
    image: percona:ps-8.0
    container_name: mysql-slave
    volumes:
      - ./slave/data:/var/lib/mysql
      - ./slave/my.cnf:/etc/my.cnf
    environment:
      TZ: Asia/Tokyo
      MYSQL_USER: slave
      MYSQL_PASSWORD: slave123@
      MYSQL_ROOT_PASSWORD: slaveslave123

4. Start 2 servers:

docker compose up -d

5. Wait for the process of building the container to be done successfully.

After that, check the process with the command:

docker-compose ps

6. Enter the "mysql-master" container to set the replication DB on the master server:

docker-compose exec mysql-master bash

7. Login to mySQL by the root account:

mysql -u root -pMastermaster123

8. Check the binary log whether enabled successfully or not:

SHOW VARIABLES LIKE 'log_bin';

You must make sure the status of the binary is ON before processing the next step.

9. Create a new user in order to the slave server can access to the master server and check the binary log.

Use the below command to create the 'replication' user.

CREATE USER 'replication'@'%' IDENTIFIED BY 'Slaverepl123';

10. Grant user replication access to allow creation for MySQL replications:

GRANT REPLICATION SLAVE ON *.* TO 'replication'@'%';

11. Confirm whether the grants of the user were successful or not:

SHOW GRANTS FOR replication@'%';

12. Confirm the status of the binary log on the master server:

SHOW MASTER STATUS\G

You need to save the File and Position.
We will use these values to set on the slave server.

13. Enter the "mysql-slave" container to set the replication DB on the slave server:

docker-compose exec mysql-slave bash

14. Login to mySQL by the root account:

mysql -u root -pslaveslave123

15. Execute the SQL command so it can read the binary log for any changes on the master server and make it the same on the slave server.

We will set the value of the MASTER_LOG_FILE and MASTER_LOG_POS by the value obtained from step 12.

CHANGE MASTER TO
MASTER_HOST='mysql-master',
MASTER_USER='replication',
MASTER_PASSWORD='Slaverepl123',
MASTER_LOG_FILE='binlog.000003',
MASTER_LOG_POS=1161;

16. Let’s start the slave on mysql:

START SLAVE;

17. Confirm the status of the replication on the slave server:

SHOW SLAVE STATUS\G

If the Slave_IO_State is "Waiting for source to send event" then we have set up successfully.

From now on, if the master server changes anything, it also changes on the slave server.

18. Confirm synchronization between the two databases.

Check all databases on the master/slave server.

SHOW DATABASES;

Create a new database on the master server and re-check all databases on the master server.

CREATE DATABASE replicate_db;

A new database has been created on the master server and it has also been created on the slave server.

Conclusion

With the use of Docker, setting up MySQL master-slave replication becomes much simpler and faster. This not only improves data management but also provides an easy environment for testing and development. Try implementing this system for your project to see its obvious benefits.

Using Docker to manage MySQL replication helps you take advantage of the benefits of containerization, such as easily deploying, scaling, and managing complex database systems efficiently.

References:

View More
TECH

June 20, 2024

Web Camera Issues on Devices: Troubleshooting Tips

Recently, projects involving device interactions, such as with cameras and audio, have become popular. I just
completed a project using a device's camera, and encountered some challenges that required significant effort to
overcome. To assist with your current or future projects that utilize a device's camera, I decided to write this
blog. It discusses some common issues encountered when working with cameras, and provides solutions to these
problems. I hope this will promptly resolve your current issues and prevent similar problems in your future
projects.

View More
TECH

April 28, 2024

How to implement automatic activation feature for Windows applications.

    While using a computer for work, you'll certainly have to work with licensed applications. One of the important feature of licensed applications is activating the license with a key, but I believe that if you're a first-time user of the application, grasping the activation process can be difficult to ensure the correct execution of the activation process, software developers need to provide detailed and simplified instructions. With a principle of prioritizing user experience and application security, developers often develop the "Auto-Activation" feature, where a single mouse click on the application link automatically activates it and makes it ready to use.

    In this article, I will guide you on how to implement the "Auto-Activation" feature via email for a Windows application. There are certainly many other ways to implement this feature, but I will try to provide the simplest instructions possible so that you can understand how this feature works and apply it to your project.

1. Development environment

For implementing the auto-activation feature via email for a Windows application, I will develop an environment with two main components:

    • ActivationServer:
      • Function: Manage data, send emails, redirect activation links
      • Email service: Gmail
      • Web Framework: ASP.NET
    • Application:
      • Function: Perform activation
      • Application Framework: Windows Form C#

2. Functional design

    Regarding the specific operation of the feature, I will describe it according to the diagram below. Through the Windows operating system allowing you to call commands to open applications via "application protocol" (reference "Registering an Application to a URI Scheme"), we will create an application protocol for our application. Then, we can pass the activation key to the application to activate it as a URI parameter.

Auto activation email sending

Picture 1. Auto activation email sending sequence diagram

Auto activation processing

Picture 2. Auto activation processing sequence diagram

3. Functional development

On the server side, I will implement the routes as follows:

    • GET Home/index: Display the email sending form.
    • POST Home/SendMail: Generate the activation key and send the email.
    • GET Home/AutoRedirect: Perform redirection to the application protocol.

HomeController.cs

using System;
using System.Net;
using System.Net.Mail;
using System.Web.Mvc;
using Utility;

namespace ActivationServer.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View("Index", new ViewDataDictionary { { "message", string.Empty } });
        }

        [HttpPost]
        public ActionResult SendMail(FormCollection form)
        {
            try
            {
                string emailAddress = form["email"];
                string host = "http://myserver.com";
                string mailAddress = "myemail@gmail.com";
                string mailPassword = "mypassword";
                string mailHost = "smtp.gmail.com";
                int maiPort = 587;
                string mailSubject = "Auto activation email";
                string mailBody = "<h2>Auto Activation Email</h2><a href="{{ActivationLink}}">Click here to active!</a>";
                mailBody = mailBody.Replace("{{ActivationLink}}",
                           $"{host}/Home/AutoRedirect?protocol=appdemo&param=myactivationkey}");

                using (SmtpClient client = new SmtpClient())
                {
                    client.Host = mailHost;
                    client.Port = maiPort;
                    client.EnableSsl = true;
                    client.Credentials = new NetworkCredential(mailAddress, mailPassword);

                    using (MailMessage message = new MailMessage())
                    {
                        message.From = new MailAddress(mailAddress);
                        message.To.Add(new MailAddress($"{emailAddress}"));
                        message.Subject = mailSubject;
                        message.Body = mailBody;
                        message.IsBodyHtml = true;

                        client.Send(message);
                    };
                };
                return View("Index", new ViewDataDictionary { { "message", "Activation mail has been sent!" } });
            }
            catch (Exception ex)
            {
                return View("Error", new ViewDataDictionary { { "message", ex.Message } });
            }
        }

        public ActionResult AutoRedirect(string protocol, string param)
        {
            return Redirect($"{protocol}://?key={param}");
        }
    }
}

In the example, I'm using email sending with an HTML code to embed a link in the email using an <a> tag.

    I'll further explain why we don't directly insert the application protocol into the activation link. The reason is that an application protocol inherently lacks the format of an HTML protocol, making it prone to being stripped out by email servers during the email sending process. Therefore, instead of directly inserting the application protocol, we'll use a route like "Home/AutoRedirect" to redirect to the application protocol.

Index.cshtml

@{
    ViewBag.Title = "Home Page";
}

<main>
    <section class="row" aria-labelledby="aspnetTitle">
        <h1 id="title">Auto Activation Demo</h1>
    </section>

    <div class="row">
        <section class="col-md-4" aria-labelledby="">
            <form method="post" action="@Url.Action("SendMail")">
                <div class="mb-3">
                    <label for="txtEmail" class="form-label">Email address</label>
                    <input type="email" class="form-control" name="email" id="txtEmail" required>
                </div>
                <button type="submit" class="btn btn-primary">Submit</button>
            </form>
        </section>

        <section class="row mt-3">
            <p id="message">@Model["message"]</p>
        </section>
    </div>
</main>

On the application side, I will implement 2 forms as follows:

    • Active: Activation screen

Active screen

Picture 3.  Active Screen

    • Main: Main screen of the application

Main screen

Picture 4 .  Main Screen

    I'll add modifications so that the application can be launched via CLI with a "key" parameter. This is aimed at inserting the activation key into the activation screen upon startup.

Programs.cs

using System.Web;
using Utility;
using AppSetting = ApplicationDemo.Properties.Settings;

namespace ApplicationDemo
{
    internal static class Program
    {
        private static Mutex? mutex = null;

        ///
        ///  The main entry point for the application.
        /// 
        [STAThread]
        static void Main()
        {
            const string appName = "ApplicationDemo";
            bool createdNew;

            mutex = new Mutex(true, appName, out createdNew);

            if (!createdNew)
            {
                MessageBox.Show("Application already running!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            ApplicationConfiguration.Initialize();

            Form startForm = new Main();
            if (string.IsNullOrWhiteSpace(AppSetting.Default.Key) || !CommonCode.IsValidKey(AppSetting.Default.Key))
            {
                string[] args = Environment.GetCommandLineArgs();
                string key = string.Empty;
                if (args.Length > 1)
                {
                    Uri uri = new Uri(args[1]);
                    key = $"{HttpUtility.ParseQueryString(uri.Query).Get("key")}";
                }
                startForm = new Active(key);
            }

            Application.Run(startForm);
        }
    }
}

Active.cs

using Utility;
using AppSetting = ApplicationDemo.Properties.Settings;

namespace ApplicationDemo
{
    public partial class Active : Form
    {
        public Active(string key)
        {
            InitializeComponent();

            txtActivationKey.Text = key;
        }

        private void btnActive_Click(object sender, EventArgs e)
        {
            try
            {
                if (string.IsNullOrWhiteSpace(txtActivationKey.Text))
                {
                    throw new Exception("Please enter activation key!");
                }

                if (CommonCode.IsValidKey(txtActivationKey.Text))
                {
                    MessageBox.Show("Activation Successful", "Information", MessageBoxButtons.OK,
                                    MessageBoxIcon.Information);
                    AppSetting.Default.Key = txtActivationKey.Text;
                    AppSetting.Default.Save();
                    Main main = new();
                    main.Show();
                    Hide();
                }
                else
                {
                    MessageBox.Show("The activation key is invalid!", "Error", MessageBoxButtons.OK,
                                     MessageBoxIcon.Warning);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
        }

        private void Active_FormClosed(object sender, FormClosedEventArgs e)
        {
            Application.Exit();
        }

        private void Active_Shown(object sender, EventArgs e)
        {

            if (!string.IsNullOrWhiteSpace(txtActivationKey.Text))
            {
                btnActive.PerformClick();
            }
        }
    }
}

Main.cs

using AppSetting = ApplicationDemo.Properties.Settings;

namespace ApplicationDemo
{
    public partial class Main : Form
    {
        public Main()
        {
            InitializeComponent();
        }

        private void btnDeactive_Click(object sender, EventArgs e)
        {
            AppSetting.Default.Key = string.Empty;
            AppSetting.Default.Save();

            Active active = new Active(string.Empty);
            active.Show();
            Hide();
        }

        private void Main_FormClosed(object sender, FormClosedEventArgs e)
        {
            Application.Exit();
        }
    }
}

    To make the application operational with an application protocol, we'll register it through the registry as follows, with the "ExecFilePath" is the location where the application is installed.

AppDemo.reg

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\appdemo]
"URL Protocol"=""
@="URL:appdemo"

[HKEY_CLASSES_ROOT\appdemo\shell]

[HKEY_CLASSES_ROOT\appdemo\shell\open]

[HKEY_CLASSES_ROOT\appdemo\shell\open\command]
@="ExecFilePath"   \"%1\""

To perform registry registration during application setup, you'll need to use a third-party software to package the application.

4. Operational demo:

5. Advantages and disadvantages:

    • Advantages:
      • User Experience: Streamlined activation process enhances user experience, reducing user frustration and increasing satisfaction.
      • Automation: Automating the activation process saves time and effort for both users and administrators.
      • Security: By encrypting the activation key and embedding it in a link, security is enhanced compared to manual entry or transmission via insecure channels.
      • Ease of Use: One-click activation simplifies the process for users, especially for those who may not be technically savvy.
    • Disadvantages:
      • Complexity of Implementation: Setting up the infrastructure for automated activation, including server-side processing and email integration, can be complex and require additional development effort.
      • Compatibility Issues: Because the information of the application protocol needs to be registered with the exact path to the application to be opened, it will not work if the user moves the application to a different directory.

Cover image from freepik.com

Reference:

  1. Sending emails using Gmail in C#
  2. Sign in with app passwords
  3. Registering an Application to a URI Scheme
View More
TECH

June 19, 2024

What is Socket.IO? Building a real-time chat application with Socket.IO

In the current digital landscape, real-time features have become crucial for modern web applications. Real-time features allow applications to provide instantaneous feedback, live updates, and continuous data streams, creating a more dynamic and interactive user experience. There are many technologies available to implement real-time functionality in web applications. Each technology has its own strengths. Today, I will introduce you to one of the most popular and flexible options. It is Socket.IO.
 

What is Socket.IO?

Socket.IO is a JavaScript library that enables real-time, bidirectional, and event-based communication between web clients and servers. It simplifies the complexities of real-time transport protocols, providing a unified API that makes it straightforward to implement real-time features in web applications.
 

The features provided by Socket.IO

Socket.IO is a powerful library for implementing real-time, bidirectional communication in web applications. Here are some of the key features provided by Socket.IO:
  1. Real-Time Communication:
    Socket.IO enables real-time, low-latency communication between clients and servers, allowing for instant data exchange.
  2. Bidirectional Data Flow:
    Both clients and servers can send and receive data simultaneously, enabling interactive and responsive applications.
  3. Event-Driven Architecture:
    Socket.IO uses an event-driven model, where clients and servers can emit and listen for events. This simplifies the handling of real-time interactions.
  4. Cross-Platform Compatibility:
    Socket.IO is compatible with multiple platforms, including web browsers, Node.js servers, and mobile devices. This ensures consistent real-time communication across different environments.
  5. Automatic reconnection:
    Socket.IO automatically handles connection loss and attempts to reconnect the client to the server in the event of lost connection or network problems. This ensures a stable and reliable communication channel.
  6. Scalability:
    Socket.IO supports namespaces and rooms, allowing you to organize and scale real-time communication efficiently. A Namespace is a communication channel that allows you to split the logic of your application over a single shared connection, while rooms enable broadcasting messages to specific groups of clients.
  7. Broadcasting:
    Socket.IO allows you to broadcast messages to all connected clients or to specific groups of clients (rooms). This enables scenarios like sending chat messages to all users in a chat room or notifying specific users about relevant events.

How does Socket.IO work?

Socket.IO works by establishing a bidirectional communication channel between a client (such as a web browser) and a server (typically implemented using Node.js). This communication channel enables real-time data exchange, allowing both the client and server to send and receive messages instantly.
 
In Socket.IO, sending and receiving messages involves emitting and listening for events. Here's how you can send and receive messages using Socket.IO:
  1. Sending Messages (Emitting Events):
    To send a message from the server to the client, you use the emit() method provided by the Socket.IO server library within the context of a connection event. You need to specify the name of the event you want to emit.
    io.on('connection', (socket) => {
        // Sending a 'new message' event to the client
       socket.emit('new message', 'Welcome to the chat!');
    });
     
  2. Receiving Messages (Listening for Events):
    To receive a message on the client, you listen for the event emitted by the server using the on() method provided by the Socket.IO client library.
    // Listening for the 'new message' event on the client
    socket.on('new message', (msg) => {
        console.log('New message received:', msg);
    });
     
  3. Broadcasting events:
    Broadcasting events in Socket.IO allows a server to send a message to multiple clients simultaneously. Socket.IO provides several methods for broadcasting events, allowing developers to target specific groups of clients or all connected clients.
    // Server-side code
    io.on('connection', (socket) => {
        socket.on(new message', (msg) => {
            // Broadcast to all clients except the sender
            socket.broadcast.emit(new message', msg);
        });
    });
    In this example, when a client sends a ‘new message' event, the server broadcasts that message to all connected clients except the one that sent the message.
     
  4. Broadcasting to Specific Rooms:
    Socket.IO supports the concept of rooms, which allow you to broadcast messages to a subset of clients. A room is simply a named channel that sockets can join and leave.
    Joining a room: Clients can join a room using the join() method.
    // Server-side code
    io.on('connection', (socket) => {
        socket.on('join room', (roomName) => {
            socket.join(roomName);
        });
    });
     
    Broadcasting to a Room: To broadcast an event to all clients in a specific room, use the to() or in() method.
    // Server-side code
    io.on('connection', (socket) => {
        socket.on(new message', (roomName, msg) => {
            // Broadcast to all clients in the specified room
            io.to(roomName).emit(new message', msg);

            // Broadcast to all clients in room1 and room2
            io.to('room1').to('room2').emit('chat message', msg);
        });
    });

Building a real-time chat application with Socket.IO

Description: Building a real-time Chat application where users can join rooms, send, and receive messages within those rooms using Vue 3, Express, and Socket.io.
Server Setup (Node.js):
  1. Initialize a new project:
    mkdir socketio-chat
    cd socketio-chat
    npm init -y
  2. Install Express and Socket.IO:
    npm install express socket.io
  3. Create the server file (index.js):
    const express = require('express');
    const http = require('http');
    const socketIo = require('socket.io');

    const app = express();
    const server = http.createServer(app);
    const io = socketIo(server);
    app.use(express.static('public'));

    let socketsConected = new Set()
    io.on('connection', (socket) => {
        console.log('Socket connected', socket.id)
        socketsConected.add(socket.id)
        // Emit 'clients-total' event to clients to display numbers user in the room
        io.emit('clients-total', socketsConected.size)

        socket.on('disconnect', () => {
            console.log('Socket disconnected', socket.id)
            socketsConected.delete(socket.id)
            io.emit('clients-total', socketsConected.size)
        })

        socket.on('joinRoom', (room) => {
            // Subscribe the socket to a given channel
            socket.join(room);
            socket.room = room;
        });

        socket.on('chat-message', (data) => {
            data.sender = socket.id
            // Broadcast to all clients in the specified room
            io.to(socket.room).emit('chat-message', data)
        })
    })
    server.listen(4000, () => console.log(`💬 server on port 4000`))
    Explanation: The server is listening for two main events 'joinRoom' and 'chat-message'. The ‘joinRoom’ event will subscribe the socket to a given channel (The room name user selected). The ‘chat-message’ event will listen for the ‘chat-message’ event sent from the client, and then it will send the data to all clients in the specified room via the ‘chat-message’ event.
Client Setup (Vue 3 + Socket.io-client)
Create an HTML file (index.html):
 
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>App Chat</title>
    </head>
    <body>
        <div id="app" style="width: 416px;">
            <h1 class="title">App Chat 💬</h1>
            <!-- Join room -->
            <div v-if="!joinedRoom" class="join-room d-flex flex-column">
                <select v-model="selectedRoom" class="form-select">
                    <option v-for="room in rooms" :key="room" :value="room">{{ room }}</option>
                </select>
                <input v-model="nameInput" type="text" class="form-control mt-1 mb-1" placeholder="Please enter name">
                <button type="submit" class="btn w-25 btn-light" @click="joinRoom()">Join Room</button>
            </div>
            <!-- Box chat -->
            <div v-else class="main">
                <div class="name">
                    <span class="name-input w-75">{{ selectedRoom }}</span>
                    <span class="name-input w-25 text-end">{{ clientsTotal }} <i class="far fa-user"></i></span>
                </div>
                <ul class="message-container" id="message-container" >
                </ul>
                <form class="message-form" id="message-form">
                    <inputtype="text"
                        v-model="messageInput"
                        class="message-input"
                        ref="messageInputEl"
                    />
                    <div class="v-divider"></div>
                    <button type="submit" class="send-button" @click="sendMessage($event)">
                        send <span><i class="fas fa-paper-plane"></i></span>
                    </button>
                </form>
            </div>
        </div>
   
        <!-- Include Vue 3 from CDN -->
        <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
        <script src="/socket.io/socket.io.js"></script>
        <script>
        const {
            createApp,
            ref,
            onMounted
        } = Vue;
   
        createApp({
            setup() {
                const socket = io()
                const messageInput = ref('');
                const clientsTotal = ref(0)
                const nameInput = ref('')
                const selectedRoom = ref(null)
                const joinedRoom = ref(false)
                const socketId = ref(null)
                const rooms = ref(['Room 1', 'Room 2', 'Room 3'])
                onMounted(() => {
                    socket.on('connect', () => {
                        socketId.value = socket.id;
                    });
                });
   
                const sendMessage = async (e) => {
                    e.preventDefault()
                    if (messageInput.value === '') return
                    const data = {
                        name: nameInput.value,
                        message: messageInput.value,
                        dateTime: new Date(),
                    }
                    socket.emit('chat-message', data)
                    addMessageToUI(true, data)
                    messageInput.value = ''
                }
   
                const addMessageToUI = (isOwnMessage, data) => {
                    const element = `
                        <li class="${isOwnMessage ? 'message-right' : 'message-left'}">
                            <p class="message">
                                ${data.message}
                                <span>${data.name} ● ${moment(data.dateTime).fromNow()}</span>
                            </p>
                        </li>`;
                    const messageContainer = document.getElementById('message-container')
                    messageContainer.innerHTML += element
                }
   
                socket.on('chat-message', (data) => {
                    if (socketId.value != data.sender) {
                        addMessageToUI(false, data)
                    }
                })
                socket.on('clients-total', (value) => {
                    clientsTotal.value = value
                })
   
                const joinRoom = () => {
                    joinedRoom.value = true
                    socket.emit('joinRoom', selectedRoom.value);
                }
   
                return {
                    messageInput,clientsTotal, rooms,
                    selectedRoom,joinedRoom,nameInput,
                    sendMessage, joinRoom
                };
            }
        }).mount('#app');
        </script>
    </body>
    </html>

 

Explanation:

  • Vue 3 Setup: Use Vue 3 from the CDN to create a simple Vue application.
  • Socket.io-client: Include Socket.io-client from the CDN and establish a connection to the server.
  • Event Handling: Listen for 'chat-message' and 'clients-total' events from the server to update the message list and number of users in the room. Emit 'joinRoom' and 'chat-message' events to the server.
  • Joining Room: Allow users to join a room by emitting a 'joinRoom' event to the server.
  • Sending Messages: Allow users to send messages to the room by emitting a 'chat-message' event to the server when the Enter key is pressed.
 

Demo:

Image 1

 

Image 2

 

Conclusion

Socket.IO offers numerous advantages that make it an excellent choice for implementing real-time features in web applications. Its real-time communication capabilities, bidirectional data flow, event-driven architecture, and cross-platform compatibility provide a robust foundation for building interactive and responsive applications. Additionally, features like automatic reconnection, transport layer abstraction, scalability, and a rich ecosystem further enhance its appeal. By leveraging Socket.IO, developers can create engaging and highly responsive web applications that meet the needs of today's users.
 

References

 

 

 

View More
TECH

June 19, 2024

NFC and how to use it in the Android Studio Project

NFC stands for Near Field Communication, this is a technology that has emerged as a game changer in mobile application development, providing seamless interaction between devices with just a touch. Among its many capabilities, NFC tag reading stands out as a powerful feature that allows developers to create innovative and user-friendly experiences in their Android apps. In this article, we will explore NFC in detail while also understanding how to read NFC tags in Android Studio

What is NFC?

NFC is a wireless technology that allows data to be transferred between devices over very close distances, usually just a few centimeters. This technology works on the principle of creating an electromagnetic field and communicating through it to transmit data between devices. NFC is commonly used for a variety of purposes, including mobile payments, data exchange, device connectivity, and task automation. In the mobile industry, NFC has become an important technology, especially in creating utility applications and services such as mobile payments, smart cards, and device connectivity. The strength of NFC lies in its convenience, high security, and compatibility with many different types of devices.

The convenience that NFC brings

1. Quick and easy connection:

NFC allows connection between devices simply by bringing them close together without needing to set up or search for devices in Bluetooth or Wi-Fi lists. This reduces the time and complicated steps required to connect, providing a fast and convenient experience for users. We can easily see it through Sony devices, just turn on NFC on the Sony speaker and the Sony phone, and then put them close to each other and they will connect immediately.

2. Mobile payment:

NFC has changed the way payments are made by allowing users to make financial transactions simply by touching or placing their mobile phone near an NFC reader, such as a payment terminal or machine. POS. This creates a quick, convenient, and secure payment process. The most prominent include Google Pay, Samsung Pay, and Apple Pay

3. File and data transfer:

NFC provides a convenient means of transferring files and data between devices without the need for an internet connection. Users can share photos, videos, contacts, or other files simply by touching their phone to another device.

4. Connecting IoT devices (Internet of Things):

NFC provides a simple way to connect IoT devices to each other or to the user's mobile phone. This may include configuring and connecting to devices such as lights, door locks, or other smart control devices.

5. Bluetooth and Wi-Fi connection:

NFC can also be used to initiate a Bluetooth or Wi-Fi connection between devices. Instead of having to search and connect manually, NFC can start this process automatically, reducing inconvenience for users.

How to use NFC in the Android Studio Project

Here's a basic guide to setting up the NFC tag reading in the Android Studio project:

1. Check NFC availability:
Before integrating NFC into your app, ensure that the device is capable of reading NFC and that NFC is enabled. You can do this programmatically using the NfcAdapter class.


2. Add NFC permission and declare NFC feature to Manifest:
Open your app's AndroidManifest.xml file and add the necessary permissions:

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"  xmlns:tools="http://schemas.android.com/tools">

              <uses-permission android:name="android.permission.NFC" />
              <uses-feature android:name="android.hardware.nfc" android:required="true" />


</manifest>

3. Define NFC Intent Filter:
Specify intent filters in your manifest to indicate which activities in your app should handle NFC-related actions, such as tag detection, reading, or writing.

For example:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools">
    <uses-permission android:name="android.permission.NFC" />
    <application
        android:name=".yourApplication">
        <activity
            android:name=".nfc.NfcActivity">
                <intent-filter>
                    <action android:name="android.nfc.action.NDEF_DISCOVERED" />
                    <category android:name="android.intent.category.DEFAULT" />
                </intent-filter>
        </activity>
    </application>
</manifest>


4. Handle Intent in Activity:
In your activity's onCreate() method, handle the NFC intent:

@Override
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {
                  // Handle the NFC intent here
                  handleNfcIntent(getIntent());
         }
}

private void handleNfcIntent(Intent intent) {
        // Extract the NFC tag information from the intent
        Parcelable[] rawMessages = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
        if (rawMessages != null) {
                NdefMessage[] messages = new NdefMessage[rawMessages.length];
                for (int i = 0; i < rawMessages.length; i++) {
                       messages[i] = (NdefMessage) rawMessages[I];
                 }
                // Process the NDEF messages
               // ...
        }
}

Conclusion

In conclusion, NFC tag reading offers a myriad of benefits for Android app developers, ranging from seamless interaction and information retrieval to authentication and security. By leveraging NFC technology in your Android Studio projects, you can create immersive and intuitive experiences that delight users

[Reference- resource]
- https://developer.android.com/reference/android/nfc/NfcAdapter#summary  (Docs)
- https://www.linkedin.com/pulse/how-use-nfc-your-flutter-app-ahmed-ghaith (Image)

View More
TECH

June 12, 2024

Exploring the role of "Cushion Words" in Japanese Language

Have you ever heard of the Japanese word "cushion words"? Today, I would like to introduce some of the politest Japanese words, "cushion words".

1. What is "cushion words"? 

“Cushion words” are also called business pillow words, and by adding them as an introduction to a conversation, they have the effect of softening the impression that the words convey to the other person than if they were said directly. It is just like a “cushion” to soften the impact of words.

You can also express your feelings of “sorry” (申し訳ない) or “I may be a nuisance to the other person, but…” (もしかしたら相手としては迷惑かもしれないけれど…) in a way that shows compassion for the other person.

2.Scenes and effects where "cushion words" should be used

It is mainly used in situations where you want to make a “request”, “refusal”, or “opinion/refutation”. By using a variety of words and using them appropriately, you should be able to express your words in a way that touches the other person’s heart. It also has the effect of making it easier to convey to the other person that “You are an important person who I want to take care of”.

You can use “cushion words” when talking to customers, seniors, superiors, etc.

When making an offer or asking something, when offering something that you don’t know if the other person needs, or when you want to ask something, cushion words such as “If you don’t mind” (もしよろしければ), or “If it’s not a problem” (差し支えなければ) are used. This phrase is useful when you want to extract information from the other party during a business negotiation or meeting, or when making a proposal.

The cushion words are used not only in face-to-face communication, but also in emails and documents where it is difficult to convey nuances and feelings. This will avoid giving the impression that you are bossy or one-sided, and it will show that you are considerate.  Cushion words are essential for building trusting relations.

Here are some cases to use cushion words.

a. When asking something.

「お尋ねしてよろしいでしょうか」 (May I ask?)

「失礼ですが」 (Excuse me)

「差し支えなければ」 (If it’s not a problem)

「お教えいただきたいのですが」 (I would like to ask you to tell me.)

Ex:失礼ですが、お名前をフルネームでお聞かせいただけますでしょうか。

b. When requesting something.

「恐縮ですが」 (I’m sorry)

「ご面倒をお掛けしますが」 (I apologize for the inconvenience.)

「ご迷惑とは存じますが」 (I understand this is nuisance.)

「こちらの都合で恐れ入りますが」 (We apologize for the inconvenience.)

Ex:ご面倒をお掛けしますが、お引き受けいただけないでしょうか。

c. When you want cooperation.

「恐れ入りますが」 (Excuse me)

「お手数をおかけしますが」 (I apologize for the inconvenience)

「ご面倒をおかけします」 (I'm sorry for the inconvenience)

「お忙しいところ恐れ入りますが」 (I apologize for the inconvenience, but I'm busy)

Ex:恐れ入りますが、〇日までにメールでお返事をいただいてもよろしいでしょうか。

d. When to refuse.

「あいにく」(Unfortunately)

「残念なのですが」(Unfortunately)

「お気持ちはとてもよくわかるのですが」 (I know exactly how you feel.)

「せっかくのお申し出をいただき、大変ありがたいのですが」 (I would like to thank you very much for your request.)

「ご期待に添えず、大変申し訳ないのですが」 (I am very sorry that I did not meet your expectations.)

「私どもの力不足で、大変恐縮なのですが」 (I am very sorry that I'm lacking in ability.) 

Ex: ご希望のデザインには添えず、申し訳ございません。再度、見直しを行います。

e. When giving an opinion or counterargument.

「僭越ながら」 (Please allow me (to say something))

「おっしゃることは重々承知をしておりますが」 (I am fully aware of what you are saying.)

「余計なこととは存じますが」 (I know it's unnecessary, but...)

「私の考え過ぎかもしれませんが」 (Maybe I'm thinking too much)

Ex: おっしゃることは重々承知をしておりますが、今回はA案をご提案させていただけないでしょうか。

f. When a request cannot be met.

「せっかくお声をかけていただいたのですが」 (Thank you for taking time to contact me but)

「ぜひご期待にお応えしたかったのですが」 (I really wanted to meet your expectations but)

「身に余るお話、光栄なのですが」 (It's an honor to hear your story)

「申し上げにくいのですが」 (It's hard to tell)

Ex: 身に余るお話、光栄なのですが、今回は辞退させていただいてもよろしいでしょうか?

g. When you want something to be improved.

「細かいことを言ってしまい恐縮ですが」 (I apologize for mentioning such details)

「こちらの都合ばかりで申し訳ございませんが」 (I apologize for the inconvenience)

「説明が足りず失礼いたしました」 (I apologize for not explaining enough)

Ex: 説明が足りず失礼いたしました。決定の理由を2つ以上あげていただけると助かります。

h. When offering assistance.

「もしよろしければ」 (If you do not mind)

「私でよければ」 (If you're okay with me)

「差し支えなければ」 (If it's not a problem)

「お力になれるのであれば」 (If I can help)

Ex: もしよろしければ〇〇までご提案させていただきます。

3. Examples of NG usage of cushion words that are easy to mistake.

  • 「申し訳ございません」 (I’m sorry)

「申し訳ございません」 (I’m sorry) is a cushion word used when you can’t meet someone’s request or when you apologize. Therefore, if you use it casually when it is not needed, it will end up sounding too light in the situation where you originally wanted to use it.

NG example:

「申し訳ございませんが、お名前をお聞かせいただけますか」

「申し訳ございませんが、こちらにご記入いただいてもよろしいでしょうか」

OK example:

「申し訳ございませんが、セール品につきましては返品をご遠慮いただいております」

  • 「差し支えなければ」 (If you don’t mind)

「差し支えなければ」 (If you don’t mind) includes the meaning of “Please decline the request if it is inconvenient.” Therefore, it can only be used in situations where there is no inconvenience caused even if the other party refuses. For example, it cannot be used when you are responding to a telephone call and need to know the person’s name to be connected to the agent, so be careful.

NG example:

「差し支えなければ、 お名前を伺ってもよろしいでしょうか」

→「差し支えなければ」を「恐れ入りますが」に変更しましょう。

OK example:

「差し支えなければ資料をご自宅にお送りしましょうか」

4. Precautions when using cushion words

Cushion words are soft expressions that give a polite impression, but if used too often, they can give a dull impression and make it difficult to get the main point across.

Cushion words can be used in a variety of situations, such as emails, chats, telephone calls, and face-to-face meetings, but especially in phone calls and face-to-face situations where sentences cannot be confirmed in text, it is likely that your intentions will be difficult to convey, so avoid using cushion words frequently.

5. How to respond to 「差し支えなけれ」 (If you don’t mind)

So far, we have looked at the expressions 「差し支えなけれ」 (If you don’t mind) used when requesting someone to do something, but there may be times when the other makes a request “If you don’t mind” to us. When accepting a request, a response such as 「承知しました」 (I understand) or 「かしこまりました」 (I understand) is appropriate.

Also, as mentioned above, 「差し支えなけれ」 (If you don’t mind) is an expression that leaves the decision on whether or not to accept the request to you, so of course you can decline it.

When you say “No”, be sure to show that you are sorry.

For example, if you can respond with something like 「大変申し訳ないですが、〇〇があるため、行うことが難しい状況です。お役に立てず恐縮でございます」 (I’m very sorry, but due to 〇〇, I’m unable to help you, I’m sorry that I can’t help you.)

This will leave a good impression.

6. Use cushion words appropriately to facilitate communication.

Cushion words are used in a variety of business situations to show consideration for others and facilitate communication.

It’s good idea to understand multiple phrases and appropriate situations, as they will be useful in situations where you need to convey something that is difficult to convey, such as when declining a request from someone or making a counterargument.

Make sure to use cushion words appropriately depending on the situation and maintain smooth communication and good relationships.

Reference Image and Document Source:

  • https://www.pexels.com/
  • https://go.chatwork.com/
  • https://allabout.co.jp/
View More
TECH

April 25, 2024

Dependency Injection Overview: Using Dependency Injection in ASP.NET Core

In the world of software development, Dependency Injection (DI) stands as a fundamental concept, often hailed as a cornerstone of good design and modular architecture. But, for many developers, especially those newer to the field, the term can evoke confusion or difficult to understand. I hope that the blog will help you understand DI better.

1. Understanding Dependency Injection

At its core, Dependency Injection is a design pattern used to manage dependencies between different components or modules within a software system. It can be understood simply as follows:
  • Modules do not communicate directly with each other, but through an interface. The low-level module will implement the interface, the high-level module will call the low-level module through the interface.
  • For example: To communicate with customer service, we have the ICustomerService interface, the low-level modules are CustomerService. The high-level module CustomerController will only use the ICustomerService interface.
  • Initialization of low-level modules will be performed by DI Container. For example: In the CustomerController, we will not initialize ICustomerService service = new CustomerService (), this will be done by DI Container. The CustomerController will not know anything about the class CustomerService
  • Which Module is attached to which interface will be configured in the class Program.cs

2. The Three Types of Dependency Injection

Dependency Injection can be implemented in three main ways: Constructor Injection, Setter Injection, and Interface Injection.
  • Constructor Injection: In this approach, dependencies are provided through a class's constructor. This ensures that all required dependencies are available when an object is instantiated, promoting immutability and simplifying testing.
  • Setter Injection: Also known as property injection, Setter Injection involves providing dependencies through setter methods. While not as preferred as Constructor Injection due to the potential for objects to be in an invalid state, Setter Injection can be useful for optional dependencies.
  • Interface Injection: This approach is less common and involves providing dependencies through an interface that the client class implements. However, this method can introduce tight coupling between the client class and the injector, making it less flexible compared to Constructor Injection.
Among the three types of Inject, Constructor Injection method is very popular because of its flexibility, ease of building DI libraries...

3. Advantages and disadvantages of Dependency Injection

Advantage

  • Reduce adhesion between modules
  • Code is easy to maintain, easy to replace modules
  • Very easy to test and write Unit Test

Disadvantages

  • The concept of DI is quite difficult to understand new developers will have difficulty learning it
  • Objects are completely initialized from the beginning, which can reduce performance Increases code complexity

4. Using DI in .NET CORE

Use Dependency Injection through these steps:
  1. Use an interface or base class to abstract implementation dependencies.
  2. Register the dependency in the service container. ASP.NET Core allows us to register our application services with the IoC container, in the Program.cs class use IServiceCollection to register application services
  3. Include the service in the constructor of the class in which it is used. The framework will create an instance of the dependency and remove it when it is no longer needed.
Example: The ICustomerService interface defines the SendMessage method
public interface ICustomerService {
    void SendMessage(string message);
}
We have the CustomerAService implement ICustomerService1
public class CustomerAService : ICustomerService {
    public void SendMessage(string message)
    {
        Console.WriteLine($"CustomerAService.SendMessage Message: {message}");
    }
}
The AddScoped method registers the service with scoped lifetime, the lifetime of a singleton request
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<ICustomerAService, CustomerAService>();
 
var app = builder.Build();
There are 3 lifecycle levels: addTransient, addScoped, addSingleton.
  • Transient: Instance is initialized each time a service is created
  • Scoped: Instance is initialized per scope. (Scope here is each request sent to the application). In the same scope, the service will be reused.
  • Singleton: The service instance is created uniquely at application launch and is used everywhere
ICustomerService is required and used to call the SendMesasge method
public class CustomerController : PageModel {
    private readonly ICustomerService _customerService;
    public Index2Model(ICustomerService customerService) {
        _customerService = customerService;
    }
    public void OnSendMesasge() {
        _customerService.SendMessage("Send message");
    }
}
By using the DI pattern, the controller will: Not use CustomerAService, only use the ICustomerService interface. That makes it easy to change the Controller's implementation without modifying the Controller.

View More
TECH

April 9, 2024

Top Angular interview questions (part 1)

Interviews allow the interviewer to collect relevant information from the interviewee. This information can include details about qualifications, experiences, skills, and suitability for the position.

In a technical interview for a position related to Angular framework, the candidates need to prepare for the understanding of the framework, it will help them perform well during the interview.

This post will introduce the top 5 common questions about Angular framework.

View More
TECH

April 3, 2024

The Power of Memos

Memo-taking habits recommended for all people.
People who are good at work, such as people who have the ability to take action, people who are good at speaking and writing, people who have planning skills, and people who have good judgment and decision-making skills, have the ability to take notes. They frequently write down in notebooks or diaries things they see or hear, ideas that come into their minds, lessons learned insights and so on. They often review their notes and use them in their work.
The human brain is not a universal device that remembers all events and information. On the contrary, it forgets most things. When you are busy at work, you may break out in a cold sweat and say, "I can't remember what you said to me just now".
A good way to avoid the risk of forgetting is to take memos. A memo is like an external hard disk for the brain. Therefore, you don't have to remember what you have written down.

Here are memo-taking situations and their respective purposes.

1. Meetings and conferences

There are many situations where memos are taken in the company, such as meetings and conferences, where there are many items to be noted, such as what was said and what was decided on the spot.
The memos taken in these situations are sometimes written down in the form of minutes, so the memos taken on the spot are positioned as a record for the purpose of writing them down. People's conversations in meetings unfold faster than the speed at which notes are taken, so speed and technique are required to successfully jot down only the main points.

2. Business negotiations and meetings with clients

Business negotiations and meetings with suppliers and customers are activities outside the company, so handling memos is more important than within the company. If you are inside the company, you can ask the same question again if you bow down, but if you are outside the company, it can become a trust issue for the company if it continues over and over again. Therefore, taking notes is essential, but another effect is that it is also a business technique to show that you are listening carefully to what the other person is saying and that you are taking notes to ensure that there are no mistakes.

3. Training and seminars

Memos are also very useful in training courses, seminars and other opportunities for self-improvement. In this case, the main purpose is to efficiently take home the information necessary for self-improvement, so the notes should be easy to understand when you read them back later and reproducible, so that you can easily remember them.

4. Writing down inspiring ideas

Writing down information that comes in from the outside is not the only role of a memo. It is also very important to take notes on ideas that occur to you in your mind. Ideas come to you suddenly and disappear quickly, but by writing them down, you can remember them later. It also allows you to brush them up further and develop them into a draft plan.

Conclusions

No one has a bad impression when they see the person they are talking to taking copious notes. At the same time, it gives a good impression that they are listening attentively so as not to miss anything you say, and at the same time, it gives you a sense of security that your message will be understood if they are taking this many notes.

Since the obvious act of taking memos out of necessity can make such a good impression on the other person, you should make great use of memos as a consistent communication technique.
When you see someone taking memos seriously, the person you are talking to will naturally become more serious. If that is how you get information of high value, then thank goodness for memos.

※Image source and Reference website:

  • [https://www.pexels.com]
  • [https://navi.dropbox.jp/memo]
  • [https://next.rikunabi.com/journal/20170425_m1/]
View More
1 2 3 8