1 00:00:02,100 --> 00:00:07,110 Now this React Native application is built with the help of expo as you know. Now as I mentioned at the 2 00:00:07,110 --> 00:00:12,690 beginning of the course, this still is a regular React Native application, therefore expo 3 00:00:12,690 --> 00:00:19,170 just as an extra wrapper which provides a lot of convenience features out of the box that makes building 4 00:00:19,170 --> 00:00:20,820 this app easier 5 00:00:20,820 --> 00:00:27,770 and it's like a shell, an app in which our app runs which therefore makes tapping into a lot of native device 6 00:00:27,780 --> 00:00:31,860 functionalities easier and especially easier to set up. 7 00:00:31,860 --> 00:00:37,380 Now as I also mentioned at the beginning of this module, in a separate module I will show you how you 8 00:00:37,380 --> 00:00:40,590 can add native device features to non-expo apps 9 00:00:40,890 --> 00:00:46,170 but actually, if you don't have a reason for not using expo, I would strongly recommend that you do use 10 00:00:46,170 --> 00:00:53,400 it because expo if you check their docs, has a lot of built-in features and a lot of built-in native 11 00:00:53,400 --> 00:00:54,570 modules you can use, 12 00:00:54,570 --> 00:00:56,460 so native device features you can easily add 13 00:00:57,090 --> 00:01:01,620 and you see a full list here by going to the docs and there to the API reference. 14 00:01:01,620 --> 00:01:07,650 Here, you see a bunch of things you can add to your app and these are mostly native device functionalities 15 00:01:08,070 --> 00:01:11,220 which are all provided by expo out of the box, 16 00:01:11,340 --> 00:01:16,770 so you'll still need to install something but you'll need to do no manual configuration 17 00:01:16,770 --> 00:01:23,700 and as you'll see in the building an app without expo module, there is quite some configuration you might 18 00:01:23,790 --> 00:01:30,270 need to do if you're adding native modules, native device feature packages to a non-expo app, 19 00:01:30,270 --> 00:01:32,560 so this is really sweet here. 20 00:01:32,650 --> 00:01:38,360 Now I want to use a camera and indeed there is a camera package which you can add. 21 00:01:38,610 --> 00:01:42,510 We could actually use that to access the device camera, 22 00:01:42,510 --> 00:01:43,620 this is perfect 23 00:01:43,620 --> 00:01:49,620 if you need to build an application that really needs to do a lot with the camera, 24 00:01:49,620 --> 00:01:55,440 if you're building like Instagram, like an application where you really want to control the entire 25 00:01:55,440 --> 00:02:00,760 camera screen and the entire camera flow, it's actually not what I want to do here. 26 00:02:00,780 --> 00:02:02,960 I want to use the regular device camera, I 27 00:02:02,970 --> 00:02:04,660 don't need anything special there, 28 00:02:04,740 --> 00:02:08,150 I just care about getting an image which the user can take 29 00:02:08,230 --> 00:02:13,620 and for that we actually get the image picker. This also allows us to use the device camera but it doesn't 30 00:02:13,620 --> 00:02:16,200 allow us to customize the camera that much 31 00:02:16,200 --> 00:02:18,450 but that's exactly what I need here. 32 00:02:18,450 --> 00:02:25,050 Now all we need to do to get access to it is run this expo install command, which by the way is like 33 00:02:25,170 --> 00:02:30,750 npm install, just that it makes sure that it installs a version of the package that is guaranteed to 34 00:02:30,750 --> 00:02:38,070 work with our specific version of expo we're using in this app. So I'll just copy this command 35 00:02:38,090 --> 00:02:45,530 and then in this project, we can run expo install expo image picker. Hit enter and this will now install 36 00:02:45,530 --> 00:02:49,580 this package with npm install behind the scenes, this is what it does here, 37 00:02:49,610 --> 00:02:55,880 just in a version that fits our expo SDK where we're using in this project and this will now install 38 00:02:55,880 --> 00:02:58,190 it and that's it, 39 00:02:58,190 --> 00:03:00,410 no extra configuration is needed. 40 00:03:00,410 --> 00:03:02,800 We don't need to touch any config file, 41 00:03:02,810 --> 00:03:04,570 we don't need to do anything else, 42 00:03:04,640 --> 00:03:07,970 this make sure that we can use this functionality 43 00:03:07,970 --> 00:03:09,690 and that's of course very easy. 44 00:03:09,770 --> 00:03:15,280 With that we can start using it and I want to start using it here on the new place screen where we have 45 00:03:15,350 --> 00:03:17,270 text input right, there 46 00:03:17,270 --> 00:03:22,480 I also want to now add a button which the user can press to open up the camera. 47 00:03:22,490 --> 00:03:27,680 Now of course for all these native device features and also the many features we of course can't cover 48 00:03:27,680 --> 00:03:32,660 in this course, you always learned how you may use them here in your official expo docs, there you learned 49 00:03:32,660 --> 00:03:38,260 which methods and properties you can access there, you find some short examples for many packages, 50 00:03:38,300 --> 00:03:43,220 so that's definitely something you should check out. For the camera, of course you can also follow along 51 00:03:43,220 --> 00:03:46,920 because we'll use it together here to take a picture. 52 00:03:46,970 --> 00:03:54,830 I'll actually create a new component, ImageSelector.js, you can name it whatever you want in 53 00:03:54,830 --> 00:04:05,810 which I import React from React and where I import a view and a button and also a text from React Native 54 00:04:06,290 --> 00:04:11,710 and also the stylesheet of course because I want to style my image picker. 55 00:04:11,870 --> 00:04:16,130 Let's actually name it image picker, I like that, my image picker component. 56 00:04:16,790 --> 00:04:23,570 So here, image picker is then a regular component of course where I want to set up some styles with 57 00:04:23,580 --> 00:04:31,290 the Stylesheet.create method and where I in the end export the image picker component. 58 00:04:31,310 --> 00:04:37,510 Now what's going to happen here in the image picker component? In the end, in here 59 00:04:37,780 --> 00:04:44,750 I'll return a view which should then hold another view where I can display a preview of the image. 60 00:04:44,770 --> 00:04:50,500 So in there, I want to have the text component where I say no image picked yet which is my fallback content 61 00:04:50,530 --> 00:04:52,840 if no image was picked yet, 62 00:04:52,930 --> 00:04:54,920 otherwise I'll display an image, 63 00:04:54,940 --> 00:04:59,700 so we'll also need to import the image component from React Native and below that view, 64 00:04:59,710 --> 00:05:09,180 I'll add a button with a title of take image where I want to set the color to Colors.primary, so for 65 00:05:09,190 --> 00:05:17,410 that, make sure you import that colors constant and upon a press, I want to open up the camera and 66 00:05:17,500 --> 00:05:27,530 display it to the user. So I'll also add a constant here, take image handler and that's a function which 67 00:05:27,530 --> 00:05:29,480 should open up the camera 68 00:05:29,480 --> 00:05:33,140 and that's the function I'll bind to this button. 69 00:05:33,170 --> 00:05:42,470 Now some styling would be nice. On that outer view, I'll add a style of image picker, on this inner view 70 00:05:42,800 --> 00:05:45,860 which displays a preview of my image, 71 00:05:45,860 --> 00:05:55,020 I'll add a style of image preview. Then this text here could also be styled but I actually need no special 72 00:05:55,020 --> 00:06:02,910 style there and of course, I also want to output an image here and I'll soon add an if/else condition 73 00:06:02,910 --> 00:06:05,120 to only display either the text or the image 74 00:06:05,270 --> 00:06:09,030 and this should also get some style of maybe just image. 75 00:06:09,030 --> 00:06:13,620 So now we have three style identifiers which we need to add to the stylesheet, 76 00:06:13,890 --> 00:06:17,440 we get the image picker for the overall component, 77 00:06:17,610 --> 00:06:22,440 get the image preview and then we got the image itself. 78 00:06:22,440 --> 00:06:28,560 The image itself is simple, there I just want to set a width of 100% and a height of 100% 79 00:06:28,560 --> 00:06:32,670 so that it takes the full width and height of the surrounding preview container. 80 00:06:32,670 --> 00:06:36,750 That container then can of course be configured however you want to configure it, 81 00:06:36,870 --> 00:06:41,550 I'll give it a width of 100% and a height of 200 pixels 82 00:06:41,550 --> 00:06:47,880 but of course you can change that or calculate it dynamically with the dimensions API. Add a margin to 83 00:06:47,880 --> 00:06:55,400 the bottom of 10 and make sure that my placeholder text would actually be centered by adding justify 84 00:06:55,400 --> 00:06:59,470 content center and align items center here 85 00:06:59,730 --> 00:07:08,460 and besides that, I also will add a border here with border color light gray color and border width of 86 00:07:08,460 --> 00:07:09,500 one. 87 00:07:09,720 --> 00:07:15,270 Now for the image picker component in general, I'll just set this to align items center to 88 00:07:15,270 --> 00:07:19,020 make sure that all the items are centered horizontally 89 00:07:19,080 --> 00:07:22,910 but now we're all here to really open the device camera, right? 90 00:07:23,010 --> 00:07:28,680 So before we do anything else here regarding the preview and so on, let's make sure that here in the 91 00:07:28,700 --> 00:07:40,720 take image handler, we do actually open up the camera and for that, let's import everything as image picker 92 00:07:41,500 --> 00:07:48,460 from expo image picker, so from this package you just installed. Now 93 00:07:48,490 --> 00:07:54,360 since I used image picker here and also for the component, we'll have a name clash, so I'll name this img 94 00:07:54,390 --> 00:07:59,860 picker here just to avoid this name clash which would otherwise cause problems, 95 00:07:59,920 --> 00:08:02,910 also change it here in the export of course. 96 00:08:02,920 --> 00:08:06,610 So now with the image picker imported here, we can use it here, 97 00:08:06,610 --> 00:08:07,700 image picker 98 00:08:07,840 --> 00:08:10,930 and there we can call launch camera async. 99 00:08:10,960 --> 00:08:13,810 You can also open the gallery instead if you wanted to 100 00:08:13,810 --> 00:08:18,000 but here, I'll call launch camera async. 101 00:08:18,140 --> 00:08:25,070 Now this will open up the device camera and the async part here kind of implies that this is an async 102 00:08:25,070 --> 00:08:26,120 operation. 103 00:08:26,120 --> 00:08:31,550 Indeed this does return a promise, which makes sense because it opens the camera and we don't know when 104 00:08:31,550 --> 00:08:33,740 the user will be done taking the image, 105 00:08:33,740 --> 00:08:39,920 so it will then just register a function which it should execute once the user is done and resolve the 106 00:08:39,920 --> 00:08:44,930 promise to execute that function once that happens, once the user is done or also of course 107 00:08:44,930 --> 00:08:51,400 once the user cancels. We can handle all of that and the result and so on later, for now let's see whether 108 00:08:51,400 --> 00:08:57,450 that works and for that, let's include the image picker component in the new place screen. 109 00:08:57,490 --> 00:09:03,670 So there, we can import image picker and here of course we can use that name because we're not using 110 00:09:03,670 --> 00:09:10,960 the image picker package in here, import that from components, from the image picker component and add 111 00:09:10,990 --> 00:09:17,780 this image picker here below our text input as a self-closing component like this. 112 00:09:17,800 --> 00:09:22,300 Now if we save that, let's have a look. Here, 113 00:09:22,320 --> 00:09:27,720 that text and so on, that's not positioned properly, we don't need to worry about that right now, let's instead 114 00:09:27,720 --> 00:09:35,930 press take image and what you'll see is that nothing happens but then I get a warning - missing camera 115 00:09:35,960 --> 00:09:45,360 or camera roll permission. On Android if I try it, there I'm asked whether I want to grant permissions. I need 116 00:09:45,360 --> 00:09:51,240 to press allow here and with that, the camera does open. So that's good, 117 00:09:51,240 --> 00:09:52,450 it generally works, 118 00:09:52,470 --> 00:09:56,790 I can use it, I can press this button therefore and then confirm the image I took 119 00:09:57,090 --> 00:10:04,440 but on iOS it does not work and why is that the case? Well on iOS, we got a permission error here 120 00:10:04,480 --> 00:10:05,260 right 121 00:10:05,320 --> 00:10:10,810 and that simply happens because permissions are important but they work differently on iOS and Android. 122 00:10:11,500 --> 00:10:12,100 On Android 123 00:10:12,100 --> 00:10:18,600 you'll also set up permissions in advance and since we're using this expo application where expo provides 124 00:10:18,600 --> 00:10:26,440 this wrapper, expo actually does all these permissions set up and requesting for us. For iOS, the permission 125 00:10:26,440 --> 00:10:28,310 system works a bit differently. 126 00:10:28,360 --> 00:10:34,390 You don't really set it up such that you have a config file where you define all permissions in advance 127 00:10:34,390 --> 00:10:34,900 and that's it, 128 00:10:34,900 --> 00:10:38,540 instead you have to ask for permission at runtime. 129 00:10:38,590 --> 00:10:43,040 Side note by the way in some versions of Android, you also now should do that. 130 00:10:43,210 --> 00:10:49,600 Still there expo as you see does it for you, so for Android, it kind of works like that, on iOS it doesn't, 131 00:10:49,600 --> 00:10:56,050 you need to ask the user for a permission to access the camera and that's simply something we need to 132 00:10:56,050 --> 00:10:59,890 do here and we can do it with the help of another expo package 133 00:10:59,890 --> 00:11:02,680 and that is the permissions package. 134 00:11:02,710 --> 00:11:08,890 You also need to install that with this command here into your project so that you can ask the user 135 00:11:08,890 --> 00:11:10,230 for permissions. 136 00:11:10,420 --> 00:11:12,640 So let's do this here in the project, 137 00:11:12,640 --> 00:11:19,570 let's run expo install expo permissions which again is just this wrapper around npm install to install 138 00:11:19,570 --> 00:11:26,050 the permissions for this project or to install this package for this project and then in the image 139 00:11:26,050 --> 00:11:33,580 picker before we try launching the camera and opening the camera, we'll need to ask for permissions. For 140 00:11:33,580 --> 00:11:41,260 that I'll actually add a new constant here, get or verify permissions 141 00:11:41,430 --> 00:11:47,310 and this is a function, that holds a function which will use this newly added permissions package. 142 00:11:47,310 --> 00:11:52,050 So here I import everything as permissions from expo permissions, 143 00:11:52,050 --> 00:11:55,280 so the same kind of import as we have it for the image picker 144 00:11:55,590 --> 00:12:00,420 and then there, we can use permissions and ask asynchronously. 145 00:12:00,420 --> 00:12:06,270 Again, that's an async task which returns a promise because this will open up a prompt and before the 146 00:12:06,270 --> 00:12:08,810 user has chosen an answer, nothing will happen, 147 00:12:08,820 --> 00:12:14,760 so therefore we have a promise which resolves or is rejected once the user confirmed or declined. 148 00:12:16,560 --> 00:12:20,090 So here we then need to be more specific about which permission we need though, 149 00:12:20,100 --> 00:12:26,940 so we pass something to ask async and that's a constant we get from the permissions object we're importing 150 00:12:26,940 --> 00:12:27,270 here. 151 00:12:27,660 --> 00:12:33,630 So with the dot notation here, we can access different kinds of permissions and here all these capitalized 152 00:12:33,690 --> 00:12:36,660 names are the different permissions you can ask for 153 00:12:36,660 --> 00:12:41,660 and here we need the camera permission because we want to access the camera. 154 00:12:41,710 --> 00:12:46,340 If you wanted to access the gallery, that would be the camera roll permission by the way. 155 00:12:47,850 --> 00:12:49,850 Now I'll go for camera here 156 00:12:50,700 --> 00:12:54,390 and as I said, this returns a promise, I want to use async await here 157 00:12:54,420 --> 00:13:00,410 hence I can add async here in front of the function and then await this and get the result and store it 158 00:13:00,440 --> 00:13:01,500 in a constant, 159 00:13:01,500 --> 00:13:06,470 the alternative to that would be using then and catch on this 160 00:13:06,780 --> 00:13:14,010 and there we now can check if result.status is not equal to granted which means the user declined, 161 00:13:14,010 --> 00:13:16,210 the user did not grant permissions, 162 00:13:16,290 --> 00:13:18,770 in that case we can't continue. 163 00:13:18,780 --> 00:13:25,590 So here I'll actually throw an alert then, so import alert from React Native and throw an alert where 164 00:13:25,590 --> 00:13:40,420 I say alert.alert, insufficient permissions, you need to grant camera app permissions to use this 165 00:13:40,480 --> 00:13:42,890 app or any output you want to show 166 00:13:43,000 --> 00:13:49,690 and then I'll add one button here where I say OK. Now the user will need to change the permissions in 167 00:13:49,690 --> 00:13:55,000 the system settings after declining them anyways, so we can't ask for permissions again here once they 168 00:13:55,000 --> 00:13:56,950 were declined. 169 00:13:56,950 --> 00:14:02,950 So here, I want to return false because it's the verify permissions function and I want to return 170 00:14:02,950 --> 00:14:06,790 false if the user kind of didn't grant us permissions, 171 00:14:06,790 --> 00:14:10,080 I return true otherwise because now we know we have permissions 172 00:14:10,090 --> 00:14:14,920 By the way if we call this function multiple times and the user did already grant permissions in the 173 00:14:14,920 --> 00:14:20,050 past, the user will not be presented with this prompt again, the same by the way if 174 00:14:20,050 --> 00:14:27,880 the user declined, in both cases the result is stored automatically by iOS and this function will just return 175 00:14:27,880 --> 00:14:33,400 true or false depending on whether the user denied or granted access in the past. 176 00:14:33,400 --> 00:14:37,350 So verify permissions is now simply a function which we need to call in the take 177 00:14:37,350 --> 00:14:40,590 image handler before we try to use the camera. 178 00:14:40,780 --> 00:14:46,600 So here, I also want to use async await because verify permissions of course is a function that returns 179 00:14:46,660 --> 00:14:47,600 a promise, 180 00:14:47,770 --> 00:14:55,080 so I will await this and store the result which I'll store in a constant named has permission 181 00:14:56,100 --> 00:15:01,020 because this is either true or false depending on whether the user granted permissions or not. 182 00:15:01,020 --> 00:15:05,580 Now if this is false, so if it's not true, I'll just return here, 183 00:15:05,580 --> 00:15:11,100 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 184 00:15:11,100 --> 00:15:11,580 continue, 185 00:15:11,580 --> 00:15:12,360 that's the thing. 186 00:15:13,500 --> 00:15:17,910 Otherwise we can continue and now we should be able to launch a camera on iOS as well. 187 00:15:17,940 --> 00:15:18,810 So let's give it a try, 188 00:15:18,810 --> 00:15:19,720 let's save that 189 00:15:19,830 --> 00:15:25,750 and now with that changed, I'll actually uninstall expo here to make sure that regarding the permissions 190 00:15:25,750 --> 00:15:29,530 I granted there, it's back to the initial state. 191 00:15:29,640 --> 00:15:33,860 You probably don't need to do that if you never granted any permissions in your expo app 192 00:15:33,900 --> 00:15:39,810 but if you played around with permissions and native packages before, you should uninstall and then reinstall 193 00:15:39,810 --> 00:15:44,870 the expo app by again running your app on iOS by pressing i in the console down there to make sure 194 00:15:44,890 --> 00:15:47,910 that all these permissions are reset. 195 00:15:47,910 --> 00:15:53,220 So now if we go to the new play screen and click take image, I'm asked whether I want to grant 196 00:15:53,490 --> 00:16:00,120 access to the camera and here I can click OK and now we still get that error. 197 00:16:01,490 --> 00:16:07,420 Now the only reason for that is that even though we're trying to access the camera here, this actually 198 00:16:07,420 --> 00:16:12,450 and that's just something I know for this package requires permissions for the full camera roll. 199 00:16:12,450 --> 00:16:16,020 So let's change this to ask for permissions to the camera roll here and 200 00:16:22,830 --> 00:16:25,880 with that, go back and click take image, 201 00:16:25,880 --> 00:16:32,550 now I'm asking if I want to allow access to photos and now if I click yes, I get camera not 202 00:16:32,560 --> 00:16:37,710 available on the simulator which now is a different error and is pretty clear regarding what's the issue, 203 00:16:37,780 --> 00:16:43,510 the iOS simulator simply doesn't have a camera. So I will continue on Android from now on but of course 204 00:16:43,510 --> 00:16:47,380 you can test this on a real iPhone to see that there, 205 00:16:47,380 --> 00:16:54,250 it also does work. If you there scan your expo barcode with the expo app and run the app there as you 206 00:16:54,250 --> 00:16:56,000 saw it at the beginning of the course, 207 00:16:56,050 --> 00:17:02,020 you can take images with a real phone and I'll also demonstrate the finished app at the end of this module 208 00:17:02,200 --> 00:17:05,750 on both iOS and Android devices, on real devices. 209 00:17:05,890 --> 00:17:10,600 So for the moment, I'll focus on Android and there of course, we were already able to open this. 210 00:17:10,690 --> 00:17:18,530 I'm again asked here but now I can open this and take an image. That's of course nice but taking the image 211 00:17:18,530 --> 00:17:20,490 like this is not everything I want to do, 212 00:17:20,570 --> 00:17:24,590 I also want to able to configure how I take it and I want to be able to use it.