Last updated: September 29th, 2020

Welcome to XServer

XServer is JSON-based BaaS (backend-as-a-service) for your iOS, Android and/or Web applications. It's a valid alternative to Firebase or Parse Server, since you can manage any kind of data and send Push Notifications, and it's 5x faster than those backend services.
XServer is not dependent on MySQL or phpMyAdmin, not even Node.js.

In order to connect your apps to the backend, download the SDKs, include them in your projects and keep reading this guide.

The video below covers the basics of how the XServer database works, you can also keep reading this guide to learn it.

Requirements

Check you the requirements you need to have to properly work with XServer and its SDKs.

For iOS apps

  • The latest official version of Xcode – Beta versions of an IDE usually never work properly. The code of the iOS SDK is written in Swift.
  • An Apple Mac computer, updated to its latest OS version.
  • An Apple Developer Account with an active iOS Development Program – This is needed for you to publish apps on the iTunes App Store and also test your applications on a real device
  • Knowledge of Xcode and Swift programming
  • A real iOS device to test your apps - the Simulator is not reliable as a real device

For Android apps

  • The latest official version of Android Studio – Beta versions of an IDE usually never work properly. The code of the Android SDK is written in Java.
  • An Apple Mac or Windows computer, updated to its latest OS version.
  • A Google Play Developer Account, in order to publish apps on the Play Store.
  • Knowledge of Android Studio and Java/XML programming.
  • A real Android device to test your apps - the Emulator is not reliable as a real device

For Websites

  • A domain and a VPS server to upload and store your website's files
  • SSL certficate installed an active on your server
  • Sublime Text (recommended), or any other HTML/text editor of your choice
  • An FTP account confgured in FileZilla, needed to upload/edit files
  • Chrome Web Browser (recommended), and/or Safari or Firefox

Account

XServer offers a Free account subscription where you get 10 MB of storage and 1 database. That's enough to test your applications with some demo data and fully manipulate your database through a UI that looks similar to the Excel's one, easy to use and distraction-free.

Sign Up

Signing up on XServer requires a valid email address (which will be your username), and a password of your choice.
After signing up, a verification email will be sent to the email address you've set as username. Click on that link to confirm your account and access your Dashboard.

Please note

In case you will sign up with a fake email address, you will not be able to complete your registration and access your Dashboard.
Remember to check your Inbox and your Junk/Spam folder as well.
In case you registered with an existing email address but got no verification email from us after 5 minutes, then click the Support button and drop us a message.


Once you verified your account, you will be able to sign in and enter your Dashboard, or Logout.

Pricing

XServer offers 3 Subscription Plans and the Self-hosted option.
Click the Pricing button to check out all options:
  • Basic plan: 1 GB storage, unlimited databases (the number of databases you can create always depends by the available storage)
  • Standard plan: 3 GB storage, unlimited databases (the number of databases you can create always depends by the available storage)
  • Pro plan: 5 GB storage, unlimited databases (the number of databases you can create always depends by the available storage)
  • Self-hosted: Buy the XServer's source code, host it on your own VPS server and take full-control over it. (This option is a one-time fee, not a subscription)

Please note

If you already subscribed to a Plan, your old Plan will not be cancelled.
In case you need to cancel a Plan, click the Support button and email us your request.

Dashboard

The Dashboard is the page where you can create/enter your Database(s), as well as edit their Settings.
You can also change your own username or password, and Logout.

Create a Database

Once you are into your Dashboard, click the Create Database button and type a name for your new database. Please note that no spaces or special characters are allowed, so you can type a name like:
MyDatabase
The following example names are not allowed:
My Database  // not allowed
MyDatabase@! // not allowed

When the Database is created, the page redirects to the Users table.
If you want to get back to your Dashboard, just click the ( Dshboard) button.

Database Settings

Click the Settings button to view the available options:
  • Change Database Name
    Spaces and special characters are not allowed when registering a new Database name.
  • Copy Database Path
    This option will copy the URL of your Database to the clipboard, so you can paste it in the iOS, Android and/or Web XServerSDK files.
  • Delete Database
    Deleting a Database will remove all data and uploaded files, freeing your available Storage space
  • Google Sign in Web Key
    In order to sing in with Google in a Login page of a Web application (a website) that is using XServer as backend, you must provide your own Goolgle OAuth key.
    Create it on your own Google Cloud Platform and paste it in the field of this option, so users of your Web application will be allowed to sign in with Google.
  • iOS Push Notifications
    You must upload a AuthKey.p8 file, set the Bundle Identifier of your iOS app and your Apple developer Team ID in order to make your app send/receive Push Notifications.
  • Android Push Notifications
    Create a Firebase project on the Firebase Console (unless you already have one), enter the Project Overview -> Cloud Messaging section, copy the Server key from the Project credentials box and paste your key in the field of this option. In this way your Android app will send/receive Push Notifications.
  • Email Verification on Sign Up
    If you enable this option, new users who registered in your Mobile or Web application will get an email with a verification link.
  • Set/Change Admin Email
    Your apps may need to send emails to the Admin, either for support, info requests, new orders, etc.
    Register the email address you want to get users messages.
  • View Configurations
    In this section you can check your Database settings.


User Settings

Click the dropdown button that shows your username to select one of these options:
  • Edit username
    After changing your username, you will be logged out and need to sign in again using your new username. Please also note that it must be a valid email address.
  • Change password
    After changing your password, you will be logged out and need to sign in again using your new password.
  • Logout
    After logging out you'll be rediredted to the Home page.

Database

From your Dashboard, click the Database button to enter the selected Database.

The look of the UI is similar to Microsoft Excel. The Tables are listed on the left side, as well as the section where you can send Push Notification to all registered users.
The navigation bar shows your Database name and the utility buttons.
The central part of the page is where you see your data.
The Users table gets created when you create a new Database, and it cannot be deleted.

Add a Table

Click the Options button and select Add Table to create a new Table.

Give it a name and click Add table. A new Table will be created and you'll see it on the left-side menu.

Like you did on naming a Database, you must type a Table name with no spaces or special characters!

Import Tables

You can import JSON files as Tables, although their syntax must be compatible with the XServer's syntax, so you may import tables you have previously exported (maybe to make a backup of you data), or Tables provided by an app template you've bought on a marketplace (whic of course is compatible with XServer).
Click the Import Tables button, choose one or more JSON files and let the system load them for you.

Please note that Tables with the same name of the choosen ones will be overwitten.



Export Tables

Click the Export a Table button, choose a Table from the dropdown menu and click the Download Table button. The browser will ask you to save a download.zip file, accept it. Once it's downloaded, unzip it and get the JSON file of your Table, you can save it in a safe place to make a backup of your data.

Rename a Table

Click the Rename a Table button, select the table you want to rename (please note that the Users table won't be shown because it cannot be renamed), type a new name and click the Rename Table button.

Again, you must type a Table name with no spaces or special characters!

Delete a Table

Click the Delete Table button, select the table you want to delete (please note that the Users table won't be shown because it cannot be deleted), and click the Delete Table button.

Add Row

Click the Add Row button and fill the fields you wish, then click the Save button.

Add Column

Click the Add Column button, select the type of your column from the dropdown menu, type your column name (no spaces or special characters allowed), and click the Add Column button.

Please note

When you add a column, XServer adds a custom prefix to its name based on the Type you chose (for instance, if you chose the String type, the prefix will be ST_).
So, when you are about to configure your mobile or web application, you need to use the full column name, which is made by its prefix, an underscore (_) and its name.
Some examples: ST_myString, NU_myNumber, PO_userPointer_Users, etc...

Column Types

There are 9 types of columns for your data, it's important to know their syntax and meaning, so you'll be able to wisely choose them while creating new columns for your database.

  • ID | composed by ID_ prefix and id suffix
    This is the object Identifier of each row of the Table. It cannot be created manually, it gets automatically created by the API and its name is always ID_id
  • String | composed by ST_ prefix | the suffix will be the name you assigned to it.
    This is a String to host and display text.
  • Number | composed by NU_ prefix | the suffix will be the name you assigned to it
    It supports integer and decimal numbers. PLEASE NOTE that decimal numbers must have the dot (.) as separator, not the comma (,).
    Example: 123.45
  • Array | composed by AR_ prefix | the suffix will be the name you assigned to it
    This type is usually used to store arrays of Strings like object IDs or other text.
    It's comma-separated, so be careful while storing an array, put commas where they are needed and do not use the array [""] brackets, just type values separated by commas in the array's input field.
    Example: item1,item2,item3
  • File | composed by FL_ prefix | the suffix will be the name you assigned to it
    This type hosts the URL of an uploaded file. Physical files get stored inside the uploads folder
  • Boolean | composed by BL_ prefix | the suffix will be the name you assigned to it
    It's either true or false, as per standard JSON syntax.
    Just keep in mind that if you use an input text field instead of a Checkbox or Switch button, the values you must pass are either "0" (True) or "1" (False)
  • GPS | composed by GPS_ prefix | the suffix will be the name you assigned to it
    This type is an array of 2 decimal numbers, the latitude and longitude coordinates, usually used for location purposes.
    Like the Number data type, the decimal separator of the coordinates must be a dot (.), and the separator of the 2 coordinates must be a comma (,)
    Example: 12.236548,-45.845987
  • Pointer | composed by PO_ prefix | the suffix will be the name you assigned to it | the Table name where it points to
    This type hosts the ID of an object taken from another Table. For instance, a user's ID from the Users table.
    If you click it in the Database, it shows the selected object's row
  • Date | composed by DT_ prefix | the suffix will be the name you assigned to it
    The Date format is YYYY-MM-DDTHH:mm:ss and it's a String
It's important to first plan what tables and columns you need to display and edit in your app or website, then create the necessary Tables and columns in your Database.

Rename Column

Click the Rename Column button, select the column you want to rename from the dropdown menu, type your new column name (no spaces or special characters allowed), and click the Rename Column button.

Delete Column

Click the Delete Column button, select the column you want to delete from the dropdown menu and click the Delete Column button.

Query

Click the Query button, select a column and a condition from the dropdown menus, type value you want to query and click the Apply Filters button.

Order By

Click the Order By button, select a column and a sorting condition from the dropdown menus, then click the Order Data button.

Push Notifications

You can send Push Notifications to all those users who registered i your database and allowed Push Notifications on their devices.
Click the Send Push Notification button on the left-side menu, select an audience (All, iOS or Android users), type your message and click the Send Push Notification button.

In order to be able to send Pushes, you must perform the iOS and Android configurations mentioned in the Database Settings paragraph.

iOS SDK

This section covers the foundamentals of the iOS SDK functions to interact with your database on XServer.
Download the SDK from GitHub as a ZIP file.

Download iOS SDK

Installation

Follow these simple steps to integrate the iOS XServer SDK in your existing Xcoder project:

1. Unzip the downloaded iOS SDK and drag the XServerSDK.swift and SwiftyJSON.swift files into your project.



2. Enter the XServerSDK.swift file and paste your database path in the following variable:

let DATABASE_PATH = "PASTE_YOUR_DATABASDE_PATH"



3. You're ready to go!

Please note that you must not edit the rest of the code in the XServerSDK.swift file, unless you're an experienced developer, otherwise the SDK will not work properly.

Get Current User XSCurrentUser

You can get the Current User data by calling the XSCurrentUser() function.
Here's an example:

XSCurrentUser { (currentUser) in
	if currentUser != nil  {
		// User is logged in
		print(currentUser!["ST_username"].string!)
	} else {
		// User is logged out -> do your stuff
	}
}

Sign In XSSignIn

The Sign In function can be called in a Login screen with a couple of TextFields: username and password.

XSSignIn(username: "myusername", password: "mypassword") { (succ, e) in
	if e == nil {
	print("Signed in!")
		// Do stuff...

	// Wrong username/password
	} else { print(e!) }
}

Sign Up XSSignUp

The Sign Up function can be called in a SignUp screen with 3 TextFields: username, password and email.

XSSignUp(username: "myusername", password: "mypassword", email: "myemail", signInWith: "") { (userID, e) in
	if e == nil {
		let params = [
			self.param(columnName: "tableName", value: "Users"),
			self.param(columnName: "ID_id", value: userID!),

         // (Optional) Additional data
         // self.param(columnName: "ST_fullname", value: "myFullname"), 
      ]

      self.XSObject(params) { (succ, e) in
      	if e == nil {
      		print("Signed up!")
      		// Do stuff...

      	// error
      	} else { print(e!)
      }} // ./ XSObject

   // error
	} else { print(e!) }
}
As you can see in the code above, you're allowed to register additional data in the Users table by adding extra parameters. Just remember to first add their relative column(s) in the Database.

Reset Password XSResetPassword

Users have the ability to also request the app to reset their password, in case they forgot it (or just need to change it).

XSResetPassword(email: "myemail@address.com") { (result, e) in
	if e == nil { 
		print(result!)
	} else { print(e!) }
}

The result string you'll get will be "Thanks, an email with a link to reset your password has been sent. Check your Inbox in a little while.".
If you want to change such message, display it like this:
XSResetPassword(email: "myemail@address.com") { (result, e) in
	if e == nil { 
		print("Your new alert message here!")
	} else { print(e!) }
}

Logout XSLogout

Users can logout from the app by calling the XSLogout function.

XSLogout { (succ) in
	if succ! {
		print("Logged out!")
		// Do stuff...
    
  	} else { print("Something went wrong. Try again.") }
} 

Get Pointer object XSGetPointer

In order to retrieve data of a Pointer object use the XSGetPointer function and pass the object[columnName].string! and tableName parameters.

XSGetPointer(myObj["PO_userPointer_Users"].string!, tableName: "Users") { (userPointer) in
	print(userPointer!["ST_username"].string!)
} 

Refresh object data XSRefreshObjectData

In order to get updated data of an object, call the XSRefreshObjectData function and pass the tableName and object parameters.

XSRefreshObjectData(tableName: "Posts", object: myObj) { (myObj, e) in
	if e == nil {
		// Do stuff with the updated 'myObj'...

	// error
   } else { print(e!) }
}  

Delete object XSDelete

In order to delete an object, call the XSDelete function and pass the tableName and object["ID_id"].string! parameters.

XSDelete(tableName: "Posts", id: myObj["ID_id"].string!) { (succ, e) in
	if e == nil {
		print("Object successfully deleted!")
	} else { print(e!) }
}

Query objects XSQuery

In order to query objects you have to call the XSQuery function and pass it the tableName, columnName and orderBy parameters.
Then, inside a for loop, based on the length of an array of objects, set an if statement to filter your query by the desired columns and values.

In this example, the query goes by keywords from a UISearchBar:

XSQuery(tableName: "Posts", columnName: "DT_createdAt", orderBy: "") { (objects, e) in
	if e == nil {
		// For
		for i in 0..< objects!.count {
			let pObj = objects![i]

			// [Search for keywords typed in a UISearchBar]
			let keywords = self.searchBar.text!.components(separatedBy: " ")
			for j in 0..< keywords.count{
				if pObj["ST_text"].string!.lowercased().contains(keywords[j].lowercased())
				{ self.objectsArray.append(pObj) }
			} // ./ For 
        
    		// [Finalize array of objects]
    		if i == objects!.count-1 { self.objectsArray.XSRemoveDuplicatesFromArray() }
   	} // ./ For

   	if self.objectsArray.count != 0 {
   		print(objectsArray)

   	} else { print("No results!") }
   
  	// error on query
  	} else { print(e!) }
}

Here instead we launch a query based on some filter:
XSQuery(tableName: "Posts", columnName: "DT_createdAt", orderBy: "") { (objects, e) in
	if e == nil {
		// For
		for i in 0..< objects!.count {
			let pObj = objects![i]

			pObj["ST_category"].string! == "Fun"
        	|| pObj["NU_likes"].int! == 123
        	{ self.objectsArray.append(pObj) }
        
    		// [Finalize array of objects]
    		if i == objects!.count-1 { self.objectsArray.XSRemoveDuplicatesFromArray() }
   	} // ./ For

   	if self.objectsArray.count != 0 {
   		print(objectsArray)

   	} else { print("No results!") }
   
  	// error on query
  	} else { print(e!) }
}
In both the above queries, inside the for loop, you need to call the XSRemoveDuplicatesFromArray function to remove duplicates in case your query finds the same objects more than once.

Add/Edit an object XSObject

In order to save an Object you need to call the XSObject function and pass it the necessary parameters.

let params = [
  param(columnName: "tableName", value: "Posts"),
   param(columnName: "ID_id", value: myObj["ID_id"].string!), // Used to update an object
   param(columnName: "ST_text", value: "Lorem ipsum dolor sit"),
   param(columnName: "NU_number", value: "123"),  // A number must be formatted as a String
   param(columnName: "FL_file", value: "https://example.com/myimage.jpg"),
   param(columnName: "DT_date", value: XSGetStringFromDate(myDate)), // Used to save a Date()
   param(columnName: "AR_array", value: XSGetStringFromArray(myStringArray)), // Used to save an Array[String]
   param(columnName: "GPS_coords", value: XSGetStringFromLocation(myLocation)), // used to save a CLLocation()
   param(columnName: "PO_userPointer_Users", value: currentUser["ID_id"].string!),
]
XSObject(params) { (e, obj) in
	if e == nil {
		print("Object Updated!")
   
   // error
   } else { print(e!) }
}
IMPORTANT:
  1. You may also use an alternative way to create an array of parameters:
    var params = [String]()
    params.append(param(columnName: "ID_id", value: myObj["ID_id"].string!))
    params.append(param(columnName: "ST_text", value: "Lorem ipsum dolor sit"))
    ...
    
  2. In order to update an object, add its ID in the parameters array:
     param(columnName: "ID_id", value: myObj["ID_id"].string!)
    
  3. In order to save a File, make sure to get its URL with the XSUploadFile function:
    XSUploadFile(fileData: myImageData!, fileName: "image.jpg") { (fileURL, e) in
    	if e == nil {
    		print(fileURL!)
    		// Add parameter to save the File object
    		param(columnName: "FL_file", value: fileURL!)
    		...
        
      	// error
      	} else { print(e!) }
    }
    
  4. In order to save a GPS value (CLLocation object with latitude and longitude coordinates), you must convert it into a String with the XSGetStringFromLocation function:
    param(columnName: "GPS_coords", value: XSGetStringFromLocation(myLocation))
    
  5. In order to save a Pointer object, you must get its ID:
    param(columnName: "PO_userPointer_Users", value: currentUser["ID_id"].string!)
    
  6. In order to save an Array value, you must convert it into a String with the XSGetStringFromArray function:
    var myArray = ["lorem","ipsum","dolor"]
    param(columnName: "AR_array", value: XSGetStringFromArray(myArray)
    
  7. In order to save a Number, either Double or Integer, you must convert it into a String:
    param(columnName: "NU_intNumber", value: "\(123)")
    param(columnName: "NU_doubleNumber", value: "\(12.345)")
    
  8. In order to save a Boolean value, you must convert it into a String and use either 0 or 1:
    var myBool = false
    var boolStr = ""
    if myBool { boolStr = "1" } else { boolStr = "0" }  // "0" is True -- "1" is False
    param(columnName: "BL_boolean", value: boolStr)
    
  9. In order to save a Date value, you must convert it into a String with the XSGetStringFromDate function:
    var myDate = Date()
    param(columnName: "DT_date", value: XSGetStringFromDate(myDate))
    
  10. In order to save a String value, just pass it as a String:
     param(columnName: "ST_text", value: "Lorem ipsum dolor sit")
    

Send a Push Notification XSSendiOSPush

You can send Push Notifications through the app to iOS and Android devices by calling the XSSendiOSPush and XSSendAndroidPush functions and passing the message, deviceToken and pushType parameters.
The pushType parameter can also be empty, like "", that's needed just in case you need to pass an extra parameter to your Push Notification, the Push alert will not show that parameter in its message.

// Send iOS Push notification
XSSendiOSPush(message: "Hi there, this is a Push!", deviceToken: userPointer!["ST_iosDeviceToken"].string!, pushType: "chat") { (succ, e) in
	if e == nil {
		print("iOS PUSH SENT!")
	}
}

// Send Android Push notification
XSSendAndroidPush(message: "Hi there, this is a Push!", deviceToken: userPointer!["ST_androidDeviceToken"].string!, pushType: "chat") { (succ, e) in
	if e == nil {
		print("ANDROID PUSH SENT!")
	}
}
In order to be able to send/receive Push Notifications on your device, you must configure it in your Xcode project.
Start by pasting this code into the didFinishLaunchingWithOptions() function of the AppDelegate.swift file:
let notifTypes:UIUserNotificationType  = [.alert, .badge, .sound]
let settings = UIUserNotificationSettings(types: notifTypes, categories: nil)
DispatchQueue.main.async(execute: {
	application.registerUserNotificationSettings(settings)
   application.registerForRemoteNotifications()
   application.applicationIconBadgeNumber = 0
})
This will register your device for Push Notifications.

Still inside the AppDelegate.swift, add some more delegates:
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
	IOS_DEVICE_TOKEN = deviceToken.map { String(format: "%02x", $0) }.joined()
	print("\nIOS DEVICE TOKEN -> AppDelegate: \(IOS_DEVICE_TOKEN)\n")
}

func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
	print("application:didFailToRegisterForRemoteNotificationsWithError: %@", error)
}

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
	let notif = JSON(userInfo)
   print("Push Body: \(notif["aps"]["alert"]["body"])")
   print("Push Type: \(notif["aps"]["alert"]["pushType"])")
}

func applicationDidBecomeActive(_ application: UIApplication) {
	// Reset app icon's badge number
	application.applicationIconBadgeNumber = 0
}	

Android SDK

This section covers the foundamentals of the Android SDK functions to interact with your database on XServer.
Download the SDK from GihHub as a ZIP file.

Download Android SDK

Installation

Follow these simple steps to integrate the Android XServer SDK in your existing Xcoder project:

1. Unzip the downloaded Android SDK, copy the xserver folder and paste it into the app/src/main/java folder.

2. Now you have to add a library to allow the SDK to send HTTP requests to the server. Its name is async-http-client-2.1.2.jar, so copy such file from the libs folder and paste it into the app/libs folder of your project.

3. Open the XServerSDK.java and _FCMService.java files, expand the import tag and replace
: com.myname.myapp with your own project's package name (leave .R at the end of the name), and don't worry for the red errors in the code, they'll get fixed later.

4. Open the build.gradle (Project:YourApp) file and add this dependency:

classpath 'com.google.gms:google-services:4.3.3'
Make sure to get the latest version of the Google Services Gradle Plugin at https://developers.google.com/

5. Open the build.gradle (Module:app) file and add the following repositories and dependencies:

repositories {
	google()
   mavenCentral()
   jcenter()
   maven { url 'https://jitpack.io' }
}

dependencies {
	// Load JAR libraries from the 'libs' folder
	implementation fileTree(dir: "libs", include: ["*.jar"])
	
	...

   // CircleImageView
   implementation 'de.hdodenhof:circleimageview:3.1.0'

   // ActionSheet
   implementation 'com.github.mkhoiron:Actionsheet-android:0.1'

   // JSON parser
   implementation 'com.github.amirdew:JSON:v1.0.0'

   // Glide (to load images into ImageViews)
   implementation 'com.github.bumptech.glide:glide:4.11.0'
   annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'

   // DateTime Picker
   implementation 'com.github.Kunzisoft:Android-SwitchDateTimePicker:2.0'


   // Google & Firebase
   implementation 'com.google.android.gms:play-services-auth:18.0.0'
   implementation 'com.google.android.gms:play-services-location:17.0.0'
   implementation 'com.google.firebase:firebase-core:17.4.3'
   implementation 'com.google.firebase:firebase-messaging:20.2.1'
}
apply plugin: 'com.google.gms.google-services'

After that, you may need to Sync your project, so click the Sync Now link and wait for Gradle to sync:


6. Since you've added apply plugin: 'com.google.gms.google-services', which is needed for Firebase Push Notifications, you have to download the google-services.json file from your Firebase Project settings and copy it into the app folder of your project.


In case you don't use Firebase FCM in your app, you may remove this line: apply plugin: 'com.google.gms.google-services'. Also, don't download the google-services.json and don't put it into the app folder.

7. Open the Manifest.xml file and paste the following permissions:

<!-- Permissions -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.camera" />
<uses-permission android:name="android.permission.CAMERA" />

<!-- Push Notifications -->
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />	

8. Then paste this code inside the <application> tag:

android:usesCleartextTraffic="true"
android:requestLegacyExternalStorage="true"
tools:ignore="AllowBackup,GoogleAppIndexingWarning,UnusedAttribute"

9. If you use Firebase FCM in your app, add this piece of code before the <application> closure:

<!-- FCM Service -->
<service
	android:name="xserver.app._FCMService"
   android:stopWithTask="false"
   tools:ignore="ExportedService">
<intent-filter>
	<action android:name="com.google.firebase.MESSAGING_EVENT" />
   </intent-filter>
</service>

10. Open the XServerSDK.java file and paste your own database path, then you'll be ready to go:

public static String  DATABASE_PATH = "YOUR_DATABASE_PATH";

11. You're ready to go!

Please note that you must not edit the rest of the code in the XServerSDK.java file, unless you're an experienced developer, otherwise the SDK will not work properly.

Current User XSCurrentUser

You can get the Current User data by calling the XSCurrentUser() function.
XSCurrentUser(this, new XSCurrentUserHandler() { @Override public void done(final JSON currentUser) {
   if (currentUser != null) {
   	// Do stuff with currentUser

   } else { 
   	Log.i("xs", "Current User is not logged in");
   }
}});

Sign In XSSignIn

The Sign In function can be called in a Login screen with a couple of EditText widgets: username and password.
XSSignIn((Activity)ctx, "myusername", "mypassword", new XSSignInHandler() {
	@Override
	public void done(boolean success, String e) {
		if (e == null) {
			Log.i("xs", "Signed in!");
      	
    	// error
    	} else { Log.i("xs", e); }
}});

Sign Up XSSignUP

The Sign Up function can be called in a SignUp screen with 4 EditText widgets: username, password, email and fullname (this last one is optional, but we'll keep it for this example).
XSSignUp((Activity) ctx, "myusename", "mypassword", "myemail", "", new XSSignUpHandler() {
	@Override public void done(String result, String e) {
	if (e == null) {
		RequestParams params = new RequestParams();
      params.put("tableName", "Users"); // <- Mandatory
      params.put("ID_id", result); // <- Mandatory
      
      // Additional data (full name)
      params.put("ST_fullname", fullnameTxt.getText().toString());
      
      XSObject((Activity)ctx, params, new XSObjectHandler() { @Override public void done(boolean success, String e) {
      	if (e == null) {
      		Log.i("xs", "Signed up!");
   		  
   		// error
   	   } else { Log.i("xs", e);
   	}}});
    
   // error
   } else { Log.i("xs", e); }
}});
As you may notice from the code above, it calls 2 functions: the XSSignUp one to store the default User's data, such as username, password and email, then the XSObject function. This updates the new User with additional data - in this case, it's the fullname string.
So, in case you have additional data to store in the Users table while signing up, you must add them as parameteres inside the XSSignUp function, as shown above, keeping in mind the the "tableName" and the "ID_id" parameters must be included.

Reset Password XSResetPassword

Users have the ability to also request the app to reset their password, in case they forgot it (or just need to change it).
XSResetPassword((Activity)ctx, "myemail@address.com", new ResetPasswordHandler() {
	@Override
   public void done(String result, String error) {
     	if (error == null) {
     		Log.i("xs", result);
     	} else {
     		Log.i("xs", error);
     	}
}});
The result string you'll get will be "Thanks, an email with a link to reset your password has been sent. Check your Inbox in a little while.".
If you want to change such message, just set your own message string:
XSResetPassword((Activity)ctx, "myemail@address.com", new ResetPasswordHandler() {
	@Override
   public void done(String result, String error) {
     	if (error == null) {
     		Log.i("xs", "My new message here");
     	}
}});

Logout XSLogout

Users can logout from the app by calling the XSLogout function.
XSLogout(new LogoutHandler() { @Override public void done(boolean success) {
	if (success) {
		Log.i("xs", "Logged out!");
	} else {
		Log.i("xs", Something went wrong. Try again");
	}
}});

Get Pointer object XSGetPointer

If you've set a column of type Pointer in your Database and you need to retrieve the data of its object, use the XSGetPointer function and pass the object.key(columnName).stringValue() and tableName parameters.
XSGetPointer((Activity) ctx, myObj.key("PO_userPointer_Users").stringValue(), "Users", new XSPointerHandler() {
	@SuppressLint("SetTextI18n")
	@Override
	public void done(final JSON userPointer, final String e) {
		if (e == null) {
			// Do stuff with this 'userPointer' object

    	// error
    	} else { log.i("xs", e);
}}});

Refresh object data XSRefreshObjectData

In order to get updated data of an object, call the XSRefreshObjectData function and pass the tableName and Object parameters.
XSRefreshObjectData((Activity)ctx, "Posts", myObj, new XSRefreshObjectDataHandler() {
	@Override
  	public void done(JSON myObj, String error) {
  		if (error == null){
  			// Use 'myObj' as you wish...

    	// error
    	} else { hideHUD(); simpleAlert(error, ctx); }
}});

Delete an object XSDelete

You can delete an object by calling the XSDelete function and passing the tableName and object.key("ID_id").stringValue() parameters.
XSDelete((Activity)ctx, "Posts", pObj.key("ID_id").stringValue(), new XSDeleteHandler() {
	@Override
	public void done(boolean success, String e) {
		if (e == null) {
			Log.i("xs", "Object successfully deleted!");

		// error
     	} else { Log.i("xs", e);
}}});

Query objects XSQuery

You can query objects by calling the XSQuery function and passing it the tableName, columnName and orderBy (optional) parameters.
Then, inside a for loop based on the length of an array of JSON objects, set an if statement to filter your query by the desired columns and values.

In this example, the query goes by keywords from an EditText:
XSQuery((Activity)ctx, "Posts", "DT_createdAt", "descending", new XSQueryHandler() {
	@Override public void done(JSON objects, String error) {
	if (error == null) {
		for (int i = 0; i < objects.count(); i++) {
			JSON pObj = objects.index(i);

			// [Search for keywords typed in an EditText]
			String[] keywords = myEditText.toLowerCase().split(" ");
			if (searchText.length() != 0) {
				for (String keyword : keywords) {
					objectsArray.add(pObj);
         	}
        	}

        	// [Finalize array of objects]
        	if (i == objects.count()-1) { objectsArray = XSRemoveDuplicatesFromArray(objectsArray); }
      }// ./ For

      if (objectsArray.size() != 0) {
      	Log.i("xs", objectsArray);
      // No results
      } else { Log.i("xs", "No results!"); }

   // error
   } else { Log.i("xs", error); }
}});

Here instead we launch a query based on some filter:
XSQuery((Activity)ctx, "Posts", "DT_createdAt", "", new XSQueryHandler() {
	@Override public void done(JSON objects, String error) {
	if (error == null) {
		for (int i = 0; i < objects.count(); i++) {
			JSON pObj = objects.index(i);

			if (
				pObj.key("ST_category").stringValue().matches("Fun")
          	|| pObj.key("NU_likes").intValue() == 789789
        	){ objectsArray.add(pObj); }

        	// [Finalize array of objects]
        	if (i == objects.count()-1) { objectsArray = XSRemoveDuplicatesFromArray(objectsArray); }
      }// ./ For

      if (objectsArray.size() != 0) {
      	Log.i("xs", objectsArray);
      // No results
      } else { Log.i("xs", "No results!"); }

   // error
   } else { Log.i("xs", error); }
}});
In both the above queries, inside the for loop, you need to call the XSRemoveDuplicatesFromArray function to remove duplicates in case your query finds the same objects more than once.

Add/Edit an object XSObject

In order to save a new Object you need to call the XSObject function and pass it the necessary parameters.
// Parameters
RequestParams params = new RequestParams();
params.put("tableName", "Posts");
params.put("ID_id", myObj.key("ID_id").stringValue()); // Used to update an object | remove this line if you're saving a NEW object!
params.put("ST_text", "Lorem ipsum dolor sit");
params.put("NU_number", "123.45"); // A number must be formatted as a String
params.put("FL_file", "https://example.com/myimage.jpg");
params.put("DT_date",  XSGetStringFromDate(selectedDate)); // Used to save a Date()
params.put("AR_array", XSGetStringFromArray(myArray)); // Used to save a ListArray()
params.put("GPS_coords", XSGetStringFromLocation(myLocation));// Used to save a Location()
params.put("PO_userPointer_Users", currentUser.key("ID_id").stringValue());

XSObject((Activity)ctx, params, new XSObjectHandler() { @Override public void done(String e, JSON obj) {
	if (e == null) {
		Log.i("xs", "Object saved!");

   // error
   } else { Log.i("xs", e);
}}});
IMPORTANT:

In order to UPDATE an object, add its ID in the parameters array.
params.put("ID_id", myObj.key("ID_id").stringValue());

In order to save a File, make sure to get its URL with the XSUploadFile function.
XSUploadFile(filePath,"image.jpg", (Activity)ctx, new XSFileHandler() {
    @Override public void done(String fileURL, String e) {
      if (fileURL != null) {
        // Add parameter to save the File object
        params.put("FL_file", fileURL);
        ...

      // Upload failed
      } else { Log.i("xs", e); }
}});

In order to save a GPS value (Location with latitude and longitude coordinates), you must use the XSGetStringFromLocation() function to convert the Location object into a String.
params.put("GPS_coords", XSGetStringFromLocation(myLocation));

In order to save a Pointer object, you must get its ID.
params.put("PO_userPointer_Users", currentUser.key("ID_id").stringValue());

In order to save an Array value, you must convert it into a String with the XSGetStringFromArray function.
List<String> myArray = new ArrayList<>();
myArray.add("asD4e5Fo9");
params.put("AR_array", XSGetStringFromArray(myArray));

In order to save a Number, you must convert it into a String.
params.put("NU_number", String.valueOf(1234));
// OR
params.put("NU_number", String.valueOf(45.678));

In order to save a Boolean value, you must convert it into a String and set it either as "0" or "1".
String bool = "1"; // "0" is False, "1" is True
params.put("BL_bool", bool);

In order to save a Date value, you must convert it into a String with the XSGetStringFromDate function.
Date date = new Date();
params.put("DT_date",  XSGetStringFromDate(date));

In order to save a String value, just pass it as a String.
params.put("ST_text", "Lorem ipsum dolor sit");

Send a Push Notification

You can send Push Notifications through the app to iOS and Android devices by calling the XSSendiOSPush and XSSendAndroidPush functions and passing the message, deviceToken and pushType parameters.
The pushType value can also be empty, like "", that's needed just in case you need to pass an extra parameter to your Push Notification.
// Send Android Push Notification
XSSendAndroidPush((Activity)ctx, pushMessage, userPointer.key("ST_androidDeviceToken").stringValue(), "chat", new XSAndroidPushHandler() {
	@Override
	public void done(boolean success, String e) {
		if (e == null) {
			Log.i("xs", "ANDROID PUSH SENT!");

     	// error
     	} else { Log.i("xs", e); }
}});

// Send iOS Push Notification
XSSendiOSPush((Activity)ctx, pushMessage, userPointer.key("ST_iosDeviceToken").stringValue(), "chat", new XSiOSPushHandler() {
	@Override
   public void done(boolean success, String e) {
     	if (e == null) {
     		Log.i("xs", "iOS PUSH SENT");

     	// error
     	} else { Log.i("xs", e); }
}});

Web SDK

This section covers the foundamentals of the Web SDK functions to interact with your database on XServer.

Download Web SDK



The SDK code is mainly written in Javascript, with the aid of jQuery, so you would need to use the $('#yourDivID').append() function to display data in your website.

Installation

Follow these simple steps to integrate the Android XServer SDK in your existing Xcoder project:

1. Unzip the downloaded Web SDK, then upload the js folder and the XServerSDK.php file in the root of your website (usually the public_html directory.

If you already have a js folder in the root of your server, then just upload the jquery-3.4.1.min.js and swal2.js files inside your js folder, so you will not overwrite your existing files.

2. In the index.php file of your website, make sure to import those 2 files inside the <head> tag:

<script src="js/jquery-3.4.1.min.js"></script>
<script src="js/swal2.js"></script>

3. You're ready to go.

Current User XSCurrentUser

You can get the Current logged in User by calling the XSCurrentUser function.
<script>
	var currentUser = XSCurrentUser();
	if (currentUser != null) {
		console.log(currentUser['ID_id']);
	} else {
   	console.log('currentUser is not logged in...');
	}
</script>

Sign In XSSignIn

To sign in as an existing user you have to call the XSSignIn() function and pass the usename and password parameters, usually via HTML inputs with the id and name tags set as 'username' and 'password'.
The Sign in button gets called by a JQuery function like this: $(function () { $("#loginButton").click(function () ... });.
<!-- Sing in form -->
<input id="username" name="username" type="text" placeholder="Username">
<input id="password" name="password" type="password" placeholder="Password">

<!-- Sign In Button -->
<button id="loginButton" name="loginButton">Sign In</button>
<script>
	$(function () { $("#loginButton").click(function () {
		XSSignIn();
	}); 
});
</script>

Sign Up XSSignUp

To sign up as a new user you have to call the XSSignUp() function and pass usename, password and email parameters, usually via HTML inputs with id and name tags set as 'usename', 'password', 'email'.
For additional parameters - for example, fullname, file, etc. - you must add them into the XSSignUp() function in the XServerSDK.php file, like this example:
var p2 = 
  	'tableName=Users' +
  	'&ID_id=' + cuArr[0]

  	// Additional data (based on your database structure)
	+ '&ST_fullname=' + $('#fullName').val()
	+ '&FL_file=' + 'https://xserver.app/assets/img/default_avatar.png'
;

XSObject(p2);

As you can see by the code above, the function's parameters needs to get the Users table, the ID of the signed up User and then the additional data (the extra columns you may have added to the Users table).
The Sign up button gets called by the JQuery function: $(function () { $("#signUpButton").click(function () { ... });

<!-- Inputs to pass data -->
<input id="username" name="username" type="text">
<input id="password" name="password" type="password">
<input id="email" name="email" type="email">
<!-- Additional data -->
<input id="fullName" name="fullName" type="text">

<!-- Sign up button -->
<button id="signUpButton">Sign Up</button>
<script>
	// Sign Up
	$(function () { $("#signUpButton").click(function () {
		if ($('#username').val() == '' 
			|| $('#password').val() == ''
         || $('#email').val() == ''
         || $('#fullName').val() == ''
     		){ console.log('Please fill all the fields.');

     	} else {
     		var params = 
	         'signInWith=' + '' +
	         '&ST_username=' + $('#username').val() +
	         '&ST_password=' + $('#password').val() +
	         '&ST_email=' + $('#email').val() +
	         '&ST_iosDeviceToken=' +
	         '&ST_androidDeviceToken=';
	      XSSignUp(params);
     	}// ./ If
 	}); 
});

Reset Password XSResetPassword

The XSResetPassword() function fires an alert by SweetAlert with a text input where you have to type your valid email address and click the Reset Password button.
Here's how you call that function:
<a class="btn btn-warning" href="#" onclick="XSResetPassword()">Reset password</a>
Check the XSResetPassword() function in the XServerSDK.php file to edit the alert messages as you wish.


Query objects XSQuery

You can query objects by calling the XSQuery function and passing it the tableName, columnName and orderBy parameters.
Then, inside a for loop based on the length of an array of JSON objects, set an if statement to filter your query by the desired keys (columns) and values.
<script>
	function queryObjects() {
		var objects = XSQuery(
	     'tableName=' + 'Posts'  // Set the Table name
	     + '&columnName=' + 'DT_createdAt'  // Get the createdAt Date column
	     + '&orderBy=' + 'descending'  // OR 'ascending' 
   	);

   	// array of objects
   	var objectsArray = [];
   	for (var i = 0; i < objects.length; i++) {
   		// JSON Obj
   		var obj = objects[i];

   		// Query filters
   		if(
	   		obj['ST_category'] == 'Fun'
	   		|| obj['NU_likes'] == 1234
   		){ objectsArray.push(obj); }

   		// Finalize array of objects by removing duplicates (if any)
   		if (i == objects.length-1) { 
   			objectsArray = XSRemoveDuplicatesFromArray(objectsArray);
   			showData(objectsArray);
   		}
   	}// ./ For
	}

	// Show Data
	function showData(objectsArray) {
  		for(var i = 0; i < objectsArray.length; i++){
    		// JSON object
    		var obj = objectsArray[i];
    		
    		console.log(obj['ID_id']);
    		...
  		}
	}
</script>

Refresh object data XSRefreshObjectData

In order to get updated data of an object, call the XSRefreshObjectData function and pass the tableName and the object's ID parameters.
<script>
	var refreshedObject = XSRefreshObjectData("Users", "as34FiPrT");
 	console.log(refreshedObject['ST_username']);
 	...
</script>

Get Pointer object XSGetPointer

You can get the object's data of a Pointer object by calling the XSGetPointer() function and passing it the columnName and tableName parameters.
<script>
	var userPointer = XSGetPointer(obj['PO_userPointer_Users'], 'Users');
	console.log(userPointer['ST_username']);
</script>

Add/Edit an object XSObject

In order to save an Object you need to call the XSObject() function and pass it the necessary parameters, usually via HTML inputs with the id and name tags set as the column names that need to be saved.
<textarea type="text" id="ST_text" name="ST_text" rows="2" ></textarea>
<input type="number" step="any" id="NU_likes" name="NU_likes" value="0">
...

<!-- Save Object button -->
<a href="#" id="saveButton" class="btn btn-info btn-block">Save Object</a>

<script>
	$(function () {
		// Save object [XSObject]
		$("#saveButton").click(function () {

     	// Prepare data
     	var params = 
     		'tableName=' + 'Posts' +
       	'&ID_id=' 	 + 'asSe45Fru' +  // USE THIS TO UPDATE AN OBJECT - REMOVE THIS LINE TO SAVE A NEW OBJECT
       	'&ST_text='  + $('#ST_text').val() +
       	'&NU_likes=' + $('#NU_likes').val()
      ;

      // Save
      var result = XSObject(params);
      if (result.includes('ID_id')) {
      	console.log('Object successfully saved/updated');
      }
});
</script>

Delete an object XSDelete

In order to delete an object you need to call the XSDelete() function and pass the tableName and id parameters.
<script>
	var params = 
 		'tableName=' + 'Posts' +
     	'&id=' + 'nh6Ft8Lit'
   ;
   var result = XSDelete(params);
   if (result == 'deleted') {
   	console.log('Object successfully deleted!');
	}
</script>

Send Push Notifications XSSendPushNotification

In order to send Push Notifications to iOS and Android devices, call the XSSendPushNotification() function and pass the tokentString (made by the iOS Token + the Android token), the message and the piuhType parameters.
<script>
  var pushMessage = 'Hi there, this is a Push message!';
  var tokensStr = receiverUser['ST_iosDeviceToken'] + ',' + receiverUser['ST_androidDeviceToken'];
  XSSendPushNotification(tokensStr, pushMessage, 'chat');
</script>