Configuration Handler for Deno with Support for .env and Scopes

Cfg

Cfg is a configuration handler for Deno with support for .env and scopes.

Run

Some features require access to the file system and the environment. Please run your project with the flags --allow-read, --allow-env and --unstable.

Import

Import the latest release.

// As named import
import { cfg } from "https://deno.land/x/cfg@v1.0.0-beta.3/mod.ts";

// Or as default import
import cfg from "https://deno.land/x/cfg@v1.0.0-beta.3/mod.ts";

Basic usage

This is the easiest way to use Cfg.

const config = cfg([{ foo: "bar" }]);

console.log(config.get("foo"))
// Or
console.log(config.getp("foo"))

A type parameter can also be used in order to benefit from intellisense.

const obj = { foo: "bar" };

const config = cfg<typeof obj>([obj]);

console.log(config.get("foo"));
// Or
console.log(config.getp("foo"))

Usage

cfg() function

cfg is an overloaded function. It has the following signature:

cfg<Config extends Configuration, Env extends ENVConfiguration = {}>(arguments): Cfg<Config, Env>

Call without argument

Calling cfg() without arguments is used to access a configuration which has already been initiated on the default scope

Call with a scope

Calling cfg(scope) is used to access a configuration which has already been initiated on the specified scope

Call with a Loadable

Calling cfg([Loadable]) is used to create a configuration on the default scope. An array of Loadable is passed as argument. Loadable is the type that describes anything that can loaded to the configuration.

Call with a scope and a Loadable

Calling cfg(scope, [Loadable]) is used to create a configuration on the specified scope.

Call with an options object

Calling cfg(options) allow to specify additional options

Persistance & scopes

All configurations are stored by Cfg and can be accessed at any time by calling cfg(scope). There are a few things to keep in mind:

  • When no scope is specified in the options, a default scope is used.
  • Scopes are shared between modules. It’s therefore best for each module to have its own scope and leave the default scope for the final user.
  • Only one configuration per scope can be created. Once it’s there, it can’t be deleted

Options

The Opts interface describes the option object that is used to configure cfg().

interface Opts {
  env?: boolean | string | EnvOpt;
  load?: Loadable | Loadable[];
  scope?: string;
}

The Opts.scope property defines the scope. The Opts.load property is used to specify one or several Loadable which will be merged to a Configuration object. The Opts.env property is used to configure the environment variables. When Opts.env is true, Cfg will look for an .env file in the root directory. Opts.env can also be a string that represents the path to the .env file. More options can be configured by using an EnvOpt object.

interface EnvOpt {
  export?: boolean;
  import?: ENVConfiguration;
  merge?: boolean;
  path?: string;
}

When true, EnvOpt.export exports the environment variables to Deno.env. It will never overwrite a value that already exist. With EnvOpt.import, an object of type ENVConfiguration can be imported. This can be useful if, for example, one needs to use another parser for .env files. When true, EnvOpt.merge will merge the environment variables with the configuration. Finally, EnvOpt.path is a string that that represents the path to the .env file.

Methods

Cfg.get(…props: string[])

This method returns the configuration. Parameters can be used to return a specific property. When cfg() is called with a type parameter, intellisense checks that the paramters are correct and knows the type of the value returned.

Cfg.get() allows up to 4 parameters.

Cfg.getp(path: string)

This method returns the configuration just like Cfg.get(). But it accepts a path as parameter with the syntax a.b.c or a[b][c]. This method will always return a value with type unknown.

Cfg.getEnv(prop: string)

Cfg.getEnv() is similar to Cfg.get() except that it accepts only one parameter and returns environment variables.

Types

Configuration

The interface Configuration defines the configuration object.

interface Configuration {
  [key: string]: (string | number | boolean | Configuration) | (string | number | boolean | Configuration)[];
}

ENVConfiguration

The interface ENVConfiguration defines the .env object

interface ENVConfiguration {
  [key: string]: string | number | boolean;
}

Loadable

The type Loadable defines the sources of configuration that will be merged to a Configuration object.

type Loadable = string | Configuration | ConfigFunction;

ConfigFunction

The type ConfigFunction defines a function that returns a Configuration object.

type ConfigFunction<T extends ENVConfiguration = {}> = (env: T) => Configuration;

Contributions

PRs are welcome!

Download Details:

Author: ymonb1291

Source Code: https://github.com/ymonb1291/cfg

#deno #nodejs #node #javascript

What is GEEK

Buddha Community

Configuration Handler for Deno with Support for .env and Scopes

Background Fetch for React Native Apps

react-native-background-fetch

Background Fetch is a very simple plugin which attempts to awaken an app in the background about every 15 minutes, providing a short period of background running-time. This plugin will execute your provided callbackFn whenever a background-fetch event occurs.

There is no way to increase the rate which a fetch-event occurs and this plugin sets the rate to the most frequent possible — you will never receive an event faster than 15 minutes. The operating-system will automatically throttle the rate the background-fetch events occur based upon usage patterns. Eg: if user hasn't turned on their phone for a long period of time, fetch events will occur less frequently or if an iOS user disables background refresh they may not happen at all.

:new: Background Fetch now provides a scheduleTask method for scheduling arbitrary "one-shot" or periodic tasks.

iOS

  • There is no way to increase the rate which a fetch-event occurs and this plugin sets the rate to the most frequent possible — you will never receive an event faster than 15 minutes. The operating-system will automatically throttle the rate the background-fetch events occur based upon usage patterns. Eg: if user hasn't turned on their phone for a long period of time, fetch events will occur less frequently.
  • scheduleTask seems only to fire when the device is plugged into power.
  • ⚠️ When your app is terminated, iOS no longer fires events — There is no such thing as stopOnTerminate: false for iOS.
  • iOS can take days before Apple's machine-learning algorithm settles in and begins regularly firing events. Do not sit staring at your logs waiting for an event to fire. If your simulated events work, that's all you need to know that everything is correctly configured.
  • If the user doesn't open your iOS app for long periods of time, iOS will stop firing events.

Android

Installing the plugin

⚠️ If you have a previous version of react-native-background-fetch < 2.7.0 installed into react-native >= 0.60, you should first unlink your previous version as react-native link is no longer required.

$ react-native unlink react-native-background-fetch

With yarn

$ yarn add react-native-background-fetch

With npm

$ npm install --save react-native-background-fetch

Setup Guides

iOS Setup

react-native >= 0.60

Android Setup

react-native >= 0.60

Example

ℹ️ This repo contains its own Example App. See /example

import React from 'react';
import {
  SafeAreaView,
  StyleSheet,
  ScrollView,
  View,
  Text,
  FlatList,
  StatusBar,
} from 'react-native';

import {
  Header,
  Colors
} from 'react-native/Libraries/NewAppScreen';

import BackgroundFetch from "react-native-background-fetch";

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      events: []
    };
  }

  componentDidMount() {
    // Initialize BackgroundFetch ONLY ONCE when component mounts.
    this.initBackgroundFetch();
  }

  async initBackgroundFetch() {
    // BackgroundFetch event handler.
    const onEvent = async (taskId) => {
      console.log('[BackgroundFetch] task: ', taskId);
      // Do your background work...
      await this.addEvent(taskId);
      // IMPORTANT:  You must signal to the OS that your task is complete.
      BackgroundFetch.finish(taskId);
    }

    // Timeout callback is executed when your Task has exceeded its allowed running-time.
    // You must stop what you're doing immediately BackgroundFetch.finish(taskId)
    const onTimeout = async (taskId) => {
      console.warn('[BackgroundFetch] TIMEOUT task: ', taskId);
      BackgroundFetch.finish(taskId);
    }

    // Initialize BackgroundFetch only once when component mounts.
    let status = await BackgroundFetch.configure({minimumFetchInterval: 15}, onEvent, onTimeout);

    console.log('[BackgroundFetch] configure status: ', status);
  }

  // Add a BackgroundFetch event to <FlatList>
  addEvent(taskId) {
    // Simulate a possibly long-running asynchronous task with a Promise.
    return new Promise((resolve, reject) => {
      this.setState(state => ({
        events: [...state.events, {
          taskId: taskId,
          timestamp: (new Date()).toString()
        }]
      }));
      resolve();
    });
  }

  render() {
    return (
      <>
        <StatusBar barStyle="dark-content" />
        <SafeAreaView>
          <ScrollView
            contentInsetAdjustmentBehavior="automatic"
            style={styles.scrollView}>
            <Header />

            <View style={styles.body}>
              <View style={styles.sectionContainer}>
                <Text style={styles.sectionTitle}>BackgroundFetch Demo</Text>
              </View>
            </View>
          </ScrollView>
          <View style={styles.sectionContainer}>
            <FlatList
              data={this.state.events}
              renderItem={({item}) => (<Text>[{item.taskId}]: {item.timestamp}</Text>)}
              keyExtractor={item => item.timestamp}
            />
          </View>
        </SafeAreaView>
      </>
    );
  }
}

const styles = StyleSheet.create({
  scrollView: {
    backgroundColor: Colors.lighter,
  },
  body: {
    backgroundColor: Colors.white,
  },
  sectionContainer: {
    marginTop: 32,
    paddingHorizontal: 24,
  },
  sectionTitle: {
    fontSize: 24,
    fontWeight: '600',
    color: Colors.black,
  },
  sectionDescription: {
    marginTop: 8,
    fontSize: 18,
    fontWeight: '400',
    color: Colors.dark,
  },
});

export default App;

Executing Custom Tasks

In addition to the default background-fetch task defined by BackgroundFetch.configure, you may also execute your own arbitrary "oneshot" or periodic tasks (iOS requires additional Setup Instructions). However, all events will be fired into the Callback provided to BackgroundFetch#configure:

⚠️ iOS:

  • scheduleTask on iOS seems only to run when the device is plugged into power.
  • scheduleTask on iOS are designed for low-priority tasks, such as purging cache files — they tend to be unreliable for mission-critical tasks. scheduleTask will never run as frequently as you want.
  • The default fetch event is much more reliable and fires far more often.
  • scheduleTask on iOS stop when the user terminates the app. There is no such thing as stopOnTerminate: false for iOS.
// Step 1:  Configure BackgroundFetch as usual.
let status = await BackgroundFetch.configure({
  minimumFetchInterval: 15
}, async (taskId) => {  // <-- Event callback
  // This is the fetch-event callback.
  console.log("[BackgroundFetch] taskId: ", taskId);

  // Use a switch statement to route task-handling.
  switch (taskId) {
    case 'com.foo.customtask':
      print("Received custom task");
      break;
    default:
      print("Default fetch task");
  }
  // Finish, providing received taskId.
  BackgroundFetch.finish(taskId);
}, async (taskId) => {  // <-- Task timeout callback
  // This task has exceeded its allowed running-time.
  // You must stop what you're doing and immediately .finish(taskId)
  BackgroundFetch.finish(taskId);
});

// Step 2:  Schedule a custom "oneshot" task "com.foo.customtask" to execute 5000ms from now.
BackgroundFetch.scheduleTask({
  taskId: "com.foo.customtask",
  forceAlarmManager: true,
  delay: 5000  // <-- milliseconds
});

API Documentation

Config

Common Options

@param {Integer} minimumFetchInterval [15]

The minimum interval in minutes to execute background fetch events. Defaults to 15 minutes. Note: Background-fetch events will never occur at a frequency higher than every 15 minutes. Apple uses a secret algorithm to adjust the frequency of fetch events, presumably based upon usage patterns of the app. Fetch events can occur less often than your configured minimumFetchInterval.

@param {Integer} delay (milliseconds)

ℹ️ Valid only for BackgroundFetch.scheduleTask. The minimum number of milliseconds in future that task should execute.

@param {Boolean} periodic [false]

ℹ️ Valid only for BackgroundFetch.scheduleTask. Defaults to false. Set true to execute the task repeatedly. When false, the task will execute just once.

Android Options

@config {Boolean} stopOnTerminate [true]

Set false to continue background-fetch events after user terminates the app. Default to true.

@config {Boolean} startOnBoot [false]

Set true to initiate background-fetch events when the device is rebooted. Defaults to false.

NOTE: startOnBoot requires stopOnTerminate: false.

@config {Boolean} forceAlarmManager [false]

By default, the plugin will use Android's JobScheduler when possible. The JobScheduler API prioritizes for battery-life, throttling task-execution based upon device usage and battery level.

Configuring forceAlarmManager: true will bypass JobScheduler to use Android's older AlarmManager API, resulting in more accurate task-execution at the cost of higher battery usage.

let status = await BackgroundFetch.configure({
  minimumFetchInterval: 15,
  forceAlarmManager: true
}, async (taskId) => {  // <-- Event callback
  console.log("[BackgroundFetch] taskId: ", taskId);
  BackgroundFetch.finish(taskId);
}, async (taskId) => {  // <-- Task timeout callback
  // This task has exceeded its allowed running-time.
  // You must stop what you're doing and immediately .finish(taskId)
  BackgroundFetch.finish(taskId);
});
.
.
.
// And with with #scheduleTask
BackgroundFetch.scheduleTask({
  taskId: 'com.foo.customtask',
  delay: 5000,       // milliseconds
  forceAlarmManager: true,
  periodic: false
});

@config {Boolean} enableHeadless [false]

Set true to enable React Native's Headless JS mechanism, for handling fetch events after app termination.

  • 📂 index.js (MUST BE IN index.js):
import BackgroundFetch from "react-native-background-fetch";

let MyHeadlessTask = async (event) => {
  // Get task id from event {}:
  let taskId = event.taskId;
  let isTimeout = event.timeout;  // <-- true when your background-time has expired.
  if (isTimeout) {
    // This task has exceeded its allowed running-time.
    // You must stop what you're doing immediately finish(taskId)
    console.log('[BackgroundFetch] Headless TIMEOUT:', taskId);
    BackgroundFetch.finish(taskId);
    return;
  }
  console.log('[BackgroundFetch HeadlessTask] start: ', taskId);

  // Perform an example HTTP request.
  // Important:  await asychronous tasks when using HeadlessJS.
  let response = await fetch('https://reactnative.dev/movies.json');
  let responseJson = await response.json();
  console.log('[BackgroundFetch HeadlessTask] response: ', responseJson);

  // Required:  Signal to native code that your task is complete.
  // If you don't do this, your app could be terminated and/or assigned
  // battery-blame for consuming too much time in background.
  BackgroundFetch.finish(taskId);
}

// Register your BackgroundFetch HeadlessTask
BackgroundFetch.registerHeadlessTask(MyHeadlessTask);

@config {integer} requiredNetworkType [BackgroundFetch.NETWORK_TYPE_NONE]

Set basic description of the kind of network your job requires.

If your job doesn't need a network connection, you don't need to use this option as the default value is BackgroundFetch.NETWORK_TYPE_NONE.

NetworkTypeDescription
BackgroundFetch.NETWORK_TYPE_NONEThis job doesn't care about network constraints, either any or none.
BackgroundFetch.NETWORK_TYPE_ANYThis job requires network connectivity.
BackgroundFetch.NETWORK_TYPE_CELLULARThis job requires network connectivity that is a cellular network.
BackgroundFetch.NETWORK_TYPE_UNMETEREDThis job requires network connectivity that is unmetered. Most WiFi networks are unmetered, as in "you can upload as much as you like".
BackgroundFetch.NETWORK_TYPE_NOT_ROAMINGThis job requires network connectivity that is not roaming (being outside the country of origin)

@config {Boolean} requiresBatteryNotLow [false]

Specify that to run this job, the device's battery level must not be low.

This defaults to false. If true, the job will only run when the battery level is not low, which is generally the point where the user is given a "low battery" warning.

@config {Boolean} requiresStorageNotLow [false]

Specify that to run this job, the device's available storage must not be low.

This defaults to false. If true, the job will only run when the device is not in a low storage state, which is generally the point where the user is given a "low storage" warning.

@config {Boolean} requiresCharging [false]

Specify that to run this job, the device must be charging (or be a non-battery-powered device connected to permanent power, such as Android TV devices). This defaults to false.

@config {Boolean} requiresDeviceIdle [false]

When set true, ensure that this job will not run if the device is in active use.

The default state is false: that is, the for the job to be runnable even when someone is interacting with the device.

This state is a loose definition provided by the system. In general, it means that the device is not currently being used interactively, and has not been in use for some time. As such, it is a good time to perform resource heavy jobs. Bear in mind that battery usage will still be attributed to your application, and shown to the user in battery stats.


Methods

Method NameArgumentsReturnsNotes
configure{FetchConfig}, callbackFn, timeoutFnPromise<BackgroundFetchStatus>Configures the plugin's callbackFn and timeoutFn. This callback will fire each time a background-fetch event occurs in addition to events from #scheduleTask. The timeoutFn will be called when the OS reports your task is nearing the end of its allowed background-time.
scheduleTask{TaskConfig}Promise<boolean>Executes a custom task. The task will be executed in the same Callback function provided to #configure.
statuscallbackFnPromise<BackgroundFetchStatus>Your callback will be executed with the current status (Integer) 0: Restricted, 1: Denied, 2: Available. These constants are defined as BackgroundFetch.STATUS_RESTRICTED, BackgroundFetch.STATUS_DENIED, BackgroundFetch.STATUS_AVAILABLE (NOTE: Android will always return STATUS_AVAILABLE)
finishString taskIdVoidYou MUST call this method in your callbackFn provided to #configure in order to signal to the OS that your task is complete. iOS provides only 30s of background-time for a fetch-event -- if you exceed this 30s, iOS will kill your app.
startnonePromise<BackgroundFetchStatus>Start the background-fetch API. Your callbackFn provided to #configure will be executed each time a background-fetch event occurs. NOTE the #configure method automatically calls #start. You do not have to call this method after you #configure the plugin
stop[taskId:String]Promise<boolean>Stop the background-fetch API and all #scheduleTask from firing events. Your callbackFn provided to #configure will no longer be executed. If you provide an optional taskId, only that #scheduleTask will be stopped.

Debugging

iOS

🆕 BGTaskScheduler API for iOS 13+

  • ⚠️ At the time of writing, the new task simulator does not yet work in Simulator; Only real devices.
  • See Apple docs Starting and Terminating Tasks During Development
  • After running your app in XCode, Click the [||] button to initiate a Breakpoint.
  • In the console (lldb), paste the following command (Note: use cursor up/down keys to cycle through previously run commands):
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.transistorsoft.fetch"]
  • Click the [ > ] button to continue. The task will execute and the Callback function provided to BackgroundFetch.configure will receive the event.

Simulating task-timeout events

  • Only the new BGTaskScheduler api supports simulated task-timeout events. To simulate a task-timeout, your fetchCallback must not call BackgroundFetch.finish(taskId):
let status = await BackgroundFetch.configure({
  minimumFetchInterval: 15
}, async (taskId) => {  // <-- Event callback.
  // This is the task callback.
  console.log("[BackgroundFetch] taskId", taskId);
  //BackgroundFetch.finish(taskId); // <-- Disable .finish(taskId) when simulating an iOS task timeout
}, async (taskId) => {  // <-- Event timeout callback
  // This task has exceeded its allowed running-time.
  // You must stop what you're doing and immediately .finish(taskId)
  print("[BackgroundFetch] TIMEOUT taskId:", taskId);
  BackgroundFetch.finish(taskId);
});
  • Now simulate an iOS task timeout as follows, in the same manner as simulating an event above:
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateExpirationForTaskWithIdentifier:@"com.transistorsoft.fetch"]

Old BackgroundFetch API

  • Simulate background fetch events in XCode using Debug->Simulate Background Fetch
  • iOS can take some hours or even days to start a consistently scheduling background-fetch events since iOS schedules fetch events based upon the user's patterns of activity. If Simulate Background Fetch works, your can be sure that everything is working fine. You just need to wait.

Android

  • Observe plugin logs in $ adb logcat:
$ adb logcat *:S ReactNative:V ReactNativeJS:V TSBackgroundFetch:V
  • Simulate a background-fetch event on a device (insert <your.application.id>) (only works for sdk 21+:
$ adb shell cmd jobscheduler run -f <your.application.id> 999
  • For devices with sdk <21, simulate a "Headless JS" event with (insert <your.application.id>)
$ adb shell am broadcast -a <your.application.id>.event.BACKGROUND_FETCH

Download Details:
Author: transistorsoft
Source Code: https://github.com/transistorsoft/react-native-background-fetch
License: MIT license

#react  #reactnative  #mobileapp  #javascript 

Configuration Handler for Deno with Support for .env and Scopes

Cfg

Cfg is a configuration handler for Deno with support for .env and scopes.

Run

Some features require access to the file system and the environment. Please run your project with the flags --allow-read, --allow-env and --unstable.

Import

Import the latest release.

// As named import
import { cfg } from "https://deno.land/x/cfg@v1.0.0-beta.3/mod.ts";

// Or as default import
import cfg from "https://deno.land/x/cfg@v1.0.0-beta.3/mod.ts";

Basic usage

This is the easiest way to use Cfg.

const config = cfg([{ foo: "bar" }]);

console.log(config.get("foo"))
// Or
console.log(config.getp("foo"))

A type parameter can also be used in order to benefit from intellisense.

const obj = { foo: "bar" };

const config = cfg<typeof obj>([obj]);

console.log(config.get("foo"));
// Or
console.log(config.getp("foo"))

Usage

cfg() function

cfg is an overloaded function. It has the following signature:

cfg<Config extends Configuration, Env extends ENVConfiguration = {}>(arguments): Cfg<Config, Env>

Call without argument

Calling cfg() without arguments is used to access a configuration which has already been initiated on the default scope

Call with a scope

Calling cfg(scope) is used to access a configuration which has already been initiated on the specified scope

Call with a Loadable

Calling cfg([Loadable]) is used to create a configuration on the default scope. An array of Loadable is passed as argument. Loadable is the type that describes anything that can loaded to the configuration.

Call with a scope and a Loadable

Calling cfg(scope, [Loadable]) is used to create a configuration on the specified scope.

Call with an options object

Calling cfg(options) allow to specify additional options

Persistance & scopes

All configurations are stored by Cfg and can be accessed at any time by calling cfg(scope). There are a few things to keep in mind:

  • When no scope is specified in the options, a default scope is used.
  • Scopes are shared between modules. It’s therefore best for each module to have its own scope and leave the default scope for the final user.
  • Only one configuration per scope can be created. Once it’s there, it can’t be deleted

Options

The Opts interface describes the option object that is used to configure cfg().

interface Opts {
  env?: boolean | string | EnvOpt;
  load?: Loadable | Loadable[];
  scope?: string;
}

The Opts.scope property defines the scope. The Opts.load property is used to specify one or several Loadable which will be merged to a Configuration object. The Opts.env property is used to configure the environment variables. When Opts.env is true, Cfg will look for an .env file in the root directory. Opts.env can also be a string that represents the path to the .env file. More options can be configured by using an EnvOpt object.

interface EnvOpt {
  export?: boolean;
  import?: ENVConfiguration;
  merge?: boolean;
  path?: string;
}

When true, EnvOpt.export exports the environment variables to Deno.env. It will never overwrite a value that already exist. With EnvOpt.import, an object of type ENVConfiguration can be imported. This can be useful if, for example, one needs to use another parser for .env files. When true, EnvOpt.merge will merge the environment variables with the configuration. Finally, EnvOpt.path is a string that that represents the path to the .env file.

Methods

Cfg.get(…props: string[])

This method returns the configuration. Parameters can be used to return a specific property. When cfg() is called with a type parameter, intellisense checks that the paramters are correct and knows the type of the value returned.

Cfg.get() allows up to 4 parameters.

Cfg.getp(path: string)

This method returns the configuration just like Cfg.get(). But it accepts a path as parameter with the syntax a.b.c or a[b][c]. This method will always return a value with type unknown.

Cfg.getEnv(prop: string)

Cfg.getEnv() is similar to Cfg.get() except that it accepts only one parameter and returns environment variables.

Types

Configuration

The interface Configuration defines the configuration object.

interface Configuration {
  [key: string]: (string | number | boolean | Configuration) | (string | number | boolean | Configuration)[];
}

ENVConfiguration

The interface ENVConfiguration defines the .env object

interface ENVConfiguration {
  [key: string]: string | number | boolean;
}

Loadable

The type Loadable defines the sources of configuration that will be merged to a Configuration object.

type Loadable = string | Configuration | ConfigFunction;

ConfigFunction

The type ConfigFunction defines a function that returns a Configuration object.

type ConfigFunction<T extends ENVConfiguration = {}> = (env: T) => Configuration;

Contributions

PRs are welcome!

Download Details:

Author: ymonb1291

Source Code: https://github.com/ymonb1291/cfg

#deno #nodejs #node #javascript

Rahim Makhani

Rahim Makhani

1622519505

Maintain your Magento app for a smoother and faster user Experience

Magento is the best open-source eCommerce platform for website development. We all know the importance of a website in every sector; whether it is about business or anything else, it can represent the look and feel of your company. So to maintain these websites, it is essential to do Magento support and maintenance so that you can also maintain your website.

Nevina Infotech is the best Magento app development company that can guide you to maintain and support your Magento app. We have a dedicated team of developers who will help you with the maintenance and support of your app.

#magento maintenance and support services #magento support services #magento support and maintenance #magento support #magento maintenance support #magento technical support

Rahim Makhani

Rahim Makhani

1626851156

Get the latest Features in your current eCommerce App

Are you tired of outdated features in your eCommerce app and do you want some latest and trending features in your eCommerce app? Then you need to update your app regularly to stay updated with the latest and trending features to add to your app. For updating it you need to update to the latest version of the Magento so that whatever update occurs in the latest version can also occur in your eCommerce app.

You can hire Nevina Infotech that can help you to provide Magento support and maintenance and will also guide you to update your eCommerce app for adding the latest features to your app. Our team is great and enthusiastic about their work. You can easily rely on us to get the latest features in your eCommerce app.

#magento maintenance and support services #magento support services #magento support and maintenance #magento support #magento maintenance support #magento technical support

Rahim Makhani

Rahim Makhani

1618806778

Get the best Magento maintenance service for your e-commerce store

Magento is the most demanded technology for developing online eCommerce stores and Magento service is important for better working of the online store. Nevina Infotech provides you with Magento maintenance and services.

Magento maintenance is the process of checking the performance of the website and checking the regular updates, backups, online store management, and much more.

You can choose Nevina Infotech for Magento maintenance service for your eCommerce store. We provide the best Magento support and maintenance for eCommerce stores.

#magento maintenance and support services #magento support services #magento support and maintenance #magento support #magento maintenance support #magento technical support