John David

John David

1559701654

Create a File Storage Mobile App with NativeScript 5


SimpleFileTransfer is a virtual file locker. Users can sign up for the service and get 100 MB of free virtual storage space. Users can then download and upload files on a server. Users will be able to increase their storage space by filling a form.

Let’s jot down the App functionalities before moving ahead:

  • Signup: User can signup for the app.
  • Login: Authenticates user.
  • Details Page: Provides user details like current quota and total space. Also, we can display a list of files.
  • Download file: Download file from the server to a device.
  • Upload file: Upload files from a device to the server.
  • Increase Quota: Increases storage quota of a user by a specified amount.

You can find the whole code on GitHub.


Structuring the Backend

The backend must provide the functionalities of managing routes, providing basic authentication and storage, and facilitating file transfers.

Based on the requirements above, we will be using the following stack:

We will also be using libraries like multer and bcrypt for specific functionalities that will be explained later on.


Initializing the Backend Project

We will be using express-generator to set up the project. Install express-generator globally using:

npm install express-generator -g  

Start a new project using the command:

express file-server

Navigate to the file-server directory and install the dependencies using npm install. Also, install the following dependencies:

npm install multer async sequelize sqlite3 body-parser bcrypt --save

Additionally, we will be creating some extra folders for:

  • Database: Storing SQLite DB & DB script.
  • Model: Storing Models.
  • Upload: Temporarily storing uploaded files.
  • Storage: storing file for specific users.

Starting with Sequelize

Sequelize is an ORM middleware for SQLite, MySQL, PostgreSQL and MSSQL. For small projects, it’s convenient to use the Sequelize + SQLite combo.

In our current scenario, we require only one Model. We will define our model user as follows:

   const User = sequelize.define(‘user’, {
uid: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
username: { type: Sequelize.STRING, unique: true },
password: Sequelize.STRING,
quota: {type: Sequelize.INTEGER, defaultValue: 104857600},
createdAt: Sequelize.DATE,
updatedAt: Sequelize.DATE,
})

We can use Sequelize’s Model.sync to initialize Models Table in a database. To initialize users table, we will be using the code below.

     User.sync({force: true}).then(() => {
// Table created
});

We will store the user model in the user.js file in the model folder.


Signup & Login

This part is pretty straightforward. For signup, the server accepts a username and password and stores it in the database. We will be using the bcrypt library to salt the passwords. As shown below, we are salting the password 10 times before storing it in the database. We are using Sequelize’s Model.create to store the value. Once a user is created, we will create a directory on our server for his uploads.

The code is as below:

     router.post(‘/’, function(req, res, next) {
console.log(req);
bcrypt.hash(req.body.password, 10, function(err, hash) {
User
.create({ username: req.body.username, password: hash })
.then(user => {
if (!fs.existsSync(‘storage/’+user.get(‘uid’))){
fs.mkdirSync(‘storage/’+user.get(‘uid’));
}
res.send({status: true, msg: ‘User created’, uid: user.get(‘uid’)});
}).catch(err => {
res.send({status: false, msg: err });
})
});
});

For login, the server accepts a username and password and validates it against the database. We are using Model.findAll to get the database record. We use bcrypt.compare to compare passwords.

   router.post(‘/’, function(req, res, next) {
console.log(req);
User.findAll({
attributes: [“username”, “password”],
where: {
username: req.body.username
}
}).then(dbQ => {
if(dbQ.length > 0) {
bcrypt.compare(req.body.password, dbQ[0].dataValues.password, function(err, result) {
if (result == true){
res.send({status: true, msg: ‘Login Success’});
} else {
res.send({status: false, msg: ‘Incorrect Password’});
}
});
} else {
res.send({status: false, msg: ‘User not found’});
}
});
});

Defining the Users Route

An authenticated user is allowed to perform the following functions:

  • Upload file
  • Download file
  • Get Details
  • Increase quota

Let’s define the routes for those functions:

  • Upload: POST /users/:id/upload
  • Download: GET /users/:id/download/:filename
  • Details: GET /users/:id/details
  • Increase Quota: POST /users/:id/increasequota

Uploading a File to the Server

We will be using multer to handle uploads.

The multer library is useful to handle multi-part form data. Initially, we will be uploading the file to the uploads folder. Then, the file will be moved to /storage/uid folder where uid is user id.

   var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, ‘uploads/’)
},
filename: function (req, file, cb) {
cb(null, file.originalname )
}
});

router.post(‘/:id/upload’, upload.single(‘fileparam’), function(req, res, next) {
if (!req.file) {
console.log(“No file received”);
return res.send({
success: false,
msg: “Error Uploading files”
});
} else {
console.log(‘file received’);
fs.rename(‘uploads/’+ req.file.originalname, ‘storage/’+req.params.id+‘/’+req.file.originalname, function (err) {
if (err) {
console.log(err);
return;
}
return res.send({
success: true,
msg: “File Uploaded”
})
});
}
});

The upload.single method is used for handling uploads. This route is expecting a file with name fileparam in the URL call. This is quickly done by adding a name attribute an HTML form. We will need the name attribute app side.


Download Route

ExpressJS provides us with a function to set the download route, conveniently called download.

This is the logic we are following:

  • A user logs into the app.
  • He selects a file and initiates the download.
  • The server receives a request with userid and filename.
  • The server sends the file back to the user.

The code for the route is below

    router.get(‘/:id/download/:filename’, function(req, res, next) {
const file = ‘storage/’+req.params.id + ‘/’ + req.params.filename;
res.download(file);
});

Increase User Quota Route

We will be invoking Model.update to adjust the quota. By default, we have 104857600 bytes — which is equivalent to 100 MB — assigned to each user. You can find the query below.

   router.post(‘/:id/increasequota’, function(req, res, next) {
User.update({
quota: req.body.quota,
}, {
where: {
uid: req.params.id
}
}).then(response => {
res.send({status: true, msg: “Quota Increased”});
}).catch(err => {
res.send({status: false, msg: err});
});
});

User Details Route

This is a route which we will be using for fetching multiple data, such as:

  • Storage Limit of user: from the DB,
  • Current File Space occupied: from the /storage/userid directory,
  • Space Remaining: it’s just Point 1 - Point 2,
  • File List: list of Files,

We can fetch the storage limit of a user using Model.findAll. For fetching file names and storage space, we are using fs .readdir, fs.stat and async.

   function getStorageSpace(relpath) {
let space = 0;
let fileNames = [];
let promise = new Promise(function (resolve, reject) {
fs.readdir(relpath, function (err, items) {
if (err){
reject(err);
}
fileNames = items;
let filesArr = items.map(function (val) {
return relpath + ‘/’ + val;
});
async.map(filesArr, fs.stat, function (err, results) {

       for (let i = 0; i < results.length; i++) {
         if (err) {
           reject(err);
         }
         space = space + results[i].size;
       }
       resolve({fileNames: fileNames, space: space });
     });
   });
 });
 return promise;

}

function getStorageLimit(uid){
let promise = new Promise(function (resolve, reject) {
User.findAll({
attributes: [“quota”],
where: {
uid: uid
}
}).then(dbQ => {

     if(dbQ.length < 1) {
       reject("Quota Not Found")
     } else {
       resolve(dbQ[0].dataValues.quota);
     }     
   }).catch(err => {
     reject(err);
   });
 });
 return promise; 

}

router.get(‘/:id/details’, function(req, res, next) {
let it;
let relpath = ‘storage/’+req.params.id;
Promise.all([getStorageSpace(relpath), getStorageLimit(req.params.id)]).then(result => {

   res.send({storageLimit: result[1], occupiedSpace: result[0].space, fileNames: result[0].fileNames, remainingSpace: result[1]- result[0].space});
 })

});

N.B.: The code works on assumption that user is not allowed to create a subdirectory in his folder.

The code for enforcing storage limit will be discussed later in the article.


NativeScript App

For the app side, we will be taking an alternative approach. A demo project based on Angular-Blank template will be shared with users. A significant part of this article will cover details about plugins concerning the plugin functionalities.


Consuming Web Services

We are consuming data from simple web services for Login / Signup / User Details Page.

As mentioned in the previous article, we can access these webservices using the HttpClient Module. The basic steps are as follows:

  • Import NativeScriptHttpClientModule in the PageModule.
  • Import HttpClient and HttpHeaders in Component or Provider.
  • Consume the URL as you will in an Angular application.
  • We will set Content-Type header to application/json.

For the JavaScript/TypeScript templates, we can use the NativeScript Core http module. The http. getJson function provides the required framework to consume webservices. Alternatively, we can also use the fetch module.

As a response from the server, we will be receiving the uid of a user. After authentication, we need to store the uid so we can allow a mobile user to access /users/uid route.


Storing Data

The NativeScript framework doesn’t have any method to store data persistently. We can add that functionality using plugins. We are going to look at two of these plugins.

  • nativescript-sqlite: This plugin provides an interface for the SQLite library. This works well if your app needs to store a large volume of records. Install it with:
tns plugin add nativescript-sqlite

  • nativescipt-localstorage: This plugins provides a key value API for string data, similar to window.localstorage. This works well if your app doesn’t have lot of records. Install it with:
tns plugin add nativescript-localstorage

The demo app will be using nativescript-localstorage.


Uploading Files from a Device to a Server

Let’s break this functionality into subtasks:

  1. Choose Files from the device.
  2. Get File Path.
  3. Upload File over uploads WebService.

To choose a file and get a file path, we will be using the nativescript-mediapicker plugin. The plugin has multiple modes and we can customize it for specific use cases. You can check the plugin documentation here.

To select a file, first, we need to define extensions. This is different for both OSes.

For Android devices, we have to use file extensions based on mime types like let extensions = [“xlsx”, “xls”, “doc”, “docx”, “ppt”, “pptx”, “pdf”, “txt”, “png”]

For iOS devices, we have to define extensions from list for Unified Type identifiers: let extensions = [kUTTypePDF, kUTTypeText];

You can read more about UTIs here and here.

The code for invoking filepicker is as below:

   let options: FilePickerOptions = {
android: {
extensions: extensions,
maxNumberFiles: 1
},
ios: {
extensions: extensions,
multipleSelection: false
}
};

let mediafilepicker = new Mediafilepicker();
mediafilepicker.openFilePicker(options);

`mediafilepicker.on(“getFiles”, function (res) {
let results = res.object.get(‘results’);
console.dir(results);
});

mediafilepicker.on(“error”, function (res) {
let msg = res.object.get(‘msg’);
console.log(msg);
});

mediafilepicker.on(“cancel”, function (res) {
let msg = res.object.get(‘msg’);
console.log(msg);
});`

As above, we will receive the filepath of a file in the getFiles event.

We will send the file to the server using the nativescript-background-http plugin. You can read about the plugin here.

Earlier, we defined the /users/:id/upload route. As mentioned earlier, our server is expecting the file in the attribute named fileparam.

The background http provides us with two functions: uploadFile and multipartUpload. Since we need to set the name attribute, we will be using the multiPartUpload function.

    let session = bgHttp.session(“image-upload”);
let request: bgHttp.Request = {
url: Config.apiUrl + ‘/users/’ + localStorage.getItem(‘uid’) + ‘/upload’ ,
method: “POST”,
headers: {
“Content-Type”: “multipart/form-data”
},
description: ‘FileName’
};
let params = [{
name: ‘file’,
filename: path
}];
let task: bgHttp.Task = session.multipartUpload(params, request);
task.on(“error”, (e) => {
reject(e);
});
task.on(“complete”, (e) => {
resolve(e);
});

Downloading a File to the Device

We will be using the core file-system, platform and utils modules to achieve the result. Both Android and iOS handle downloads differently. We will be using isAndroid and isIOS variables from platform module to segregate the code.

The file-system module provides us with a knownFolders sub-module. Three predefined folders for both Android and iOS are available:

  • knownFolders.currentApp()
  • knownFolders.documents()
  • knownFolders.temp()

Additionally, an iOS sub-module provides us with some other predefined folders. E.g:

  • knownFolders.ios.download
  • knownFolders.ios.sharedPublic

iOS Code

On an iOS scenario, this is straightforward:

  • Show a list of server files.
  • Download the files to the documents folder.
  • List downloaded files in a separate view
  • Use the utils.openFile function to open the file.

To download the files, we will be using the http module of the NativeScript framework. The getFile function can be used to fetch files from the server and save them to a specific file location. The snippet for iOS is below:

      let filePath: string = path.join(knownFolders.documents().path, fileName);
getFile(download_url + fileName, filePath).then((resultFile) => {
// The returned result will be File object
}, (e) => {
console.log(e);

Once the file has been downloaded, we can use the openFile function from the utils module to open a file on iOS.


Android Code

The Android side of coding is a bit trickier. The locations of the knownFolders module are as below.

  • currentFolder: /data/data/:appid/files/app
  • documents: /data/user/:androiduser/:appid/files
  • temp: /data/user/:androiduser/:appid/cache

As you can see, all the folders are located in /data. /data is inaccessible to normal users. Furthermore, external apps won’t be able to access the files in those folders. Also, there is no openFile function for Android.

As of now, the best we can do is:

  • Show a list of server files.
  • Download a file to a user accessible location.
  • List the files present in the location.

To implement the functionality, we will be using a bit of native code.

Before moving ahead, we will have to install tns-platform-declarations with:

npm i tns-platform-declarations --save

Create a reference.d.ts file in the root folder and add the following lines:

/// <reference path="./node_modules/tns-platform-declarations/ios.d.ts" />
/// <reference path="./node_modules/tns-platform-declarations/android.d.ts" />

You can check the readme for more details.

Android OS provides us with a function to access the external storage.

We will be using the constant DIRECTORY_DOWNLOADS and the function getExternalStoragePublicDirectory to create a publicly accessible download location.

We will append a path “SimpleFileTransfer” to create a custom folderPath and filePath.

   const androidDownloadsPath = android.os.Environment.getExternalStoragePublicDirectory(android.os.Environment.DIRECTORY_DOWNLOADS).toString();
const androidFolderPath = fs.path.join(androidDownloadsPath, “SimpleFileTransfer”);
const filePath: string = fs.path.join(androidFolderPath, fileName);
getFile(download_url + fileName, filePath).then((resultFile) => {
// The returned result will be File object
}, (e) => {
console.log(e);

If you check your file explorer, a new directory will be created in the Downloads folder called SimpleFileTransfer. You will find all the files downloaded there.


Listing Downloaded Files

We will be using the file-system module. The Folder class of the file-system module has a getEntities function which allows us to list files in a folder. As with fs.readdir in Node.js, we can only list the files.

For iOS, the path is

const  folderPath:  string  =  fs.knownFolders.documents().path;

For Android, the path is

const androidDownloadsPath  =  android.os.Environment.getExternalStoragePublicDirectory(android.os.Environment.DIRECTORY_DOWNLOADS).toString();

const folderPath= fs.path.join(androidDownloadsPath, "SimpleFileTransfer");

To access the Folder functions, we define a folder using

let  internalFolder  =  fs.Folder.fromPath(folderPath);

Then, we use getEntities to get a list of files:

   internalFolder.getEntities()
.then((entities) => {
// entities is array with the document’s files and folders.

               entities.forEach((entity) => {
               let  fileSize  =  fs.File.fromPath(entity.path).size;
                   this.listArray.push({
                       name: entity.name,
                       path: entity.path,
                       lastModified: entity.lastModified.toString(),
                       size : fileSize
                   });
               });                  
           }).catch((err) => {
               // Failed to obtain folder's contents.
               console.log(err.stack);
           });

Additionally, we have used the size property of File class to get file size.


Enforcing Storage Limit

The storage limit can be enforced in two ways:

  • Upload file to the server --> Checking remaining space --> Reject the upload on the server side.
  • Check remaining space using webservice --> Check file size --> Cancel the upload on the app side.

To enforce the former, we can modify the upload route as below:

   Promise.all([getStorageSpace(relpath), getStorageLimit(req.params.id)]).then(result => {
if (result[1] - result[0].space > req.file.size){
fs.rename(‘uploads/’+ req.file.originalname, ‘storage/’+req.params.id+‘/’+req.file.originalname, function (err) {
if (err) {
return res.send({
success: false,
msg: “Error Uploading files”
});
}
return res.send({
success: true,
msg: “File Uploaded”
})
});
} else {
return res.send({
success: false,
msg: “Storage Limit Exceeded”
});
}
})

To enforce the latter, we get the file size of the file selected by the mediafilepicker plugin and check it against the space remaining using the details webservice.

let  fileSize  =  fs.File.fromPath(results[0].file).size;

if(fileSize < remainingSpace){
// Send To server

}`else {
// alert user about lack of space
}

Closing Thoughts

This demo covers quite a few different concepts.

We divided the solution in a series of functionalities. We used core NativeScript for UX, interacting with the backend, file system management, and Routing. We extended the framework by installing plugins for functionalities like picking files. Going further, we used a bit of native code for solving a specific problem.

Using NativeScript allowed us to develop the app faster for both platforms as against individually.

Learn More

Building a mobile chat app with Nest.js and Ionic 4

Android Studio for beginners

Learn React - React Crash Course 2019 - React Tutorial with Examples

React Hooks Tutorial for Beginners: Getting Started With React Hooks

Learn React.js for Beginners

Build a Basic CRUD App with Laravel and React

Build a Simple CRUD App with Python, Flask, and React

The Complete React Web Developer Course (2nd Edition)

Node with React: Fullstack Web Development

Beginner Full Stack Web Development: HTML, CSS, React & Node

React JS and Redux - Mastering Web Apps

React 16 - The Complete Guide (incl. React Router 4 & Redux)

Originally published by Karan Gandhi at https://blog.jscrambler.com

#react-native #mobile-apps #ios #android

What is GEEK

Buddha Community

Create a File Storage Mobile App with NativeScript 5
Easter  Deckow

Easter Deckow

1655630160

PyTumblr: A Python Tumblr API v2 Client

PyTumblr

Installation

Install via pip:

$ pip install pytumblr

Install from source:

$ git clone https://github.com/tumblr/pytumblr.git
$ cd pytumblr
$ python setup.py install

Usage

Create a client

A pytumblr.TumblrRestClient is the object you'll make all of your calls to the Tumblr API through. Creating one is this easy:

client = pytumblr.TumblrRestClient(
    '<consumer_key>',
    '<consumer_secret>',
    '<oauth_token>',
    '<oauth_secret>',
)

client.info() # Grabs the current user information

Two easy ways to get your credentials to are:

  1. The built-in interactive_console.py tool (if you already have a consumer key & secret)
  2. The Tumblr API console at https://api.tumblr.com/console
  3. Get sample login code at https://api.tumblr.com/console/calls/user/info

Supported Methods

User Methods

client.info() # get information about the authenticating user
client.dashboard() # get the dashboard for the authenticating user
client.likes() # get the likes for the authenticating user
client.following() # get the blogs followed by the authenticating user

client.follow('codingjester.tumblr.com') # follow a blog
client.unfollow('codingjester.tumblr.com') # unfollow a blog

client.like(id, reblogkey) # like a post
client.unlike(id, reblogkey) # unlike a post

Blog Methods

client.blog_info(blogName) # get information about a blog
client.posts(blogName, **params) # get posts for a blog
client.avatar(blogName) # get the avatar for a blog
client.blog_likes(blogName) # get the likes on a blog
client.followers(blogName) # get the followers of a blog
client.blog_following(blogName) # get the publicly exposed blogs that [blogName] follows
client.queue(blogName) # get the queue for a given blog
client.submission(blogName) # get the submissions for a given blog

Post Methods

Creating posts

PyTumblr lets you create all of the various types that Tumblr supports. When using these types there are a few defaults that are able to be used with any post type.

The default supported types are described below.

  • state - a string, the state of the post. Supported types are published, draft, queue, private
  • tags - a list, a list of strings that you want tagged on the post. eg: ["testing", "magic", "1"]
  • tweet - a string, the string of the customized tweet you want. eg: "Man I love my mega awesome post!"
  • date - a string, the customized GMT that you want
  • format - a string, the format that your post is in. Support types are html or markdown
  • slug - a string, the slug for the url of the post you want

We'll show examples throughout of these default examples while showcasing all the specific post types.

Creating a photo post

Creating a photo post supports a bunch of different options plus the described default options * caption - a string, the user supplied caption * link - a string, the "click-through" url for the photo * source - a string, the url for the photo you want to use (use this or the data parameter) * data - a list or string, a list of filepaths or a single file path for multipart file upload

#Creates a photo post using a source URL
client.create_photo(blogName, state="published", tags=["testing", "ok"],
                    source="https://68.media.tumblr.com/b965fbb2e501610a29d80ffb6fb3e1ad/tumblr_n55vdeTse11rn1906o1_500.jpg")

#Creates a photo post using a local filepath
client.create_photo(blogName, state="queue", tags=["testing", "ok"],
                    tweet="Woah this is an incredible sweet post [URL]",
                    data="/Users/johnb/path/to/my/image.jpg")

#Creates a photoset post using several local filepaths
client.create_photo(blogName, state="draft", tags=["jb is cool"], format="markdown",
                    data=["/Users/johnb/path/to/my/image.jpg", "/Users/johnb/Pictures/kittens.jpg"],
                    caption="## Mega sweet kittens")

Creating a text post

Creating a text post supports the same options as default and just a two other parameters * title - a string, the optional title for the post. Supports markdown or html * body - a string, the body of the of the post. Supports markdown or html

#Creating a text post
client.create_text(blogName, state="published", slug="testing-text-posts", title="Testing", body="testing1 2 3 4")

Creating a quote post

Creating a quote post supports the same options as default and two other parameter * quote - a string, the full text of the qote. Supports markdown or html * source - a string, the cited source. HTML supported

#Creating a quote post
client.create_quote(blogName, state="queue", quote="I am the Walrus", source="Ringo")

Creating a link post

  • title - a string, the title of post that you want. Supports HTML entities.
  • url - a string, the url that you want to create a link post for.
  • description - a string, the desciption of the link that you have
#Create a link post
client.create_link(blogName, title="I like to search things, you should too.", url="https://duckduckgo.com",
                   description="Search is pretty cool when a duck does it.")

Creating a chat post

Creating a chat post supports the same options as default and two other parameters * title - a string, the title of the chat post * conversation - a string, the text of the conversation/chat, with diablog labels (no html)

#Create a chat post
chat = """John: Testing can be fun!
Renee: Testing is tedious and so are you.
John: Aw.
"""
client.create_chat(blogName, title="Renee just doesn't understand.", conversation=chat, tags=["renee", "testing"])

Creating an audio post

Creating an audio post allows for all default options and a has 3 other parameters. The only thing to keep in mind while dealing with audio posts is to make sure that you use the external_url parameter or data. You cannot use both at the same time. * caption - a string, the caption for your post * external_url - a string, the url of the site that hosts the audio file * data - a string, the filepath of the audio file you want to upload to Tumblr

#Creating an audio file
client.create_audio(blogName, caption="Rock out.", data="/Users/johnb/Music/my/new/sweet/album.mp3")

#lets use soundcloud!
client.create_audio(blogName, caption="Mega rock out.", external_url="https://soundcloud.com/skrillex/sets/recess")

Creating a video post

Creating a video post allows for all default options and has three other options. Like the other post types, it has some restrictions. You cannot use the embed and data parameters at the same time. * caption - a string, the caption for your post * embed - a string, the HTML embed code for the video * data - a string, the path of the file you want to upload

#Creating an upload from YouTube
client.create_video(blogName, caption="Jon Snow. Mega ridiculous sword.",
                    embed="http://www.youtube.com/watch?v=40pUYLacrj4")

#Creating a video post from local file
client.create_video(blogName, caption="testing", data="/Users/johnb/testing/ok/blah.mov")

Editing a post

Updating a post requires you knowing what type a post you're updating. You'll be able to supply to the post any of the options given above for updates.

client.edit_post(blogName, id=post_id, type="text", title="Updated")
client.edit_post(blogName, id=post_id, type="photo", data="/Users/johnb/mega/awesome.jpg")

Reblogging a Post

Reblogging a post just requires knowing the post id and the reblog key, which is supplied in the JSON of any post object.

client.reblog(blogName, id=125356, reblog_key="reblog_key")

Deleting a post

Deleting just requires that you own the post and have the post id

client.delete_post(blogName, 123456) # Deletes your post :(

A note on tags: When passing tags, as params, please pass them as a list (not a comma-separated string):

client.create_text(blogName, tags=['hello', 'world'], ...)

Getting notes for a post

In order to get the notes for a post, you need to have the post id and the blog that it is on.

data = client.notes(blogName, id='123456')

The results include a timestamp you can use to make future calls.

data = client.notes(blogName, id='123456', before_timestamp=data["_links"]["next"]["query_params"]["before_timestamp"])

Tagged Methods

# get posts with a given tag
client.tagged(tag, **params)

Using the interactive console

This client comes with a nice interactive console to run you through the OAuth process, grab your tokens (and store them for future use).

You'll need pyyaml installed to run it, but then it's just:

$ python interactive-console.py

and away you go! Tokens are stored in ~/.tumblr and are also shared by other Tumblr API clients like the Ruby client.

Running tests

The tests (and coverage reports) are run with nose, like this:

python setup.py test

Author: tumblr
Source Code: https://github.com/tumblr/pytumblr
License: Apache-2.0 license

#python #api 

Autumn  Blick

Autumn Blick

1594770710

How To Succeed In Mobile App Wireframe Design?

In the world of overrated terms “web development”, a mobile app wireframe design is one of the most underrated terms. The design of wireframes is considered when people look for the bigger picture.

While designing the UI-UX, people forget the simple norm of general to specific shifting. As the complexity increases and so does the approach become more difficult, this is where the designing of the wireframes comes in handy.

Before diving into the “How to”, let’s first see why we need them in the first place.

What are mobile app wireframes?

Wireframes are the skeletal layouts of an application or a website that is being designed. The specificity comes into play, the elements and the features have to be placed at specific locations. Take a building, in the process of making it, first the foundation is laid and then pieces are fitted together from the skeleton structure on a piece of paper, wireframes do the same for the website or application structure such as a smart home application.

The designing of wireframes is commonly known as wireframing. For the construction of a building, the framework or the skeletal structure is important while designing a web application or mobile application, wireframing is important to make it user-friendly. This entirely and solely works to make the journey smooth and destination easy to reach.

As for the building, the layers of cementing and painting is done later to increase the visual appeal, the visual contents and appealing stuff are added after wireframing. The simpler it sounds after the definition, the complex it gets when it is being done.

It is a very goal-oriented procedure, one has to keep in mind is the goal of the product or the destination of the service. The main focus should be on UX. The arrangement of the elements and their interaction with each other and with the user is the utmost important task in mobile app wireframing.

What not to do while designing the mobile app wireframe?

  • Do not even think of skipping the process.
  • Do not beautify (visually appealing designs added first) and then get into the wireframing business).
  • Do not do it just for the sake of doing it.

One has to keep in mind that skipping this entirely can lead to the failure of the entire process of web and mobile app development at the end.

Again taking the example of the construction of a building, the foundation must be laid first based on the skeletal framework that has been prepared, then only you can jump to beautify your building, as a designer one has to understand and follow the steps where designing the mobile app wireframe comes first and then the visually appealing content is added next not the other way round.

For the most part, people do not understand the importance and come up with some trashy design of wireframes and the main foundation becomes faulty, hence the entire designing at later stages becomes faulty. If one wants to skip the reworking part, mobile app wireframing must never be ignored.

#android app #ios app #minimum viable product (mvp) #mobile app development #app designing #mobile app wireframe designing #mobile app wireframing #mobile application wireframing #mobile wireframing #web app wireframing #wireframe designing

Top 5 Mobile App Development Companies in New York

New York is the best potential business hub for the technology and IT industry. Thousands of companies are established in New York for the mobile app development or technology industry. So, here quite a confusion is that how to choose the right company for your business amongst all the companies. No need to worry about it, We have found the most reliable and trustworthy mobile app development companies that are the top-tiers in New York. Before we share the companies list you need to know the benefits of mobile app development for your business.

Key Benefits of Mobile App Development:

· Improves Efficiency

· Offers High Scalability

· Secures Your App Data

· Integrates With Existing Software

· Easy to Maintain

· Improves Customer Relationship

· Facilitates New Client Data Retrieval

· Provides Real-time Project Access

Are you looking for top mobile app development companies in New York that help to create a custom mobile app as per your business requirements? Please go through these Top 5 mobile app development companies that provide exceptional development and design services for your business.

Top Mobile App Development Companies in New York:

1. AppCluesInfotech

AppClues Infotech is one of the leading mobile app development company based in New York that builds high-quality mobile apps. Being a versatile mobile app development company, they provide services on various platforms like Android, iOS, Cross-platform, Windows, etc. They are aware of the latest technologies and features of industry. They utilize them to create a user-engaging mobile app. They have the best team of designers and developers who are delivered a mobile app with high-quality and performance.

Founded In: 2014

Employees: 50 to 200 Members

Location: USA

Website: https://www.appcluesinfotech.com/

2. Data EximIT

Data EximIT is one of the leading mobile app development company in New York that provides top-notch and elegant mobile app development services with the latest market trends and functionalities at a competitive price. They have highly experienced mobile app designers and developers team who have the best strength of developing all types of a mobile app. They deliver a feature-rich mobile app for their clients that give the best business ROI.

Founded In: 2004

Employees: 50 to 150 Members

Location: USA & India

Website: https://www.dataeximit.com/

3. WebClues Infotech

WebClues Infotech is the most reliable & superior company that builds custom mobile apps to do things differently. They are the best mobile app development company in New York, USA and as well as globally with high proficiency. They have a highly experienced app developers team that has the best strength of work on multiple platforms like android, Cross-platform, and iOS.

They have successfully delivered 950+ mobile app projects effectively and on-time. Build your Robust, Secure, Scalable & High-performance mobile app with WebClues Infotech with creative & dynamic designs at an affordable price.

Founded In: 2014

Employees: 50 to 250 Members

Location: USA, INDIA, UAE, UK & CANADA

Website: https://www.webcluesinfotech.com/

4. AppClues Studio

AppClues Studio is a leading mobile app development company in New York, USA. The company is versatile in developing custom mobile app development solutions to its customers across the globe. With an experience of 8+ years in mobile app development, Utility is hyper-focused on Return on Investment (ROI) and building good relationships with partner companies. The company is worked with a start-up to large enterprises.

Founded In: 2014

Employees: 50 to 150 Members

Location: USA & UK

Website: https://appcluesstudio.com/

5. WebClues Global

WebClues Global is a prominent mobile application development company in New York, USA. They are one of the top-tier mobile app developers who deliver high-end Android mobile app solutions to their clients. The company operated with 100+ qualified developers who working in different domains to give the best solution for their development. WebClues Global offers various services including web and mobile design & development, E-Commerce Development, Ui/Ux Development.

Founded In: 2014

Employees: 50 to 150 Members

Location: USA, INDIA, UAE, UK & CANADA

Website: https://www.webcluesglobal.com/

#top 5 mobile app development companies in new york #top 5 mobile app development companies in usa #top mobile app development companies in new york #top mobile app development companies in usa #top 5 mobile app development companies

Jones Brianna

Jones Brianna

1614154249

List Of The Top Pittsburgh Mobile App Development Companies

https://clutch.co/app-developers/pittsburgh
Let’s look at the list of top list of the top Pittsburgh mobile app development companies which are known for providing top-notch services globally. They are great developers who provide quality services for all your needs.

#mobile app developers #mobile app development services #mobile app development #mobile app developers #mobile apps #mobile app development solutions

Jones Brianna

Jones Brianna

1608183156

Top 10 Mobile App Development Companies in India

https://yourstory.com/mystory/top-10-mobile-app-development-companies-in-india-djq13xfgd8

Here’s a rundown of Top 10 mobile app development companies in India, carefully evaluated on the various performance indicators based on proven track record and diversified portfolio, quality of end-product, experience, core technical expertise, project management strategy, adherence to timelines and budget, app prototyping and UI/UX design.

#mobile app development #mobile app development company #mobile app development services #mobile app developers #mobile app development solutions #mobile apps