In this tutorial, you'll see Firebase integration within a native context, basically over an iOS and Android application. You will also implement some of the basic, as well as advanced features, that are found in any modern mobile application in both, Android and iOS ecosystems. So let's get busy!
This article is an excerpt taken from the book,' Firebase Cookbook', written by Houssem Yahiaoui.
We're going to start first with Android and see how we can manage this feature:
Now we've created this simple wish list application. It might not be the most visually pleasing but will serve us well in this experiment with TextEdit, a Button, and a ListView.
//[*] UI reference. EditText wishListText; Button addToWishList; ListView wishListview;
// [*] Getting a reference to the Database Root.
DatabaseReference fRootRef =
FirebaseDatabase.getInstance().getReference();
//[*] Getting a reference to the wishes list.
DatabaseReference wishesRef =
fRootRef.child("wishes");
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//[*] UI elements
wishListText = (EditText)
findViewById(R.id.wishListText);
addToWishList = (Button)
findViewById(R.id.addWishBtn);
wishListview = (ListView)
findViewById(R.id.wishsList);
}
@Override
protected void onStart() {
super.onStart();
//[*] Listening on Button click event
addToWishList.setOnClickListener(new
View.OnClickListener() {
@Override
public void onClick(View v) {
//[*] Getting the text from our EditText UI Element.
String wish =
wishListText.getText().toString();
//[*] Pushing the Data to our Database.
wishesRef.push().setValue(wish);
AlertDialog alertDialog = new
AlertDialog.Builder(MainActivity.this).create();
alertDialog.setTitle("Success");
alertDialog.setMessage("wish was added to Firebase");
alertDialog.show();
}
});
}
In the preceding code, we're doing the following:
wishesRef.addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String s) { //[*] Grabbing the data Snapshot String newWish = dataSnapshot.getValue(String.class); wishes.add(newWish); adapter.notifyDataSetChanged(); } @Override public void onChildChanged(DataSnapshot dataSnapshot, String s) {} @Override public void onChildRemoved(DataSnapshot dataSnapshot) {} @Override public void onChildMoved(DataSnapshot dataSnapshot, String s) {} @Override public void onCancelled(DatabaseError databaseError) {} });
//[*] Adding an adapter. adapter = new ArrayAdapter<String>(this, R.layout.support_simple_spinner_dropdown_item, wishes); //[*] Wiring the Adapter wishListview.setAdapter(adapter);
ArrayList<String> wishes = new ArrayList<String>(); ArrayAdapter<String> adapter;
So, in the preceding code, we're doing the following:
Congratulations! You've just wired and exploited the Real-time Database functionality and created your very own wishes tracker.
Now, let's see how we can create our very own iOS wishes tracker application using nothing but Swift and Firebase:
pod 'Firebase/Database'
This will download and install the Firebase Database dependencies locally, in your very own awesome wishes tracker application. There are two view controllers, one for the wishes table and the other one for adding a new wish to the wishes list, the following represents the main wishes list view.
Once we click on the + sign button in the Header, we'll be navigated with a segueway to a new ViewModal, where we have a text field where we can add our new wish and a button to push it to our list.
import UIKit import FirebaseDatabase class newWishViewController: UIViewController { @IBOutlet weak var wishText: UITextField
//[*] Adding the Firebase Database Reference var ref: FIRDatabaseReference? override func viewDidLoad() { super.viewDidLoad() ref = FIRDatabase.database().reference() } @IBAction func addNewWish(_ sender: Any) { let newWish = wishText.text // [*] Getting the UITextField content. self.ref?.child("wishes").childByAutoId().setValue( newWish!) presentedViewController?.dismiss(animated: true, completion:nil) } }
In the preceding code, besides the self-explanatory UI element code, we're doing the following:
Authentication is one of the most tricky, time-consuming and tedious tasks in any web application. and of course, maintaining the best practices while doing so is truly a hard job to maintain. For mobiles, it's even more complex, because if you're using any traditional application it will mean that you're going to create a REST endpoint, an endpoint that will take an email and password and return either a session or a token, or directly a user's profile information. In Firebase, things are a bit different and in this recipe, we're going to see how we can use anonymous authentication—we will explain that in a second.
You might wonder, but why? The why is quite simple: to give users an anonymous temporal, to protect data and to give users an extra taste of your application's inner soul. So let's see how we can make that happen.
We will first see how we can implement anonymous authentication in Android:
compile 'com.google.firebase:firebase-auth:11.0.2'
A simple UI with a button and a TextView, where we put our user data after a successful authentication process.
Here's the code for that simple UI:
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/ apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.hcodex.anonlogin.MainActivity">
<Button
android:id="@+id/anonLoginBtn"
android:layout_width="289dp"
android:layout_height="50dp"
android:text="Anounymous Login"
android:layout_marginRight="8dp"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginLeft="8dp"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginTop="47dp"
android:onClick="anonLoginBtn"
app:layout_constraintTop_toBottomOf=
"@+id/textView2"
app:layout_constraintHorizontal_bias="0.506"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Firebase Anonymous Login"
android:layout_marginLeft="8dp"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginRight="8dp"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="80dp" />
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Profile Data"
android:layout_marginTop="64dp"
app:layout_constraintTop_toBottomOf=
"@+id/anonLoginBtn"
android:layout_marginLeft="156dp"
app:layout_constraintLeft_toLeftOf="parent" />
<TextView
android:id="@+id/profileData"
android:layout_width="349dp"
android:layout_height="175dp"
android:layout_marginBottom="28dp"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text=""
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.526"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf=
"@+id/textView3" />
</android.support.constraint.ConstraintLayout>
//[*] Step 1 : Defining Logic variables. FirebaseAuth anonAuth; FirebaseAuth.AuthStateListener authStateListener; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); anonAuth = FirebaseAuth.getInstance(); setContentView(R.layout.activity_main); };
//[*] Step 2: Listening on the
Login button click event.
public void anonLoginBtn(View view) {
anonAuth.signInAnonymously()
.addOnCompleteListener(
this, new OnCompleteListener<AuthResult>() {
@Override public void onComplete(@NonNull
Task<AuthResult> task) {
if(!task.isSuccessful()) {
updateUI(null);
} else {
FirebaseUser fUser =
anonAuth.getCurrentUser();
Log.d("FIRE", fUser.getUid());
updateUI(fUser);
}
});
}
}
//[*] Step 3 : Getting UI Reference
private void updateUI(FirebaseUser user) {
profileData = (TextView) findViewById(
R.id.profileData);
profileData.append("Anonymous Profile Id : n" +
user.getUid());
}
Now, let's see how we can implement anonymous authentication on iOS:
What we'll achieve in this test is the following :
pod 'Firebase/Auth'
~> pod install
This will download the needed dependency and configure our application accordingly.
@IBAction func connectAnon(_ sender: Any) { Auth.auth().signInAnonymously() { (user, error) in if let anon = user?.isAnonymous { print("i'm connected anonymously here's my id (user?.uid)") } } }
Let's digest the preceding code:
Pretty simple steps. Now simply build and run your project and test your shiny new features.
Email and password authentication is the most common way to authenticate anyone and it can be a major risk point if done wrong. Using Firebase will remove that risk and make you think of nothing but the UX that you will eventually provide to your users. In this recipe, we're going to see how you can do this on iOS.
import UIKit import Firebase import FirebaseAuth class EmailLoginViewController: UIViewController { @IBOutlet weak var emailField: UITextField! @IBOutlet weak var passwordField: UITextField! override func viewDidLoad() { super.viewDidLoad() } @IBAction func loginEmail(_ sender: Any) { if self.emailField.text</span> == "" || self.passwordField.text == "" { //[*] Prompt an Error let alertController = UIAlertController(title: "Error", message: "Please enter an email and password.", preferredStyle: .alert) let defaultAction = UIAlertAction(title: "OK", style: .cancel, handler: nil) alertController.addAction(defaultAction) self.present(alertController, animated: true, completion: nil) } else { FIRAuth.auth()?.signIn(withEmail: self.emailField.text!, password: self.passwordField.text!) { (user, error) in if error == nil { //[*] TODO: Navigate to Application Home Page. } else { //[*] Alert in case we've an error. let alertController = UIAlertController(title: "Error", message: error?.localizedDescription, preferredStyle: .alert) let defaultAction = UIAlertAction(title: "OK", style: .cancel, handler: nil) alertController.addAction(defaultAction) self.present(alertController, animated: true, completion: nil) } } } } }
Let's digest the preceding code:
And as simple as that, we're done. The User object will be transported, as well, so you may do any additional processing to the name, email, and much more.
To make things easier in terms of Android, we're going to use the awesome Firebase Auth UI. Using the Firebase Auth UI will save a lot of hassle when it comes to building the actual user interface and handling the different intent calls between the application activities. Let's see how we can integrate and use it for our needs.
Let's start first by configuring our project and downloading all the necessary dependencies. Head to your build.gradle file and copy/paste the following entry:
compile 'com.firebaseui:firebase-ui-auth:3.0.0'
Now, simply sync and you will be good to start.
Now, let's see how we can make the functionality work:
FirebaseAuth auth; private static final int RC_SIGN_IN = 17;
auth = FirebaseAuth.getInstance(); if(auth.getCurrentUser() != null) { Log.d("Auth", "Logged in successfully"); } else { startActivityForResult( AuthUI.getInstance() .createSignInIntentBuilder() .setAvailableProviders( Arrays.asList(new AuthUI.IdpConfig.Builder( AuthUI.EMAIL_PROVIDER).build())).build(), RC_SIGN_IN);findViewById(R.id.logoutBtn) .setOnClickListener(this);
public class MainActivity extends AppCompatActivity implements View.OnClickListener {}
@Override public void onClick(View v) { if(v.getId() == R.id.logoutBtn) { AuthUI.getInstance().signOut(this) .addOnCompleteListener( new OnCompleteListener<Void>() { @Override public void onComplete(@NonNull Task<Void> task) { Log.d("Auth", "Logged out successfully"); // TODO: make custom operation. } }); } }
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if(requestCode == RC_SIGN_IN) { if(resultCode == RESULT_OK) { //User is in ! Log.d("Auth",auth.getCurrentUser().getEmail()); } else { //User is not authenticated Log.d("Auth", "Not Authenticated"); } } }
This interface will be shown in case you're not authenticated and your application will list all the saved accounts on your device. If you click on the NONE OF THE ABOVE button, you will be prompted with the following interface:
From the preceding code, it's clear that we didn't create any user interface. The Firebase UI is so powerful, so let's explore what happens:
Google authentication is the process of logging in/creating an account using nothing but your existing Google account. It's easy, fast, and intuitive and removes a lot of hustle we face, usually when we register any web/mobile application. I'm talking basically about form filling. Using Firebase Google Sign-in authentication, we can manage such functionality; plus we have had the user basic metadata such as the display name, picture URL, and more. In this recipe, we're going to see how we can implement Google Sign-in functionality for both Android and iOS.
Before doing any coding, it's important to do some basic configuration in our Firebase Project console.
Head directly to your Firebase project Console | Authentication | SIGN-IN METHOD | Google and simply activate the switch and follow the instructions there in order to get the client. Please notice that Google Sign-in is automatically configured for iOS, but for Android, we will need to do some custom configuration.
Let us first look at getting ready for Android to implement Google Sign-in authentication:
compile 'com.google.firebase:firebase-auth:11.4.2' compile 'com.google.android.gms:play-services- auth:11.4.2'
Moving on to getting ready in iOS for implementation of Google Sign-in authentication:
pod 'Firebase/Auth' pod 'GoogleSignIn'
~> pod install
This command will install the required dependencies and configure your project accordingly.
First, let us take a look at how we will implement this recipe in Android:
<com.google.android.gms.common.SignInButton android:id="@+id/gbtn" android:layout_width="368dp" android:layout_height="wrap_content" android:layout_marginLeft="16dp" android:layout_marginTop="30dp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toTopOf="parent" android:layout_marginRight="16dp" app:layout_constraintRight_toRightOf="parent" />
The result will be this:
SignInButton gBtn; FirebaseAuth mAuth; GoogleApiClient mGoogleApiClient; private final static int RC_SIGN_IN = 3; FirebaseAuth.AuthStateListener mAuthListener;
@Override
protected void onStart() {
super.onStart();
mAuth.addAuthStateListener(mAuthListener);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mAuth = FirebaseAuth.getInstance();
gBtn = (SignInButton) findViewById(R.id.gbtn);
button.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v) {
signIn();
}
});
mAuthListener = new FirebaseAuth.AuthStateListener()
{
@Override
public void onAuthStateChanged(@NonNull
FirebaseAuth firebaseAuth) {
if(firebaseAuth.getCurrentUser() != null) {
AlertDialog alertDialog = new
AlertDialog.Builder(MainActivity.this).create();
alertDialog.setTitle("User");
alertDialog.setMessage("I have a user loged
in");
alertDialog.show();
}
}
};
mGoogleApiClient = new GoogleApiClient.Builder(this)
.enableAutoManage(this, new
GoogleApiClient.OnConnectionFailedListener() {
@Override
public void onConnectionFailed(@NonNull
ConnectionResult connectionResult) {
Toast.makeText(MainActivity.this, "Something
went wrong", Toast.LENGTH_SHORT).show();
}
})
.addApi(Auth.GOOGLE_SIGN_IN_API, gso)
.build();
}
GoogleSignInOptions gso = new
GoogleSignInOptions.Builder(
GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail()
.build();
private void signIn() {
Intent signInIntent =
Auth.GoogleSignInApi.getSignInIntent(
mGoogleApiClient);
startActivityForResult(signInIntent, RC_SIGN_IN);
}
@Override
public void onActivityResult(int requestCode, int
resultCode, Intent data) {
super.onActivityResult(requestCode,
resultCode, data);
if (requestCode == RC_SIGN_IN) {
GoogleSignInResult result = Auth.GoogleSignInApi
.getSignInResultFromIntent(data);
if (result.isSuccess()) {
// Google Sign In was successful,
authenticate with Firebase
GoogleSignInAccount account =
result.getSignInAccount();
firebaseAuthWithGoogle(account);
} else {
Toast.makeText(MainActivity.this,
"Connection Error", Toast.LENGTH_SHORT).show();
}
}
}
private void firebaseAuthWithGoogle(
GoogleSignInAccount account) {
AuthCredential credential =
GoogleAuthProvider.getCredential(
account.getIdToken(), null);
mAuth.signInWithCredential(credential)
.addOnCompleteListener(this, new
OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull
Task<AuthResult> task) {
if (task.isSuccessful()) {
// Sign in success, update UI with the
signed-in user's information
Log.d("TAG",
"signInWithCredential:success");
FirebaseUser user =
mAuth.getCurrentUser();
Log.d("TAG", user.getDisplayName());
} else {
Log.w("TAG",
"signInWithCredential:failure",
task.getException());
Toast.makeText(MainActivity.this,
"Authentication failed.", Toast.LENGTH_SHORT)
.show();
}
// ...
}
});
}
Now we will take a look at an implementation of our recipe in iOS:
import GoogleSignIn
//Google sign in let googleBtn = GIDSignInButton() googleBtn.frame =CGRect(x: 16, y: 50, width: view.frame.width - 32, height: 50) view.addSubview(googleBtn) GIDSignIn.sharedInstance().uiDelegate = self
class ViewController: UIViewController, FBSDKLoginButtonDelegate, GIDSignInUIDelegate {}
Now, if you build and run your project, you will get the following:
import GoogleSignIn
GIDSignIn.sharedInstance().clientID = FirebaseApp.app()?.options.clientID
GIDSignIn.sharedInstance().handle(url, sourceApplication:options[ UIApplicationOpenURLOptionsKey.sourceApplication] as? String, annotation: options[UIApplicationOpenURLOptionsKey.annotation])
This will simply help the transition to the URL we already specified within the URL schemes.
With that, the ongoing authentication process is done, but what will happen when you select your account and authorize the application? Typically, you need to go back to your application with all the needed profile information, don't you? isn't? Well, for now, it's not the case, so let's fix that.
GIDSignIn.sharedInstance().delegate = self
This will simply let us go back to the application with all the tokens we need to finish the authentication process with Firebase.
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) { if let err = error { print("Can't connect to Google") return } print("we're using google sign in", user) }
Now, once you're fully authenticated, you will receive the success message over your terminal.
import FirebaseAuth
guard let authentication = user.authentication else { return } let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken, accessToken: authentication.accessToken)
Auth.auth().signIn(with: credential, completion: {(user, error) in if let error = error { print("[*] Can't connect to firebase, with error :", error) } print("we have a user", user?.displayName) })
This code will use the successfully logged in user token and call the Firebase Authentication logic to create a new Firebase user. Now we can retrieve the basic profile information that Firebase delivers.
Let's explain what we did in the Android section:
Let's now explain what we just did in the iOS section:
To summarize, we learned how to integrate Firebase within a native context, basically over an iOS and Android application. If you've enjoyed reading this, do check,'Firebase Cookbook' for recipes to help you understand features of Firebase and implement them in your existing web or mobile applications.
Using the Firebase Real-Time Database
How to integrate Firebase with NativeScript for cross-platform app development
Build powerful progressive web apps with Firebase