Airo Global Software

Think Beyond Future !

How Geofencing define the Dart API?

Before signing any platform-specific code, I’ll first want to determine the Dart API for the geofencing plugin. Since Android and ios, several have their own APIs for designating and managing geofencing events, I want the Dart interface to present a reduced geofencing abstraction that is harmonious with both programs. Without going into too much detail about Android and iOS APIs, the following approximately represents the Dart interface that I’ll use for my plugin:

abstract class GeofenceRegion {
 /// The ID associated with the geofence.
 ///
 /// This ID identifies the geofence and is required to delete a
 /// specific geofence.
 final String id;

 /// The location (center point) of the geofence.
 final Location location;

 /// The radius around `location` that is part of the geofence.
 final double radius;

 /// Listen to these geofence events.
 final List triggers;

 /// Android-specific settings for a geofence.
 final AndroidGeofencingSettings androidSettings;

 GeofenceRegion(
   this.id, double latitude, double longitude, this.radius, this.triggers,
   {AndroidGeofencingSettings androidSettings});
}

abstract class GeofencingPlugin {
 /// Initialize the plugin and request relevant permissions from the user.
 static Future initialize() async;

 /// Register for geofence events for a [GeofenceRegion].
 ///
 /// `region` is the geofence region to register with the system.
 /// `callback` is the method to be called when a geofence event associated
 /// with `region` occurs.
 static Future registerGeofence(
   GeofenceRegion region,
   void Function(List id, Location location, GeofenceEvent event) callback);

 /// Stop receiving geofence events for a given [GeofenceRegion].
 static Future removeGeofence(GeofenceRegion region);


 /// Stop receiving geofence events for an identifier associated with a
 /// geofence region.
 static Future removeGeofenceById(String id) async;
}

This interface provides the below programmers to users of the plugin: The ability to create instances of GeofenceRegion, which include the emerges and radius of a geofence, a unique ID, and a collection of geofencing programmers to listen for. Since Android gives a richer set of lists for explaining geofences than iOS, Android-specific opportunities are made getting through the chosen androidSettings property. GeofencingPlugin.registerGeofence makes for the registration of a GeofenceRegion instance with a callback that is involved when a geofence event for that area is got. GeofencingPlugin.removeGeofence and GeofencingPlugin.removeGeofenceById will not a GeofenceRegion from triggering more events. Overall, this mode is rather simple and action-agnostic, making the plugin simple to act on both Android and iOS. Dart surronding execution This segment covers how to set up your isolate for experience execution. You will discover how to reference callbacks, and how to use the callback dispatcher. Referencing Callbacks Now that the Dart interface is defined, start figuring plumbing to communicate with the platform-particular divisions of the plugin. For example, the following program initializes the geofencing plugin and displays the geofences:

abstract class GeofencingPlugin {

 static const MethodChannel _channel =

     const MethodChannel('plugins.flutter.io/geofencing_plugin');

 static Future initialize() async {


   final callback = PluginUtilities.getCallbackHandle(callbackDispatcher);


   await _channel.invokeMethod('GeofencingPlugin.initializeService',


       [callback.toRawHandle()]);


 }

 static Future registerGeofence(


     GeofenceRegion region,


     void Function(List id, Location location, GeofenceEvent event)


         callback) {


   if (Platform.isIOS &&


       region.triggers.contains(GeofenceEvent.dwell) &&


       (region.triggers.length == 1)) {


     throw UnsupportedError("iOS does not support 'GeofenceEvent.dwell'");


   }


   final args = [


     PluginUtilities.getCallbackHandle(callback).toRawHandle()


   ];


   args.addAll(region._toArgs());
   _channel.invokeMethod('GeofencingPlugin.registerGeofence', args);


 }

 /*


 * … `removeGeofence` methods here …


 */


}

If you’ve previously created Flutter plugins and are common with MethodChannel, this should look is wanted, for the most part. However, the two calls to PluginUtilities.getCallbackHandle might outstanding. In order to provoke a Dart callback as a result of a surrounding event, you must take a handle that is passed among Dart and platform code while also keeping for lookup of the callback across platform threads and Dart. Retrieving a CallbackHandle for a method from PluginUtilities.getCallbackHandle has the bad effect of multiplying a callback cache within the Flutter engine. These cache maps details needed to wanted callbacks to raw integer handles, which are easy hashes added aimed at the properties of the callback. This cache persists across publishes, but be know that callback lookups may lose if the callback is renamed or moved and PluginUtilities.getCallbackHandle is not called for the updated version of callback. In the code that given, two instances of CallbackHandle are accessed one for the callback, which is assisted with a GeofenceRegion, and another for an option of the name callbackDispatcher. The callbackDispatcher method, the entry point of the background loneliness, is the reason for preprocessing raw geofence event data, seeing up callbacks via PluginUtilities.getCallbackFromHandle, and invoking them for registered geofences. The Callback Dispatcher As told at the end of the previous section, This pattern access for acting the initialization needed to establish connection channels with acting code while also allowing for the formation of non-trivial interfaces for callback options. For this geofencing plugin, the callback dispatcher planning is as follows:

void callbackDispatcher() {

 const MethodChannel _backgroundChannel =
    MethodChannel('plugins.flutter.io/geofencing_plugin_background');

 WidgetsFlutterBinding.ensureInitialized();

 _backgroundChannel.setMethodCallHandler((MethodCall call) async {


   final args = call.arguments;


   final Function callback = PluginUtilities.getCallbackFromHandle(


       CallbackHandle.fromRawHandle(args[0]));


   assert(callback != null);

   // 3.2. Preprocess arguments.

   final triggeringGeofences = args[1].cast();

   final locationList = args[2].cast();

   final triggeringLocation = locationFromList(locationList);


   final GeofenceEvent event = intToGeofenceEvent(args[3]);

   callback(triggeringGeofences, triggeringLocation, event);

 });
 _backgroundChannel.invokeMethod('GeofencingService.initialized');


}

As you can see, on the formation of callbackDispatcher only four methods are performed. a MethodChannel is made for listening to options from the plugin. Next, WidgetsFlutterBinding.ensureInitialized() is known to initialize the state required to connect with the Flutter engine. At this point, the MethodCall handler is set to work with plugin programmers before at last notifying the action portion of the plugin that the background makes it is mentioned and starting the handling events. Once the plugin is created transform programmers to the callback dispatcher, the callback given to the plugin user can be invoked. First, PluginUtilities.getCallbackFromHandle is known to take a moment of the callback assisted with the provoked geofencing program using the raw callback to hold. Next, the raw materials from the MethodCall are made into: A moment of List for the IDs of the geofences that were provoked. The moment that Location describes the current area of the machine An instance of the GeofenceEvent enum represents whether the machine has typed, exited, or dwelled within the provoked geofences. Then give this info as rules to our callback.

Background execution: Android For the Android installation of the plugin, I’ll be required to make the following sections: The GeofencingPlugin class, which is created with the Flutter engine in order to get and handle method calls built from Dart code A GeofencingBroadcastReceiver, which is provoked by the system on a geofence event The GeofencingService, which made the background isolate, merges the callback dispatcher defined earlier and processes geofence programmes invoking the callback dispatcher. Creating Geofences In order to influence options, select an instance of MethodChannel on the same channel from the past, and then register the GeofencingPlugin instance with this channel in the implementation of onAttachedToEngine:

override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {

 mContext = binding.getApplicationContext()

 mGeofencingClient = LocationServices.getGeofencingClient(mContext!!)


 val channel = MethodChannel(binding.getBinaryMessenger(), "plugins.flutter.io/geofencing_plugin")


 channel.setMethodCallHandler(this)


}

In order to manage these needs, onMethodCall is required to install:

override fun onMethodCall(call: MethodCall, result: Result) {




 val args = call.arguments>()


 when(call.method) {


   "GeofencingPlugin.initializeService" -> {


     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {


       mActivity?.requestPermissions(REQUIRED_PERMISSIONS, 12312)


     }


     // Simply stores the callback handle for the callback dispatcher


     initializeService(mContext!!, args)


     result.success(true)


   }


   "GeofencingPlugin.registerGeofence" -> registerGeofence(mContext!!,


           mGeofencingClient!!,


           args,


           result,


           true)


   "GeofencingPlugin.removeGeofence" -> removeGeofence(mContext!!,


           mGeofencingClient!!,


           args,


           result)


   else -> result.notImplemented()


 }


}

At last, include the skill to register geofences

@JvmStatic

private fun getGeofencingRequest(geofence: Geofence, initialTrigger: Int): GeofencingRequest {


 return GeofencingRequest.Builder().apply {


   setInitialTrigger(initialTrigger)


   addGeofence(geofence)


 }.build()

}
@JvmStatic


private fun getGeofencePendingIndent(context: Context, callbackHandle: Long): PendingIntent {


val intent = Intent(context, GeofencingBroadcastReceiver::class.java)


       .putExtra(CALLBACK_HANDLE_KEY, callbackHandle)


return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)


}

// TODO(bkonyi): Reregister geofences after reboot


// https://developer.android.com/training/location/geofencing


@JvmStatic


private fun registerGeofence(context: Context,


                             geofencingClient: GeofencingClient,


                             args: ArrayList<*>?,


                             result: Result?) {


 val callbackHandle = args!![0] as Long


 val id = args[1] as String


 val lat = args[2] as Double


 val long = args[3] as Double


 val radius = (args[4] as Number).toFloat()


 val fenceTriggers = args[5] as Int


 val initialTriggers = args[6] as Int


 val expirationDuration = (args[7] as Int).toLong()


 val loiteringDelay = args[8] as Int


 val notificationResponsiveness = args[9] as Int


 val geofence = Geofence.Builder()


         .setRequestId(id)


         .setCircularRegion(lat, long, radius)

         .setTransitionTypes(fenceTriggers)

         .setLoiteringDelay(loiteringDelay)
         .setNotificationResponsiveness(notificationResponsiveness)
         .setExpirationDuration(expirationDuration)
         .build()
 // Ensure permissions are set properly.

 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
        (context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)

                 == PackageManager.PERMISSION_DENIED)) {
   val msg = "'registerGeofence' requires the ACCESS_FINE_LOCATION permission."
   Log.w(TAG, msg)
   result?.error(msg, null, null)

 }
 ofencingClient.addGeofences(getGeofencingRequest(geofence, initialTriggers),

         getGeofencePendingIndent(context, callbackHandle))?.run {

   addOnSuccessListener {
     Log.i(TAG, "Successfully added geofence")
     result?.success(true)
   }
   addOnFailureListener {
     Log.e(TAG, "Failed to add geofence: $it")
     result?.error(it.toString(), null, null)
   }
 }
}

Scheduling the geofencing service:

class GeofencingBroadcastReceiver : BroadcastReceiver() {
 companion object {
   private const val TAG = "GeofencingBroadcastReceiver"
 }
 override fun onReceive(context: Context, intent: Intent) {
   FlutterMain.startInitialization(context)
   FlutterMain.ensureInitializationComplete(context, null)
   GeofencingService.enqueueWork(context, intent)
 }
}

Handling Geofence Events using the below code:

private fun startGeofencingService(context: Context) {
 synchronized(sServiceStarted) {
   mContext = context
   // dispatcher.
   if (sBackgroundFlutterEngine == null) {
     val callbackHandle = context.getSharedPreferences(

        GeofencingPlugin.SHARED_PREFERENCES_KEY,

         Context.MODE_PRIVATE)
         .getLong(GeofencingPlugin.CALLBACK_DISPATCHER_HANDLE_KEY, 0)

     if (callbackHandle == 0L) {

       Log.e(TAG, "Fatal: no callback registered")
       return
     }
     val callbackInfo = FlutterCallbackInformation.lookupCallbackInformation(callbackHandle)

     if (callbackInfo == null) {

       Log.e(TAG, "Fatal: failed to find callback")

       return

     }

     sBackgroundFlutterEngine = FlutterEngine(context)

     val args = DartCallback(

       context.getAssets(),

       FlutterMain.findAppBundlePath(context)!!,

       callbackInfo

     )
     sBackgroundFlutterEngine!!.getDartExecutor().executeDartCallback(args)

   }

 }
 mBackgroundChannel = MethodChannel(sBackgroundFlutterEngine!!.getDartExecutor().getBinaryMessenger(),

     "plugins.flutter.io/geofencing_plugin_background")

 mBackgroundChannel.setMethodCallHandler(this)

}

After startGeofencingService is completed execution, onHandleWork is called by the machine with the Intent that was lined up earlier:

 val locationList = listOf(location.latitu
override fun onHandleWork(intent: Intent) {

 val callbackHandle = intent.
   getLongExtra(GeofencingPlugin.CALLBACK_HANDLE_KEY, 0)
 val geofencingEvent = GeofencingEvent.fromIntent(intent)
 if (geofencingEvent.hasError()) {
   Log.e(TAG, "Geofencing error: ${geofencingEvent.errorCode}")

   return
 }
 val geofenceTransition = geofencingEvent.geofenceTransition
 val triggeringGeofences = geofencingEvent.triggeringGeofences.map {
   it.requestId
 }

 val location = geofencingEvent.triggeringLocation

de,
   location.longitude)
 val geofenceUpdateList = listOf(callbackHandle,
   triggeringGeofences,
   locationList,
   geofenceTransition)
  
 synchronized(sServiceStarted) {
   if (!sServiceStarted.get()) {
    
     queue.add(geofenceUpdateList)
   } else {
     
     mBackgroundChannel.invokeMethod("", geofenceUpdateList)
   }
 }
}

You have to write the below code next:

override fun onMethodCall(call: MethodCall, result: Result) {

 if (call.method == "GeofencingService.initialized") {

   synchronized(sServiceStarted) {

     while (!queue.isEmpty()) {

       mBackgroundChannel.invokeMethod("", queue.remove())

     }

     sServiceStarted.set(true)

     result.success(null)

   }

 } else {

   result.notImplemented()
 }
}

At this point, the GeofencingService is fully initialized and any geofencing events that have lined up are sent to the callback dispatcher. Background execution: iOS Now that the geofencing plugin implementation for Android is completed, the same geofencing program is required to be implemented for iOS. Initializing the plugin using the below code

+ (void)registerWithRegistrar:(NSObject *)registrar {

 @synchronized(self) {

   if (instance == nil) {

     NSLog(@"Registering with registrar");

     instance = [[GeofencingPlugin alloc] init:registrar];

     [registrar addApplicationDelegate:instance];

   }

 }

}

Additional state for the plugin is created when the GeofencingPlugin instance is build during plugin registration:

- (instancetype)init:(NSObject *)registrar {

 self = [super init];

 NSAssert(self, @"super init cannot be nil");

 _persistentState = [NSUserDefaults standardUserDefaults];

 _locationManager = [[CLLocationManager alloc] init];

 [_locationManager setDelegate:self];

 [_locationManager requestAlwaysAuthorization];

 _locationManager.accessBackgroundLocationUpdates = YES;

 _headlessRunner = [[FlutterEngine alloc]

                       initWithName:@"GeofencingIsolate"

                       project:nil

                       allowHeadlessExecution:YES];


 _registrar = registrar;

 _mainChannel = [FlutterMethodChannel
                 methodChannelWithName:@"plugins.flutter.io/geofencing_plugin"


                 binaryMessenger:[registrar messenger]];

 [registrar addMethodCallDelegate:self channel:_mainChannel];
 _callbackChannel =
   [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/geofencing_plugin_background"

     binaryMessenger:_headlessRunner];
 return self;

}

Starting the callback dispatcher:

- (void)startGeofencingService:(int64_t)handle {

 NSLog(@"Initializing GeofencingService");


 [self setCallbackDispatcherHandle:handle];

 FlutterCallbackInformation *info = [FlutterCallbackCache lookupCallbackInformation:handle];


 NSAssert(info != nil, @"failed to find callback");

 NSString *entrypoint = info.callbackName;

 NSString *uri = info.callbackLibraryPath;
 [_headlessRunner runWithEntrypointAndLibraryUri:entrypoint libraryUri:uri];
 NSAssert(registerPlugins != nil, @"failed to set registerPlugins");
 registerPlugins(_headlessRunner);
 [_registrar addMethodCallDelegate:self channel:_callbackChannel];
}

Handling method calls:

- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result {
 NSArray *arguments = call.arguments;
 if ([@"GeofencingPlugin.initializeService" isEqualToString:call.method]) {
   NSAssert(arguments.count == 1,
   @"Invalid argument count for 'GeofencingPlugin.initializeService'");
   [self startGeofencingService:[arguments[0] longValue]];
   result(@(YES));
 } else if ([@"GeofencingService.initialized" isEqualToString:call.method]) {
   // Ignored on iOS.
   result(nil);
 } else if ([@"GeofencingPlugin.registerGeofence" isEqualToString:call.method]) {
   [self registerGeofence:arguments];
   result(@(YES));
 } else if ([@"GeofencingPlugin.removeGeofence" isEqualToString:call.method]) {

   result(@([self removeGeofence:arguments]));
 } else {
   result(FlutterMethodNotImplemented);
 }
}

Registering geofences:

- (void)registerGeofence:(NSArray *)arguments {
 NSLog(@"RegisterGeofence: %@", arguments);
 int64_t callbackHandle = [arguments[0] longLongValue];
 NSString *identifier = arguments[1];
 double latitude = [arguments[2] doubleValue];
 double longitude = [arguments[3] doubleValue];
 double radius = [arguments[4] doubleValue];
 CLCircularRegion *region =
   [[CLCircularRegion alloc] initWithCenter:CLLocationCoordinate2DMake(latitude, longitude)
     radius:radius
     identifier:identifier];
 region.notifyOnEntry = YES;
 region.notifyOnExit = YES;
 [self setCallbackHandleForRegionId:callbackHandle regionId:identifier];
 [self->_locationManager startMonitoringForRegion:region];
}

For Handling geofence events use the below code:

- (void)sendLocationEvent:(CLRegion *)region eventType:(int)event {
 NSAssert([region isKindOfClass:[CLCircularRegion class]], @"region must be CLCircularRegion");
 CLLocationCoordinate2D center = region.center;
 int64_t handle = [self getCallbackHandleForRegionId:region.identifier];
 [_callbackChannel
   invokeMethod:@""
   arguments:@[
     @(handle),
     @[ region.identifier ],
     @[ @(center.latitude), @(center.longitude) ],
     @(event)
   ]
 ];
}
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {
 [self sendLocationEvent:region eventType:kEnterEvent];
}
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region {
 [self sendLocationEvent:region eventType:kExitEvent];
}

For Geofence events in a suspended state below code:

- (BOOL)application:(UIApplication *)application
 didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

 // Check to see if we're being launched due to a location event.

 if (launchOptions[UIApplicationLaunchOptionsLocationKey] != nil) {

   // Restart the headless service.


   [self startGeofencingService:[self getCallbackDispatcherHandle]];
 }
 return YES;
}

To get Permissions: Android use the below code:





To get Permissions in iOS:

UIRequiredDeviceCapabilities

 

   location-services

   gps

   armv7

 

 UIBackgroundModes

 

   location

 

NSLocationAlwaysAndWhenInUseUsageDescription

 YOUR DESCRIPTION HERE

 NSLocationWhenInUseUsageDescription

 YOUR DESCRIPTION HERE

These descriptions are shown to the person who uses them when the app requests access to themarea.

#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"

// Add the import for the GeofencingPlugin.

#import 

void registerPlugins(NSObject* registry) {

 [GeneratedPluginRegistrant registerWithRegistry:registry];

}

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application

   didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

 // Register the plugins with the AppDelegate


 registerPlugins(self);

 // Set registerPlugins as a callback within GeofencingPlugin. This access

 // for the Geofencing plugin to register the plugins with the background

 // FlutterEngine instance created to handle events. If this step is skipped,

 // other plugins will not work in the geofencing callbacks!

 [GeofencingPlugin setPluginRegistrantCallback:registerPlugins];

 // Override point for customization after application launch.

 return [super application:application didFinishLaunchingWithOptions:launchOptions];

}

@end

Bringing it all together:

Future initialize() async {

 // Perform other initialization

 // …

 // Initialize the geofencing plugin.

 await GeofencingManager.initialize();

}

Widget _proximityTriggerToggle() => Container(

   padding: const EdgeInsets.fromLTRB(26.0, 2.5, 26.0, 2.5),

   child: Row(

     mainAxisAlignment: MainAxisAlignment.spaceBetween,

     children: [

       const Text('Proximity Trigger',

           style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16.0)),

       Switch(

         value: _proximityTriggerEnabled,

         onChanged: (bool state) async {

           setState(() {

             _proximityTriggerEnabled = state;

           });

           if (state) {

             await GeofencingManager.registerGeofence(

                 GeofenceTrigger.homeRegion,

                 GeofenceTrigger.homeGeofenceCallback);

           } else {

             await GeofencingManager.removeGeofence(

                 GeofenceTrigger.homeRegion);

           }

         },

       ),

     ],

   )

 );

abstract class GeofenceTrigger {

 static final _androidSettings = AndroidGeofencingSettings(

     initialTrigger: [GeofenceEvent.exit],

     notificationResponsiveness: 0,

     loiteringDelay: 0);

 static bool _isInitialized = false;

 static final homeRegion = GeofenceRegion(

     'home', HOME_LAT, HOME_LONG, 300.0, [GeofenceEvent.enter],

     androidSettings: _androidSettings);

 static Future homeGeofenceCallback(

     List id, Location location, GeofenceEvent event) async {

   // Check to see if this is the first time the callback is being called.

   if (!_isInitialized) {

     // Re-initialize state required to communicate with the garage door

     // server.

     await initialize();

     _isInitialized = true;

   }

   if (event == GeofenceEvent.enter) {

     await GarageDoorRemote.openDoor();

   }

 }

}

At last, you execute a dart in the background with flutter plugin and geofencing

If you have any questions about the above topic or have to get services and consultations to setup flutter and dart plugins. Feel free to contact us. AIRO GLOBAL SOFTWARE will be your digital solution. E-mail id: [email protected]

enter image description here Author - Johnson Augustine
Chief Technical Director and Programmer
ounder: Airo Global Software Inc
LinkedIn Profile: www.linkedin.com/in/johnsontaugustine/

enter image description here

We all understand that ASP.NET MVC is a great stage that allows us to build and maintain web applications in a much easier manner related to form-based web applications. There are a few things in MVC-based web applications that run a little more uncomfortably than standard web applications, one of them is routing.

Until now, there has been a routing table that you can set either in the Global. asax or in the RouteConfig.cs and all incoming calls would look it up to determine the rendering of a targeted view.

Here is the code that you might have seen previously to have note-variable routes in MVC 4 in the following example of the Route collection object.

routes.MapRoute( 
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Product",
action = "List", id = UrlParameter.Optional }
);

What is the necessity for this new routing methodology?

There was nothing really incorrect with the previous method of routing and in fact, you can however use it in MVC 5 or use this new routing method in connection with the old one.

Here are a few benefits of attribute-based routing,

  • Helps developers in the debugging / troubleshooting form by providing data about routes.
  • Decreases the chances for mistakes, if a route is changed incorrectly in RouteConfig.cs then it may affect the whole application's routing.
  • May decouple controller and business names from route entirely.
  • Simple to map two routes guiding to the same action.

Let's see specifically how we can configure it and see it working.

First, we will need to enable attribute-based routing on our MVC web application that can be made by only one line. All you want to do is put this line in the RegisterRoutes method of the application.

public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
tes.MapMvcAttributeRoutes(); //Enables Attribute Based Routing
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Product", action = "List", id = UrlParameter.Optional }
);
}

Now, here is how you can use attribute-based routing on a particular action method.

[Route("products/{id?}")]
public ActionResult Details(string id)
{
if (string.IsNullOrEmpty(id))
{
return View("List", GetProductList());
}
return View("Details", GetProductDetails());
}

As shown in the method above, the Route is determined on a Details action method that lets users enter the product specifications page either by of these paths,

/Product/Details/Id or /products/id

You might have recognized the problem mark in the route above, all it means is that the id is an arbitrary parameter of the route and hence the business process logic checks if the id is null. It will show you the result listing page.

Route Prefixes

Route Prefixes are blank but the prefix for any route that we need to apply, all we need to do is to determine the route prefix on a controller so that all the action plans inside it can follow the prefix.

[RoutePrefix("products")]
public class ProductController : Controller  
{
//This will be translated to /products
[Route]
public ActionResult List()
{
return View();
}
//This will be translated to /products/2
[Route("{id?}")]
public ActionResult Details(string id)
{
if (string.IsNullOrEmpty(id))
{
return View("List");
}
return View("Details");
}
}

Route Constraints

Route constraints are zero but a set of practices that you can set on your routing model/parameters that users want to follow when entering the established routes.

The way to determine a constraint is by using the ":" character, let's have a look at the example below.

//route gets called as /products/productname
[Route("products/{id:alpha}")]
public ActionResult GetProduct(string name) 
{
return View();
}
//route gets called as /products/2  
[Route("products/{id:int}")]
 public ActionResult GetProduct(int id)
{
return View();
}

Now your strength has recognized in the example above that though the system name is equal the route's parameter has some restrictions on it. In other words, the first order will be called if the route is located with a string as a parameter and the second method will be called if the route is obtained with an integer in the route parameter.

You can also define your system route limitations using an IRouteConstraint interface.

Route Areas

A Route area is zero but the way to define the area in a route, essentially just to let the route know that the controller goes to some area.

[RouteArea("business")]
[RoutePrefix("products")]
public class ProductController : Controller
{
//This will be translated to /business/products/list 
[Route]
public ActionResult List()
{
return View();
}
}

If you have any questions about the above topic or have to get services and consultations and get the best ASP.Net MVC Application Development Services. Feel free to contact us. AIRO GLOBAL SOFTWARE will be your digital solution. E-mail id: [email protected].

enter image description here

Author - Johnson Augustine
Chief Technical Director and Programmer
Founder: Airo Global Software Inc
LinkedIn Profile: www.linkedin.com/in/johnsontaugustine/

enter image description here

In this blog, we will integrate the Firebase Authentication for Google Sign-In in our Android application using Google and Firebase APIs.

What is firebase authentication?

Firebase Authentication ensures backend services for we can simply use the SDKs and in-built UI libraries to authenticate the person that is used in the android app. Most of the apps need the identity of the person that is using, and after knowing their activity status, the app saves the user's data safely in the cloud. It targets building a safe authentication system.

Using Firebase Authentication, we will authenticate the log-in of Google, Facebook, GitHub and more.

What are the steps to create and configure the android app on the google firebase account?

  • The first step is to create a firebase developer account and after installing it ‘Go to console’.
  • Select the option 'Add project'.
  • Complete the project name and select the analytics area, cloud Firestore location, accept the controller terms and select 'Create project'.
  • When your new project is completely ready select 'Continue'.
  • The next step is to click on the android platform SDK.
  • The next step is to Register your app to Firebase by providing the required app information and clicking on 'Register app'.
  • The next step is to download the 'google-services.json' file to integrate it into the Android app and select 'Next.
  • The next step is to add the Firebase SDK dependencies in the .gradle folders of your app and select 'Sync now' in the IDE and select the option 'Next'.

BUILD GRADLE (PROJECT):

dependencies { 
classpath 'com.android.tools.build:gradle:3.0.1'
classpath 'com.google.gms:google-services:4.0.1'
// in the individual module build. Gradle files  
}

BUILD GRADLE(MODULE):

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])  
 implementation 'com.android.support:appcompat-v7:27.1.1'
  implementation 'com.android.support.constraint:constraint-layout:1.1.3'
 testImplementation 'junit:junit:4.12'
 androidTestImplementation 'com.android.support.test:runner:1.0.2' 
androidTestImplementation 
'com.android.support.test.espresso:espresso-core:3.0.2' 
implementation 'com.google.firebase:firebase-auth:16.0.3' 
 implementation 'com.google.firebase:firebase-core:16.0.3'
 implementation 'com.google.android.gms:play-services-auth:16.0.0'  
  implementation 'com.github.bumptech.glide:glide:3.7.0' 
}
apply : 'com.google.gms.google-services' 

What should I do in androidmanifest.xml?

First, add internet permission:
<uses-permission android:name="android.permission.INTERNET" />
  • Execute your app to verify the installation configuration, if everything is alright it shows a success message and then select the 'Continue to console'.
  • Next step you need to know that on the console page click on Authentication -gt; Sign-in method -gt;Google -gt; Enable and at last select on 'Save'.

Give an example of android firebase authentication with a google sign-in example?

Add the below code in an activity_main.xml file. In this activity, we use the custom Google Sign-in button:

<?xml version="1.0" encoding="utf-8"?> 
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" 
android:layout_width="match_parent" 
android:layout_height="match_parent" 
tools:context="example.javatpoint.com.firebasegooglelogin.MainActivity">
   <TextView 
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp" 
android:textSize="20dp"
 android:text="This is main activity, sign in to move next activity." /> 

   <com.google.android.gms.common.SignInButton
android:layout_width="match_parent"
 android:layout_height="wrap_content"
android:id="@+id/sign_in_button"
 android:layout_marginLeft="20dp" 
android:layout_marginRight="20dp" 
 android:layout_alignParentBottom="true"
android:layout_marginBottom="20dp">
  </com.google.android.gms.common.SignInButton>
</RelativeLayout> 

The next step is to add your web client id in the string.xml file.

<resources>
<string name="app_name">Firebase Google Login</string>
<string name="web_client_id">xxxxxxxx..place your web client id </string>
</resources> 

Next, add the below code in MainActivity.java class

package example.javatpoint.com.firebasegooglelogin;
import android.content.Intent; 
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle; 
import android.util.Log;
import android.view.View; 
import android.widget.Toast;  

import com.google.android.gms.auth.api.Auth; 
import com.google.android.gms.auth.api.signin.GoogleSignInAccount; 
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
import com.google.android.gms.auth.api.signin.GoogleSignInResult;
import com.google.android.gms.common.ConnectionResult;  
import com.google.android.gms.common.SignInButton; 
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.tasks.OnCompleteListener; 
import com.google.android.gms.tasks.Task; 
import com.google.firebase.auth.AuthCredential;
import com.google.firebase.auth.AuthResult; 
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;  
import com.google.firebase.auth.GoogleAuthProvider; 

OnConnectionFailedListener {  

   private static final String TAG = "MainActivity";  
private SignInButton signInButton;
private GoogleApiClient googleApiClient; 
private static final int RC_SIGN_IN = 1;
String name, email; 
  String idToken; 
private FirebaseAuth firebaseAuth;
 private FirebaseAuth.AuthStateListener authStateListener; 

@Override
protected void onCreate(Bundle savedInstanceState) {  
super.onCreate(savedInstanceState); 
setContentView(R.layout.activity_main);  
firebaseAuth = com.google.firebase.auth.FirebaseAuth.getInstance(); 
 authStateListener = new FirebaseAuth.AuthStateListener(){  
@Override
public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
  // Get signedIn user 
FirebaseUser user = firebaseAuth.getCurrentUser(); 
//if user is signed in, we call a helper method to save the user details to Firebase 
if (user != null) { 
// User is signed in 
 Log.d(TAG, "onAuthStateChanged:signed_in:" + user.getUid());  
 } else { 
// User is signed out
 Log.d(TAG, "onAuthStateChanged:signed_out");  
}
  }  
 };

GoogleSignInOptions gso =  new
GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) 
.requestIdToken(getString(R.string.web_client_id))//you can also use R.string.default_web_client_id
 .requestEmail()
.build(); 
 googleApiClient=new GoogleApiClient.Builder(this)
 .enableAutoManage(this,this)
.addApi(Auth.GOOGLE_SIGN_IN_API,gso)
 .build(); 

signInButton = findViewById(R.id.sign_in_button);
signInButton.setOnClickListener(new View.OnClickListener() {  
@Override 
public void onClick(View view) {  
 Intent intent = Auth.GoogleSignInApi.getSignInIntent(googleApiClient); 
startActivityForResult(intent,RC_SIGN_IN); 
 }  
 });  
  } 

@Override
public void onConnectionFailed(@NonNull ConnectionResult
connectionResult) { 
  }  
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { 
 super.onActivityResult(requestCode, resultCode, data); 
 if(requestCode==RC_SIGN_IN){ 
GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
handleSignInResult(result);  
 } 
 }

private void handleSignInResult(GoogleSignInResult result){  
 if(result.isSuccess()){
GoogleSignInAccount account = result.getSignInAccount();
  idToken = account.getIdToken();
 name = account.getDisplayName(); 
 email = account.getEmail(); 
AuthCredential credential = GoogleAuthProvider.getCredential(idToken, null); 
firebaseAuthWithGoogle(credential); 
 }else{  

Log.e(TAG, "Login Unsuccessful. "+result); 
Toast.makeText(this, "Login Unsuccessful", Toast.LENGTH_SHORT).show();
 }
 } 
 private void firebaseAuthWithGoogle(AuthCredential credential){ 
firebaseAuth.signInWithCredential(credential)
 .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
 @Override
 public void onComplete(@NonNull Task<AuthResult> task) {

Log.d(TAG, "signInWithCredential:onComplete:" + task.isSuccessful());

   if(task.isSuccessful()){
Toast.makeText(MainActivity.this, "Login successful", Toast.LENGTH_SHORT).show(); 
gotoProfile();
  }else{  
   Log.w(TAG, "signInWithCredential" + task.getException().getMessage()); 
 task.getException().printStackTrace();
  Toast.makeText(MainActivity.this, "Authentication failed.",  
                                Toast.LENGTH_SHORT).show();
  }  
 }
 });
}
  private void gotoProfile(){ 
 Intent intent = new Intent(MainActivity.this, ProfileActivity.class);
 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 
 startActivity(intent);  
 finish();
 } 
 @Override
 protected void onStart() {  
super.onStart();
  if (authStateListener != null){  
 FirebaseAuth.getInstance().signOut();
 }
  firebaseAuth.addAuthStateListener(authStateListener); 
 } 
@Override 
  protected void onStop() {
super.onStop(); 
 if (authStateListener != null){ 
firebaseAuth.removeAuthStateListener(authStateListener);
 }
 }
 }

The next step is to add the below code in the profile_activity.xml file:

<?xml version="1.0" encoding="utf-8"?> 
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
  android:layout_height="match_parent"  
tools:context="example.javatpoint.com.firebasegooglelogin.ProfileActivity">
    <LinearLayout
 android:layout_width="match_parent" 
android:layout_height="match_parent"
 android:gravity="center"
 android:orientation="vertical">
<ImageView
android:layout_width="80dp"
android:layout_height="80dp"
android:id="@+id/profileImage" 
   /> 
  <TextView  
android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:id="@+id/name" 
android:text="name" 
 android:textSize="20dp"
android:layout_marginTop="20dp"/>
   <TextView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content" 
android:id="@+id/email"
android:textSize="20dp"
android:text="email"
 android:layout_marginTop="20dp"/>  
  <TextView  

android:layout_width="wrap_content"

android:layout_height="wrap_content"
 android:id="@+id/userId" 
android:textSize="20dp"
 android:text="id" 
android:layout_marginTop="20dp"/>
<Button
android:layout_width="wrap_content" 
android:layout_height="wrap_content"
android:id="@+id/logoutBtn"
 android:text="Logout"
android:layout_marginTop="30dp"/> 
 </LinearLayout> 
</RelativeLayout>

Next, we have to add profileActivity.java

package example.javatpoint.com.firebasegooglelogin;
import android.content.Intent;
import android.support.annotation.NonNull; 
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;  
import android.view.View;  
import android.widget.Button;
import android.widget.ImageView; 
import android.widget.TextView; 
import android.widget.Toast;

import com.bumptech.glide.Glide;
import com.google.android.gms.auth.api.Auth; 
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
import com.google.android.gms.auth.api.signin.GoogleSignInResult; 
import com.google.android.gms.common.ConnectionResult; 
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.OptionalPendingResult;  
import com.google.android.gms.common.api.ResultCallback;  
import com.google.android.gms.common.api.Status;
import com.google.firebase.auth.FirebaseAuth;  

public class ProfileActivity extends AppCompatActivity implements
GoogleApiClient.OnConnectionFailedListener { 
Button logoutBtn;
TextView userName,userEmail,userId;
 ImageView profileImage; 
private GoogleApiClient googleApiClient;
private GoogleSignInOptions gso;  
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_profile); 
  logoutBtn = findViewById(R.id.logoutBtn); 
userName = findViewById(R.id.name);
userEmail = findViewById(R.id.email);
 userId = findViewById(R.id.userId); 
profileImage = findViewById(R.id.profileImage); 
gso =  new 
GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) 
.requestEmail() 
 .build();  
  googleApiClient=new GoogleApiClient.Builder(this) 
 .enableAutoManage(this,this)
 .addApi(Auth.GOOGLE_SIGN_IN_API,gso)
 .build();  
    logoutBtn.setOnClickListener(new View.OnClickListener() {  
  @Override  
 public void onClick(View view) { 
 FirebaseAuth.getInstance().signOut();
Auth.GoogleSignInApi.signOut(googleApiClient).setResultCallback(
 new ResultCallback<Status>() { 
 @Override
 public void onResult(Status status) { 
 if (status.isSuccess()){ 
gotoMainActivity();
 }else{ 
Toast.makeText(getApplicationContext(),"Session not close",Toast.LENGTH_LONG).show(); 
}
} 
 });
 }
  });  
 }
 @Override 
 protected void onStart() {
 super.onStart();
OptionalPendingResult<GoogleSignInResult> opr= Auth.GoogleSignInApi.silentSignIn(googleApiClient);  
  if(opr.isDone()){  
GoogleSignInResult result=opr.get();  
handleSignInResult(result); 
  }else{
opr.setResultCallback(new ResultCallback<GoogleSignInResult>() { 
@Override
 public void onResult(@NonNull GoogleSignInResult googleSignInResult)
{ 
handleSignInResult(googleSignInResult);
  } 
});  
  }
  }
  private void handleSignInResult(GoogleSignInResult result){  
  if(result.isSuccess()){
GoogleSignInAccount account=result.getSignInAccount();  
userName.setText(account.getDisplayName());
userEmail.setText(account.getEmail());  
 userId.setText(account.getId());
try{
Glide.with(this).load(account.getPhotoUrl()).into(profileImage);  
  }catch (NullPointerException e){ 
Toast.makeText(getApplicationContext(),"image not found",Toast.LENGTH_LONG).show(); 
 }
 }else{  
gotoMainActivity(); 
}  
}  
private void gotoMainActivity(){ 
 Intent intent=new Intent(this,MainActivity.class); 
startActivity(intent); 
} 
@Override 
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {  
   } 
   } 

If you have any questions about the above topic or have to get services and consultations and get the best android application development services. Feel free to contact us. AIRO GLOBAL SOFTWARE will be your strong digital partner. E-mail id: [email protected]

enter image description here Author - Johnson Augustine
Chief Technical Director and Programmer
Founder: Airo Global Software Inc
LinkedIn Profile: www.linkedin.com/in/johnsontaugustine/

enter image description here

ASP.NET Core Web APIs often need to serialize JSON data to JavaScript clients. On the server, your C# classes basically use Pascal Casing to name properties whereas JavaScript code uses Camel Casing for property names. Therefore it would be worthwhile to take a quick look at how ASP.NET Core serializes data in JSON form of Web API and MVC controllers.

There are three basic places from where C# objects are serialized in JSON format to the client:

  • Web API controllers
  • MVC controllers
  • Custom C# code using JsonSerializer

How camel casing and web API?

Let's first see the behavior of ASP.NET Core Web APIs when it comes to JSON serialization.

Analyze the resulting Web API controller:

[ApiController]
[Route("[controller]")]
public class EmployeeController : ControllerBase
{
[HttpGet]
public List<Employee> Get()
{
List<Employee> empList =
new List<Employee>();
empList.Add(new Employee()
{ EmployeeID=1,FirstName="Nancy",
LastName="Davolio" });
empList.Add(new Employee()
{ EmployeeID = 2, FirstName = "Andrew",
LastName = "Fuller" });
 return empList;
}
}

This code simply returns a List of Employee objects to the caller. The Employee class practiced in this system looks similar this:

public class Employee
{
public int EmpID { get; set; }
public string First { get; set; }
public string Last{ get; set; }
}

Begin the Startup class and modify the ConfigureServices() step as shown below:

public void ConfigureServices
(IServiceCollection services)
{
services.AddControllers()
 .AddJsonOptions(options =>
{
options.JsonSerializerOptions
.PropertyNamingPolicy = null;
});
}

As you can understand, the code does the AddJsonOptions() way to configure this behavior. The PropertyNamingPolicy section of JsonSerializerOptions is set to nothing.

If you need to explicitly set the casing to camel casing then you can print:

options.JsonSerializerOptions.
ropertyNamingPolicy = JsonNamingPolicy.CamelCase;

How camel casing and MVC controllers?

In ASP.NET Core MVC you can use Json() method to serialize data JSON format. Consider the following work that explains how this can be done.

public class HomeController: Controller
{
public IActionResult Index()
 {
List<Employee> empList =
new List<Employee>();
empList.Add(new Employee() 
{ EmployeeID = 1, FirstName = "Nancy",
LastName = "Davolio" });
empList.Add(new Employee()
{ EmployeeID = 2, FirstName = "Andrew",
LastName = "Fuller" });
return Json(empList);
 }
}

The Index() action made a List of Employee objects. The easiest is then serialized to the client handling Json() method.

In order to show MVC to pause using camel casing, you can type this code in the ConfigureServices().

services.AddControllersWithViews()
 .AddJsonOptions(options =>
{
options.JsonSerializerOptions
.PropertyNamingPolicy = null;
});

You run the AddJsonOptions() step on AddControllersWithViews().

You can also replace this behavior for single Json() calls as shown below:

var options = new JsonSerializerOptions()
{
 PropertyNamingPolicy = 
JsonNamingPolicy.CamelCase
};
return Json(empList, options);

How is custom code using JSON serializer?

At times you are required to serialize data yourself via custom code. Basically, you will use JsonSerializer to perform this task. Examine the following code:

public IActionResult Index()
{
List<Employee> empList =
new List<Employee>();
empList.Add(new Employee() 
{ EmployeeID = 1, FirstName = "Nancy", 
LastName = "Davolio" });
empList.Add(new Employee() 
{ EmployeeID = 2, FirstName = "Andrew", LastName = "Fuller" });
string json = JsonSerializer.Serialize(empList);
return Ok(json);
}

If you need JsonSerializer class to do camel casing you can do the serving:

var options = new JsonSerializerOptions()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
string json = JsonSerializer.Serialize(empList, options);
return Ok(json);

As you can see, the code makes a JsonSerializerOptions object and sets PropertyNamingPolicy property to JsonNamingPolicy.CamelCase. Next, the Serialize() system is called by giving empList and JsonSerializerOptions objects.

If you have any questions about the above topic or have to get services and consultations and get the best JSON services. Feel free to contact us. AIRO GLOBAL SOFTWARE will be your digital solution. E-mail id: [email protected]

enter image description here Author - Johnson Augustine
Chief Technical Director and Programmer
Founder: Airo Global Software Inc
LinkedIn Profile: www.linkedin.com/in/johnsontaugustine/

enter image description here

One way or another we develop our apps to make cash out of it. Sometimes we sell ads and make users upgrade for a great experience and we have premium content only for paid users.

We should know about each and every piece of information before handling the payment-related stuff. Be prepared before you start to know about Subscriptions In-App Purchase in Flutter.

Before we get into the programming part let’s learn more about in-app purchases and how it is different for both Android and IOS platforms.

What are the types of products in Android and IOS?

There are types of products in both Android and IOS: First Know about the Products in Android:

  • Consumable product: Products like in-game currency are examples of this type of product. Once the user starts to use them, the user can purchase it again.
  • Non-Consumable product: These can be bought only once and it gives a permanent benefit.
  • Subscriptions: These give benefits to the user for a limited period. You can compare Netflix, Spotify subscriptions to these products.

Know about Products in iOS

  • Consumable product.

  • Non-Consumable product.

  • Auto-Renewable Subscriptions: same as android subscriptions.

  • Non-Renewing Subscriptions: It is almost the same as Auto-Renewable Subscriptions.

What are the steps to add a subscription in the Flutter app?

  • First step is we need to Create a Product

    How to create a product for Android:

  • First go to Google Play Console

  • Second step is to click the app for which you want to build a subscription.

  • Third step is to go Inside the menu under Monetize > Products click Subscriptions.

  • Fourth step is to fill the form with the needed details. Here product id is the most essential field. Product id is used to uniquely find each product.

How to create a product for iOS:

  • First you have to Go to App Store Connect.
  • Second step is to click the app for which you want to make a subscription.
  • Third step is to click In the side menu under the in-app purchase section and now click the manage option.
  • Fourth step is to press the plus sign to include new products and select the type of subscription you want.
  • Fifth step is to type the product name and product id. Here product id is the same as android.
  • Sixth step is to select a subscription group and make a new group.
  • Seventh step is to add a subscription group or make a new group.
  • The last step is to Fill every mandatory information. If any given information is missing for the product then the status of the product will be Missing Metadata. Fill every single information until the status changes to Ready to Submit.
  • Second step is to set up testing accounts.

How to setup testing account for Android:

  • Include the tester’s email address in the applications license testers.
  • Include the same used email address in the app’s tester list.

How to setup testing account for IOS:

  • First step is to go to the app store connect.
  • Second step is to go to User and Access.
  • Third step is to find the Tester option under Sandbox. Press on that.
  • Fourth step is to press on the plus sign to include an apple pay sandbox tester.

Need to understand how the purchase of subscription works:

  • The application notifies the billing server that the present user wants to buy a product with id.
  • The billing Server needs the purchase and gives back the response.
  • The response gets at the application through PurchaseUpdateStream.
  • Your application always checks the status of the purchase and takes the action accordingly.
  • If the purchase was correct the application will notify the application’s back end that this user has perfectly purchased a product and this is the shopping token I got from the billing server.
  • The application’s back end will check the purchase with the billing server before giving any advantages to the user. After perfect validation, your app’s back end will mark the present user as a premium user and notify the application.
  • Now the app has to finish the transaction with the billing server. Completion of the transaction is a way of ordering the billing server that you have perfectly delivered the product to the user.
  • Transaction that finished is different for Android and iOS. In Android, you just have to finish the transaction which is successful. In iOS, you have to finish every transaction irrespective of the status of the transaction.
  • Transaction completion is very important because if you don’t finish the transaction in Android, Google will give back the amount of purchase considering that the purchase was failed.
  • In case of application crashes or network problems happening during the transaction when the user opens the application again your app will be notified with all the unfinished transactions through the PurchaseUpdateStream. So, you can continue the plan of purchase of the product.
  • Based on the response received from the app’s back end you will show a special display to the user.

Next step is to integrate in-App purchases with flutter.

To manage our code we will develop one new folder:

class
PaymentService {
/// We want singleton object of ``PaymentService`` so 
create private constructor
 ///
/// Use PaymentService as ``PaymentService.instance``
PaymentService._internal();
static final PaymentService instance = 
PaymentService._internal();
}

Next is to create some variables:

class 
PaymentService
{
/// We want singleton object of ``PaymentService`` so create
private constructor
///
/// Use PaymentService as ``PaymentService.instance``
PaymentService._internal();
static final PaymentService instance =
PaymentService._internal();
/// To listen the status of connection between app and the billing server
StreamSubscription<ConnectionResult>
_connectionSubscription;
/// To listen the status of the purchase made inside or outside of the app (App Store / Play Store)
///
/// If status is not error then app will be notied by this stream
StreamSubscription<PurchasedItem>
_purchaseUpdatedSubscription;
/// To listen the errors of the purchase
StreamSubscription<PurchaseResult>
_purchaseErrorSubscription;
/// List of product ids you want to fetch
final List<String> _productIds = [
'monthly_subscription'
];

/// All available products will be store in this list List _products;

/// All past purchases will be store in this list
List<PurchasedItem> _pastPurchases;
/// view of the app will subscribe to this to get notified
/// when premium status of the user changes
ObserverList<Function> _proStatusChangedListeners =
new ObserverList<Function>();
/// view of the app will subscribe to this to get errors of the purchase
 ObserverList<Function(String)> _errorListeners =
 new ObserverList<Function(String)>();
/// logged in user's premium status
bool _isProUser = false;
bool get isProUser => _isProUser;
}

Want to get the UI latest updates use the below code:

 addToProStatusChangedListeners(Function callback) {
 _proStatusChangedListeners.add(callback);
}
/// view can cancel to _proStatusChangedListeners using this method

removeFromProStatusChangedListeners(Function callback) {

_proStatusChangedListeners.remove(callback);
 }
/// view can subscribe to _errorListeners using this method
addToErrorListeners(Function callback) {
_errorListeners.add(callback);
}
 /// view can cancel to _errorListeners using this method
removeFromErrorListeners(Function callback) {
_errorListeners.remove(callback);
 }

Following code to notify the user:

void _callProStatusChangedListeners() {
_proStatusChangedListeners.forEach((Function callback) {
 callback();
 });
}

 /// Call this method to notify all the subsctibers of _errorListeners
void _callErrorListeners(String error) {
 _errorListeners.forEach((Function callback) {
callback(error);
});
}

Call the interconnection method at the start your app using below code:

 /// with billing server and get all the necessary data
 void initConnection() async {
await FlutterInappPurchase.instance.initConnection;
 _connectionSubscription =
FlutterInappPurchase.connectionUpdated.listen((connected) {});
 _purchaseUpdatedSubscription =
FlutterInappPurchase.purchaseUpdated.listen(_handlePurchaseUpdate);
_purchaseErrorSubscription =
FlutterInappPurchase.purchaseError.listen(_handlePurchaseError);
_getItems();
 _getPastPurchases();
}
 /// call when user close the app
void dispose() {
_connectionSubscription.cancel();
 _purchaseErrorSubscription.cancel();
 _purchaseUpdatedSubscription.cancel();
FlutterInappPurchase.instance.endConnection;
}

Handle purchase errors using the below code:

void _handlePurchaseError(PurchaseResult
purchaseError) {
_callErrorListeners(purchaseError.
message);
}

For handle purchase updates:

void _handlePurchaseUpdate(PurchasedItem productItem) async
{
if (Platform.isAndroid) {
await _handlePurchaseUpdateAndroid(productItem);
 } else {
await _handlePurchaseUpdateIOS(productItem);
}
}

For finish the transaction:

Future<void> 
_handlePurchaseUpdateI
OS(PurchasedItem
purchasedItem) async {
switch (purchasedItem.transactionStateIOS) {
case TransactionState.deferred:
// Edit: This was a bug that was pointed out here : 
https://github.com/dooboolab/flutter_inapp_purchase/issues/234
//
FlutterInappPurchase.instance.finishTransaction(purchasedItem);
break;
case TransactionState.failed:
 _callErrorListeners("Transaction Failed");
FlutterInappPurchase.instance.finishTransaction(purchasedItem);
 break;
case TransactionState.purchased:
await
_verifyAndFinishTransaction(purchasedItem);
break;
case TransactionState.purchasing:
break;
 case TransactionState.restored:
FlutterInappPurchase.instance.finishTransaction(purchasedItem);
break;
default:
}
 }
/// three purchase state 
https://developer.android.com/reference/com/android/billingclient/api/Purchase.PurchaseState
/// 0 : UNSPECIFIED_STATE
/// 1 : PURCHASED
 /// 2 : PENDING
Future<void> 
_handlePurchaseUpdateAndroid(PurchasedItem purchasedItem) async {
switch (purchasedItem.purchaseStateAndroid) {
 case 1:
if (!purchasedItem.isAcknowledgedAndroid) {
 await 
_verifyAndFinishTransaction(purchasedItem);

}
  break;
default:
_callErrorListeners("Something went wrong");
 }
}

For verify purchase is success:

 /// Call API of your back end to verify the receipt
 /// back end has to call billing server's API to verify the purchase token
 _verifyAndFinishTransaction(PurchasedItem purchasedItem) async {
bool isValid = false;
try {
// Call API
isValid = await _verifyPurchase(purchasedItem);
} on NoInternetException {
_callErrorListeners("No Internet");
 return;
} on Exception {
 _callErrorListeners("Something went wrong");
return;
}
 if (isValid) {
 FlutterInappPurchase.instance.finishTransaction(purchasedItem);
 _isProUser = true;
// save in sharedPreference here
_callProStatusChangedListeners();
} else {
_callErrorListeners("Verification failed");
}
}

For get all the available products:

Future<List<IAPItem>> get
products async {
 if (_products == null) {
await _getItems();
}
}
return _products;
}
Future<void> _getItems() async {
List<IAPItem> items =
await 
FlutterInappPurchase.instance.getSubscriptions
(_productIds);
 _products = List();
 for (var item in items) {
this._products.add(item);
}
}

To buy the product:

Future<Null> 
buyProduct(IAPItem item)
async {
try {
await 
FlutterInappPurchase.instance.requestSubscription(item.productId);
 } catch (error) {
 }
}

If you have any doubts about the above topic or have to get services and consultations and get the best Flutter application services. Feel free to contact us. AIRO GLOBAL SOFTWARE will be your strong digital partner. E-mail id: [email protected].

enter image description here Author - Johnson Augustine
Chief Technical Director and Programmer
Founder: Airo Global Software Inc
LinkedIn Profile: www.linkedin.com/in/johnsontaugustine/

How To Use MQTT in PHP?

- Posted in Web Development by

enter image description here

PHP is a globally-used open source to do multiple functions in scripting language, which can be embedded in HTML and is very suitable for Web development. This blog mainly introduces how to make use of the php-mqtt/client client library in PHP projects to make the functions of connection, subscription, unsubscribing, message receiving and sending between Mqtt client and server.

What is MQTT?

Message Queuing Telemetry Transport is a communicative message protocol for restricted networks and IoT machines with extremely high latency. Because Message Queuing Telemetry Transport is concentrated in low bandwidth, high latency environments, it is an ideal protocol for machine-to-machine communication. MQTT is used in IoT and IIoT up to the connection of the cloud ecosystem.

What is PHP?

PHP is a server-side scripting programming language. that is used to create Static websites, Dynamic websites, and Web applications. PHP stands for Hypertext Preprocessor, which earlier stood for Personal Home Pages.

How do we do MQTT clients library selection?

This blog chooses the library php-mqtt/client, which has the highest installs on composer. MQTT communicative messages belong to network communication cases outside the HTTP system. Due to the restriction of PHP characteristics, using the extensions for network controls such as Swoole/Workerman in the PHP device can bring a better experience. The relevant MQTT client libraries are as follows:

  • workerman/mqtt
  • simps/mqtt

How do we do project initialization?

This project uses 7.4.21 for development. Readers can confirm their PHP with the below command.

php --version
PHP 7.4.21 (cli) (built: Jul 12 2021 11:52:30) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
with Zend OPcache v7.4.21, Copyright (c), by Zend Technologies

How to use composer to install php-mqtt/client?

Composer is a dependency management option for PHP, which can control all the dependencies your PHP project requirements.

composer require php-mqtt/client

How is PHP-MQTT usage?

The server access information is as given below:

  • roker: broker.emqx.io
  • TCP Port: 1883
  • SSL/TLS Port: 8883

Import composer autoload file and php-mqtt/client

require('vendor/autoload.php');
use \PhpMqtt\Client\MqttClient;

Set MQTT Broker connection parameters

Set the MQTT Broker communication address, port, and topic. At the same time, we call the PHP rand function to create the MQTT client id.

$server   = 'broker.emqx.io';
$port     = 1883;
$clientId = rand(5, 15);
$username = 'emqx_user';
$password = null;
$clean_session = false;

Write about MQTT connection function?

Use the above units to communicate, and set the communication units through ConnectionSettings, such as:

$connectionSettings  = new ConnectionSettings();
$connectionSettings
->setUsername($username)
->setPassword(null)
 ->setKeepAliveInterval(60)
 ->setLastWillTopic('emqx/test/last-will')
 ->setLastWillMessage('client disconnect')
->setLastWillQualityOfService(1);

How to subscribe?

Program to subscribe to the heading of emqx/test, and configure a callback program for the subscription to process the received message. Here we print out the topic and communication obtained from the subscription:

$mqtt->subscribe('emqx/test', function ($topic, $message) {
printf("Received message on topic [%s]: %s\n", $topic, $message);
}, 0);

How to Publish?

Construct a payload and call the publish function to upload messages to the emqx/test topic. After uploading, the client needs to enter the polling status to process the incoming communications and the retransmission queue:

for ($i = 0; $i< 10; $i++) {
$payload = array(
'protocol' => 'tcp',
'date' => date('Y-m-d H:i:s'),
'url' => 'https://github.com/emqx/MQTT-Client-Examples'
 );
 $mqtt->publish(
 // topic
'emqx/test',
// payload
json_encode($payload),
// qos
0,
 // retain
true
 );
printf("msg $i send\n");
 sleep(1);
}
// The client loop to process incoming messages and retransmission queues
$mqtt->loop(true);

Server connection, message uploading and receiving code.

<?PHP
require('vendor/autoload.php');
use \PhpMqtt\Client\MqttClient;
use \PhpMqtt\Client\ConnectionSettings;
$server   = 'broker.emqx.io';
$port     = 1883;
$clientId = rand(5, 15);
$username = 'emqx_user';
$password = null;
$clean_session = false;
$connectionSettings  = new ConnectionSettings();
$connectionSettings
 ->setUsername($username)
->setPassword(null)
->setKeepAliveInterval(60)
->setLastWillTopic('emqx/test/last-will')
->setLastWillMessage('client disconnect')
->setLastWillQualityOfService(1);
$mqtt = new MqttClient($server, $port, $clientId);
$mqtt->connect($connectionSettings, $clean_session);
printf("client connected\n");
$mqtt->subscribe('emqx/test', function ($topic, $message) {
 printf("Received message on topic [%s]: %s\n", $topic, $message);
}, 0);
for ($i = 0; $i< 10; $i++) {
$payload = array(
'protocol' => 'tcp',
'date' => date('Y-m-d H:i:s'),
'url' => 'https://github.com/emqx/MQTT-Client-Examples'
 );
$mqtt->publish(
 // topic
  'emqx/test',
// payload
 json_encode($payload),
// qos
 0,
 // retain
true
  );
 printf("msg $i send\n");
sleep(1);
}
$mqtt->loop(true);

How to Test?

After executing the MQTT message uploading code, we will view that the client has perfectly connected, and the messages have been upload one by one and received successfully:

php pubsub_tcp.php

If you have any doubts about the above topic or have to get services and consultations and get the PHP and MQTT services. Feel free to contact us. AIRO GLOBAL SOFTWARE will be your strong digital partner. E-mail id: [email protected].

enter image description here Author - Johnson Augustine
Chief Technical Director and Programmer
Founder: Airo Global Software Inc
LinkedIn Profile: www.linkedin.com/in/johnsontaugustine/

enter image description here

CodeIgniter has been created to make testing both the framework and the app as simple as possible. Support for PHPUnit is created in, and the framework provides a number of convenient helper methods to make testing every aspect of your application as painless as possible.

How to install PHPUnit?

CodeIgniter uses PHPUnit as the backbone for all of the testing. There are two ways to enable PHPUnit to use within your system.

Install Composer

The recommended method is to enable it in your project using composer. While it’s possible to install it in the whole world we do not recommend it, since it can cause many issues with other projects on your system as time goes on. Make sure that you installed it. From the project root type the following from the command line:

> composer require --dev phpunit/phpunit

Once that is done, you can execute all of the tests for this project by typing:

> ./vendor/bin/phpunit

Install Phar

The other option is to install the .phar file from the PHP site. This is a foundation file that should be placed within your project.

How to test your application?

The framework has a phpunit.xml.dist file . This enables unit testing of the framework . If you enable your own phpunit.xml, it will override this.

Your phpunit.xml should get out of the system folder, as well as any vendor or ThirdParty directories, if you are working unit testing your application.

In order to take advantage of the more options provided, your tests must extend CIUnitTestCase. All tests are expected to be situated in the tests/app directory by default.

To test the library, you would built a new file at tests/app/Libraries/FooTest.php:

<?PHP
namespace App\Libraries;
use CodeIgniter\Test\CIUnitTestCase;
class FooTest extends CIUnitTestCase
{
public function testFooNotBar()
{
// ...
}
}

To test one of the program, you might enter this code in tests/app/Models/OneOfMyModelsTest.php:

<?php
namespace App\Models;
use CodeIgniter\Test\CIUnitTestCase;
class OneOfMyModelsTest extends CIUnitTestCase
{
 public function testFooNotBar()
{
 // ...
}
}

You can make any directory structure that fits your testing style. When namespacing the classes, remember that the app directory is the main root of the App namespace, so any classes you use must have the correct namespace relative to App.

What is staging?

Most tests need some preparation in order to work correctly. PHPUnit’s TestCase have four methods to help with staging and clean up:

public static function setUpBeforeClass(): void
public static function tearDownAfterClass(): void
public function setUp(): void
public function tearDown(): void

The static methods execute before and after the test case, whereas the local methods work between each test. If you run any of these special functions make sure you run their parent as well so extended cases do not interfere with staging:

public function setUp(): void
{
 parent::setUp();
helper('text');
}

After to these methods, CIUnitTestCase also comes with a property for parameter-free methods you want to execute during set up and tear down:

protected $setUpMethods = [
'mockEmail',
'mockSession',
];
protected $tearDownMethods = [];

You can see by automatically these manage the mocking of intrusive services, but your class may overlap that or provide their own:

class OneOfMyModelsTest extends CIUnitTestCase
{
protected $tearDownMethods = [
 'purgeRows',
];
 protected function purgeRows()
{
 $this->model->purgeDeleted()
 }

What are Traits?

The basic way to encourage your tests is by using traits to consolidate staging across various test cases. CIUnitTestCase will identify any class traits and look for staging methods to execute names for the trait itself.

trait AuthTrait
{
protected setUpAuthTrait()
{
$user = $this->createFakeUser();
$this->logInUser($user);
 }
...
class AuthenticationFeatureTest
{
use AuthTrait;
...

What about Additional Assertions?

CIUnitTestCase provides unit testing that you might find very useful.

assertLogged:

Ensure that something you expected to be signed actually was:

$config = new LoggerConfig();

$logger = new Logger($config);
... do something that you expect a log entry from
$logger->log('error', "That's no moon");
$this->assertLogged('error', "That's no moon");

assertEventTriggered: Make sure that an event you expected to be triggered actually was:

Events::on('foo', function ($arg) use(&$result) {
$result = $arg;
});
Events::trigger('foo', 'bar');
$this->assertEventTriggered('foo');

assertHeaderEmitted:

Make sure that a header or cookie was actually emitted:

$response->setCookie('foo', 'bar');
ob_start();
$this->response->send();
$output = ob_get_clean(); // in case you want to check the actual body
$this->assertHeaderEmitted("Set-Cookie: foo=bar");

assertHeaderNotEmitted:

Make sure that a header or cookie was not emitted:

$response->setCookie('foo', 'bar');
ob_start();
$this->response->send();
$output = ob_get_clean(); // in case you want to check the actual body
$this->assertHeaderNotEmitted("Set-Cookie: banana");

assertCloseEnough:

For extended execution time testing, tests that the absolute difference between expected and original time is within the prescribed tolerance.:

$timer = new Timer();
$timer->start('longjohn', strtotime('-11 minutes'));
$this->assertCloseEnough(11 * 60, $timer->getElapsedTime('longjohn'));

assertCloseEnoughString:

For extended running time testing, tests that the absolute difference between expected and original time, formatted as strings, is within the prescribed tolerance.:

$timer = new Timer();
$timer->start('longjohn', strtotime('-11 minutes'));
$this->assertCloseEnoughString(11 * 60, $timer->getElapsedTime('longjohn'));

getPrivateMethodInvoker:

Install you to call private methods from outside the class. This returns a program that can be called. The first parameter is an instance of the class . The second parameter is the name of the method you want to make a call.

// Create an instance of the class to test
$obj = new Foo();
// Get the invoker for the 'privateMethod' method.
$method = $this->getPrivateMethodInvoker($obj, 'privateMethod');
// Test the results
$this->assertEquals('bar', $method('param1', 'param2'));

getPrivateProperty:

Retrieves the value of a private class property from an instance of a class.

// Create an instance of the class to test
$obj = new Foo();
// Test the value
$this->assertEquals('bar', $this->getPrivateProperty($obj, 'baz'));

setPrivateProperty:

// Create an instance of the class to test
$obj = new Foo();

// Set the value
$this->setPrivateProperty($obj, 'baz', 'oops!');

// Do normal testing...

What are mocking services?

You will often find that you have to mock one of the services defined in app/Config/Services.php to limit your tests to only the code in question while simulating multiple responses from the services. This is especially true when testing controllers and other testing.

injectMock()

This method allows you to define the exact instance that will be returned by the Services class.

public function testSomething()
{
$curlrequest = $this->getMockBuilder('CodeIgniter\HTTP\CURLRequest')
->setMethods(['request'])
->getMock();
 Services::injectMock('curlrequest', $curlrequest);
 // Do normal testing here....
}

reset()

Removes all mocked classes from the class, bringing it back to its actual state.

resetSingle: Delete any mock and shared instances for a single service, by its name. you may identify yourself needing to supply a pre-configured class instance during testing that will be used with Factories. Use the similar injectMock() andreset()` methods like Services, but they take an additional preceding parameter for the component name:

protected function setUp()
{
parent::setUp();
 $model = new MockUserModel();
 Factories::injectMock('models', 'App\Models\UserModel', $model);
}

CITestStreamFilter provides an alternative to these helper methods. You may need to test things that are difficult to test. Sometimes, capturing a stream, like PHP’s own STDOUT, or STDERR, might be useful. An example demonstrating this inside one of your test cases:

public function setUp()
{
 CITestStreamFilter::$buffer = '';
$this->stream_filter = stream_filter_append(STDOUT, 'CITestStreamFilter');
}
public function tearDown()
{
stream_filter_remove($this->stream_filter);
}


public function testSomeOutput()
{
 CLI::write('first.');
 $expected = "first.\n";
$this->assertSame($expected, CITestStreamFilter::$buffer);
}

If you have any doubts about the above topic or have to get services and consultations and get CodeIgniter testing services. Feel free to contact us. AIRO GLOBAL SOFTWARE will be your strong digital partner. E-mail id: [email protected].

enter image description here Author - Johnson Augustine
Chief Technical Director and Programmer
Founder: Airo Global Software Inc
LinkedIn Profile: www.linkedin.com/in/johnsontaugustine/

enter image description here

What is a PHP unit?

PHPUnit is one of the most well-known and optimized unit testing packages of PHP. It is the most relevant choice of many engineers for rectifying various developmental loopholes of the app. Its main action is to perform overall unit testing for apps, therefore, you can test your code every minute. Yet you can also use it for different actions, as it is more flexible to do just more than unit testing. Moreover, it carries all major PHP frameworks including Laravel. Many expert programmers recommend it for all Laravel unit testing processes, because of its optimized standards.

PHPUnit is created with simple assertions, which make it pretty easy for you to test your code successfully. Further, it gives great results when you are testing code components personally, giving you exotic results, so that mistakes can be identified easily. However, this means that testing much more advanced components like controllers, models and form validations can be a bit difficult as well.

How is unit testing with laravel possible?

Laravel is the most popular PHP framework for app development. From the testing perspective, it is also known to be highly popular for its creative testing features. In Laravel, there are two ways to test the application. One is with Unit testing and Feature testing. Unit testing allows you to test your classes models etc, while Feature testing allows you to test your code base.

If you look inside your Laravel installation folder, you can see that there are two test subfolders available. These two subdirectories in hosting for the Laravel project are Unit testing and Feature testing. These are the areas where you will be conducting your app's test, either be Unit testing or Feature testing.

How to create a Laravel unit test case?

When you use PHPUnit, the first step is to create the latest test classes. These test classes are stored in the./tests/ folder of your Laravel app. Each test class is named Test.php inside a directory. As this format helps PHPUnit to find each test class simply, and further avoid any other class that doesn’t reside in the Test.php file. In your latest Laravel app, you will find two files in the ./tests/ directory, one is ExampleTest.php and the other is TestCase.php. TestCase.php file is commonly a bootstrap file that sets the Laravel ecosystem and variety of features within our tests. It makes it very simple to use Laravel features in tests, and also allows a framework for testing assistance. The ExampleTest.php merges an example code test class which contains a basic test case using app testing helpers.

For creating a front test class, you can either create a new file manually or can run the artisan make:test command .

Laravel will build a basic test class that looks like this:

<?PHP
class BasicTest extends TestCase{
 /*** A basic test example
** @return void
*/ public function testExample(){
$this->assertTrue(true);  }
}?>

Though this folder includes a lot of information regarding tests, the most important note, for now, is the testsuite folder definition:

<?xml version="1.0" encoding="UTF-8"?>
<PHPUnit ... >
<testsuites>
<testsuite name="Application Test Demo">
<directory>./tests/</directory>
 </testsuite>
</testsuites>
...</phpunit>

How to write a basic unit test?

After finishing the above-mentioned processes successfully, you can now write the basic unit test case for your Laravel app by directing it to the tests/Feature/ExampleTest.php.

Here, you can write test cases for testing the fundamental specialties of the application.

<?php
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class ExampleTest extends TestCase
{
 /**
* A basic test example.
*
* @return void
*/
public function testBasicTest()
 {
$response = $this->get('/');
$response->assertStatus(200);
}
}

You can use seven of the basic PHPUnit assertions to write tests for your Basket class. These assertions are:

  • assertTrue()
  • assertFalse()
  • assertEquals()
  • assertNull()
  • assertContains()
  • assertCount()
  • assertEmpty()

What is given, when, then in testing?

If you want to test particular actions of your code, then you should follow the intrinsic pattern of Given, When and Then. Given – It states the primary ecosystem setup you would like to test. You can use some data, or can even set up a model factory in this step. When – When refers to a specific function and called at some particular stage of the testing process. Then – In this part, you declare what the result should look like.

How to test a Laravel application?

Unit testing is not the only testing process you will need for your app. Though it is most necessary in most cases and must be an important part of your creating process, it is not necessary that it will complete all your testing needs. Sometimes your application has complex displays, navigation, and forms, and you want to test these particulars too.

This is where Laravel’s test assistance comes into play and makes testing of a few particulars simple just as the Unit testing does.

In past Laravel unit testing examples, we saw the automatic files within the /tests/ directory and left the ./tests/ExampleTest.php file to review at the next stage. So open it now, it should look like this:

<?php
class ExampleTest extends TestCase
{
/**
* A basic functional test example
*
* @return void
*/
public function testBasicExample()
{
$this->visit('/')
->see('Laravel 5.5');
}
}

As you can see, the test in this scene is normally simple and very quick to understand. Without having any prior knowledge about how Laravel test assistance works, you can understand that it outputs some like this:

  • when I visit / (webroot)
  • I should see ‘Laravel 5

When you open the app in your web browser, you will see a splash screen with “Laravel 5” written on it. After successfully passing a test with PHPUnit, it is right to say that the output of this example test is spot-on.

This test ensures that the web page made at the / path, gives output with the text ‘Laravel 5’. Though it looks like an easy test and doesn’t seem much critical, whenever your web app needs to display sensitive information, tests like this can simply avert you from deploying broken apps.

If you have any doubts about the above topic or have to get services and consultations and get the Laravel test services. Feel free to contact us. AIRO GLOBAL SOFTWARE will be your strong digital partner. E-mail id: [email protected]

enter image description here

Author - Johnson Augustine
Chief Technical Director and Programmer
Founder: Airo Global Software Inc
LinkedIn Profile: www.linkedin.com/in/johnsontaugustine/

enter image description here

Testing code is one of the most major parts to understand your app and the behavior of code. All the developer knows how hurtful bugs can be, especially in the creating stage as it takes hours of effort. Testing each stage can be as good as believing in yourself. You write and run your code, without knowing how it actually behaves if some exception occurs, this may cause a problem in production. So, executing test cases plays a major role in SDLC. You give a better life and believe your code with your eyes closed, once your examination coverage is at a good level.

Today, we are going to write an easy test method and execute test code using tools like Xdebug and PHPUnit. PHPUnit is a programmer-based testing framework. This is an excellent testing framework for writing Unit tests for PHP Web Apps. With the help of PHPUnit, we can direct test-driven improvement.

How to install PHPUnit?

wget https://phar.phpunit.de/phpunit-7.5.phar
chmod +x phpunit-7.5.phar
sudo mv phpunit-7.5.phar /usr/bin/phpunit

There are several other possible ways to install PHPUnit. Now, To get our Test Coverage full record we need to install Xdebug:

sudo apt install php-pear
pecl install xdebug
sudo apt-get install php-xdebug

Without making any further delay Let's dive into our easy test stages where we test whether an array is empty:

  • Create a file UnitTest where we will include all testable files
  • In this folder, create a subfolder test
  • Create a new file phpunit.xml
  • In this subfolder and add the below code

    /UnitTest/tests/ /UnitTest/tests/

What are the Basic Conventions to Write Unit Test Case?

Below are some basic conventions and steps for writing tests with PHPUnit:

  • Test File names should have a suffix Test.
  • Same as it, If the class name is MyFirstClass then the test class name will be MyFirstClassTest.
  • Add test as the prefix for method names.
  • All testing types are public.
  • MyFirstClassTest class should be inherited

These are the major instructions for the PHP unit testing framework. The basic configurations and settings are all set up. It is now time to write the prime test case.

How to Write The First Unit Test Case in PHP?

Build a file EmptyTest.php in UnitTest/tests. Add the below code to it.

<?PHP
use PHPUnit\Framework\TestCase;
class EmptyTest extends TestCase
{
 public function testFailure()
{
$this->assertEmpty(['something']);
 }
}
?>

Now, to execute our test code inside terminal type:

phpunit i.e. phpunit tests/EmptyTest.php.

By executing only phpunit, it automatically executes all test file inside UnitTest/tests folder because we have mentioned this directory in phpunit.xml above. We will get our test failure because the value in the array is not empty. In the terminal you are getting like this:

F                                                                   1 / 1 (100%)
Time: 513 ms, Memory: 10.00MB
There was 1 failure:
EmptyTest::testFailure
Failed asserting that an array is empty
/home/anu/myproj/testcase/tests/EmptyTest.php:8

FAILURES!

Tests: 1, Assertions: 1, Failures: 1

Let's pass our above test case and add one more step to test equals.

<?PHP
use PHPUnit\Framework\TestCase;
class EmptyTest extends
TestCase
{

public function

testFailure()
{
$this->assertEmpty([]);
 }
}
?>
<?PHP
use PHPUnit\Framework\TestCase;
class EqualsTest extends TestCase
{
public function testFailure()
{
$this->assertEquals(0,1); //failed asserting 0 and 1 are equal
}
}
?>

It gives an unexpected error when $expected is not the same as $actual. If $expected is the same to $actual then it is true. Don’t forget that the first argument is expected and the other is actual. The above test only walks away if the expected (0) is equal to (1). To pass the above test case just replace 0 with 1 or 1 with 0. Now, execute both test cases. Your both tests must pass:

PHPUnit 7.5.6 by Sebastian Bergmann and contributors.
Runtime:       PHP 7.3.1-1+ubuntu18.04.1+deb.sury.org+1 with Xdebug 2.7.0
Configuration: /home/samrat/myproj/UnitTest/phpunit.xml
..                                                                  2 / 2 (100%)
Time: 200 ms, Memory: 10.00MB
OK (2 tests, 2 assertions)

Let’s see another example in which a developer has built a code to parse the items received in JSON and validate it for the possible use case may be an exception or for other validation instruction.

Let's build an app/item_parser.php file inside the UnitTest directory

<?PHP
namespace App\Parser;
require_once 'exceptions.php';
use App\Exceptions\JsonParseException;
use App\Exceptions\InvalidNumberFormatException;
class ItemsParser {
public static function parse($response){
$jDecode=json_decode($response);
if (JSON_ERROR_NONE !== json_last_error()){
throw new JsonParseException('Error parsing
JSON:'.$response);
}
$jd=$jDecode->_embedded->menu_items;
$parsed = [];
foreach($jd as $key => $item){
$price=$item->price_per_unit;
if (is_string($price)) {
throw new 
InvalidNumberFormatException('Invalid numeric value encountered:'.$price);
}
$parsed[$item->id] = [
"label" => $item->name,
"value" => number_format(($price/100), 2, '.', ' ')
];
}
return $parsed;
}
}
?>

create a class called ItemsParser with a static option called parse that takes one argument called a response, received from JSON data. We have checked for different types of exceptions and returned parsed data as an array.

Let's add some exceptions classes for the above use cases inside the same folder:

<?PHP
namespace App\Exceptions;
class JsonParseException extends
\Exception {}
class InvalidNumberFormatException 
extends \Exception {}
?>

Next in the UnitTest/tests folder, create a new file called ItemsParserTest.php and add the below test code:

<?php
use PHPUnit\Framework\TestCase;
require_once __DIR__ . '/../app/item_parser.php';
use App\Parser\ItemsParser;
class ItemsParserTest extends TestCase
{   
public function test_create_ItemsParser() {
 $ip = new ItemsParser();
$this->assertEquals('App\Parser\ItemsParser',
\get_class($ip));
}
}
?>

Run the above test code, Our test case must pass. By, this we can make sure that ItemParser class is successfully instantiated. As we don't invoke real endpoint. Let's create a fake json data called one.json inside UnitTest/json and test.

{
"_embedded": {
"menu_items": [
{
"id": "1",
"in_stock": null,
"name": "Pizza",
"open": false,
"pos_id": "101",
"price_per_unit": 899
}
]
}
}

Now Let's write another test code to ensure our data is actually parsed the way we aimed it:

<?php
use PHPUnit\Framework\TestCase;
require_once __DIR__ . '/../app/item_parser.php';
require_once __DIR__ . '/../app/exceptions.php';
use App\Exceptions\JsonParseException;
use App\Parser\ItemsParser;
use App\Exceptions\InvalidNumberFormatException;
class ItemsParserTest extends TestCase
{
var $dir = __DIR__ . '/../json/';
 public function test_create_ItemsParser() {
$ip = new ItemsParser();
 $this->assertEquals('App\Parser\ItemsParser', \get_class($ip));
}
 public function     test_parseSingleItem_shouldReturnArrayOneLabelAndValue(){
$items=file_get_contents($this->dir.'one.json');
$expected = ['1' => ['label' => 'Pizza', 'value' => '8.99']];
$this->assertSame($expected, ItemsParser::parse($items));
 }
}
?>

As, we make fake data, and send that data to ItemParser::parse() option. If we now execute our test case we get the data the way we expected.

PHPUnit 7.5.8 by Sebastian Bergmann and contributors.
Runtime:       PHP 7.2.15-0ubuntu0.18.04.1 with Xdebug 2.6.0
Configuration: /home/samrat/myproj/UnitTest/phpunit.xml
..                                                                  2 / 2 (100%)
Time: 377 ms, Memory: 10.00 MB
OK (2 tests, 2 assertions)

Basically, what we did is we send json details and decoupled them on our data only with label and value as keys and returned only data that is needed as an array. Next, Let's test for the array keys we are getting:

<?php
use PHPUnit\Framework\TestCase;
require_once __DIR__ . '/../app/item_parser.php';
require_once __DIR__ . '/../app/exceptions.php';
use App\Exceptions\JsonParseException;
use App\Parser\ItemsParser;
use App\Exceptions\InvalidNumberFormatException;
class ItemsParserTest extends TestCase
{
 var $dir = __DIR__ . '/../json/';
 public function 
test_JsonParser_arrayKey_shouldReturnKey_label_and_value(){
$items=file_get_contents($this->dir.'one.json');
 $itemId='1';
$parsed=ItemsParser::parse($items);
$this->assertArrayHasKey('label',$parsed[$itemId]);
 $this->assertArrayHasKey('value',$parsed[$itemId]);
 }
}
?>

From the above test code, we can make sure that whether we are getting the key we expected that is label and value. What about the wishes we included in our methods, whether they are executed the way we wanted? Well, I am not sure so let's test it. Say, we have some JSON data with price in the string:

{
"_embedded": {
"menu_items": [
{
"id": "1",
"in_stock": 
"name": "Pizza",
"open": false,
"pos_id": "101",
"price_per_unit": "iamstring"
},
{
"id": "2",
"in_stock": 
"name": "Burger",
"open": false,
"pos_id": "101",
"price_per_unit":"gfsgd"
}
]
}
}

How does our test code block respond when we send price as string, well we must be sure our test code handles such kinds of exceptions.

public function
test_stringPriceValueProvided_shouldThrow_InvalidNumberFormatException(){
$items=file_get_contents("stringPrice.json")
 $this->expectException(InvalidNumberFormatException::class);
$parsed=ItemsParser::parse($items);
}

expectException() method is to ensure whether an expected exception is thrown by our code block. Run phpunit and check the above code it must by the way!

Rechange, the ItemParser.php file we have used the number_format function to convert cent price into the dollar. So, Let's enter a test to check whether our price has been perfectly converted.

public function testCent_Conversion_toDollar(){
$items=$this->file_get_contents('one.json');
 $itemId='1';
 $parsed= ItemsParser::parse($items);
$this->assertEquals(8.99,$parsed[$itemId]['value']);
 }

And These tests can go on and on, wouldn't it be nice to get a report or analyzer for our test case. To get a total test coverage report would be best. So, we have written some test code and let's generate our test report. It's easy just type this in terminal: phpunit --coverage-html . In my case phpunit --coverage-html report/. You will get whole report in html format. By the way, we can generate in xml, php, text and many other formats. Just type: phpunit --help for more.

The above test coverage explains, our first example test coverage is complete but ItemParserTest could be great and we only covered some part of our code so let's stick with partially completed. I leave it up to you to make it full. Actually, partially test code coverage can be acceptable coverage in the software world.

We also get the report of where we can do better. Here are some other code coverage statistics:

How about the Conclusion?

This article explains a basic setup that helps you in getting started with PHPUnit for PHP unit testing.

Hope you find this blog helpful. Unit Testing is a vast topic. Here I have given you a brief introduction so that you can start entering your own tests.

If you have any questions about the above topic or have to get services and consultations and get the unit test services. Feel free to contact us. AIRO GLOBAL SOFTWARE will be your strong digital partner.

E-mail id: [email protected]

enter image description here

Author - Johnson Augustine
Chief Technical Director and Programmer
Founder: Airo Global Software Inc
LinkedIn Profile: www.linkedin.com/in/johnsontaugustine/

enter image description here

If you are using mostly open-source in your business, and have few MS SQL server databases, you might want to consider migrating those to MySQL.

Why do you want to consider migrating Microsoft SQL Server to MySQL database?

  • To avoid the License and fees of MS SQL Server. In MySQL, even if you decide to use the MySQL enterprise, it really costs less money.
  • Unlike SQL Server, MySQL supports a wide range of OS including several Linux distros, Solaris and more.
  • To implement a highly structured database infrastructure
  • To take benefit of several advanced features of the MySQL database that have been tested accurately over the years by a huge open source community

We can migrate the MS SQL database to MySQL using the migration module of the “MySQL Workbench” facilities. The easiest way to enable MySQL Workbench is to install “Oracle MySQL for windows”, which enables several MySQL options including the Workbench.

Download and install this My SQL which includes Workbench and other needed connectors and drivers that are most important for the migration.

What are the steps involved in the migration of the MsSql database to MySQL using Workbench?

  • The first step is to take care of prerequisites.
  • The second step is to select the source and target the database
  • The third step is to migrate the objects
  • The final step is data migration

Take care of Prerequisites

Before starting the MySQL database migration, we are required to ensure that ODBC drivers are present for communicating to the source Microsoft SQL Server database.

Check that the max_allowed_packet tool in the MySQL server is required for the largest field to be migrated.

Make sure that we can connect to both the targeted MySQL server database and source Microsoft SQL database with the needed requirements that are required for migrating the data across.

Select Source and Target Database

First, explain the source Microsoft SQL Server database parameter. Click “MS SQL Server” from the database system list. In the tab, select the DSN, and specify the username to the database.

Next, define the target MySQL database connection. Select “Local Instance MySQL” depending on your situation. In the parameters tab, specify the hostname where the MySQL database is executing, the MySQL port, username. If you don’t specify the password, it will promote you.

Once you target the source and destination, all available schemas and databases will be collected. You can click the specific schema that you like to migrate, and you can also specify personal schema mapping to the destination MySQL database.

Migrate the Objects

In this step, the Microsoft SQL Server schema objects, table objects, data types, default values, indexes, primary keys are changed. Please note that view objects, function objects and stored programs are just copied and commented out as we will need to convert those automatically.

Data Migration

In this step, the default copy of data is done from source to destination server for the migrated tables. Please note that using the migration we can only change tables and copy data but cannot convert the triggers and views.

If you have any questions about the above topic or have to get database management services and consultations and get the server hosting services. Feel free to contact us. AIRO GLOBAL SOFTWARE will be your strong digital partner. E-mail id: [email protected]

enter image description here

Author - Johnson Augustine
Chief Technical Director and Programmer
Founder: Airo Global Software Inc
LinkedIn Profile: www.linkedin.com/in/johnsontaugustine/