Now this React Native application is built with the help of expo as you know. Now as I mentioned at the
beginning of the course, this still is a regular React Native application, therefore expo
just as an extra wrapper which provides a lot of convenience features out of the box that makes building
this app easier
and it's like a shell, an app in which our app runs which therefore makes tapping into a lot of native device
functionalities easier and especially easier to set up.
Now as I also mentioned at the beginning of this module, in a separate module I will show you how you
can add native device features to non-expo apps
but actually, if you don't have a reason for not using expo, I would strongly recommend that you do use
it because expo if you check their docs, has a lot of built-in features and a lot of built-in native
modules you can use,
so native device features you can easily add
and you see a full list here by going to the docs and there to the API reference.
Here, you see a bunch of things you can add to your app and these are mostly native device functionalities
which are all provided by expo out of the box,
so you'll still need to install something but you'll need to do no manual configuration
and as you'll see in the building an app without expo module, there is quite some configuration you might
need to do if you're adding native modules, native device feature packages to a non-expo app,
so this is really sweet here.
Now I want to use a camera and indeed there is a camera package which you can add.
We could actually use that to access the device camera,
this is perfect
if you need to build an application that really needs to do a lot with the camera,
if you're building like Instagram, like an application where you really want to control the entire
camera screen and the entire camera flow, it's actually not what I want to do here.
I want to use the regular device camera, I
don't need anything special there,
I just care about getting an image which the user can take
and for that we actually get the image picker. This also allows us to use the device camera but it doesn't
allow us to customize the camera that much
but that's exactly what I need here.
Now all we need to do to get access to it is run this expo install command, which by the way is like
npm install, just that it makes sure that it installs a version of the package that is guaranteed to
work with our specific version of expo we're using in this app. So I'll just copy this command
and then in this project, we can run expo install expo image picker. Hit enter and this will now install
this package with npm install behind the scenes, this is what it does here,
just in a version that fits our expo SDK where we're using in this project and this will now install
it and that's it,
no extra configuration is needed.
We don't need to touch any config file,
we don't need to do anything else,
this make sure that we can use this functionality
and that's of course very easy.
With that we can start using it and I want to start using it here on the new place screen where we have
text input right, there
I also want to now add a button which the user can press to open up the camera.
Now of course for all these native device features and also the many features we of course can't cover
in this course, you always learned how you may use them here in your official expo docs, there you learned
which methods and properties you can access there, you find some short examples for many packages,
so that's definitely something you should check out. For the camera, of course you can also follow along
because we'll use it together here to take a picture.
I'll actually create a new component, ImageSelector.js, you can name it whatever you want in
which I import React from React and where I import a view and a button and also a text from React Native
and also the stylesheet of course because I want to style my image picker.
Let's actually name it image picker, I like that, my image picker component.
So here, image picker is then a regular component of course where I want to set up some styles with
the Stylesheet.create method and where I in the end export the image picker component.
Now what's going to happen here in the image picker component? In the end, in here
I'll return a view which should then hold another view where I can display a preview of the image.
So in there, I want to have the text component where I say no image picked yet which is my fallback content
if no image was picked yet,
otherwise I'll display an image,
so we'll also need to import the image component from React Native and below that view,
I'll add a button with a title of take image where I want to set the color to Colors.primary, so for
that, make sure you import that colors constant and upon a press, I want to open up the camera and
display it to the user. So I'll also add a constant here, take image handler and that's a function which
should open up the camera
and that's the function I'll bind to this button.
Now some styling would be nice. On that outer view, I'll add a style of image picker, on this inner view
which displays a preview of my image,
I'll add a style of image preview. Then this text here could also be styled but I actually need no special
style there and of course, I also want to output an image here and I'll soon add an if/else condition
to only display either the text or the image
and this should also get some style of maybe just image.
So now we have three style identifiers which we need to add to the stylesheet,
we get the image picker for the overall component,
get the image preview and then we got the image itself.
The image itself is simple, there I just want to set a width of 100% and a height of 100%
so that it takes the full width and height of the surrounding preview container.
That container then can of course be configured however you want to configure it,
I'll give it a width of 100% and a height of 200 pixels
but of course you can change that or calculate it dynamically with the dimensions API. Add a margin to
the bottom of 10 and make sure that my placeholder text would actually be centered by adding justify
content center and align items center here
and besides that, I also will add a border here with border color light gray color and border width of
one.
Now for the image picker component in general, I'll just set this to align items center to
make sure that all the items are centered horizontally
but now we're all here to really open the device camera, right?
So before we do anything else here regarding the preview and so on, let's make sure that here in the
take image handler, we do actually open up the camera and for that, let's import everything as image picker
from expo image picker, so from this package you just installed. Now
since I used image picker here and also for the component, we'll have a name clash, so I'll name this img
picker here just to avoid this name clash which would otherwise cause problems,
also change it here in the export of course.
So now with the image picker imported here, we can use it here,
image picker
and there we can call launch camera async.
You can also open the gallery instead if you wanted to
but here, I'll call launch camera async.
Now this will open up the device camera and the async part here kind of implies that this is an async
operation.
Indeed this does return a promise, which makes sense because it opens the camera and we don't know when
the user will be done taking the image,
so it will then just register a function which it should execute once the user is done and resolve the
promise to execute that function once that happens, once the user is done or also of course
once the user cancels. We can handle all of that and the result and so on later, for now let's see whether
that works and for that, let's include the image picker component in the new place screen.
So there, we can import image picker and here of course we can use that name because we're not using
the image picker package in here, import that from components, from the image picker component and add
this image picker here below our text input as a self-closing component like this.
Now if we save that, let's have a look. Here,
that text and so on, that's not positioned properly, we don't need to worry about that right now, let's instead
press take image and what you'll see is that nothing happens but then I get a warning - missing camera
or camera roll permission. On Android if I try it, there I'm asked whether I want to grant permissions. I need
to press allow here and with that, the camera does open. So that's good,
it generally works,
I can use it, I can press this button therefore and then confirm the image I took
but on iOS it does not work and why is that the case? Well on iOS, we got a permission error here
right
and that simply happens because permissions are important but they work differently on iOS and Android.
On Android
you'll also set up permissions in advance and since we're using this expo application where expo provides
this wrapper, expo actually does all these permissions set up and requesting for us. For iOS, the permission
system works a bit differently.
You don't really set it up such that you have a config file where you define all permissions in advance
and that's it,
instead you have to ask for permission at runtime.
Side note by the way in some versions of Android, you also now should do that.
Still there expo as you see does it for you, so for Android, it kind of works like that, on iOS it doesn't,
you need to ask the user for a permission to access the camera and that's simply something we need to
do here and we can do it with the help of another expo package
and that is the permissions package.
You also need to install that with this command here into your project so that you can ask the user
for permissions.
So let's do this here in the project,
let's run expo install expo permissions which again is just this wrapper around npm install to install
the permissions for this project or to install this package for this project and then in the image
picker before we try launching the camera and opening the camera, we'll need to ask for permissions. For
that I'll actually add a new constant here, get or verify permissions
and this is a function, that holds a function which will use this newly added permissions package.
So here I import everything as permissions from expo permissions,
so the same kind of import as we have it for the image picker
and then there, we can use permissions and ask asynchronously.
Again, that's an async task which returns a promise because this will open up a prompt and before the
user has chosen an answer, nothing will happen,
so therefore we have a promise which resolves or is rejected once the user confirmed or declined.
So here we then need to be more specific about which permission we need though,
so we pass something to ask async and that's a constant we get from the permissions object we're importing
here.
So with the dot notation here, we can access different kinds of permissions and here all these capitalized
names are the different permissions you can ask for
and here we need the camera permission because we want to access the camera.
If you wanted to access the gallery, that would be the camera roll permission by the way.
Now I'll go for camera here
and as I said, this returns a promise, I want to use async await here
hence I can add async here in front of the function and then await this and get the result and store it
in a constant,
the alternative to that would be using then and catch on this
and there we now can check if result.status is not equal to granted which means the user declined,
the user did not grant permissions,
in that case we can't continue.
So here I'll actually throw an alert then, so import alert from React Native and throw an alert where
I say alert.alert, insufficient permissions, you need to grant camera app permissions to use this
app or any output you want to show
and then I'll add one button here where I say OK. Now the user will need to change the permissions in
the system settings after declining them anyways, so we can't ask for permissions again here once they
were declined.
So here, I want to return false because it's the verify permissions function and I want to return
false if the user kind of didn't grant us permissions,
I return true otherwise because now we know we have permissions
By the way if we call this function multiple times and the user did already grant permissions in the
past, the user will not be presented with this prompt again, the same by the way if
the user declined, in both cases the result is stored automatically by iOS and this function will just return
true or false depending on whether the user denied or granted access in the past.
So verify permissions is now simply a function which we need to call in the take
image handler before we try to use the camera.
So here, I also want to use async await because verify permissions of course is a function that returns
a promise,
so I will await this and store the result which I'll store in a constant named has permission
because this is either true or false depending on whether the user granted permissions or not.
Now if this is false, so if it's not true, I'll just return here,
I won't continue, I won't open the camera because I'll not be allowed to do that anyways, so we just can't
continue,
that's the thing.
Otherwise we can continue and now we should be able to launch a camera on iOS as well.
So let's give it a try,
let's save that
and now with that changed, I'll actually uninstall expo here to make sure that regarding the permissions
I granted there, it's back to the initial state.
You probably don't need to do that if you never granted any permissions in your expo app
but if you played around with permissions and native packages before, you should uninstall and then reinstall
the expo app by again running your app on iOS by pressing i in the console down there to make sure
that all these permissions are reset.
So now if we go to the new play screen and click take image, I'm asked whether I want to grant
access to the camera and here I can click OK and now we still get that error.
Now the only reason for that is that even though we're trying to access the camera here, this actually
and that's just something I know for this package requires permissions for the full camera roll.
So let's change this to ask for permissions to the camera roll here and
with that, go back and click take image,
now I'm asking if I want to allow access to photos and now if I click yes, I get camera not
available on the simulator which now is a different error and is pretty clear regarding what's the issue,
the iOS simulator simply doesn't have a camera. So I will continue on Android from now on but of course
you can test this on a real iPhone to see that there,
it also does work. If you there scan your expo barcode with the expo app and run the app there as you
saw it at the beginning of the course,
you can take images with a real phone and I'll also demonstrate the finished app at the end of this module
on both iOS and Android devices, on real devices.
So for the moment, I'll focus on Android and there of course, we were already able to open this.
I'm again asked here but now I can open this and take an image. That's of course nice but taking the image
like this is not everything I want to do,
I also want to able to configure how I take it and I want to be able to use it.